ob-metaflow 2.11.13.1__py2.py3-none-any.whl → 2.19.7.1rc0__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.
Files changed (289) hide show
  1. metaflow/R.py +10 -7
  2. metaflow/__init__.py +40 -25
  3. metaflow/_vendor/imghdr/__init__.py +186 -0
  4. metaflow/_vendor/importlib_metadata/__init__.py +1063 -0
  5. metaflow/_vendor/importlib_metadata/_adapters.py +68 -0
  6. metaflow/_vendor/importlib_metadata/_collections.py +30 -0
  7. metaflow/_vendor/importlib_metadata/_compat.py +71 -0
  8. metaflow/_vendor/importlib_metadata/_functools.py +104 -0
  9. metaflow/_vendor/importlib_metadata/_itertools.py +73 -0
  10. metaflow/_vendor/importlib_metadata/_meta.py +48 -0
  11. metaflow/_vendor/importlib_metadata/_text.py +99 -0
  12. metaflow/_vendor/importlib_metadata/py.typed +0 -0
  13. metaflow/_vendor/typeguard/__init__.py +48 -0
  14. metaflow/_vendor/typeguard/_checkers.py +1070 -0
  15. metaflow/_vendor/typeguard/_config.py +108 -0
  16. metaflow/_vendor/typeguard/_decorators.py +233 -0
  17. metaflow/_vendor/typeguard/_exceptions.py +42 -0
  18. metaflow/_vendor/typeguard/_functions.py +308 -0
  19. metaflow/_vendor/typeguard/_importhook.py +213 -0
  20. metaflow/_vendor/typeguard/_memo.py +48 -0
  21. metaflow/_vendor/typeguard/_pytest_plugin.py +127 -0
  22. metaflow/_vendor/typeguard/_suppression.py +86 -0
  23. metaflow/_vendor/typeguard/_transformer.py +1229 -0
  24. metaflow/_vendor/typeguard/_union_transformer.py +55 -0
  25. metaflow/_vendor/typeguard/_utils.py +173 -0
  26. metaflow/_vendor/typeguard/py.typed +0 -0
  27. metaflow/_vendor/typing_extensions.py +3641 -0
  28. metaflow/_vendor/v3_7/importlib_metadata/__init__.py +1063 -0
  29. metaflow/_vendor/v3_7/importlib_metadata/_adapters.py +68 -0
  30. metaflow/_vendor/v3_7/importlib_metadata/_collections.py +30 -0
  31. metaflow/_vendor/v3_7/importlib_metadata/_compat.py +71 -0
  32. metaflow/_vendor/v3_7/importlib_metadata/_functools.py +104 -0
  33. metaflow/_vendor/v3_7/importlib_metadata/_itertools.py +73 -0
  34. metaflow/_vendor/v3_7/importlib_metadata/_meta.py +48 -0
  35. metaflow/_vendor/v3_7/importlib_metadata/_text.py +99 -0
  36. metaflow/_vendor/v3_7/importlib_metadata/py.typed +0 -0
  37. metaflow/_vendor/v3_7/typeguard/__init__.py +48 -0
  38. metaflow/_vendor/v3_7/typeguard/_checkers.py +906 -0
  39. metaflow/_vendor/v3_7/typeguard/_config.py +108 -0
  40. metaflow/_vendor/v3_7/typeguard/_decorators.py +237 -0
  41. metaflow/_vendor/v3_7/typeguard/_exceptions.py +42 -0
  42. metaflow/_vendor/v3_7/typeguard/_functions.py +310 -0
  43. metaflow/_vendor/v3_7/typeguard/_importhook.py +213 -0
  44. metaflow/_vendor/v3_7/typeguard/_memo.py +48 -0
  45. metaflow/_vendor/v3_7/typeguard/_pytest_plugin.py +100 -0
  46. metaflow/_vendor/v3_7/typeguard/_suppression.py +88 -0
  47. metaflow/_vendor/v3_7/typeguard/_transformer.py +1207 -0
  48. metaflow/_vendor/v3_7/typeguard/_union_transformer.py +54 -0
  49. metaflow/_vendor/v3_7/typeguard/_utils.py +169 -0
  50. metaflow/_vendor/v3_7/typeguard/py.typed +0 -0
  51. metaflow/_vendor/v3_7/typing_extensions.py +3072 -0
  52. metaflow/_vendor/yaml/__init__.py +427 -0
  53. metaflow/_vendor/yaml/composer.py +139 -0
  54. metaflow/_vendor/yaml/constructor.py +748 -0
  55. metaflow/_vendor/yaml/cyaml.py +101 -0
  56. metaflow/_vendor/yaml/dumper.py +62 -0
  57. metaflow/_vendor/yaml/emitter.py +1137 -0
  58. metaflow/_vendor/yaml/error.py +75 -0
  59. metaflow/_vendor/yaml/events.py +86 -0
  60. metaflow/_vendor/yaml/loader.py +63 -0
  61. metaflow/_vendor/yaml/nodes.py +49 -0
  62. metaflow/_vendor/yaml/parser.py +589 -0
  63. metaflow/_vendor/yaml/reader.py +185 -0
  64. metaflow/_vendor/yaml/representer.py +389 -0
  65. metaflow/_vendor/yaml/resolver.py +227 -0
  66. metaflow/_vendor/yaml/scanner.py +1435 -0
  67. metaflow/_vendor/yaml/serializer.py +111 -0
  68. metaflow/_vendor/yaml/tokens.py +104 -0
  69. metaflow/cards.py +5 -0
  70. metaflow/cli.py +331 -785
  71. metaflow/cli_args.py +17 -0
  72. metaflow/cli_components/__init__.py +0 -0
  73. metaflow/cli_components/dump_cmd.py +96 -0
  74. metaflow/cli_components/init_cmd.py +52 -0
  75. metaflow/cli_components/run_cmds.py +546 -0
  76. metaflow/cli_components/step_cmd.py +334 -0
  77. metaflow/cli_components/utils.py +140 -0
  78. metaflow/client/__init__.py +1 -0
  79. metaflow/client/core.py +467 -73
  80. metaflow/client/filecache.py +75 -35
  81. metaflow/clone_util.py +7 -1
  82. metaflow/cmd/code/__init__.py +231 -0
  83. metaflow/cmd/develop/stub_generator.py +756 -288
  84. metaflow/cmd/develop/stubs.py +12 -28
  85. metaflow/cmd/main_cli.py +6 -4
  86. metaflow/cmd/make_wrapper.py +78 -0
  87. metaflow/datastore/__init__.py +1 -0
  88. metaflow/datastore/content_addressed_store.py +41 -10
  89. metaflow/datastore/datastore_set.py +11 -2
  90. metaflow/datastore/flow_datastore.py +156 -10
  91. metaflow/datastore/spin_datastore.py +91 -0
  92. metaflow/datastore/task_datastore.py +154 -39
  93. metaflow/debug.py +5 -0
  94. metaflow/decorators.py +404 -78
  95. metaflow/exception.py +8 -2
  96. metaflow/extension_support/__init__.py +527 -376
  97. metaflow/extension_support/_empty_file.py +2 -2
  98. metaflow/extension_support/plugins.py +49 -31
  99. metaflow/flowspec.py +482 -33
  100. metaflow/graph.py +210 -42
  101. metaflow/includefile.py +84 -40
  102. metaflow/lint.py +141 -22
  103. metaflow/meta_files.py +13 -0
  104. metaflow/{metadata → metadata_provider}/heartbeat.py +24 -8
  105. metaflow/{metadata → metadata_provider}/metadata.py +86 -1
  106. metaflow/metaflow_config.py +175 -28
  107. metaflow/metaflow_config_funcs.py +51 -3
  108. metaflow/metaflow_current.py +4 -10
  109. metaflow/metaflow_environment.py +139 -53
  110. metaflow/metaflow_git.py +115 -0
  111. metaflow/metaflow_profile.py +18 -0
  112. metaflow/metaflow_version.py +150 -66
  113. metaflow/mflog/__init__.py +4 -3
  114. metaflow/mflog/save_logs.py +2 -2
  115. metaflow/multicore_utils.py +31 -14
  116. metaflow/package/__init__.py +673 -0
  117. metaflow/packaging_sys/__init__.py +880 -0
  118. metaflow/packaging_sys/backend.py +128 -0
  119. metaflow/packaging_sys/distribution_support.py +153 -0
  120. metaflow/packaging_sys/tar_backend.py +99 -0
  121. metaflow/packaging_sys/utils.py +54 -0
  122. metaflow/packaging_sys/v1.py +527 -0
  123. metaflow/parameters.py +149 -28
  124. metaflow/plugins/__init__.py +74 -5
  125. metaflow/plugins/airflow/airflow.py +40 -25
  126. metaflow/plugins/airflow/airflow_cli.py +22 -5
  127. metaflow/plugins/airflow/airflow_decorator.py +1 -1
  128. metaflow/plugins/airflow/airflow_utils.py +5 -3
  129. metaflow/plugins/airflow/sensors/base_sensor.py +4 -4
  130. metaflow/plugins/airflow/sensors/external_task_sensor.py +2 -2
  131. metaflow/plugins/airflow/sensors/s3_sensor.py +2 -2
  132. metaflow/plugins/argo/argo_client.py +78 -33
  133. metaflow/plugins/argo/argo_events.py +6 -6
  134. metaflow/plugins/argo/argo_workflows.py +2410 -527
  135. metaflow/plugins/argo/argo_workflows_cli.py +571 -121
  136. metaflow/plugins/argo/argo_workflows_decorator.py +43 -12
  137. metaflow/plugins/argo/argo_workflows_deployer.py +106 -0
  138. metaflow/plugins/argo/argo_workflows_deployer_objects.py +453 -0
  139. metaflow/plugins/argo/capture_error.py +73 -0
  140. metaflow/plugins/argo/conditional_input_paths.py +35 -0
  141. metaflow/plugins/argo/exit_hooks.py +209 -0
  142. metaflow/plugins/argo/jobset_input_paths.py +15 -0
  143. metaflow/plugins/argo/param_val.py +19 -0
  144. metaflow/plugins/aws/aws_client.py +10 -3
  145. metaflow/plugins/aws/aws_utils.py +55 -2
  146. metaflow/plugins/aws/batch/batch.py +72 -5
  147. metaflow/plugins/aws/batch/batch_cli.py +33 -10
  148. metaflow/plugins/aws/batch/batch_client.py +4 -3
  149. metaflow/plugins/aws/batch/batch_decorator.py +102 -35
  150. metaflow/plugins/aws/secrets_manager/aws_secrets_manager_secrets_provider.py +13 -10
  151. metaflow/plugins/aws/step_functions/dynamo_db_client.py +0 -3
  152. metaflow/plugins/aws/step_functions/production_token.py +1 -1
  153. metaflow/plugins/aws/step_functions/step_functions.py +65 -8
  154. metaflow/plugins/aws/step_functions/step_functions_cli.py +101 -7
  155. metaflow/plugins/aws/step_functions/step_functions_decorator.py +1 -2
  156. metaflow/plugins/aws/step_functions/step_functions_deployer.py +97 -0
  157. metaflow/plugins/aws/step_functions/step_functions_deployer_objects.py +264 -0
  158. metaflow/plugins/azure/azure_exceptions.py +1 -1
  159. metaflow/plugins/azure/azure_secret_manager_secrets_provider.py +240 -0
  160. metaflow/plugins/azure/azure_tail.py +1 -1
  161. metaflow/plugins/azure/includefile_support.py +2 -0
  162. metaflow/plugins/cards/card_cli.py +66 -30
  163. metaflow/plugins/cards/card_creator.py +25 -1
  164. metaflow/plugins/cards/card_datastore.py +21 -49
  165. metaflow/plugins/cards/card_decorator.py +132 -8
  166. metaflow/plugins/cards/card_modules/basic.py +112 -17
  167. metaflow/plugins/cards/card_modules/bundle.css +1 -1
  168. metaflow/plugins/cards/card_modules/card.py +16 -1
  169. metaflow/plugins/cards/card_modules/chevron/renderer.py +1 -1
  170. metaflow/plugins/cards/card_modules/components.py +665 -28
  171. metaflow/plugins/cards/card_modules/convert_to_native_type.py +36 -7
  172. metaflow/plugins/cards/card_modules/json_viewer.py +232 -0
  173. metaflow/plugins/cards/card_modules/main.css +1 -0
  174. metaflow/plugins/cards/card_modules/main.js +68 -49
  175. metaflow/plugins/cards/card_modules/renderer_tools.py +1 -0
  176. metaflow/plugins/cards/card_modules/test_cards.py +26 -12
  177. metaflow/plugins/cards/card_server.py +39 -14
  178. metaflow/plugins/cards/component_serializer.py +2 -9
  179. metaflow/plugins/cards/metadata.py +22 -0
  180. metaflow/plugins/catch_decorator.py +9 -0
  181. metaflow/plugins/datastores/azure_storage.py +10 -1
  182. metaflow/plugins/datastores/gs_storage.py +6 -2
  183. metaflow/plugins/datastores/local_storage.py +12 -6
  184. metaflow/plugins/datastores/spin_storage.py +12 -0
  185. metaflow/plugins/datatools/local.py +2 -0
  186. metaflow/plugins/datatools/s3/s3.py +126 -75
  187. metaflow/plugins/datatools/s3/s3op.py +254 -121
  188. metaflow/plugins/env_escape/__init__.py +3 -3
  189. metaflow/plugins/env_escape/client_modules.py +102 -72
  190. metaflow/plugins/env_escape/server.py +7 -0
  191. metaflow/plugins/env_escape/stub.py +24 -5
  192. metaflow/plugins/events_decorator.py +343 -185
  193. metaflow/plugins/exit_hook/__init__.py +0 -0
  194. metaflow/plugins/exit_hook/exit_hook_decorator.py +46 -0
  195. metaflow/plugins/exit_hook/exit_hook_script.py +52 -0
  196. metaflow/plugins/gcp/__init__.py +1 -1
  197. metaflow/plugins/gcp/gcp_secret_manager_secrets_provider.py +11 -6
  198. metaflow/plugins/gcp/gs_tail.py +10 -6
  199. metaflow/plugins/gcp/includefile_support.py +3 -0
  200. metaflow/plugins/kubernetes/kube_utils.py +108 -0
  201. metaflow/plugins/kubernetes/kubernetes.py +411 -130
  202. metaflow/plugins/kubernetes/kubernetes_cli.py +168 -36
  203. metaflow/plugins/kubernetes/kubernetes_client.py +104 -2
  204. metaflow/plugins/kubernetes/kubernetes_decorator.py +246 -88
  205. metaflow/plugins/kubernetes/kubernetes_job.py +253 -581
  206. metaflow/plugins/kubernetes/kubernetes_jobsets.py +1071 -0
  207. metaflow/plugins/kubernetes/spot_metadata_cli.py +69 -0
  208. metaflow/plugins/kubernetes/spot_monitor_sidecar.py +109 -0
  209. metaflow/plugins/logs_cli.py +359 -0
  210. metaflow/plugins/{metadata → metadata_providers}/local.py +144 -84
  211. metaflow/plugins/{metadata → metadata_providers}/service.py +103 -26
  212. metaflow/plugins/metadata_providers/spin.py +16 -0
  213. metaflow/plugins/package_cli.py +36 -24
  214. metaflow/plugins/parallel_decorator.py +128 -11
  215. metaflow/plugins/parsers.py +16 -0
  216. metaflow/plugins/project_decorator.py +51 -5
  217. metaflow/plugins/pypi/bootstrap.py +357 -105
  218. metaflow/plugins/pypi/conda_decorator.py +82 -81
  219. metaflow/plugins/pypi/conda_environment.py +187 -52
  220. metaflow/plugins/pypi/micromamba.py +157 -47
  221. metaflow/plugins/pypi/parsers.py +268 -0
  222. metaflow/plugins/pypi/pip.py +88 -13
  223. metaflow/plugins/pypi/pypi_decorator.py +37 -1
  224. metaflow/plugins/pypi/utils.py +48 -2
  225. metaflow/plugins/resources_decorator.py +2 -2
  226. metaflow/plugins/secrets/__init__.py +3 -0
  227. metaflow/plugins/secrets/secrets_decorator.py +26 -181
  228. metaflow/plugins/secrets/secrets_func.py +49 -0
  229. metaflow/plugins/secrets/secrets_spec.py +101 -0
  230. metaflow/plugins/secrets/utils.py +74 -0
  231. metaflow/plugins/tag_cli.py +4 -7
  232. metaflow/plugins/test_unbounded_foreach_decorator.py +41 -6
  233. metaflow/plugins/timeout_decorator.py +3 -3
  234. metaflow/plugins/uv/__init__.py +0 -0
  235. metaflow/plugins/uv/bootstrap.py +128 -0
  236. metaflow/plugins/uv/uv_environment.py +72 -0
  237. metaflow/procpoll.py +1 -1
  238. metaflow/pylint_wrapper.py +5 -1
  239. metaflow/runner/__init__.py +0 -0
  240. metaflow/runner/click_api.py +717 -0
  241. metaflow/runner/deployer.py +470 -0
  242. metaflow/runner/deployer_impl.py +201 -0
  243. metaflow/runner/metaflow_runner.py +714 -0
  244. metaflow/runner/nbdeploy.py +132 -0
  245. metaflow/runner/nbrun.py +225 -0
  246. metaflow/runner/subprocess_manager.py +650 -0
  247. metaflow/runner/utils.py +335 -0
  248. metaflow/runtime.py +1078 -260
  249. metaflow/sidecar/sidecar_worker.py +1 -1
  250. metaflow/system/__init__.py +5 -0
  251. metaflow/system/system_logger.py +85 -0
  252. metaflow/system/system_monitor.py +108 -0
  253. metaflow/system/system_utils.py +19 -0
  254. metaflow/task.py +521 -225
  255. metaflow/tracing/__init__.py +7 -7
  256. metaflow/tracing/span_exporter.py +31 -38
  257. metaflow/tracing/tracing_modules.py +38 -43
  258. metaflow/tuple_util.py +27 -0
  259. metaflow/user_configs/__init__.py +0 -0
  260. metaflow/user_configs/config_options.py +563 -0
  261. metaflow/user_configs/config_parameters.py +598 -0
  262. metaflow/user_decorators/__init__.py +0 -0
  263. metaflow/user_decorators/common.py +144 -0
  264. metaflow/user_decorators/mutable_flow.py +512 -0
  265. metaflow/user_decorators/mutable_step.py +424 -0
  266. metaflow/user_decorators/user_flow_decorator.py +264 -0
  267. metaflow/user_decorators/user_step_decorator.py +749 -0
  268. metaflow/util.py +243 -27
  269. metaflow/vendor.py +23 -7
  270. metaflow/version.py +1 -1
  271. ob_metaflow-2.19.7.1rc0.data/data/share/metaflow/devtools/Makefile +355 -0
  272. ob_metaflow-2.19.7.1rc0.data/data/share/metaflow/devtools/Tiltfile +726 -0
  273. ob_metaflow-2.19.7.1rc0.data/data/share/metaflow/devtools/pick_services.sh +105 -0
  274. ob_metaflow-2.19.7.1rc0.dist-info/METADATA +87 -0
  275. ob_metaflow-2.19.7.1rc0.dist-info/RECORD +445 -0
  276. {ob_metaflow-2.11.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/WHEEL +1 -1
  277. {ob_metaflow-2.11.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/entry_points.txt +1 -0
  278. metaflow/_vendor/v3_5/__init__.py +0 -1
  279. metaflow/_vendor/v3_5/importlib_metadata/__init__.py +0 -644
  280. metaflow/_vendor/v3_5/importlib_metadata/_compat.py +0 -152
  281. metaflow/package.py +0 -188
  282. ob_metaflow-2.11.13.1.dist-info/METADATA +0 -85
  283. ob_metaflow-2.11.13.1.dist-info/RECORD +0 -308
  284. /metaflow/_vendor/{v3_5/zipp.py → zipp.py} +0 -0
  285. /metaflow/{metadata → metadata_provider}/__init__.py +0 -0
  286. /metaflow/{metadata → metadata_provider}/util.py +0 -0
  287. /metaflow/plugins/{metadata → metadata_providers}/__init__.py +0 -0
  288. {ob_metaflow-2.11.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info/licenses}/LICENSE +0 -0
  289. {ob_metaflow-2.11.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,1070 @@
1
+ from __future__ import annotations
2
+
3
+ import collections.abc
4
+ import inspect
5
+ import sys
6
+ import types
7
+ import typing
8
+ import warnings
9
+ from enum import Enum
10
+ from inspect import Parameter, isclass, isfunction
11
+ from io import BufferedIOBase, IOBase, RawIOBase, TextIOBase
12
+ from itertools import zip_longest
13
+ from textwrap import indent
14
+ from typing import (
15
+ IO,
16
+ AbstractSet,
17
+ Any,
18
+ BinaryIO,
19
+ Callable,
20
+ Dict,
21
+ ForwardRef,
22
+ List,
23
+ Mapping,
24
+ MutableMapping,
25
+ NewType,
26
+ Optional,
27
+ Sequence,
28
+ Set,
29
+ TextIO,
30
+ Tuple,
31
+ Type,
32
+ TypeVar,
33
+ Union,
34
+ )
35
+ from unittest.mock import Mock
36
+
37
+ from metaflow._vendor import typing_extensions
38
+
39
+ # Must use this because typing.is_typeddict does not recognize
40
+ # TypedDict from typing_extensions, and as of version 4.12.0
41
+ # typing_extensions.TypedDict is different from typing.TypedDict
42
+ # on all versions.
43
+ from metaflow._vendor.typing_extensions import is_typeddict
44
+
45
+ from ._config import ForwardRefPolicy
46
+ from ._exceptions import TypeCheckError, TypeHintWarning
47
+ from ._memo import TypeCheckMemo
48
+ from ._utils import evaluate_forwardref, get_stacklevel, get_type_name, qualified_name
49
+
50
+ if sys.version_info >= (3, 11):
51
+ from typing import (
52
+ Annotated,
53
+ NotRequired,
54
+ TypeAlias,
55
+ get_args,
56
+ get_origin,
57
+ )
58
+
59
+ SubclassableAny = Any
60
+ else:
61
+ from metaflow._vendor.typing_extensions import (
62
+ Annotated,
63
+ NotRequired,
64
+ TypeAlias,
65
+ get_args,
66
+ get_origin,
67
+ )
68
+ from metaflow._vendor.typing_extensions import Any as SubclassableAny
69
+
70
+ if sys.version_info >= (3, 10):
71
+ from importlib.metadata import entry_points
72
+ from typing import ParamSpec
73
+ else:
74
+ from metaflow._vendor.importlib_metadata import entry_points
75
+ from metaflow._vendor.typing_extensions import ParamSpec
76
+
77
+ TypeCheckerCallable: TypeAlias = Callable[
78
+ [Any, Any, Tuple[Any, ...], TypeCheckMemo], Any
79
+ ]
80
+ TypeCheckLookupCallback: TypeAlias = Callable[
81
+ [Any, Tuple[Any, ...], Tuple[Any, ...]], Optional[TypeCheckerCallable]
82
+ ]
83
+
84
+ checker_lookup_functions: list[TypeCheckLookupCallback] = []
85
+ generic_alias_types: tuple[type, ...] = (type(List), type(List[Any]))
86
+ if sys.version_info >= (3, 9):
87
+ generic_alias_types += (types.GenericAlias,)
88
+
89
+ # Sentinel
90
+ _missing = object()
91
+
92
+ # Lifted from mypy.sharedparse
93
+ BINARY_MAGIC_METHODS = {
94
+ "__add__",
95
+ "__and__",
96
+ "__cmp__",
97
+ "__divmod__",
98
+ "__div__",
99
+ "__eq__",
100
+ "__floordiv__",
101
+ "__ge__",
102
+ "__gt__",
103
+ "__iadd__",
104
+ "__iand__",
105
+ "__idiv__",
106
+ "__ifloordiv__",
107
+ "__ilshift__",
108
+ "__imatmul__",
109
+ "__imod__",
110
+ "__imul__",
111
+ "__ior__",
112
+ "__ipow__",
113
+ "__irshift__",
114
+ "__isub__",
115
+ "__itruediv__",
116
+ "__ixor__",
117
+ "__le__",
118
+ "__lshift__",
119
+ "__lt__",
120
+ "__matmul__",
121
+ "__mod__",
122
+ "__mul__",
123
+ "__ne__",
124
+ "__or__",
125
+ "__pow__",
126
+ "__radd__",
127
+ "__rand__",
128
+ "__rdiv__",
129
+ "__rfloordiv__",
130
+ "__rlshift__",
131
+ "__rmatmul__",
132
+ "__rmod__",
133
+ "__rmul__",
134
+ "__ror__",
135
+ "__rpow__",
136
+ "__rrshift__",
137
+ "__rshift__",
138
+ "__rsub__",
139
+ "__rtruediv__",
140
+ "__rxor__",
141
+ "__sub__",
142
+ "__truediv__",
143
+ "__xor__",
144
+ }
145
+
146
+
147
+ def check_callable(
148
+ value: Any,
149
+ origin_type: Any,
150
+ args: tuple[Any, ...],
151
+ memo: TypeCheckMemo,
152
+ ) -> None:
153
+ if not callable(value):
154
+ raise TypeCheckError("is not callable")
155
+
156
+ if args:
157
+ try:
158
+ signature = inspect.signature(value)
159
+ except (TypeError, ValueError):
160
+ return
161
+
162
+ argument_types = args[0]
163
+ if isinstance(argument_types, list) and not any(
164
+ type(item) is ParamSpec for item in argument_types
165
+ ):
166
+ # The callable must not have keyword-only arguments without defaults
167
+ unfulfilled_kwonlyargs = [
168
+ param.name
169
+ for param in signature.parameters.values()
170
+ if param.kind == Parameter.KEYWORD_ONLY
171
+ and param.default == Parameter.empty
172
+ ]
173
+ if unfulfilled_kwonlyargs:
174
+ raise TypeCheckError(
175
+ f"has mandatory keyword-only arguments in its declaration: "
176
+ f'{", ".join(unfulfilled_kwonlyargs)}'
177
+ )
178
+
179
+ num_positional_args = num_mandatory_pos_args = 0
180
+ has_varargs = False
181
+ for param in signature.parameters.values():
182
+ if param.kind in (
183
+ Parameter.POSITIONAL_ONLY,
184
+ Parameter.POSITIONAL_OR_KEYWORD,
185
+ ):
186
+ num_positional_args += 1
187
+ if param.default is Parameter.empty:
188
+ num_mandatory_pos_args += 1
189
+ elif param.kind == Parameter.VAR_POSITIONAL:
190
+ has_varargs = True
191
+
192
+ if num_mandatory_pos_args > len(argument_types):
193
+ raise TypeCheckError(
194
+ f"has too many mandatory positional arguments in its declaration; "
195
+ f"expected {len(argument_types)} but {num_mandatory_pos_args} "
196
+ f"mandatory positional argument(s) declared"
197
+ )
198
+ elif not has_varargs and num_positional_args < len(argument_types):
199
+ raise TypeCheckError(
200
+ f"has too few arguments in its declaration; expected "
201
+ f"{len(argument_types)} but {num_positional_args} argument(s) "
202
+ f"declared"
203
+ )
204
+
205
+
206
+ def check_mapping(
207
+ value: Any,
208
+ origin_type: Any,
209
+ args: tuple[Any, ...],
210
+ memo: TypeCheckMemo,
211
+ ) -> None:
212
+ if origin_type is Dict or origin_type is dict:
213
+ if not isinstance(value, dict):
214
+ raise TypeCheckError("is not a dict")
215
+ if origin_type is MutableMapping or origin_type is collections.abc.MutableMapping:
216
+ if not isinstance(value, collections.abc.MutableMapping):
217
+ raise TypeCheckError("is not a mutable mapping")
218
+ elif not isinstance(value, collections.abc.Mapping):
219
+ raise TypeCheckError("is not a mapping")
220
+
221
+ if args:
222
+ key_type, value_type = args
223
+ if key_type is not Any or value_type is not Any:
224
+ samples = memo.config.collection_check_strategy.iterate_samples(
225
+ value.items()
226
+ )
227
+ for k, v in samples:
228
+ try:
229
+ check_type_internal(k, key_type, memo)
230
+ except TypeCheckError as exc:
231
+ exc.append_path_element(f"key {k!r}")
232
+ raise
233
+
234
+ try:
235
+ check_type_internal(v, value_type, memo)
236
+ except TypeCheckError as exc:
237
+ exc.append_path_element(f"value of key {k!r}")
238
+ raise
239
+
240
+
241
+ def check_typed_dict(
242
+ value: Any,
243
+ origin_type: Any,
244
+ args: tuple[Any, ...],
245
+ memo: TypeCheckMemo,
246
+ ) -> None:
247
+ if not isinstance(value, dict):
248
+ raise TypeCheckError("is not a dict")
249
+
250
+ declared_keys = frozenset(origin_type.__annotations__)
251
+ if hasattr(origin_type, "__required_keys__"):
252
+ required_keys = set(origin_type.__required_keys__)
253
+ else: # py3.8 and lower
254
+ required_keys = set(declared_keys) if origin_type.__total__ else set()
255
+
256
+ existing_keys = set(value)
257
+ extra_keys = existing_keys - declared_keys
258
+ if extra_keys:
259
+ keys_formatted = ", ".join(f'"{key}"' for key in sorted(extra_keys, key=repr))
260
+ raise TypeCheckError(f"has unexpected extra key(s): {keys_formatted}")
261
+
262
+ # Detect NotRequired fields which are hidden by get_type_hints()
263
+ type_hints: dict[str, type] = {}
264
+ for key, annotation in origin_type.__annotations__.items():
265
+ if isinstance(annotation, ForwardRef):
266
+ annotation = evaluate_forwardref(annotation, memo)
267
+ if get_origin(annotation) is NotRequired:
268
+ required_keys.discard(key)
269
+ annotation = get_args(annotation)[0]
270
+
271
+ type_hints[key] = annotation
272
+
273
+ missing_keys = required_keys - existing_keys
274
+ if missing_keys:
275
+ keys_formatted = ", ".join(f'"{key}"' for key in sorted(missing_keys, key=repr))
276
+ raise TypeCheckError(f"is missing required key(s): {keys_formatted}")
277
+
278
+ for key, argtype in type_hints.items():
279
+ argvalue = value.get(key, _missing)
280
+ if argvalue is not _missing:
281
+ try:
282
+ check_type_internal(argvalue, argtype, memo)
283
+ except TypeCheckError as exc:
284
+ exc.append_path_element(f"value of key {key!r}")
285
+ raise
286
+
287
+
288
+ def check_list(
289
+ value: Any,
290
+ origin_type: Any,
291
+ args: tuple[Any, ...],
292
+ memo: TypeCheckMemo,
293
+ ) -> None:
294
+ if not isinstance(value, list):
295
+ raise TypeCheckError("is not a list")
296
+
297
+ if args and args != (Any,):
298
+ samples = memo.config.collection_check_strategy.iterate_samples(value)
299
+ for i, v in enumerate(samples):
300
+ try:
301
+ check_type_internal(v, args[0], memo)
302
+ except TypeCheckError as exc:
303
+ exc.append_path_element(f"item {i}")
304
+ raise
305
+
306
+
307
+ def check_sequence(
308
+ value: Any,
309
+ origin_type: Any,
310
+ args: tuple[Any, ...],
311
+ memo: TypeCheckMemo,
312
+ ) -> None:
313
+ if not isinstance(value, collections.abc.Sequence):
314
+ raise TypeCheckError("is not a sequence")
315
+
316
+ if args and args != (Any,):
317
+ samples = memo.config.collection_check_strategy.iterate_samples(value)
318
+ for i, v in enumerate(samples):
319
+ try:
320
+ check_type_internal(v, args[0], memo)
321
+ except TypeCheckError as exc:
322
+ exc.append_path_element(f"item {i}")
323
+ raise
324
+
325
+
326
+ def check_set(
327
+ value: Any,
328
+ origin_type: Any,
329
+ args: tuple[Any, ...],
330
+ memo: TypeCheckMemo,
331
+ ) -> None:
332
+ if origin_type is frozenset:
333
+ if not isinstance(value, frozenset):
334
+ raise TypeCheckError("is not a frozenset")
335
+ elif not isinstance(value, AbstractSet):
336
+ raise TypeCheckError("is not a set")
337
+
338
+ if args and args != (Any,):
339
+ samples = memo.config.collection_check_strategy.iterate_samples(value)
340
+ for v in samples:
341
+ try:
342
+ check_type_internal(v, args[0], memo)
343
+ except TypeCheckError as exc:
344
+ exc.append_path_element(f"[{v}]")
345
+ raise
346
+
347
+
348
+ def check_tuple(
349
+ value: Any,
350
+ origin_type: Any,
351
+ args: tuple[Any, ...],
352
+ memo: TypeCheckMemo,
353
+ ) -> None:
354
+ # Specialized check for NamedTuples
355
+ if field_types := getattr(origin_type, "__annotations__", None):
356
+ if not isinstance(value, origin_type):
357
+ raise TypeCheckError(
358
+ f"is not a named tuple of type {qualified_name(origin_type)}"
359
+ )
360
+
361
+ for name, field_type in field_types.items():
362
+ try:
363
+ check_type_internal(getattr(value, name), field_type, memo)
364
+ except TypeCheckError as exc:
365
+ exc.append_path_element(f"attribute {name!r}")
366
+ raise
367
+
368
+ return
369
+ elif not isinstance(value, tuple):
370
+ raise TypeCheckError("is not a tuple")
371
+
372
+ if args:
373
+ use_ellipsis = args[-1] is Ellipsis
374
+ tuple_params = args[: -1 if use_ellipsis else None]
375
+ else:
376
+ # Unparametrized Tuple or plain tuple
377
+ return
378
+
379
+ if use_ellipsis:
380
+ element_type = tuple_params[0]
381
+ samples = memo.config.collection_check_strategy.iterate_samples(value)
382
+ for i, element in enumerate(samples):
383
+ try:
384
+ check_type_internal(element, element_type, memo)
385
+ except TypeCheckError as exc:
386
+ exc.append_path_element(f"item {i}")
387
+ raise
388
+ elif tuple_params == ((),):
389
+ if value != ():
390
+ raise TypeCheckError("is not an empty tuple")
391
+ else:
392
+ if len(value) != len(tuple_params):
393
+ raise TypeCheckError(
394
+ f"has wrong number of elements (expected {len(tuple_params)}, got "
395
+ f"{len(value)} instead)"
396
+ )
397
+
398
+ for i, (element, element_type) in enumerate(zip(value, tuple_params)):
399
+ try:
400
+ check_type_internal(element, element_type, memo)
401
+ except TypeCheckError as exc:
402
+ exc.append_path_element(f"item {i}")
403
+ raise
404
+
405
+
406
+ def check_union(
407
+ value: Any,
408
+ origin_type: Any,
409
+ args: tuple[Any, ...],
410
+ memo: TypeCheckMemo,
411
+ ) -> None:
412
+ errors: dict[str, TypeCheckError] = {}
413
+ try:
414
+ for type_ in args:
415
+ try:
416
+ check_type_internal(value, type_, memo)
417
+ return
418
+ except TypeCheckError as exc:
419
+ errors[get_type_name(type_)] = exc
420
+
421
+ formatted_errors = indent(
422
+ "\n".join(f"{key}: {error}" for key, error in errors.items()), " "
423
+ )
424
+ finally:
425
+ del errors # avoid creating ref cycle
426
+ raise TypeCheckError(f"did not match any element in the union:\n{formatted_errors}")
427
+
428
+
429
+ def check_uniontype(
430
+ value: Any,
431
+ origin_type: Any,
432
+ args: tuple[Any, ...],
433
+ memo: TypeCheckMemo,
434
+ ) -> None:
435
+ errors: dict[str, TypeCheckError] = {}
436
+ for type_ in args:
437
+ try:
438
+ check_type_internal(value, type_, memo)
439
+ return
440
+ except TypeCheckError as exc:
441
+ errors[get_type_name(type_)] = exc
442
+
443
+ formatted_errors = indent(
444
+ "\n".join(f"{key}: {error}" for key, error in errors.items()), " "
445
+ )
446
+ raise TypeCheckError(f"did not match any element in the union:\n{formatted_errors}")
447
+
448
+
449
+ def check_class(
450
+ value: Any,
451
+ origin_type: Any,
452
+ args: tuple[Any, ...],
453
+ memo: TypeCheckMemo,
454
+ ) -> None:
455
+ if not isclass(value) and not isinstance(value, generic_alias_types):
456
+ raise TypeCheckError("is not a class")
457
+
458
+ if not args:
459
+ return
460
+
461
+ if isinstance(args[0], ForwardRef):
462
+ expected_class = evaluate_forwardref(args[0], memo)
463
+ else:
464
+ expected_class = args[0]
465
+
466
+ if expected_class is Any:
467
+ return
468
+ elif getattr(expected_class, "_is_protocol", False):
469
+ check_protocol(value, expected_class, (), memo)
470
+ elif isinstance(expected_class, TypeVar):
471
+ check_typevar(value, expected_class, (), memo, subclass_check=True)
472
+ elif get_origin(expected_class) is Union:
473
+ errors: dict[str, TypeCheckError] = {}
474
+ for arg in get_args(expected_class):
475
+ if arg is Any:
476
+ return
477
+
478
+ try:
479
+ check_class(value, type, (arg,), memo)
480
+ return
481
+ except TypeCheckError as exc:
482
+ errors[get_type_name(arg)] = exc
483
+ else:
484
+ formatted_errors = indent(
485
+ "\n".join(f"{key}: {error}" for key, error in errors.items()), " "
486
+ )
487
+ raise TypeCheckError(
488
+ f"did not match any element in the union:\n{formatted_errors}"
489
+ )
490
+ elif not issubclass(value, expected_class): # type: ignore[arg-type]
491
+ raise TypeCheckError(f"is not a subclass of {qualified_name(expected_class)}")
492
+
493
+
494
+ def check_newtype(
495
+ value: Any,
496
+ origin_type: Any,
497
+ args: tuple[Any, ...],
498
+ memo: TypeCheckMemo,
499
+ ) -> None:
500
+ check_type_internal(value, origin_type.__supertype__, memo)
501
+
502
+
503
+ def check_instance(
504
+ value: Any,
505
+ origin_type: Any,
506
+ args: tuple[Any, ...],
507
+ memo: TypeCheckMemo,
508
+ ) -> None:
509
+ if not isinstance(value, origin_type):
510
+ raise TypeCheckError(f"is not an instance of {qualified_name(origin_type)}")
511
+
512
+
513
+ def check_typevar(
514
+ value: Any,
515
+ origin_type: TypeVar,
516
+ args: tuple[Any, ...],
517
+ memo: TypeCheckMemo,
518
+ *,
519
+ subclass_check: bool = False,
520
+ ) -> None:
521
+ if origin_type.__bound__ is not None:
522
+ annotation = (
523
+ Type[origin_type.__bound__] if subclass_check else origin_type.__bound__
524
+ )
525
+ check_type_internal(value, annotation, memo)
526
+ elif origin_type.__constraints__:
527
+ for constraint in origin_type.__constraints__:
528
+ annotation = Type[constraint] if subclass_check else constraint
529
+ try:
530
+ check_type_internal(value, annotation, memo)
531
+ except TypeCheckError:
532
+ pass
533
+ else:
534
+ break
535
+ else:
536
+ formatted_constraints = ", ".join(
537
+ get_type_name(constraint) for constraint in origin_type.__constraints__
538
+ )
539
+ raise TypeCheckError(
540
+ f"does not match any of the constraints " f"({formatted_constraints})"
541
+ )
542
+
543
+
544
+ def _is_literal_type(typ: object) -> bool:
545
+ return typ is typing.Literal or typ is typing_extensions.Literal
546
+
547
+
548
+ def check_literal(
549
+ value: Any,
550
+ origin_type: Any,
551
+ args: tuple[Any, ...],
552
+ memo: TypeCheckMemo,
553
+ ) -> None:
554
+ def get_literal_args(literal_args: tuple[Any, ...]) -> tuple[Any, ...]:
555
+ retval: list[Any] = []
556
+ for arg in literal_args:
557
+ if _is_literal_type(get_origin(arg)):
558
+ retval.extend(get_literal_args(arg.__args__))
559
+ elif arg is None or isinstance(arg, (int, str, bytes, bool, Enum)):
560
+ retval.append(arg)
561
+ else:
562
+ raise TypeError(
563
+ f"Illegal literal value: {arg}"
564
+ ) # TypeError here is deliberate
565
+
566
+ return tuple(retval)
567
+
568
+ final_args = tuple(get_literal_args(args))
569
+ try:
570
+ index = final_args.index(value)
571
+ except ValueError:
572
+ pass
573
+ else:
574
+ if type(final_args[index]) is type(value):
575
+ return
576
+
577
+ formatted_args = ", ".join(repr(arg) for arg in final_args)
578
+ raise TypeCheckError(f"is not any of ({formatted_args})") from None
579
+
580
+
581
+ def check_literal_string(
582
+ value: Any,
583
+ origin_type: Any,
584
+ args: tuple[Any, ...],
585
+ memo: TypeCheckMemo,
586
+ ) -> None:
587
+ check_type_internal(value, str, memo)
588
+
589
+
590
+ def check_typeguard(
591
+ value: Any,
592
+ origin_type: Any,
593
+ args: tuple[Any, ...],
594
+ memo: TypeCheckMemo,
595
+ ) -> None:
596
+ check_type_internal(value, bool, memo)
597
+
598
+
599
+ def check_none(
600
+ value: Any,
601
+ origin_type: Any,
602
+ args: tuple[Any, ...],
603
+ memo: TypeCheckMemo,
604
+ ) -> None:
605
+ if value is not None:
606
+ raise TypeCheckError("is not None")
607
+
608
+
609
+ def check_number(
610
+ value: Any,
611
+ origin_type: Any,
612
+ args: tuple[Any, ...],
613
+ memo: TypeCheckMemo,
614
+ ) -> None:
615
+ if origin_type is complex and not isinstance(value, (complex, float, int)):
616
+ raise TypeCheckError("is neither complex, float or int")
617
+ elif origin_type is float and not isinstance(value, (float, int)):
618
+ raise TypeCheckError("is neither float or int")
619
+
620
+
621
+ def check_io(
622
+ value: Any,
623
+ origin_type: Any,
624
+ args: tuple[Any, ...],
625
+ memo: TypeCheckMemo,
626
+ ) -> None:
627
+ if origin_type is TextIO or (origin_type is IO and args == (str,)):
628
+ if not isinstance(value, TextIOBase):
629
+ raise TypeCheckError("is not a text based I/O object")
630
+ elif origin_type is BinaryIO or (origin_type is IO and args == (bytes,)):
631
+ if not isinstance(value, (RawIOBase, BufferedIOBase)):
632
+ raise TypeCheckError("is not a binary I/O object")
633
+ elif not isinstance(value, IOBase):
634
+ raise TypeCheckError("is not an I/O object")
635
+
636
+
637
+ def check_signature_compatible(
638
+ subject_callable: Callable[..., Any], protocol: type, attrname: str
639
+ ) -> None:
640
+ subject_sig = inspect.signature(subject_callable)
641
+ protocol_sig = inspect.signature(getattr(protocol, attrname))
642
+ protocol_type: typing.Literal["instance", "class", "static"] = "instance"
643
+ subject_type: typing.Literal["instance", "class", "static"] = "instance"
644
+
645
+ # Check if the protocol-side method is a class method or static method
646
+ if attrname in protocol.__dict__:
647
+ descriptor = protocol.__dict__[attrname]
648
+ if isinstance(descriptor, staticmethod):
649
+ protocol_type = "static"
650
+ elif isinstance(descriptor, classmethod):
651
+ protocol_type = "class"
652
+
653
+ # Check if the subject-side method is a class method or static method
654
+ if inspect.ismethod(subject_callable) and inspect.isclass(
655
+ subject_callable.__self__
656
+ ):
657
+ subject_type = "class"
658
+ elif not hasattr(subject_callable, "__self__"):
659
+ subject_type = "static"
660
+
661
+ if protocol_type == "instance" and subject_type != "instance":
662
+ raise TypeCheckError(
663
+ f"should be an instance method but it's a {subject_type} method"
664
+ )
665
+ elif protocol_type != "instance" and subject_type == "instance":
666
+ raise TypeCheckError(
667
+ f"should be a {protocol_type} method but it's an instance method"
668
+ )
669
+
670
+ expected_varargs = any(
671
+ param
672
+ for param in protocol_sig.parameters.values()
673
+ if param.kind is Parameter.VAR_POSITIONAL
674
+ )
675
+ has_varargs = any(
676
+ param
677
+ for param in subject_sig.parameters.values()
678
+ if param.kind is Parameter.VAR_POSITIONAL
679
+ )
680
+ if expected_varargs and not has_varargs:
681
+ raise TypeCheckError("should accept variable positional arguments but doesn't")
682
+
683
+ protocol_has_varkwargs = any(
684
+ param
685
+ for param in protocol_sig.parameters.values()
686
+ if param.kind is Parameter.VAR_KEYWORD
687
+ )
688
+ subject_has_varkwargs = any(
689
+ param
690
+ for param in subject_sig.parameters.values()
691
+ if param.kind is Parameter.VAR_KEYWORD
692
+ )
693
+ if protocol_has_varkwargs and not subject_has_varkwargs:
694
+ raise TypeCheckError("should accept variable keyword arguments but doesn't")
695
+
696
+ # Check that the callable has at least the expect amount of positional-only
697
+ # arguments (and no extra positional-only arguments without default values)
698
+ if not has_varargs:
699
+ protocol_args = [
700
+ param
701
+ for param in protocol_sig.parameters.values()
702
+ if param.kind
703
+ in (Parameter.POSITIONAL_ONLY, Parameter.POSITIONAL_OR_KEYWORD)
704
+ ]
705
+ subject_args = [
706
+ param
707
+ for param in subject_sig.parameters.values()
708
+ if param.kind
709
+ in (Parameter.POSITIONAL_ONLY, Parameter.POSITIONAL_OR_KEYWORD)
710
+ ]
711
+
712
+ # Remove the "self" parameter from the protocol arguments to match
713
+ if protocol_type == "instance":
714
+ protocol_args.pop(0)
715
+
716
+ for protocol_arg, subject_arg in zip_longest(protocol_args, subject_args):
717
+ if protocol_arg is None:
718
+ if subject_arg.default is Parameter.empty:
719
+ raise TypeCheckError("has too many mandatory positional arguments")
720
+
721
+ break
722
+
723
+ if subject_arg is None:
724
+ raise TypeCheckError("has too few positional arguments")
725
+
726
+ if (
727
+ protocol_arg.kind is Parameter.POSITIONAL_OR_KEYWORD
728
+ and subject_arg.kind is Parameter.POSITIONAL_ONLY
729
+ ):
730
+ raise TypeCheckError(
731
+ f"has an argument ({subject_arg.name}) that should not be "
732
+ f"positional-only"
733
+ )
734
+
735
+ if (
736
+ protocol_arg.kind is Parameter.POSITIONAL_OR_KEYWORD
737
+ and protocol_arg.name != subject_arg.name
738
+ ):
739
+ raise TypeCheckError(
740
+ f"has a positional argument ({subject_arg.name}) that should be "
741
+ f"named {protocol_arg.name!r} at this position"
742
+ )
743
+
744
+ protocol_kwonlyargs = {
745
+ param.name: param
746
+ for param in protocol_sig.parameters.values()
747
+ if param.kind is Parameter.KEYWORD_ONLY
748
+ }
749
+ subject_kwonlyargs = {
750
+ param.name: param
751
+ for param in subject_sig.parameters.values()
752
+ if param.kind is Parameter.KEYWORD_ONLY
753
+ }
754
+ if not subject_has_varkwargs:
755
+ # Check that the signature has at least the required keyword-only arguments, and
756
+ # no extra mandatory keyword-only arguments
757
+ if missing_kwonlyargs := [
758
+ param.name
759
+ for param in protocol_kwonlyargs.values()
760
+ if param.name not in subject_kwonlyargs
761
+ ]:
762
+ raise TypeCheckError(
763
+ "is missing keyword-only arguments: " + ", ".join(missing_kwonlyargs)
764
+ )
765
+
766
+ if not protocol_has_varkwargs:
767
+ if extra_kwonlyargs := [
768
+ param.name
769
+ for param in subject_kwonlyargs.values()
770
+ if param.default is Parameter.empty
771
+ and param.name not in protocol_kwonlyargs
772
+ ]:
773
+ raise TypeCheckError(
774
+ "has mandatory keyword-only arguments not present in the protocol: "
775
+ + ", ".join(extra_kwonlyargs)
776
+ )
777
+
778
+
779
+ def check_protocol(
780
+ value: Any,
781
+ origin_type: Any,
782
+ args: tuple[Any, ...],
783
+ memo: TypeCheckMemo,
784
+ ) -> None:
785
+ origin_annotations = typing.get_type_hints(origin_type)
786
+ for attrname in sorted(typing_extensions.get_protocol_members(origin_type)):
787
+ if (annotation := origin_annotations.get(attrname)) is not None:
788
+ try:
789
+ subject_member = getattr(value, attrname)
790
+ except AttributeError:
791
+ raise TypeCheckError(
792
+ f"is not compatible with the {origin_type.__qualname__} "
793
+ f"protocol because it has no attribute named {attrname!r}"
794
+ ) from None
795
+
796
+ try:
797
+ check_type_internal(subject_member, annotation, memo)
798
+ except TypeCheckError as exc:
799
+ raise TypeCheckError(
800
+ f"is not compatible with the {origin_type.__qualname__} "
801
+ f"protocol because its {attrname!r} attribute {exc}"
802
+ ) from None
803
+ elif callable(getattr(origin_type, attrname)):
804
+ try:
805
+ subject_member = getattr(value, attrname)
806
+ except AttributeError:
807
+ raise TypeCheckError(
808
+ f"is not compatible with the {origin_type.__qualname__} "
809
+ f"protocol because it has no method named {attrname!r}"
810
+ ) from None
811
+
812
+ if not callable(subject_member):
813
+ raise TypeCheckError(
814
+ f"is not compatible with the {origin_type.__qualname__} "
815
+ f"protocol because its {attrname!r} attribute is not a callable"
816
+ )
817
+
818
+ # TODO: implement assignability checks for parameter and return value
819
+ # annotations
820
+ try:
821
+ check_signature_compatible(subject_member, origin_type, attrname)
822
+ except TypeCheckError as exc:
823
+ raise TypeCheckError(
824
+ f"is not compatible with the {origin_type.__qualname__} "
825
+ f"protocol because its {attrname!r} method {exc}"
826
+ ) from None
827
+
828
+
829
+ def check_byteslike(
830
+ value: Any,
831
+ origin_type: Any,
832
+ args: tuple[Any, ...],
833
+ memo: TypeCheckMemo,
834
+ ) -> None:
835
+ if not isinstance(value, (bytearray, bytes, memoryview)):
836
+ raise TypeCheckError("is not bytes-like")
837
+
838
+
839
+ def check_self(
840
+ value: Any,
841
+ origin_type: Any,
842
+ args: tuple[Any, ...],
843
+ memo: TypeCheckMemo,
844
+ ) -> None:
845
+ if memo.self_type is None:
846
+ raise TypeCheckError("cannot be checked against Self outside of a method call")
847
+
848
+ if isclass(value):
849
+ if not issubclass(value, memo.self_type):
850
+ raise TypeCheckError(
851
+ f"is not an instance of the self type "
852
+ f"({qualified_name(memo.self_type)})"
853
+ )
854
+ elif not isinstance(value, memo.self_type):
855
+ raise TypeCheckError(
856
+ f"is not an instance of the self type ({qualified_name(memo.self_type)})"
857
+ )
858
+
859
+
860
+ def check_paramspec(
861
+ value: Any,
862
+ origin_type: Any,
863
+ args: tuple[Any, ...],
864
+ memo: TypeCheckMemo,
865
+ ) -> None:
866
+ pass # No-op for now
867
+
868
+
869
+ def check_instanceof(
870
+ value: Any,
871
+ origin_type: Any,
872
+ args: tuple[Any, ...],
873
+ memo: TypeCheckMemo,
874
+ ) -> None:
875
+ if not isinstance(value, origin_type):
876
+ raise TypeCheckError(f"is not an instance of {qualified_name(origin_type)}")
877
+
878
+
879
+ def check_type_internal(
880
+ value: Any,
881
+ annotation: Any,
882
+ memo: TypeCheckMemo,
883
+ ) -> None:
884
+ """
885
+ Check that the given object is compatible with the given type annotation.
886
+
887
+ This function should only be used by type checker callables. Applications should use
888
+ :func:`~.check_type` instead.
889
+
890
+ :param value: the value to check
891
+ :param annotation: the type annotation to check against
892
+ :param memo: a memo object containing configuration and information necessary for
893
+ looking up forward references
894
+ """
895
+
896
+ if isinstance(annotation, ForwardRef):
897
+ try:
898
+ annotation = evaluate_forwardref(annotation, memo)
899
+ except NameError:
900
+ if memo.config.forward_ref_policy is ForwardRefPolicy.ERROR:
901
+ raise
902
+ elif memo.config.forward_ref_policy is ForwardRefPolicy.WARN:
903
+ warnings.warn(
904
+ f"Cannot resolve forward reference {annotation.__forward_arg__!r}",
905
+ TypeHintWarning,
906
+ stacklevel=get_stacklevel(),
907
+ )
908
+
909
+ return
910
+
911
+ if annotation is Any or annotation is SubclassableAny or isinstance(value, Mock):
912
+ return
913
+
914
+ # Skip type checks if value is an instance of a class that inherits from Any
915
+ if not isclass(value) and SubclassableAny in type(value).__bases__:
916
+ return
917
+
918
+ extras: tuple[Any, ...]
919
+ origin_type = get_origin(annotation)
920
+ if origin_type is Annotated:
921
+ annotation, *extras_ = get_args(annotation)
922
+ extras = tuple(extras_)
923
+ origin_type = get_origin(annotation)
924
+ else:
925
+ extras = ()
926
+
927
+ if origin_type is not None:
928
+ args = get_args(annotation)
929
+
930
+ # Compatibility hack to distinguish between unparametrized and empty tuple
931
+ # (tuple[()]), necessary due to https://github.com/python/cpython/issues/91137
932
+ if origin_type in (tuple, Tuple) and annotation is not Tuple and not args:
933
+ args = ((),)
934
+ else:
935
+ origin_type = annotation
936
+ args = ()
937
+
938
+ for lookup_func in checker_lookup_functions:
939
+ checker = lookup_func(origin_type, args, extras)
940
+ if checker:
941
+ checker(value, origin_type, args, memo)
942
+ return
943
+
944
+ if isclass(origin_type):
945
+ if not isinstance(value, origin_type):
946
+ raise TypeCheckError(f"is not an instance of {qualified_name(origin_type)}")
947
+ elif type(origin_type) is str: # noqa: E721
948
+ warnings.warn(
949
+ f"Skipping type check against {origin_type!r}; this looks like a "
950
+ f"string-form forward reference imported from another module",
951
+ TypeHintWarning,
952
+ stacklevel=get_stacklevel(),
953
+ )
954
+
955
+
956
+ # Equality checks are applied to these
957
+ origin_type_checkers = {
958
+ bytes: check_byteslike,
959
+ AbstractSet: check_set,
960
+ BinaryIO: check_io,
961
+ Callable: check_callable,
962
+ collections.abc.Callable: check_callable,
963
+ complex: check_number,
964
+ dict: check_mapping,
965
+ Dict: check_mapping,
966
+ float: check_number,
967
+ frozenset: check_set,
968
+ IO: check_io,
969
+ list: check_list,
970
+ List: check_list,
971
+ typing.Literal: check_literal,
972
+ Mapping: check_mapping,
973
+ MutableMapping: check_mapping,
974
+ None: check_none,
975
+ collections.abc.Mapping: check_mapping,
976
+ collections.abc.MutableMapping: check_mapping,
977
+ Sequence: check_sequence,
978
+ collections.abc.Sequence: check_sequence,
979
+ collections.abc.Set: check_set,
980
+ set: check_set,
981
+ Set: check_set,
982
+ TextIO: check_io,
983
+ tuple: check_tuple,
984
+ Tuple: check_tuple,
985
+ type: check_class,
986
+ Type: check_class,
987
+ Union: check_union,
988
+ # On some versions of Python, these may simply be re-exports from "typing",
989
+ # but exactly which Python versions is subject to change.
990
+ # It's best to err on the safe side and just always specify these.
991
+ typing_extensions.Literal: check_literal,
992
+ typing_extensions.LiteralString: check_literal_string,
993
+ typing_extensions.Self: check_self,
994
+ typing_extensions.TypeGuard: check_typeguard,
995
+ }
996
+ if sys.version_info >= (3, 10):
997
+ origin_type_checkers[types.UnionType] = check_uniontype
998
+ origin_type_checkers[typing.TypeGuard] = check_typeguard
999
+ if sys.version_info >= (3, 11):
1000
+ origin_type_checkers.update(
1001
+ {typing.LiteralString: check_literal_string, typing.Self: check_self}
1002
+ )
1003
+
1004
+
1005
+ def builtin_checker_lookup(
1006
+ origin_type: Any, args: tuple[Any, ...], extras: tuple[Any, ...]
1007
+ ) -> TypeCheckerCallable | None:
1008
+ checker = origin_type_checkers.get(origin_type)
1009
+ if checker is not None:
1010
+ return checker
1011
+ elif is_typeddict(origin_type):
1012
+ return check_typed_dict
1013
+ elif isclass(origin_type) and issubclass(
1014
+ origin_type,
1015
+ Tuple, # type: ignore[arg-type]
1016
+ ):
1017
+ # NamedTuple
1018
+ return check_tuple
1019
+ elif getattr(origin_type, "_is_protocol", False):
1020
+ return check_protocol
1021
+ elif isinstance(origin_type, ParamSpec):
1022
+ return check_paramspec
1023
+ elif isinstance(origin_type, TypeVar):
1024
+ return check_typevar
1025
+ elif origin_type.__class__ is NewType:
1026
+ # typing.NewType on Python 3.10+
1027
+ return check_newtype
1028
+ elif (
1029
+ isfunction(origin_type)
1030
+ and getattr(origin_type, "__module__", None) == "typing"
1031
+ and getattr(origin_type, "__qualname__", "").startswith("NewType.")
1032
+ and hasattr(origin_type, "__supertype__")
1033
+ ):
1034
+ # typing.NewType on Python 3.9 and below
1035
+ return check_newtype
1036
+
1037
+ return None
1038
+
1039
+
1040
+ checker_lookup_functions.append(builtin_checker_lookup)
1041
+
1042
+
1043
+ def load_plugins() -> None:
1044
+ """
1045
+ Load all type checker lookup functions from entry points.
1046
+
1047
+ All entry points from the ``typeguard.checker_lookup`` group are loaded, and the
1048
+ returned lookup functions are added to :data:`typeguard.checker_lookup_functions`.
1049
+
1050
+ .. note:: This function is called implicitly on import, unless the
1051
+ ``TYPEGUARD_DISABLE_PLUGIN_AUTOLOAD`` environment variable is present.
1052
+ """
1053
+
1054
+ for ep in entry_points(group="typeguard.checker_lookup"):
1055
+ try:
1056
+ plugin = ep.load()
1057
+ except Exception as exc:
1058
+ warnings.warn(
1059
+ f"Failed to load plugin {ep.name!r}: " f"{qualified_name(exc)}: {exc}",
1060
+ stacklevel=2,
1061
+ )
1062
+ continue
1063
+
1064
+ if not callable(plugin):
1065
+ warnings.warn(
1066
+ f"Plugin {ep} returned a non-callable object: {plugin!r}", stacklevel=2
1067
+ )
1068
+ continue
1069
+
1070
+ checker_lookup_functions.insert(0, plugin)