3
3
import functools
4
4
import warnings
5
5
from copy import deepcopy
6
- from typing import Any , Dict , List
6
+ from typing import Any , Callable , Dict , List , Optional , Tuple , TypeVar , Union
7
7
8
8
import numpy as np
9
9
import pkg_resources
21
21
22
22
CoordSpec = Dict [str , List [Any ]]
23
23
DimSpec = Dict [str , List [str ]]
24
+ RequiresArgTypeT = TypeVar ("RequiresArgTypeT" )
25
+ RequiresReturnTypeT = TypeVar ("RequiresReturnTypeT" )
24
26
25
27
26
28
class requires : # pylint: disable=invalid-name
@@ -32,19 +34,34 @@ class requires: # pylint: disable=invalid-name
32
34
missing. Both functionalities can be combined as desired.
33
35
"""
34
36
35
- def __init__ (self , * props ):
36
- self .props = props
37
-
38
- def __call__ (self , func ): # noqa: D202
37
+ def __init__ (self , * props : Union [str , List [str ]]) -> None :
38
+ self .props : Tuple [Union [str , List [str ]], ...] = props
39
+
40
+ # Until typing.ParamSpec (https://www.python.org/dev/peps/pep-0612/) is available
41
+ # in all our supported Python versions, there is no way to simultaneously express
42
+ # the following two properties:
43
+ # - the input function may take arbitrary args/kwargs, and
44
+ # - the output function takes those same arbitrary args/kwargs, but has a different return type.
45
+ # We either have to limit the input function to e.g. only allowing a "self" argument,
46
+ # or we have to adopt the current approach of annotating the returned function as if
47
+ # it was defined as "def f(*args: Any, **kwargs: Any) -> Optional[RequiresReturnTypeT]".
48
+ #
49
+ # Since all functions decorated with @requires currently only accept a single argument,
50
+ # we choose to limit application of @requires to only functions of one argument.
51
+ # When typing.ParamSpec is available, this definition can be updated to use it.
52
+ # See https://github.com/arviz-devs/arviz/pull/1504 for more discussion.
53
+ def __call__ (
54
+ self , func : Callable [[RequiresArgTypeT ], RequiresReturnTypeT ]
55
+ ) -> Callable [[RequiresArgTypeT ], Optional [RequiresReturnTypeT ]]: # noqa: D202
39
56
"""Wrap the decorated function."""
40
57
41
- def wrapped (cls , * args , ** kwargs ) :
58
+ def wrapped (cls : RequiresArgTypeT ) -> Optional [ RequiresReturnTypeT ] :
42
59
"""Return None if not all props are available."""
43
60
for prop in self .props :
44
61
prop = [prop ] if isinstance (prop , str ) else prop
45
62
if all ([getattr (cls , prop_i ) is None for prop_i in prop ]):
46
63
return None
47
- return func (cls , * args , ** kwargs )
64
+ return func (cls )
48
65
49
66
return wrapped
50
67
0 commit comments