diff --git a/setup.py b/setup.py index 3cec893..78ebfb7 100644 --- a/setup.py +++ b/setup.py @@ -137,9 +137,8 @@ # If there are data files included in your packages that need to be # installed, specify them here. If using Python 2.6 or less, then these # have to be included in MANIFEST.in as well. - # package_data={ - # 'sample': ['package_data.dat'], - # }, + # Note: we use the empty string so that this also works with submodules + package_data={"": ['py.typed', '*.pyi']}, # Although 'package_data' is the preferred approach, in some case you may # need to place data files outside of your packages. See: @@ -155,4 +154,10 @@ # 'sample=sample:main', # ], # }, + + # explicitly setting the flag to avoid `ply` being downloaded + # see https://github.com/smarie/python-getversion/pull/5 + # and to make mypy happy + # see https://mypy.readthedocs.io/en/latest/installed_packages.html + zip_safe=False, ) diff --git a/valid8/entry_points_annotations.py b/valid8/entry_points_annotations.py index 8cc1258..61e8331 100644 --- a/valid8/entry_points_annotations.py +++ b/valid8/entry_points_annotations.py @@ -5,7 +5,7 @@ try: # python 3.5+ # noinspection PyUnresolvedReferences - from typing import Callable, Any, List, Union + from typing import Callable, Any, List, Union, TypeVar try: # python 3.5.3- # noinspection PyUnresolvedReferences from typing import Type @@ -14,6 +14,10 @@ else: # noinspection PyUnresolvedReferences from valid8.composition import ValidationFuncs + + DecoratedClass = TypeVar("DecoratedClass", bound=Type[Any]) + DecoratedFunc = TypeVar("DecoratedFunc", bound=Callable) + use_typing = sys.version_info > (3, 0) except ImportError: use_typing = False @@ -23,7 +27,7 @@ except ImportError: from funcsigs import signature, Signature -from makefun import with_signature, wraps +from makefun import wraps from valid8.utils.decoration_tools import apply_on_each_func_args_sig from valid8.utils.typing_tools import is_pep484_nonable @@ -93,25 +97,11 @@ def get_variable_str(self): return self.validator.validated_field_name + '=' + str(self.var_value) -# Python 3+: load the 'more explicit api' -if use_typing: - new_sig = """(self, - validated_func: Callable, - *validation_func: ValidationFuncs, - error_type: 'Type[ValidationError]' = None, - help_msg: str = None, - none_policy: int = None, - **kw_context_args):""" -else: - new_sig = None - - class FuncValidator(Validator): """ Represents a special kind of `Validator` responsible to validate a function input or output """ - @with_signature(new_sig) def __init__(self, validated_func, # type: Callable *validation_func, # type: ValidationFuncs @@ -162,7 +152,6 @@ class InputValidator(FuncValidator): Represents a special kind of `Validator` responsible to validate a function input. """ - @with_signature(new_sig) def __init__(self, validated_func, # type: Callable, *validation_func, # type: ValidationFuncs @@ -203,7 +192,6 @@ def __init__(self, class OutputValidator(FuncValidator): """ Represents a special kind of `Validator` responsible to validate a function output. """ - @with_signature(new_sig) def __init__(self, validated_func, # type: Callable *validation_func, # type: ValidationFuncs @@ -260,27 +248,12 @@ def assert_valid(self, **kw_context_args) -# Python 3+: load the 'more explicit api' -if use_typing: - new_sig = """(self, - validated_class: Callable, - validated_field_name: str, - *validation_func: ValidationFuncs, - error_type: 'Type[ClassFieldValidationError]' = None, - help_msg: str = None, - none_policy: int = None, - **kw_context_args):""" -else: - new_sig = None - - class ClassFieldValidator(Validator): """ Represents a special kind of `Validator` responsible to validate a class field. As opposed to other validators, the name of the field is hardcoded. """ - @with_signature(new_sig) def __init__(self, validated_class, # type: Callable, validated_field_name, # type: str @@ -341,26 +314,12 @@ def get_validated_class_display_name(self): return self.validated_class.__name__ -# Python 3+: load the 'more explicit api' -if use_typing: - new_sig = """(cls, - field_name, - *validation_func: ValidationFuncs, - help_msg: str = None, - error_type: 'Type[InputValidationError]' = None, - none_policy: int = None, - **kw_context_args) -> 'Type':""" -else: - new_sig = None - - @class_decorator(flat_mode_decorated_name='cls') -@with_signature(new_sig) -def validate_field(cls, +def validate_field(cls, # type: DecoratedClass field_name, *validation_func, # type: ValidationFuncs **kwargs): - # type: (...) -> Callable + # type: (...) -> DecoratedClass """ A class decorator. It goes through all class variables and for all of those that are descriptors with a __set__, it wraps the descriptors' setter function with a `validate_arg` annotation @@ -385,11 +344,12 @@ def validate_field(cls, @function_decorator -def validate_io(f=DECORATED, +def validate_io(f=DECORATED, # type: DecoratedFunc none_policy=None, # type: int _out_=None, # type: ValidationFuncs **kw_validation_funcs # type: ValidationFuncs ): + # type: (...) -> DecoratedFunc """ A function decorator to add input validation prior to the function execution. It should be called with named arguments: for each function arg name, provide a single validation function or a list of validation functions to @@ -439,27 +399,13 @@ def myfunc(a, b): return decorate_several_with_validation(f, none_policy=none_policy, _out_=_out_, **kw_validation_funcs) -# Python 3+: load the 'more explicit api' -if use_typing: - new_sig = """(f, - arg_name, - *validation_func: ValidationFuncs, - help_msg: str = None, - error_type: 'Type[InputValidationError]' = None, - none_policy: int = None, - **kw_context_args) -> Callable:""" -else: - new_sig = None - - @function_decorator(flat_mode_decorated_name='f') -@with_signature(new_sig) -def validate_arg(f, +def validate_arg(f, # type: DecoratedFunc arg_name, *validation_func, # type: ValidationFuncs **kwargs ): - # type: (...) -> Callable + # type: (...) -> DecoratedFunc """ A decorator to apply function input validation for the given argument name, with the provided base validation function(s). You may use several such decorators on a given function as long as they are stacked on top of each @@ -485,21 +431,9 @@ def validate_arg(f, return decorate_with_validation(f, arg_name, *validation_func, **kwargs) -# Python 3+: load the 'more explicit api' -if use_typing: - new_sig = """(*validation_func: ValidationFuncs, - help_msg: str = None, - error_type: 'Type[OutputValidationError]' = None, - none_policy: int = None, - **kw_context_args) -> Callable:""" -else: - new_sig = None - - -@with_signature(new_sig) def validate_out(*validation_func, # type: ValidationFuncs **kwargs): - # type: (...) -> Callable + # type: (...) -> Callable[[DecoratedFunc], DecoratedFunc] """ A decorator to apply function output validation to this function's output, with the provided base validation function(s). You may use several such decorators on a given function as long as they are stacked on top of each @@ -530,25 +464,11 @@ def decorate(f): """ The reserved key for output validation """ -# Python 3+: load the 'more explicit api' -if use_typing: - new_sig = """(cls, - field_name: str, - *validation_func: ValidationFuncs, - help_msg: str = None, - error_type: 'Union[Type[InputValidationError], Type[OutputValidationError]]' = None, - none_policy: int = None, - **kw_context_args) -> Callable:""" -else: - new_sig = None - - -@with_signature(new_sig) -def decorate_cls_with_validation(cls, +def decorate_cls_with_validation(cls, # type: DecoratedClass field_name, # type: str *validation_func, # type: ValidationFuncs **kwargs): - # type: (...) -> Type[Any] + # type: (...) -> DecoratedClass """ This method is equivalent to decorating a class with the `@validate_field` decorator but can be used a posteriori. @@ -688,12 +608,12 @@ def decorate_cls_with_validation(cls, return cls -def decorate_several_with_validation(func, +def decorate_several_with_validation(func, # type: DecoratedFunc _out_=None, # type: ValidationFuncs none_policy=None, # type: int **validation_funcs # type: ValidationFuncs ): - # type: (...) -> Callable + # type: (...) -> DecoratedFunc """ This method is equivalent to applying `decorate_with_validation` once for each of the provided arguments of the function `func` as well as output `_out_`. validation_funcs keyword arguments are validation functions for each @@ -721,26 +641,11 @@ def decorate_several_with_validation(func, return func -# Python 3+: load the 'more explicit api' -if use_typing: - new_sig = """(func, - arg_name: str, - *validation_func: ValidationFuncs, - help_msg: str = None, - error_type: 'Union[Type[InputValidationError], Type[OutputValidationError]]' = None, - none_policy: int = None, - _constructor_of_cls_: 'Type'=None, - **kw_context_args) -> Callable:""" -else: - new_sig = None - - -@with_signature(new_sig) -def decorate_with_validation(func, +def decorate_with_validation(func, # type: DecoratedFunc arg_name, # type: str *validation_func, # type: ValidationFuncs **kwargs): - # type: (...) -> Callable + # type: (...) -> DecoratedFunc """ This method is the inner method used in `@validate_io`, `@validate_arg` and `@validate_out`. It can be used if you with to perform decoration manually without a decorator. @@ -798,6 +703,7 @@ def decorate_with_validation(func, def _get_final_none_policy_for_validator(is_nonable, # type: bool none_policy # type: NoneArgPolicy ): + # type: (...) -> NoneArgPolicy """ Depending on none_policy and of the fact that the target parameter is nonable or not, returns a corresponding NonePolicy @@ -826,29 +732,12 @@ class fields)""" pass -# Python 3+: load the 'more explicit api' -if use_typing: - new_sig = """(validated_func: Callable, - s: Signature, - arg_name: str, - *validation_func: ValidationFuncs, - help_msg: str = None, - error_type: 'Type[InputValidationError]' = None, - none_policy: int = None, - validated_class: 'Type'=None, - validated_class_field_name: str=None, - **kw_context_args):""" -else: - new_sig = None - - -@with_signature(new_sig) def _create_function_validator(validated_func, # type: Callable s, # type: Signature arg_name, # type: str *validation_func, # type: ValidationFuncs **kwargs): - + # type: (...) -> Union[ClassFieldValidator, InputValidator, OutputValidator] error_type, help_msg, none_policy, validated_class, validated_class_field_name = \ pop_kwargs(kwargs, [('error_type', None), ('help_msg', None), ('none_policy', None), ('validated_class', None), ('validated_class_field_name', None)], allow_others=True) @@ -895,10 +784,11 @@ def _create_function_validator(validated_func, # type: Callable error_type=error_type, help_msg=help_msg, **kw_context_args) -def decorate_with_validators(func, +def decorate_with_validators(func, # type: DecoratedFunc func_signature=None, # type: Signature **validators # type: Union[Validator, List[Validator]] ): + # type: (...) -> DecoratedFunc """ Utility method to decorate the provided function with the provided input and output Validator objects. Since this method takes Validator objects as argument, it is for advanced users. diff --git a/valid8/entry_points_annotations.pyi b/valid8/entry_points_annotations.pyi new file mode 100644 index 0000000..6482b57 --- /dev/null +++ b/valid8/entry_points_annotations.pyi @@ -0,0 +1,142 @@ +from typing import Callable, List, Union, Any, Type, TypeVar + +try: + from inspect import signature, Signature +except ImportError: + from funcsigs import signature, Signature + +from valid8.composition import ValidationFuncs +from valid8.entry_points import ValidationError, Validator + + +class InputValidationError(ValidationError): + ... + + +class OutputValidationError(ValidationError): + ... + + +class ClassFieldValidationError(ValidationError): + ... + + +class FuncValidator(Validator): + def __init__(self, + validated_func: Callable, + *validation_func: ValidationFuncs, + error_type: Type[ValidationError] = None, + help_msg: str = None, + none_policy: int = None, + **kw_context_args): + ... + + def get_validated_func_display_name(self): + ... + + +class InputValidator(FuncValidator): + ... + + +class OutputValidator(FuncValidator): + ... + + +class ClassFieldValidator(Validator): + ... + + def get_validated_class_display_name(self): + ... + + +DecoratedClass = TypeVar("DecoratedClass", bound=Type[Any]) + + +def validate_field(field_name, + *validation_func: ValidationFuncs, + help_msg: str = None, + error_type: Type[InputValidationError] = None, + none_policy: int = None, + **kw_context_args) -> Callable[[DecoratedClass], DecoratedClass]: + ... + + +DecoratedFunc = TypeVar("DecoratedFunc", bound=Callable) + + +def validate_io(none_policy: int=None, + _out_: ValidationFuncs=None, + **kw_validation_funcs: ValidationFuncs + ) -> Callable[[DecoratedFunc], DecoratedFunc]: + ... + + +def validate_arg(arg_name, + *validation_func: ValidationFuncs, + help_msg: str = None, + error_type: Type[InputValidationError] = None, + none_policy: int = None, + **kw_context_args) -> Callable[[DecoratedFunc], DecoratedFunc]: + ... + + +def validate_out(*validation_func: ValidationFuncs, + help_msg: str = None, + error_type: Type[OutputValidationError] = None, + none_policy: int = None, + **kw_context_args) -> Callable[[DecoratedFunc], DecoratedFunc]: + ... + + +def decorate_cls_with_validation(cls: DecoratedClass, + field_name: str, + *validation_func: ValidationFuncs, + help_msg: str = None, + error_type: 'Union[Type[InputValidationError], Type[OutputValidationError]]' = None, + none_policy: int = None, + **kw_context_args) -> DecoratedClass: + ... + + +def decorate_several_with_validation(func: DecoratedFunc, + _out_: ValidationFuncs = None, + none_policy: int = None, + **validation_funcs: ValidationFuncs + ) -> DecoratedFunc: + ... + + +def decorate_with_validation(func: DecoratedFunc, + arg_name: str, + *validation_func: ValidationFuncs, + help_msg: str = None, + error_type: Union[Type[InputValidationError], Type[OutputValidationError]] = None, + none_policy: int = None, + _constructor_of_cls_: Type=None, + **kw_context_args) -> DecoratedFunc: + ... + + +class InvalidNameError(ValueError): + ... + + +def _create_function_validator(validated_func: Callable, + s: Signature, + arg_name: str, + *validation_func: ValidationFuncs, + help_msg: str = None, + error_type: Type[InputValidationError] = None, + none_policy: int = None, + validated_class: Type=None, + validated_class_field_name: str=None, + **kw_context_args) -> Union[ClassFieldValidator, InputValidator, OutputValidator]: + ... + + +def decorate_with_validators(func: DecoratedFunc, + func_signature: Signature = None, + **validators: Union[Validator, List[Validator]] + ) -> DecoratedFunc: + ... diff --git a/valid8/py.typed b/valid8/py.typed new file mode 100644 index 0000000..e69de29