metaflow 2.15.5__py2.py3-none-any.whl → 2.15.6__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- metaflow/_vendor/typeguard/_checkers.py +259 -95
- metaflow/_vendor/typeguard/_config.py +4 -4
- metaflow/_vendor/typeguard/_decorators.py +8 -12
- metaflow/_vendor/typeguard/_functions.py +33 -32
- metaflow/_vendor/typeguard/_pytest_plugin.py +40 -13
- metaflow/_vendor/typeguard/_suppression.py +3 -5
- metaflow/_vendor/typeguard/_transformer.py +84 -48
- metaflow/_vendor/typeguard/_union_transformer.py +1 -0
- metaflow/_vendor/typeguard/_utils.py +13 -9
- metaflow/_vendor/typing_extensions.py +1088 -500
- metaflow/_vendor/v3_7/__init__.py +1 -0
- metaflow/_vendor/v3_7/importlib_metadata/__init__.py +1063 -0
- metaflow/_vendor/v3_7/importlib_metadata/_adapters.py +68 -0
- metaflow/_vendor/v3_7/importlib_metadata/_collections.py +30 -0
- metaflow/_vendor/v3_7/importlib_metadata/_compat.py +71 -0
- metaflow/_vendor/v3_7/importlib_metadata/_functools.py +104 -0
- metaflow/_vendor/v3_7/importlib_metadata/_itertools.py +73 -0
- metaflow/_vendor/v3_7/importlib_metadata/_meta.py +48 -0
- metaflow/_vendor/v3_7/importlib_metadata/_text.py +99 -0
- metaflow/_vendor/v3_7/importlib_metadata/py.typed +0 -0
- metaflow/_vendor/v3_7/typeguard/__init__.py +48 -0
- metaflow/_vendor/v3_7/typeguard/_checkers.py +906 -0
- metaflow/_vendor/v3_7/typeguard/_config.py +108 -0
- metaflow/_vendor/v3_7/typeguard/_decorators.py +237 -0
- metaflow/_vendor/v3_7/typeguard/_exceptions.py +42 -0
- metaflow/_vendor/v3_7/typeguard/_functions.py +310 -0
- metaflow/_vendor/v3_7/typeguard/_importhook.py +213 -0
- metaflow/_vendor/v3_7/typeguard/_memo.py +48 -0
- metaflow/_vendor/v3_7/typeguard/_pytest_plugin.py +100 -0
- metaflow/_vendor/v3_7/typeguard/_suppression.py +88 -0
- metaflow/_vendor/v3_7/typeguard/_transformer.py +1207 -0
- metaflow/_vendor/v3_7/typeguard/_union_transformer.py +54 -0
- metaflow/_vendor/v3_7/typeguard/_utils.py +169 -0
- metaflow/_vendor/v3_7/typeguard/py.typed +0 -0
- metaflow/_vendor/v3_7/typing_extensions.py +3072 -0
- metaflow/_vendor/v3_7/zipp.py +329 -0
- metaflow/cmd/develop/stubs.py +1 -1
- metaflow/extension_support/__init__.py +1 -1
- metaflow/plugins/pypi/utils.py +4 -0
- metaflow/runner/click_api.py +7 -2
- metaflow/vendor.py +1 -0
- metaflow/version.py +1 -1
- {metaflow-2.15.5.dist-info → metaflow-2.15.6.dist-info}/METADATA +2 -2
- {metaflow-2.15.5.dist-info → metaflow-2.15.6.dist-info}/RECORD +51 -25
- {metaflow-2.15.5.data → metaflow-2.15.6.data}/data/share/metaflow/devtools/Makefile +0 -0
- {metaflow-2.15.5.data → metaflow-2.15.6.data}/data/share/metaflow/devtools/Tiltfile +0 -0
- {metaflow-2.15.5.data → metaflow-2.15.6.data}/data/share/metaflow/devtools/pick_services.sh +0 -0
- {metaflow-2.15.5.dist-info → metaflow-2.15.6.dist-info}/LICENSE +0 -0
- {metaflow-2.15.5.dist-info → metaflow-2.15.6.dist-info}/WHEEL +0 -0
- {metaflow-2.15.5.dist-info → metaflow-2.15.6.dist-info}/entry_points.txt +0 -0
- {metaflow-2.15.5.dist-info → metaflow-2.15.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,108 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from collections.abc import Collection
|
4
|
+
from dataclasses import dataclass
|
5
|
+
from enum import Enum, auto
|
6
|
+
from typing import TYPE_CHECKING, TypeVar
|
7
|
+
|
8
|
+
if TYPE_CHECKING:
|
9
|
+
from ._functions import TypeCheckFailCallback
|
10
|
+
|
11
|
+
T = TypeVar("T")
|
12
|
+
|
13
|
+
|
14
|
+
class ForwardRefPolicy(Enum):
|
15
|
+
"""
|
16
|
+
Defines how unresolved forward references are handled.
|
17
|
+
|
18
|
+
Members:
|
19
|
+
|
20
|
+
* ``ERROR``: propagate the :exc:`NameError` when the forward reference lookup fails
|
21
|
+
* ``WARN``: emit a :class:`~.TypeHintWarning` if the forward reference lookup fails
|
22
|
+
* ``IGNORE``: silently skip checks for unresolveable forward references
|
23
|
+
"""
|
24
|
+
|
25
|
+
ERROR = auto()
|
26
|
+
WARN = auto()
|
27
|
+
IGNORE = auto()
|
28
|
+
|
29
|
+
|
30
|
+
class CollectionCheckStrategy(Enum):
|
31
|
+
"""
|
32
|
+
Specifies how thoroughly the contents of collections are type checked.
|
33
|
+
|
34
|
+
This has an effect on the following built-in checkers:
|
35
|
+
|
36
|
+
* ``AbstractSet``
|
37
|
+
* ``Dict``
|
38
|
+
* ``List``
|
39
|
+
* ``Mapping``
|
40
|
+
* ``Set``
|
41
|
+
* ``Tuple[<type>, ...]`` (arbitrarily sized tuples)
|
42
|
+
|
43
|
+
Members:
|
44
|
+
|
45
|
+
* ``FIRST_ITEM``: check only the first item
|
46
|
+
* ``ALL_ITEMS``: check all items
|
47
|
+
"""
|
48
|
+
|
49
|
+
FIRST_ITEM = auto()
|
50
|
+
ALL_ITEMS = auto()
|
51
|
+
|
52
|
+
def iterate_samples(self, collection: Collection[T]) -> Collection[T]:
|
53
|
+
if self is CollectionCheckStrategy.FIRST_ITEM:
|
54
|
+
if len(collection):
|
55
|
+
return [next(iter(collection))]
|
56
|
+
else:
|
57
|
+
return ()
|
58
|
+
else:
|
59
|
+
return collection
|
60
|
+
|
61
|
+
|
62
|
+
@dataclass
|
63
|
+
class TypeCheckConfiguration:
|
64
|
+
"""
|
65
|
+
You can change Typeguard's behavior with these settings.
|
66
|
+
|
67
|
+
.. attribute:: typecheck_fail_callback
|
68
|
+
:type: Callable[[TypeCheckError, TypeCheckMemo], Any]
|
69
|
+
|
70
|
+
Callable that is called when type checking fails.
|
71
|
+
|
72
|
+
Default: ``None`` (the :exc:`~.TypeCheckError` is raised directly)
|
73
|
+
|
74
|
+
.. attribute:: forward_ref_policy
|
75
|
+
:type: ForwardRefPolicy
|
76
|
+
|
77
|
+
Specifies what to do when a forward reference fails to resolve.
|
78
|
+
|
79
|
+
Default: ``WARN``
|
80
|
+
|
81
|
+
.. attribute:: collection_check_strategy
|
82
|
+
:type: CollectionCheckStrategy
|
83
|
+
|
84
|
+
Specifies how thoroughly the contents of collections (list, dict, etc.) are
|
85
|
+
type checked.
|
86
|
+
|
87
|
+
Default: ``FIRST_ITEM``
|
88
|
+
|
89
|
+
.. attribute:: debug_instrumentation
|
90
|
+
:type: bool
|
91
|
+
|
92
|
+
If set to ``True``, the code of modules or functions instrumented by typeguard
|
93
|
+
is printed to ``sys.stderr`` after the instrumentation is done
|
94
|
+
|
95
|
+
Requires Python 3.9 or newer.
|
96
|
+
|
97
|
+
Default: ``False``
|
98
|
+
"""
|
99
|
+
|
100
|
+
forward_ref_policy: ForwardRefPolicy = ForwardRefPolicy.WARN
|
101
|
+
typecheck_fail_callback: TypeCheckFailCallback | None = None
|
102
|
+
collection_check_strategy: CollectionCheckStrategy = (
|
103
|
+
CollectionCheckStrategy.FIRST_ITEM
|
104
|
+
)
|
105
|
+
debug_instrumentation: bool = False
|
106
|
+
|
107
|
+
|
108
|
+
global_config = TypeCheckConfiguration()
|
@@ -0,0 +1,237 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import ast
|
4
|
+
import inspect
|
5
|
+
import sys
|
6
|
+
from collections.abc import Sequence
|
7
|
+
from functools import partial
|
8
|
+
from inspect import isclass, isfunction
|
9
|
+
from types import CodeType, FrameType, FunctionType
|
10
|
+
from typing import TYPE_CHECKING, Any, Callable, ForwardRef, TypeVar, cast, overload
|
11
|
+
from warnings import warn
|
12
|
+
|
13
|
+
from ._config import CollectionCheckStrategy, ForwardRefPolicy, global_config
|
14
|
+
from ._exceptions import InstrumentationWarning
|
15
|
+
from ._functions import TypeCheckFailCallback
|
16
|
+
from ._transformer import TypeguardTransformer
|
17
|
+
from ._utils import Unset, function_name, get_stacklevel, is_method_of, unset
|
18
|
+
|
19
|
+
if TYPE_CHECKING:
|
20
|
+
from typeshed.stdlib.types import _Cell
|
21
|
+
|
22
|
+
_F = TypeVar("_F")
|
23
|
+
|
24
|
+
def typeguard_ignore(f: _F) -> _F:
|
25
|
+
"""This decorator is a noop during static type-checking."""
|
26
|
+
return f
|
27
|
+
|
28
|
+
else:
|
29
|
+
from typing import no_type_check as typeguard_ignore # noqa: F401
|
30
|
+
|
31
|
+
T_CallableOrType = TypeVar("T_CallableOrType", bound=Callable[..., Any])
|
32
|
+
|
33
|
+
|
34
|
+
def make_cell(value: object) -> _Cell:
|
35
|
+
return (lambda: value).__closure__[0] # type: ignore[index]
|
36
|
+
|
37
|
+
|
38
|
+
def find_target_function(
|
39
|
+
new_code: CodeType, target_path: Sequence[str], firstlineno: int
|
40
|
+
) -> CodeType | None:
|
41
|
+
target_name = target_path[0]
|
42
|
+
for const in new_code.co_consts:
|
43
|
+
if isinstance(const, CodeType):
|
44
|
+
if const.co_name == target_name:
|
45
|
+
if const.co_firstlineno == firstlineno:
|
46
|
+
return const
|
47
|
+
elif len(target_path) > 1:
|
48
|
+
target_code = find_target_function(
|
49
|
+
const, target_path[1:], firstlineno
|
50
|
+
)
|
51
|
+
if target_code:
|
52
|
+
return target_code
|
53
|
+
|
54
|
+
return None
|
55
|
+
|
56
|
+
|
57
|
+
def instrument(f: T_CallableOrType) -> FunctionType | str:
|
58
|
+
if not getattr(f, "__code__", None):
|
59
|
+
return "no code associated"
|
60
|
+
elif not getattr(f, "__module__", None):
|
61
|
+
return "__module__ attribute is not set"
|
62
|
+
elif f.__code__.co_filename == "<stdin>":
|
63
|
+
return "cannot instrument functions defined in a REPL"
|
64
|
+
elif hasattr(f, "__wrapped__"):
|
65
|
+
return (
|
66
|
+
"@typechecked only supports instrumenting functions wrapped with "
|
67
|
+
"@classmethod, @staticmethod or @property"
|
68
|
+
)
|
69
|
+
|
70
|
+
target_path = [item for item in f.__qualname__.split(".") if item != "<locals>"]
|
71
|
+
module_source = inspect.getsource(sys.modules[f.__module__])
|
72
|
+
module_ast = ast.parse(module_source)
|
73
|
+
instrumentor = TypeguardTransformer(target_path, f.__code__.co_firstlineno)
|
74
|
+
instrumentor.visit(module_ast)
|
75
|
+
|
76
|
+
if not instrumentor.target_node or instrumentor.target_lineno is None:
|
77
|
+
return "instrumentor did not find the target function"
|
78
|
+
|
79
|
+
module_code = compile(module_ast, f.__code__.co_filename, "exec", dont_inherit=True)
|
80
|
+
new_code = find_target_function(
|
81
|
+
module_code, target_path, instrumentor.target_lineno
|
82
|
+
)
|
83
|
+
if not new_code:
|
84
|
+
return "cannot find the target function in the AST"
|
85
|
+
|
86
|
+
if global_config.debug_instrumentation and sys.version_info >= (3, 9):
|
87
|
+
# Find the matching AST node, then unparse it to source and print to stdout
|
88
|
+
print(
|
89
|
+
f"Source code of {f.__qualname__}() after instrumentation:"
|
90
|
+
"\n----------------------------------------------",
|
91
|
+
file=sys.stderr,
|
92
|
+
)
|
93
|
+
print(ast.unparse(instrumentor.target_node), file=sys.stderr)
|
94
|
+
print(
|
95
|
+
"----------------------------------------------",
|
96
|
+
file=sys.stderr,
|
97
|
+
)
|
98
|
+
|
99
|
+
closure = f.__closure__
|
100
|
+
if new_code.co_freevars != f.__code__.co_freevars:
|
101
|
+
# Create a new closure and find values for the new free variables
|
102
|
+
frame = cast(FrameType, inspect.currentframe())
|
103
|
+
frame = cast(FrameType, frame.f_back)
|
104
|
+
frame_locals = cast(FrameType, frame.f_back).f_locals
|
105
|
+
cells: list[_Cell] = []
|
106
|
+
for key in new_code.co_freevars:
|
107
|
+
if key in instrumentor.names_used_in_annotations:
|
108
|
+
# Find the value and make a new cell from it
|
109
|
+
value = frame_locals.get(key) or ForwardRef(key)
|
110
|
+
cells.append(make_cell(value))
|
111
|
+
else:
|
112
|
+
# Reuse the cell from the existing closure
|
113
|
+
assert f.__closure__
|
114
|
+
cells.append(f.__closure__[f.__code__.co_freevars.index(key)])
|
115
|
+
|
116
|
+
closure = tuple(cells)
|
117
|
+
|
118
|
+
new_function = FunctionType(new_code, f.__globals__, f.__name__, closure=closure)
|
119
|
+
new_function.__module__ = f.__module__
|
120
|
+
new_function.__name__ = f.__name__
|
121
|
+
new_function.__qualname__ = f.__qualname__
|
122
|
+
new_function.__annotations__ = f.__annotations__
|
123
|
+
new_function.__doc__ = f.__doc__
|
124
|
+
new_function.__defaults__ = f.__defaults__
|
125
|
+
new_function.__kwdefaults__ = f.__kwdefaults__
|
126
|
+
return new_function
|
127
|
+
|
128
|
+
|
129
|
+
@overload
|
130
|
+
def typechecked(
|
131
|
+
*,
|
132
|
+
forward_ref_policy: ForwardRefPolicy | Unset = unset,
|
133
|
+
typecheck_fail_callback: TypeCheckFailCallback | Unset = unset,
|
134
|
+
collection_check_strategy: CollectionCheckStrategy | Unset = unset,
|
135
|
+
debug_instrumentation: bool | Unset = unset,
|
136
|
+
) -> Callable[[T_CallableOrType], T_CallableOrType]:
|
137
|
+
...
|
138
|
+
|
139
|
+
|
140
|
+
@overload
|
141
|
+
def typechecked(target: T_CallableOrType) -> T_CallableOrType:
|
142
|
+
...
|
143
|
+
|
144
|
+
|
145
|
+
def typechecked(
|
146
|
+
target: T_CallableOrType | None = None,
|
147
|
+
*,
|
148
|
+
forward_ref_policy: ForwardRefPolicy | Unset = unset,
|
149
|
+
typecheck_fail_callback: TypeCheckFailCallback | Unset = unset,
|
150
|
+
collection_check_strategy: CollectionCheckStrategy | Unset = unset,
|
151
|
+
debug_instrumentation: bool | Unset = unset,
|
152
|
+
) -> Any:
|
153
|
+
"""
|
154
|
+
Instrument the target function to perform run-time type checking.
|
155
|
+
|
156
|
+
This decorator recompiles the target function, injecting code to type check
|
157
|
+
arguments, return values, yield values (excluding ``yield from``) and assignments to
|
158
|
+
annotated local variables.
|
159
|
+
|
160
|
+
This can also be used as a class decorator. This will instrument all type annotated
|
161
|
+
methods, including :func:`@classmethod <classmethod>`,
|
162
|
+
:func:`@staticmethod <staticmethod>`, and :class:`@property <property>` decorated
|
163
|
+
methods in the class.
|
164
|
+
|
165
|
+
.. note:: When Python is run in optimized mode (``-O`` or ``-OO``, this decorator
|
166
|
+
is a no-op). This is a feature meant for selectively introducing type checking
|
167
|
+
into a code base where the checks aren't meant to be run in production.
|
168
|
+
|
169
|
+
:param target: the function or class to enable type checking for
|
170
|
+
:param forward_ref_policy: override for
|
171
|
+
:attr:`.TypeCheckConfiguration.forward_ref_policy`
|
172
|
+
:param typecheck_fail_callback: override for
|
173
|
+
:attr:`.TypeCheckConfiguration.typecheck_fail_callback`
|
174
|
+
:param collection_check_strategy: override for
|
175
|
+
:attr:`.TypeCheckConfiguration.collection_check_strategy`
|
176
|
+
:param debug_instrumentation: override for
|
177
|
+
:attr:`.TypeCheckConfiguration.debug_instrumentation`
|
178
|
+
|
179
|
+
"""
|
180
|
+
if target is None:
|
181
|
+
return partial(
|
182
|
+
typechecked,
|
183
|
+
forward_ref_policy=forward_ref_policy,
|
184
|
+
typecheck_fail_callback=typecheck_fail_callback,
|
185
|
+
collection_check_strategy=collection_check_strategy,
|
186
|
+
debug_instrumentation=debug_instrumentation,
|
187
|
+
)
|
188
|
+
|
189
|
+
if not __debug__:
|
190
|
+
return target
|
191
|
+
|
192
|
+
if isclass(target):
|
193
|
+
for key, attr in target.__dict__.items():
|
194
|
+
if is_method_of(attr, target):
|
195
|
+
retval = instrument(attr)
|
196
|
+
if isfunction(retval):
|
197
|
+
setattr(target, key, retval)
|
198
|
+
elif isinstance(attr, (classmethod, staticmethod)):
|
199
|
+
if is_method_of(attr.__func__, target):
|
200
|
+
retval = instrument(attr.__func__)
|
201
|
+
if isfunction(retval):
|
202
|
+
wrapper = attr.__class__(retval)
|
203
|
+
setattr(target, key, wrapper)
|
204
|
+
elif isinstance(attr, property):
|
205
|
+
kwargs: dict[str, Any] = dict(doc=attr.__doc__)
|
206
|
+
for name in ("fset", "fget", "fdel"):
|
207
|
+
property_func = kwargs[name] = getattr(attr, name)
|
208
|
+
if is_method_of(property_func, target):
|
209
|
+
retval = instrument(property_func)
|
210
|
+
if isfunction(retval):
|
211
|
+
kwargs[name] = retval
|
212
|
+
|
213
|
+
setattr(target, key, attr.__class__(**kwargs))
|
214
|
+
|
215
|
+
return target
|
216
|
+
|
217
|
+
# Find either the first Python wrapper or the actual function
|
218
|
+
wrapper_class: type[classmethod[Any, Any, Any]] | type[
|
219
|
+
staticmethod[Any, Any]
|
220
|
+
] | None = None
|
221
|
+
if isinstance(target, (classmethod, staticmethod)):
|
222
|
+
wrapper_class = target.__class__
|
223
|
+
target = target.__func__
|
224
|
+
|
225
|
+
retval = instrument(target)
|
226
|
+
if isinstance(retval, str):
|
227
|
+
warn(
|
228
|
+
f"{retval} -- not typechecking {function_name(target)}",
|
229
|
+
InstrumentationWarning,
|
230
|
+
stacklevel=get_stacklevel(),
|
231
|
+
)
|
232
|
+
return target
|
233
|
+
|
234
|
+
if wrapper_class is None:
|
235
|
+
return retval
|
236
|
+
else:
|
237
|
+
return wrapper_class(retval)
|
@@ -0,0 +1,42 @@
|
|
1
|
+
from collections import deque
|
2
|
+
from typing import Deque
|
3
|
+
|
4
|
+
|
5
|
+
class TypeHintWarning(UserWarning):
|
6
|
+
"""
|
7
|
+
A warning that is emitted when a type hint in string form could not be resolved to
|
8
|
+
an actual type.
|
9
|
+
"""
|
10
|
+
|
11
|
+
|
12
|
+
class TypeCheckWarning(UserWarning):
|
13
|
+
"""Emitted by typeguard's type checkers when a type mismatch is detected."""
|
14
|
+
|
15
|
+
def __init__(self, message: str):
|
16
|
+
super().__init__(message)
|
17
|
+
|
18
|
+
|
19
|
+
class InstrumentationWarning(UserWarning):
|
20
|
+
"""Emitted when there's a problem with instrumenting a function for type checks."""
|
21
|
+
|
22
|
+
def __init__(self, message: str):
|
23
|
+
super().__init__(message)
|
24
|
+
|
25
|
+
|
26
|
+
class TypeCheckError(Exception):
|
27
|
+
"""
|
28
|
+
Raised by typeguard's type checkers when a type mismatch is detected.
|
29
|
+
"""
|
30
|
+
|
31
|
+
def __init__(self, message: str):
|
32
|
+
super().__init__(message)
|
33
|
+
self._path: Deque[str] = deque()
|
34
|
+
|
35
|
+
def append_path_element(self, element: str) -> None:
|
36
|
+
self._path.append(element)
|
37
|
+
|
38
|
+
def __str__(self) -> str:
|
39
|
+
if self._path:
|
40
|
+
return " of ".join(self._path) + " " + str(self.args[0])
|
41
|
+
else:
|
42
|
+
return str(self.args[0])
|
@@ -0,0 +1,310 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import sys
|
4
|
+
import warnings
|
5
|
+
from typing import Any, Callable, NoReturn, TypeVar, Union, overload
|
6
|
+
|
7
|
+
from . import _suppression
|
8
|
+
from ._checkers import BINARY_MAGIC_METHODS, check_type_internal
|
9
|
+
from ._config import (
|
10
|
+
CollectionCheckStrategy,
|
11
|
+
ForwardRefPolicy,
|
12
|
+
TypeCheckConfiguration,
|
13
|
+
)
|
14
|
+
from ._exceptions import TypeCheckError, TypeCheckWarning
|
15
|
+
from ._memo import TypeCheckMemo
|
16
|
+
from ._utils import get_stacklevel, qualified_name
|
17
|
+
|
18
|
+
if sys.version_info >= (3, 11):
|
19
|
+
from typing import Literal, Never, TypeAlias
|
20
|
+
else:
|
21
|
+
from metaflow._vendor.v3_7.typing_extensions import Literal, Never, TypeAlias
|
22
|
+
|
23
|
+
T = TypeVar("T")
|
24
|
+
TypeCheckFailCallback: TypeAlias = Callable[[TypeCheckError, TypeCheckMemo], Any]
|
25
|
+
|
26
|
+
|
27
|
+
@overload
|
28
|
+
def check_type(
|
29
|
+
value: object,
|
30
|
+
expected_type: type[T],
|
31
|
+
*,
|
32
|
+
forward_ref_policy: ForwardRefPolicy = ...,
|
33
|
+
typecheck_fail_callback: TypeCheckFailCallback | None = ...,
|
34
|
+
collection_check_strategy: CollectionCheckStrategy = ...,
|
35
|
+
) -> T:
|
36
|
+
...
|
37
|
+
|
38
|
+
|
39
|
+
@overload
|
40
|
+
def check_type(
|
41
|
+
value: object,
|
42
|
+
expected_type: Any,
|
43
|
+
*,
|
44
|
+
forward_ref_policy: ForwardRefPolicy = ...,
|
45
|
+
typecheck_fail_callback: TypeCheckFailCallback | None = ...,
|
46
|
+
collection_check_strategy: CollectionCheckStrategy = ...,
|
47
|
+
) -> Any:
|
48
|
+
...
|
49
|
+
|
50
|
+
|
51
|
+
def check_type(
|
52
|
+
value: object,
|
53
|
+
expected_type: Any,
|
54
|
+
*,
|
55
|
+
forward_ref_policy: ForwardRefPolicy = TypeCheckConfiguration().forward_ref_policy,
|
56
|
+
typecheck_fail_callback: (TypeCheckFailCallback | None) = (
|
57
|
+
TypeCheckConfiguration().typecheck_fail_callback
|
58
|
+
),
|
59
|
+
collection_check_strategy: CollectionCheckStrategy = (
|
60
|
+
TypeCheckConfiguration().collection_check_strategy
|
61
|
+
),
|
62
|
+
) -> Any:
|
63
|
+
"""
|
64
|
+
Ensure that ``value`` matches ``expected_type``.
|
65
|
+
|
66
|
+
The types from the :mod:`typing` module do not support :func:`isinstance` or
|
67
|
+
:func:`issubclass` so a number of type specific checks are required. This function
|
68
|
+
knows which checker to call for which type.
|
69
|
+
|
70
|
+
This function wraps :func:`~.check_type_internal` in the following ways:
|
71
|
+
|
72
|
+
* Respects type checking suppression (:func:`~.suppress_type_checks`)
|
73
|
+
* Forms a :class:`~.TypeCheckMemo` from the current stack frame
|
74
|
+
* Calls the configured type check fail callback if the check fails
|
75
|
+
|
76
|
+
Note that this function is independent of the globally shared configuration in
|
77
|
+
:data:`typeguard.config`. This means that usage within libraries is safe from being
|
78
|
+
affected configuration changes made by other libraries or by the integrating
|
79
|
+
application. Instead, configuration options have the same default values as their
|
80
|
+
corresponding fields in :class:`TypeCheckConfiguration`.
|
81
|
+
|
82
|
+
:param value: value to be checked against ``expected_type``
|
83
|
+
:param expected_type: a class or generic type instance, or a tuple of such things
|
84
|
+
:param forward_ref_policy: see :attr:`TypeCheckConfiguration.forward_ref_policy`
|
85
|
+
:param typecheck_fail_callback:
|
86
|
+
see :attr`TypeCheckConfiguration.typecheck_fail_callback`
|
87
|
+
:param collection_check_strategy:
|
88
|
+
see :attr:`TypeCheckConfiguration.collection_check_strategy`
|
89
|
+
:return: ``value``, unmodified
|
90
|
+
:raises TypeCheckError: if there is a type mismatch
|
91
|
+
|
92
|
+
"""
|
93
|
+
if type(expected_type) is tuple:
|
94
|
+
expected_type = Union[expected_type]
|
95
|
+
|
96
|
+
config = TypeCheckConfiguration(
|
97
|
+
forward_ref_policy=forward_ref_policy,
|
98
|
+
typecheck_fail_callback=typecheck_fail_callback,
|
99
|
+
collection_check_strategy=collection_check_strategy,
|
100
|
+
)
|
101
|
+
|
102
|
+
if _suppression.type_checks_suppressed or expected_type is Any:
|
103
|
+
return value
|
104
|
+
|
105
|
+
frame = sys._getframe(1)
|
106
|
+
memo = TypeCheckMemo(frame.f_globals, frame.f_locals, config=config)
|
107
|
+
try:
|
108
|
+
check_type_internal(value, expected_type, memo)
|
109
|
+
except TypeCheckError as exc:
|
110
|
+
exc.append_path_element(qualified_name(value, add_class_prefix=True))
|
111
|
+
if config.typecheck_fail_callback:
|
112
|
+
config.typecheck_fail_callback(exc, memo)
|
113
|
+
else:
|
114
|
+
raise
|
115
|
+
|
116
|
+
return value
|
117
|
+
|
118
|
+
|
119
|
+
def check_argument_types(
|
120
|
+
func_name: str,
|
121
|
+
arguments: dict[str, tuple[Any, Any]],
|
122
|
+
memo: TypeCheckMemo,
|
123
|
+
) -> Literal[True]:
|
124
|
+
if _suppression.type_checks_suppressed:
|
125
|
+
return True
|
126
|
+
|
127
|
+
for argname, (value, annotation) in arguments.items():
|
128
|
+
if annotation is NoReturn or annotation is Never:
|
129
|
+
exc = TypeCheckError(
|
130
|
+
f"{func_name}() was declared never to be called but it was"
|
131
|
+
)
|
132
|
+
if memo.config.typecheck_fail_callback:
|
133
|
+
memo.config.typecheck_fail_callback(exc, memo)
|
134
|
+
else:
|
135
|
+
raise exc
|
136
|
+
|
137
|
+
try:
|
138
|
+
check_type_internal(value, annotation, memo)
|
139
|
+
except TypeCheckError as exc:
|
140
|
+
qualname = qualified_name(value, add_class_prefix=True)
|
141
|
+
exc.append_path_element(f'argument "{argname}" ({qualname})')
|
142
|
+
if memo.config.typecheck_fail_callback:
|
143
|
+
memo.config.typecheck_fail_callback(exc, memo)
|
144
|
+
else:
|
145
|
+
raise
|
146
|
+
|
147
|
+
return True
|
148
|
+
|
149
|
+
|
150
|
+
def check_return_type(
|
151
|
+
func_name: str,
|
152
|
+
retval: T,
|
153
|
+
annotation: Any,
|
154
|
+
memo: TypeCheckMemo,
|
155
|
+
) -> T:
|
156
|
+
if _suppression.type_checks_suppressed:
|
157
|
+
return retval
|
158
|
+
|
159
|
+
if annotation is NoReturn or annotation is Never:
|
160
|
+
exc = TypeCheckError(f"{func_name}() was declared never to return but it did")
|
161
|
+
if memo.config.typecheck_fail_callback:
|
162
|
+
memo.config.typecheck_fail_callback(exc, memo)
|
163
|
+
else:
|
164
|
+
raise exc
|
165
|
+
|
166
|
+
try:
|
167
|
+
check_type_internal(retval, annotation, memo)
|
168
|
+
except TypeCheckError as exc:
|
169
|
+
# Allow NotImplemented if this is a binary magic method (__eq__() et al)
|
170
|
+
if retval is NotImplemented and annotation is bool:
|
171
|
+
# This does (and cannot) not check if it's actually a method
|
172
|
+
func_name = func_name.rsplit(".", 1)[-1]
|
173
|
+
if func_name in BINARY_MAGIC_METHODS:
|
174
|
+
return retval
|
175
|
+
|
176
|
+
qualname = qualified_name(retval, add_class_prefix=True)
|
177
|
+
exc.append_path_element(f"the return value ({qualname})")
|
178
|
+
if memo.config.typecheck_fail_callback:
|
179
|
+
memo.config.typecheck_fail_callback(exc, memo)
|
180
|
+
else:
|
181
|
+
raise
|
182
|
+
|
183
|
+
return retval
|
184
|
+
|
185
|
+
|
186
|
+
def check_send_type(
|
187
|
+
func_name: str,
|
188
|
+
sendval: T,
|
189
|
+
annotation: Any,
|
190
|
+
memo: TypeCheckMemo,
|
191
|
+
) -> T:
|
192
|
+
if _suppression.type_checks_suppressed:
|
193
|
+
return sendval
|
194
|
+
|
195
|
+
if annotation is NoReturn or annotation is Never:
|
196
|
+
exc = TypeCheckError(
|
197
|
+
f"{func_name}() was declared never to be sent a value to but it was"
|
198
|
+
)
|
199
|
+
if memo.config.typecheck_fail_callback:
|
200
|
+
memo.config.typecheck_fail_callback(exc, memo)
|
201
|
+
else:
|
202
|
+
raise exc
|
203
|
+
|
204
|
+
try:
|
205
|
+
check_type_internal(sendval, annotation, memo)
|
206
|
+
except TypeCheckError as exc:
|
207
|
+
qualname = qualified_name(sendval, add_class_prefix=True)
|
208
|
+
exc.append_path_element(f"the value sent to generator ({qualname})")
|
209
|
+
if memo.config.typecheck_fail_callback:
|
210
|
+
memo.config.typecheck_fail_callback(exc, memo)
|
211
|
+
else:
|
212
|
+
raise
|
213
|
+
|
214
|
+
return sendval
|
215
|
+
|
216
|
+
|
217
|
+
def check_yield_type(
|
218
|
+
func_name: str,
|
219
|
+
yieldval: T,
|
220
|
+
annotation: Any,
|
221
|
+
memo: TypeCheckMemo,
|
222
|
+
) -> T:
|
223
|
+
if _suppression.type_checks_suppressed:
|
224
|
+
return yieldval
|
225
|
+
|
226
|
+
if annotation is NoReturn or annotation is Never:
|
227
|
+
exc = TypeCheckError(f"{func_name}() was declared never to yield but it did")
|
228
|
+
if memo.config.typecheck_fail_callback:
|
229
|
+
memo.config.typecheck_fail_callback(exc, memo)
|
230
|
+
else:
|
231
|
+
raise exc
|
232
|
+
|
233
|
+
try:
|
234
|
+
check_type_internal(yieldval, annotation, memo)
|
235
|
+
except TypeCheckError as exc:
|
236
|
+
qualname = qualified_name(yieldval, add_class_prefix=True)
|
237
|
+
exc.append_path_element(f"the yielded value ({qualname})")
|
238
|
+
if memo.config.typecheck_fail_callback:
|
239
|
+
memo.config.typecheck_fail_callback(exc, memo)
|
240
|
+
else:
|
241
|
+
raise
|
242
|
+
|
243
|
+
return yieldval
|
244
|
+
|
245
|
+
|
246
|
+
def check_variable_assignment(
|
247
|
+
value: object, varname: str, annotation: Any, memo: TypeCheckMemo
|
248
|
+
) -> Any:
|
249
|
+
if _suppression.type_checks_suppressed:
|
250
|
+
return value
|
251
|
+
|
252
|
+
try:
|
253
|
+
check_type_internal(value, annotation, memo)
|
254
|
+
except TypeCheckError as exc:
|
255
|
+
qualname = qualified_name(value, add_class_prefix=True)
|
256
|
+
exc.append_path_element(f"value assigned to {varname} ({qualname})")
|
257
|
+
if memo.config.typecheck_fail_callback:
|
258
|
+
memo.config.typecheck_fail_callback(exc, memo)
|
259
|
+
else:
|
260
|
+
raise
|
261
|
+
|
262
|
+
return value
|
263
|
+
|
264
|
+
|
265
|
+
def check_multi_variable_assignment(
|
266
|
+
value: Any, targets: list[dict[str, Any]], memo: TypeCheckMemo
|
267
|
+
) -> Any:
|
268
|
+
if max(len(target) for target in targets) == 1:
|
269
|
+
iterated_values = [value]
|
270
|
+
else:
|
271
|
+
iterated_values = list(value)
|
272
|
+
|
273
|
+
if not _suppression.type_checks_suppressed:
|
274
|
+
for expected_types in targets:
|
275
|
+
value_index = 0
|
276
|
+
for ann_index, (varname, expected_type) in enumerate(
|
277
|
+
expected_types.items()
|
278
|
+
):
|
279
|
+
if varname.startswith("*"):
|
280
|
+
varname = varname[1:]
|
281
|
+
keys_left = len(expected_types) - 1 - ann_index
|
282
|
+
next_value_index = len(iterated_values) - keys_left
|
283
|
+
obj: object = iterated_values[value_index:next_value_index]
|
284
|
+
value_index = next_value_index
|
285
|
+
else:
|
286
|
+
obj = iterated_values[value_index]
|
287
|
+
value_index += 1
|
288
|
+
|
289
|
+
try:
|
290
|
+
check_type_internal(obj, expected_type, memo)
|
291
|
+
except TypeCheckError as exc:
|
292
|
+
qualname = qualified_name(obj, add_class_prefix=True)
|
293
|
+
exc.append_path_element(f"value assigned to {varname} ({qualname})")
|
294
|
+
if memo.config.typecheck_fail_callback:
|
295
|
+
memo.config.typecheck_fail_callback(exc, memo)
|
296
|
+
else:
|
297
|
+
raise
|
298
|
+
|
299
|
+
return iterated_values[0] if len(iterated_values) == 1 else iterated_values
|
300
|
+
|
301
|
+
|
302
|
+
def warn_on_error(exc: TypeCheckError, memo: TypeCheckMemo) -> None:
|
303
|
+
"""
|
304
|
+
Emit a warning on a type mismatch.
|
305
|
+
|
306
|
+
This is intended to be used as an error handler in
|
307
|
+
:attr:`TypeCheckConfiguration.typecheck_fail_callback`.
|
308
|
+
|
309
|
+
"""
|
310
|
+
warnings.warn(TypeCheckWarning(str(exc)), stacklevel=get_stacklevel())
|