Skip to content

Commit 3050afe

Browse files
author
Sylvain MARIE
committed
Improved type hints for decorators so that they do not make the decorated item loose its type hints. Fixed #56
1 parent 637a1a0 commit 3050afe

2 files changed

Lines changed: 48 additions & 38 deletions

File tree

valid8/entry_points_annotations.py

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
try: # python 3.5+
77
# noinspection PyUnresolvedReferences
8-
from typing import Callable, Any, List, Union
8+
from typing import Callable, Any, List, Union, TypeVar
99
try: # python 3.5.3-
1010
# noinspection PyUnresolvedReferences
1111
from typing import Type
@@ -14,6 +14,10 @@
1414
else:
1515
# noinspection PyUnresolvedReferences
1616
from valid8.composition import ValidationFuncs
17+
18+
DecoratedClass = TypeVar("DecoratedClass", bound=Type[Any])
19+
DecoratedFunc = TypeVar("DecoratedFunc", bound=Callable)
20+
1721
use_typing = sys.version_info > (3, 0)
1822
except ImportError:
1923
use_typing = False
@@ -311,11 +315,11 @@ def get_validated_class_display_name(self):
311315

312316

313317
@class_decorator(flat_mode_decorated_name='cls')
314-
def validate_field(cls,
318+
def validate_field(cls, # type: DecoratedClass
315319
field_name,
316320
*validation_func, # type: ValidationFuncs
317321
**kwargs):
318-
# type: (...) -> Callable
322+
# type: (...) -> DecoratedClass
319323
"""
320324
A class decorator. It goes through all class variables and for all of those that are descriptors with a __set__,
321325
it wraps the descriptors' setter function with a `validate_arg` annotation
@@ -340,11 +344,12 @@ def validate_field(cls,
340344

341345

342346
@function_decorator
343-
def validate_io(f=DECORATED,
347+
def validate_io(f=DECORATED, # type: DecoratedFunc
344348
none_policy=None, # type: int
345349
_out_=None, # type: ValidationFuncs
346350
**kw_validation_funcs # type: ValidationFuncs
347351
):
352+
# type: (...) -> DecoratedFunc
348353
"""
349354
A function decorator to add input validation prior to the function execution. It should be called with named
350355
arguments: for each function arg name, provide a single validation function or a list of validation functions to
@@ -395,12 +400,12 @@ def myfunc(a, b):
395400

396401

397402
@function_decorator(flat_mode_decorated_name='f')
398-
def validate_arg(f,
403+
def validate_arg(f, # type: DecoratedFunc
399404
arg_name,
400405
*validation_func, # type: ValidationFuncs
401406
**kwargs
402407
):
403-
# type: (...) -> Callable
408+
# type: (...) -> DecoratedFunc
404409
"""
405410
A decorator to apply function input validation for the given argument name, with the provided base validation
406411
function(s). You may use several such decorators on a given function as long as they are stacked on top of each
@@ -428,7 +433,7 @@ def validate_arg(f,
428433

429434
def validate_out(*validation_func, # type: ValidationFuncs
430435
**kwargs):
431-
# type: (...) -> Callable
436+
# type: (...) -> Callable[[DecoratedFunc], DecoratedFunc]
432437
"""
433438
A decorator to apply function output validation to this function's output, with the provided base validation
434439
function(s). You may use several such decorators on a given function as long as they are stacked on top of each
@@ -459,11 +464,11 @@ def decorate(f):
459464
""" The reserved key for output validation """
460465

461466

462-
def decorate_cls_with_validation(cls,
467+
def decorate_cls_with_validation(cls, # type: DecoratedClass
463468
field_name, # type: str
464469
*validation_func, # type: ValidationFuncs
465470
**kwargs):
466-
# type: (...) -> Type[Any]
471+
# type: (...) -> DecoratedClass
467472
"""
468473
This method is equivalent to decorating a class with the `@validate_field` decorator but can be used a posteriori.
469474
@@ -603,12 +608,12 @@ def decorate_cls_with_validation(cls,
603608
return cls
604609

605610

606-
def decorate_several_with_validation(func,
611+
def decorate_several_with_validation(func, # type: DecoratedFunc
607612
_out_=None, # type: ValidationFuncs
608613
none_policy=None, # type: int
609614
**validation_funcs # type: ValidationFuncs
610615
):
611-
# type: (...) -> Callable
616+
# type: (...) -> DecoratedFunc
612617
"""
613618
This method is equivalent to applying `decorate_with_validation` once for each of the provided arguments of
614619
the function `func` as well as output `_out_`. validation_funcs keyword arguments are validation functions for each
@@ -636,11 +641,11 @@ def decorate_several_with_validation(func,
636641
return func
637642

638643

639-
def decorate_with_validation(func,
644+
def decorate_with_validation(func, # type: DecoratedFunc
640645
arg_name, # type: str
641646
*validation_func, # type: ValidationFuncs
642647
**kwargs):
643-
# type: (...) -> Callable
648+
# type: (...) -> DecoratedFunc
644649
"""
645650
This method is the inner method used in `@validate_io`, `@validate_arg` and `@validate_out`.
646651
It can be used if you with to perform decoration manually without a decorator.
@@ -697,7 +702,7 @@ def decorate_with_validation(func,
697702

698703
def _get_final_none_policy_for_validator(is_nonable, # type: bool
699704
none_policy # type: NoneArgPolicy
700-
):
705+
) -> NoneArgPolicy:
701706
"""
702707
Depending on none_policy and of the fact that the target parameter is nonable or not, returns a corresponding
703708
NonePolicy
@@ -731,7 +736,7 @@ def _create_function_validator(validated_func, # type: Callable
731736
arg_name, # type: str
732737
*validation_func, # type: ValidationFuncs
733738
**kwargs):
734-
739+
# type: (...) -> Union[ClassFieldValidator, InputValidator, OutputValidator]
735740
error_type, help_msg, none_policy, validated_class, validated_class_field_name = \
736741
pop_kwargs(kwargs, [('error_type', None), ('help_msg', None), ('none_policy', None),
737742
('validated_class', None), ('validated_class_field_name', None)], allow_others=True)
@@ -778,11 +783,11 @@ def _create_function_validator(validated_func, # type: Callable
778783
error_type=error_type, help_msg=help_msg, **kw_context_args)
779784

780785

781-
def decorate_with_validators(func, # type: Callable
786+
def decorate_with_validators(func, # type: DecoratedFunc
782787
func_signature=None, # type: Signature
783788
**validators # type: Union[Validator, List[Validator]]
784789
):
785-
# type: (...) -> Callable
790+
# type: (...) -> DecoratedFunc
786791
"""
787792
Utility method to decorate the provided function with the provided input and output Validator objects. Since this
788793
method takes Validator objects as argument, it is for advanced users.

valid8/entry_points_annotations.pyi

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Callable, List, Union, Type
1+
from typing import Callable, List, Union, Any, Type, TypeVar
22

33
try:
44
from inspect import signature, Signature
@@ -50,20 +50,25 @@ class ClassFieldValidator(Validator):
5050
...
5151

5252

53-
def validate_field(cls,
54-
field_name,
53+
DecoratedClass = TypeVar("DecoratedClass", bound=Type[Any])
54+
55+
56+
def validate_field(field_name,
5557
*validation_func: ValidationFuncs,
5658
help_msg: str = None,
5759
error_type: Type[InputValidationError] = None,
5860
none_policy: int = None,
59-
**kw_context_args) -> Callable[[Type], Type]:
61+
**kw_context_args) -> Callable[[DecoratedClass], DecoratedClass]:
6062
...
6163

6264

65+
DecoratedFunc = TypeVar("DecoratedFunc", bound=Callable)
66+
67+
6368
def validate_io(none_policy: int=None,
6469
_out_: ValidationFuncs=None,
6570
**kw_validation_funcs: ValidationFuncs
66-
) -> Callable[[Callable], Callable]:
71+
) -> Callable[[DecoratedFunc], DecoratedFunc]:
6772
...
6873

6974

@@ -72,44 +77,44 @@ def validate_arg(arg_name,
7277
help_msg: str = None,
7378
error_type: Type[InputValidationError] = None,
7479
none_policy: int = None,
75-
**kw_context_args) -> Callable[[Callable], Callable]:
80+
**kw_context_args) -> Callable[[DecoratedFunc], DecoratedFunc]:
7681
...
7782

7883

7984
def validate_out(*validation_func: ValidationFuncs,
8085
help_msg: str = None,
8186
error_type: Type[OutputValidationError] = None,
8287
none_policy: int = None,
83-
**kw_context_args) -> Callable[[Callable], Callable]:
88+
**kw_context_args) -> Callable[[DecoratedFunc], DecoratedFunc]:
8489
...
8590

8691

87-
def decorate_cls_with_validation(cls,
92+
def decorate_cls_with_validation(cls: DecoratedClass,
8893
field_name: str,
8994
*validation_func: ValidationFuncs,
9095
help_msg: str = None,
9196
error_type: 'Union[Type[InputValidationError], Type[OutputValidationError]]' = None,
9297
none_policy: int = None,
93-
**kw_context_args) -> Callable:
98+
**kw_context_args) -> DecoratedClass:
9499
...
95100

96101

97-
def decorate_several_with_validation(func,
102+
def decorate_several_with_validation(func: DecoratedFunc,
98103
_out_: ValidationFuncs = None,
99104
none_policy: int = None,
100105
**validation_funcs: ValidationFuncs
101-
) -> Callable:
106+
) -> DecoratedFunc:
102107
...
103108

104109

105-
def decorate_with_validation(func,
106-
arg_name: str,
107-
*validation_func: ValidationFuncs,
108-
help_msg: str = None,
109-
error_type: Union[Type[InputValidationError], Type[OutputValidationError]] = None,
110-
none_policy: int = None,
111-
_constructor_of_cls_: Type=None,
112-
**kw_context_args) -> Callable:
110+
def decorate_with_validation(func: DecoratedFunc,
111+
arg_name: str,
112+
*validation_func: ValidationFuncs,
113+
help_msg: str = None,
114+
error_type: Union[Type[InputValidationError], Type[OutputValidationError]] = None,
115+
none_policy: int = None,
116+
_constructor_of_cls_: Type=None,
117+
**kw_context_args) -> DecoratedFunc:
113118
...
114119

115120

@@ -130,8 +135,8 @@ def _create_function_validator(validated_func: Callable,
130135
...
131136

132137

133-
def decorate_with_validators(func: Callable,
138+
def decorate_with_validators(func: DecoratedFunc,
134139
func_signature: Signature = None,
135140
**validators: Union[Validator, List[Validator]]
136-
) -> Callable:
141+
) -> DecoratedFunc:
137142
...

0 commit comments

Comments
 (0)