metaflow 2.15.4__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.
Files changed (59) hide show
  1. metaflow/_vendor/typeguard/_checkers.py +259 -95
  2. metaflow/_vendor/typeguard/_config.py +4 -4
  3. metaflow/_vendor/typeguard/_decorators.py +8 -12
  4. metaflow/_vendor/typeguard/_functions.py +33 -32
  5. metaflow/_vendor/typeguard/_pytest_plugin.py +40 -13
  6. metaflow/_vendor/typeguard/_suppression.py +3 -5
  7. metaflow/_vendor/typeguard/_transformer.py +84 -48
  8. metaflow/_vendor/typeguard/_union_transformer.py +1 -0
  9. metaflow/_vendor/typeguard/_utils.py +13 -9
  10. metaflow/_vendor/typing_extensions.py +1088 -500
  11. metaflow/_vendor/v3_7/__init__.py +1 -0
  12. metaflow/_vendor/v3_7/importlib_metadata/__init__.py +1063 -0
  13. metaflow/_vendor/v3_7/importlib_metadata/_adapters.py +68 -0
  14. metaflow/_vendor/v3_7/importlib_metadata/_collections.py +30 -0
  15. metaflow/_vendor/v3_7/importlib_metadata/_compat.py +71 -0
  16. metaflow/_vendor/v3_7/importlib_metadata/_functools.py +104 -0
  17. metaflow/_vendor/v3_7/importlib_metadata/_itertools.py +73 -0
  18. metaflow/_vendor/v3_7/importlib_metadata/_meta.py +48 -0
  19. metaflow/_vendor/v3_7/importlib_metadata/_text.py +99 -0
  20. metaflow/_vendor/v3_7/importlib_metadata/py.typed +0 -0
  21. metaflow/_vendor/v3_7/typeguard/__init__.py +48 -0
  22. metaflow/_vendor/v3_7/typeguard/_checkers.py +906 -0
  23. metaflow/_vendor/v3_7/typeguard/_config.py +108 -0
  24. metaflow/_vendor/v3_7/typeguard/_decorators.py +237 -0
  25. metaflow/_vendor/v3_7/typeguard/_exceptions.py +42 -0
  26. metaflow/_vendor/v3_7/typeguard/_functions.py +310 -0
  27. metaflow/_vendor/v3_7/typeguard/_importhook.py +213 -0
  28. metaflow/_vendor/v3_7/typeguard/_memo.py +48 -0
  29. metaflow/_vendor/v3_7/typeguard/_pytest_plugin.py +100 -0
  30. metaflow/_vendor/v3_7/typeguard/_suppression.py +88 -0
  31. metaflow/_vendor/v3_7/typeguard/_transformer.py +1207 -0
  32. metaflow/_vendor/v3_7/typeguard/_union_transformer.py +54 -0
  33. metaflow/_vendor/v3_7/typeguard/_utils.py +169 -0
  34. metaflow/_vendor/v3_7/typeguard/py.typed +0 -0
  35. metaflow/_vendor/v3_7/typing_extensions.py +3072 -0
  36. metaflow/_vendor/v3_7/zipp.py +329 -0
  37. metaflow/cmd/develop/stubs.py +1 -1
  38. metaflow/extension_support/__init__.py +1 -1
  39. metaflow/plugins/argo/argo_client.py +9 -2
  40. metaflow/plugins/argo/argo_workflows.py +79 -28
  41. metaflow/plugins/argo/argo_workflows_cli.py +16 -25
  42. metaflow/plugins/argo/argo_workflows_deployer_objects.py +5 -2
  43. metaflow/plugins/cards/card_modules/main.js +52 -50
  44. metaflow/plugins/metadata_providers/service.py +16 -7
  45. metaflow/plugins/pypi/utils.py +4 -0
  46. metaflow/runner/click_api.py +7 -2
  47. metaflow/runner/deployer.py +3 -2
  48. metaflow/vendor.py +1 -0
  49. metaflow/version.py +1 -1
  50. {metaflow-2.15.4.data → metaflow-2.15.6.data}/data/share/metaflow/devtools/Tiltfile +4 -4
  51. metaflow-2.15.6.dist-info/METADATA +103 -0
  52. {metaflow-2.15.4.dist-info → metaflow-2.15.6.dist-info}/RECORD +58 -32
  53. {metaflow-2.15.4.dist-info → metaflow-2.15.6.dist-info}/WHEEL +1 -1
  54. metaflow-2.15.4.dist-info/METADATA +0 -110
  55. {metaflow-2.15.4.data → metaflow-2.15.6.data}/data/share/metaflow/devtools/Makefile +0 -0
  56. {metaflow-2.15.4.data → metaflow-2.15.6.data}/data/share/metaflow/devtools/pick_services.sh +0 -0
  57. {metaflow-2.15.4.dist-info → metaflow-2.15.6.dist-info}/LICENSE +0 -0
  58. {metaflow-2.15.4.dist-info → metaflow-2.15.6.dist-info}/entry_points.txt +0 -0
  59. {metaflow-2.15.4.dist-info → metaflow-2.15.6.dist-info}/top_level.txt +0 -0
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import sys
4
4
  import warnings
5
- from typing import Any, Callable, NoReturn, TypeVar, overload
5
+ from typing import Any, Callable, NoReturn, TypeVar, Union, overload
6
6
 
7
7
  from . import _suppression
8
8
  from ._checkers import BINARY_MAGIC_METHODS, check_type_internal
@@ -32,8 +32,7 @@ def check_type(
32
32
  forward_ref_policy: ForwardRefPolicy = ...,
33
33
  typecheck_fail_callback: TypeCheckFailCallback | None = ...,
34
34
  collection_check_strategy: CollectionCheckStrategy = ...,
35
- ) -> T:
36
- ...
35
+ ) -> T: ...
37
36
 
38
37
 
39
38
  @overload
@@ -44,8 +43,7 @@ def check_type(
44
43
  forward_ref_policy: ForwardRefPolicy = ...,
45
44
  typecheck_fail_callback: TypeCheckFailCallback | None = ...,
46
45
  collection_check_strategy: CollectionCheckStrategy = ...,
47
- ) -> Any:
48
- ...
46
+ ) -> Any: ...
49
47
 
50
48
 
51
49
  def check_type(
@@ -53,7 +51,7 @@ def check_type(
53
51
  expected_type: Any,
54
52
  *,
55
53
  forward_ref_policy: ForwardRefPolicy = TypeCheckConfiguration().forward_ref_policy,
56
- typecheck_fail_callback: (TypeCheckFailCallback | None) = (
54
+ typecheck_fail_callback: TypeCheckFailCallback | None = (
57
55
  TypeCheckConfiguration().typecheck_fail_callback
58
56
  ),
59
57
  collection_check_strategy: CollectionCheckStrategy = (
@@ -80,7 +78,7 @@ def check_type(
80
78
  corresponding fields in :class:`TypeCheckConfiguration`.
81
79
 
82
80
  :param value: value to be checked against ``expected_type``
83
- :param expected_type: a class or generic type instance
81
+ :param expected_type: a class or generic type instance, or a tuple of such things
84
82
  :param forward_ref_policy: see :attr:`TypeCheckConfiguration.forward_ref_policy`
85
83
  :param typecheck_fail_callback:
86
84
  see :attr`TypeCheckConfiguration.typecheck_fail_callback`
@@ -90,6 +88,9 @@ def check_type(
90
88
  :raises TypeCheckError: if there is a type mismatch
91
89
 
92
90
  """
91
+ if type(expected_type) is tuple:
92
+ expected_type = Union[expected_type]
93
+
93
94
  config = TypeCheckConfiguration(
94
95
  forward_ref_policy=forward_ref_policy,
95
96
  typecheck_fail_callback=typecheck_fail_callback,
@@ -244,7 +245,7 @@ def check_variable_assignment(
244
245
  value: object, varname: str, annotation: Any, memo: TypeCheckMemo
245
246
  ) -> Any:
246
247
  if _suppression.type_checks_suppressed:
247
- return
248
+ return value
248
249
 
249
250
  try:
250
251
  check_type_internal(value, annotation, memo)
@@ -262,36 +263,36 @@ def check_variable_assignment(
262
263
  def check_multi_variable_assignment(
263
264
  value: Any, targets: list[dict[str, Any]], memo: TypeCheckMemo
264
265
  ) -> Any:
265
- if _suppression.type_checks_suppressed:
266
- return
267
-
268
266
  if max(len(target) for target in targets) == 1:
269
267
  iterated_values = [value]
270
268
  else:
271
269
  iterated_values = list(value)
272
270
 
273
- for expected_types in targets:
274
- value_index = 0
275
- for ann_index, (varname, expected_type) in enumerate(expected_types.items()):
276
- if varname.startswith("*"):
277
- varname = varname[1:]
278
- keys_left = len(expected_types) - 1 - ann_index
279
- next_value_index = len(iterated_values) - keys_left
280
- obj: object = iterated_values[value_index:next_value_index]
281
- value_index = next_value_index
282
- else:
283
- obj = iterated_values[value_index]
284
- value_index += 1
285
-
286
- try:
287
- check_type_internal(obj, expected_type, memo)
288
- except TypeCheckError as exc:
289
- qualname = qualified_name(obj, add_class_prefix=True)
290
- exc.append_path_element(f"value assigned to {varname} ({qualname})")
291
- if memo.config.typecheck_fail_callback:
292
- memo.config.typecheck_fail_callback(exc, memo)
271
+ if not _suppression.type_checks_suppressed:
272
+ for expected_types in targets:
273
+ value_index = 0
274
+ for ann_index, (varname, expected_type) in enumerate(
275
+ expected_types.items()
276
+ ):
277
+ if varname.startswith("*"):
278
+ varname = varname[1:]
279
+ keys_left = len(expected_types) - 1 - ann_index
280
+ next_value_index = len(iterated_values) - keys_left
281
+ obj: object = iterated_values[value_index:next_value_index]
282
+ value_index = next_value_index
293
283
  else:
294
- raise
284
+ obj = iterated_values[value_index]
285
+ value_index += 1
286
+
287
+ try:
288
+ check_type_internal(obj, expected_type, memo)
289
+ except TypeCheckError as exc:
290
+ qualname = qualified_name(obj, add_class_prefix=True)
291
+ exc.append_path_element(f"value assigned to {varname} ({qualname})")
292
+ if memo.config.typecheck_fail_callback:
293
+ memo.config.typecheck_fail_callback(exc, memo)
294
+ else:
295
+ raise
295
296
 
296
297
  return iterated_values[0] if len(iterated_values) == 1 else iterated_values
297
298
 
@@ -2,16 +2,29 @@ from __future__ import annotations
2
2
 
3
3
  import sys
4
4
  import warnings
5
-
6
- from pytest import Config, Parser
5
+ from typing import TYPE_CHECKING, Any, Literal
7
6
 
8
7
  from metaflow._vendor.typeguard._config import CollectionCheckStrategy, ForwardRefPolicy, global_config
9
8
  from metaflow._vendor.typeguard._exceptions import InstrumentationWarning
10
9
  from metaflow._vendor.typeguard._importhook import install_import_hook
11
10
  from metaflow._vendor.typeguard._utils import qualified_name, resolve_reference
12
11
 
12
+ if TYPE_CHECKING:
13
+ from pytest import Config, Parser
14
+
13
15
 
14
16
  def pytest_addoption(parser: Parser) -> None:
17
+ def add_ini_option(
18
+ opt_type: (
19
+ Literal["string", "paths", "pathlist", "args", "linelist", "bool"] | None
20
+ ),
21
+ ) -> None:
22
+ parser.addini(
23
+ group.options[-1].names()[0][2:],
24
+ group.options[-1].attrs()["help"],
25
+ opt_type,
26
+ )
27
+
15
28
  group = parser.getgroup("typeguard")
16
29
  group.addoption(
17
30
  "--typeguard-packages",
@@ -19,11 +32,15 @@ def pytest_addoption(parser: Parser) -> None:
19
32
  help="comma separated name list of packages and modules to instrument for "
20
33
  "type checking, or :all: to instrument all modules loaded after typeguard",
21
34
  )
35
+ add_ini_option("linelist")
36
+
22
37
  group.addoption(
23
38
  "--typeguard-debug-instrumentation",
24
39
  action="store_true",
25
40
  help="print all instrumented code to stderr",
26
41
  )
42
+ add_ini_option("bool")
43
+
27
44
  group.addoption(
28
45
  "--typeguard-typecheck-fail-callback",
29
46
  action="store",
@@ -33,6 +50,8 @@ def pytest_addoption(parser: Parser) -> None:
33
50
  "handle a TypeCheckError"
34
51
  ),
35
52
  )
53
+ add_ini_option("string")
54
+
36
55
  group.addoption(
37
56
  "--typeguard-forward-ref-policy",
38
57
  action="store",
@@ -42,21 +61,31 @@ def pytest_addoption(parser: Parser) -> None:
42
61
  "annotations"
43
62
  ),
44
63
  )
64
+ add_ini_option("string")
65
+
45
66
  group.addoption(
46
67
  "--typeguard-collection-check-strategy",
47
68
  action="store",
48
69
  choices=list(CollectionCheckStrategy.__members__),
49
70
  help="determines how thoroughly to check collections (list, dict, etc)",
50
71
  )
72
+ add_ini_option("string")
51
73
 
52
74
 
53
75
  def pytest_configure(config: Config) -> None:
54
- packages_option = config.getoption("typeguard_packages")
55
- if packages_option:
56
- if packages_option == ":all:":
57
- packages: list[str] | None = None
76
+ def getoption(name: str) -> Any:
77
+ return config.getoption(name.replace("-", "_")) or config.getini(name)
78
+
79
+ packages: list[str] | None = []
80
+ if packages_option := config.getoption("typeguard_packages"):
81
+ packages = [pkg.strip() for pkg in packages_option.split(",")]
82
+ elif packages_ini := config.getini("typeguard-packages"):
83
+ packages = packages_ini
84
+
85
+ if packages:
86
+ if packages == [":all:"]:
87
+ packages = None
58
88
  else:
59
- packages = [pkg.strip() for pkg in packages_option.split(",")]
60
89
  already_imported_packages = sorted(
61
90
  package for package in packages if package in sys.modules
62
91
  )
@@ -70,11 +99,11 @@ def pytest_configure(config: Config) -> None:
70
99
 
71
100
  install_import_hook(packages=packages)
72
101
 
73
- debug_option = config.getoption("typeguard_debug_instrumentation")
102
+ debug_option = getoption("typeguard-debug-instrumentation")
74
103
  if debug_option:
75
104
  global_config.debug_instrumentation = True
76
105
 
77
- fail_callback_option = config.getoption("typeguard_typecheck_fail_callback")
106
+ fail_callback_option = getoption("typeguard-typecheck-fail-callback")
78
107
  if fail_callback_option:
79
108
  callback = resolve_reference(fail_callback_option)
80
109
  if not callable(callback):
@@ -85,14 +114,12 @@ def pytest_configure(config: Config) -> None:
85
114
 
86
115
  global_config.typecheck_fail_callback = callback
87
116
 
88
- forward_ref_policy_option = config.getoption("typeguard_forward_ref_policy")
117
+ forward_ref_policy_option = getoption("typeguard-forward-ref-policy")
89
118
  if forward_ref_policy_option:
90
119
  forward_ref_policy = ForwardRefPolicy.__members__[forward_ref_policy_option]
91
120
  global_config.forward_ref_policy = forward_ref_policy
92
121
 
93
- collection_check_strategy_option = config.getoption(
94
- "typeguard_collection_check_strategy"
95
- )
122
+ collection_check_strategy_option = getoption("typeguard-collection-check-strategy")
96
123
  if collection_check_strategy_option:
97
124
  collection_check_strategy = CollectionCheckStrategy.__members__[
98
125
  collection_check_strategy_option
@@ -20,17 +20,15 @@ type_checks_suppress_lock = Lock()
20
20
 
21
21
 
22
22
  @overload
23
- def suppress_type_checks(func: Callable[P, T]) -> Callable[P, T]:
24
- ...
23
+ def suppress_type_checks(func: Callable[P, T]) -> Callable[P, T]: ...
25
24
 
26
25
 
27
26
  @overload
28
- def suppress_type_checks() -> ContextManager[None]:
29
- ...
27
+ def suppress_type_checks() -> ContextManager[None]: ...
30
28
 
31
29
 
32
30
  def suppress_type_checks(
33
- func: Callable[P, T] | None = None
31
+ func: Callable[P, T] | None = None,
34
32
  ) -> Callable[P, T] | ContextManager[None]:
35
33
  """
36
34
  Temporarily suppress all type checking.
@@ -37,6 +37,7 @@ from ast import (
37
37
  Module,
38
38
  Mult,
39
39
  Name,
40
+ NamedExpr,
40
41
  NodeTransformer,
41
42
  NodeVisitor,
42
43
  Pass,
@@ -45,7 +46,6 @@ from ast import (
45
46
  RShift,
46
47
  Starred,
47
48
  Store,
48
- Str,
49
49
  Sub,
50
50
  Subscript,
51
51
  Tuple,
@@ -65,9 +65,6 @@ from copy import deepcopy
65
65
  from dataclasses import dataclass, field
66
66
  from typing import Any, ClassVar, cast, overload
67
67
 
68
- if sys.version_info >= (3, 8):
69
- from ast import NamedExpr
70
-
71
68
  generator_names = (
72
69
  "typing.Generator",
73
70
  "collections.abc.Generator",
@@ -159,13 +156,12 @@ class TransformMemo:
159
156
  if isinstance(child, ImportFrom) and child.module == "__future__":
160
157
  # (module only) __future__ imports must come first
161
158
  continue
162
- elif isinstance(child, Expr):
163
- if isinstance(child.value, Constant) and isinstance(
164
- child.value.value, str
165
- ):
166
- continue # docstring
167
- elif sys.version_info < (3, 8) and isinstance(child.value, Str):
168
- continue # docstring
159
+ elif (
160
+ isinstance(child, Expr)
161
+ and isinstance(child.value, Constant)
162
+ and isinstance(child.value.value, str)
163
+ ):
164
+ continue # docstring
169
165
 
170
166
  self.code_inject_index = index
171
167
  break
@@ -349,15 +345,25 @@ class AnnotationTransformer(NodeTransformer):
349
345
  def __init__(self, transformer: TypeguardTransformer):
350
346
  self.transformer = transformer
351
347
  self._memo = transformer._memo
348
+ self._level = 0
352
349
 
353
350
  def visit(self, node: AST) -> Any:
351
+ # Don't process Literals
352
+ if isinstance(node, expr) and self._memo.name_matches(node, *literal_names):
353
+ return node
354
+
355
+ self._level += 1
354
356
  new_node = super().visit(node)
357
+ self._level -= 1
358
+
355
359
  if isinstance(new_node, Expression) and not hasattr(new_node, "body"):
356
360
  return None
357
361
 
358
362
  # Return None if this new node matches a variation of typing.Any
359
- if isinstance(new_node, expr) and self._memo.name_matches(
360
- new_node, *anytype_names
363
+ if (
364
+ self._level == 0
365
+ and isinstance(new_node, expr)
366
+ and self._memo.name_matches(new_node, *anytype_names)
361
367
  ):
362
368
  return None
363
369
 
@@ -367,10 +373,18 @@ class AnnotationTransformer(NodeTransformer):
367
373
  self.generic_visit(node)
368
374
 
369
375
  if isinstance(node.op, BitOr):
370
- # If either side of the operation resolved to None, return None
376
+ # If either branch of the BinOp has been transformed to `None`, it means
377
+ # that a type in the union was ignored, so the entire annotation should e
378
+ # ignored
371
379
  if not hasattr(node, "left") or not hasattr(node, "right"):
372
380
  return None
373
381
 
382
+ # Return Any if either side is Any
383
+ if self._memo.name_matches(node.left, *anytype_names):
384
+ return node.left
385
+ elif self._memo.name_matches(node.right, *anytype_names):
386
+ return node.right
387
+
374
388
  if sys.version_info < (3, 10):
375
389
  union_name = self.transformer._get_import("typing", "Union")
376
390
  return Subscript(
@@ -395,9 +409,9 @@ class AnnotationTransformer(NodeTransformer):
395
409
 
396
410
  # The subscript of typing(_extensions).Literal can be any arbitrary string, so
397
411
  # don't try to evaluate it as code
398
- if not self._memo.name_matches(node.value, *literal_names) and node.slice:
412
+ if node.slice:
399
413
  if isinstance(node.slice, Index):
400
- # Python 3.7 and 3.8
414
+ # Python 3.8
401
415
  slice_value = node.slice.value # type: ignore[attr-defined]
402
416
  else:
403
417
  slice_value = node.slice
@@ -408,20 +422,22 @@ class AnnotationTransformer(NodeTransformer):
408
422
  # forward reference
409
423
  items = cast(
410
424
  typing.List[expr],
411
- [self.generic_visit(slice_value.elts[0])]
412
- + slice_value.elts[1:],
425
+ [self.visit(slice_value.elts[0])] + slice_value.elts[1:],
413
426
  )
414
427
  else:
415
428
  items = cast(
416
429
  typing.List[expr],
417
- [self.generic_visit(item) for item in slice_value.elts],
430
+ [self.visit(item) for item in slice_value.elts],
418
431
  )
419
432
 
420
433
  # If this is a Union and any of the items is Any, erase the entire
421
434
  # annotation
422
435
  if self._memo.name_matches(node.value, "typing.Union") and any(
423
- isinstance(item, expr)
424
- and self._memo.name_matches(item, *anytype_names)
436
+ item is None
437
+ or (
438
+ isinstance(item, expr)
439
+ and self._memo.name_matches(item, *anytype_names)
440
+ )
425
441
  for item in items
426
442
  ):
427
443
  return None
@@ -441,9 +457,11 @@ class AnnotationTransformer(NodeTransformer):
441
457
  # If the transformer erased the slice entirely, just return the node
442
458
  # value without the subscript (unless it's Optional, in which case erase
443
459
  # the node entirely
444
- if self._memo.name_matches(node.value, "typing.Optional"):
460
+ if self._memo.name_matches(
461
+ node.value, "typing.Optional"
462
+ ) and not hasattr(node, "slice"):
445
463
  return None
446
- elif sys.version_info >= (3, 9) and not hasattr(node, "slice"):
464
+ if sys.version_info >= (3, 9) and not hasattr(node, "slice"):
447
465
  return node.value
448
466
  elif sys.version_info < (3, 9) and not hasattr(node.slice, "value"):
449
467
  return node.value
@@ -477,15 +495,6 @@ class AnnotationTransformer(NodeTransformer):
477
495
 
478
496
  return node
479
497
 
480
- def visit_Str(self, node: Str) -> Any:
481
- # Only used on Python 3.7
482
- expression = ast.parse(node.s, mode="eval")
483
- new_node = self.visit(expression)
484
- if new_node:
485
- return copy_location(new_node.body, node)
486
- else:
487
- return None
488
-
489
498
 
490
499
  class TypeguardTransformer(NodeTransformer):
491
500
  def __init__(
@@ -497,11 +506,32 @@ class TypeguardTransformer(NodeTransformer):
497
506
  self.target_node: FunctionDef | AsyncFunctionDef | None = None
498
507
  self.target_lineno = target_lineno
499
508
 
509
+ def generic_visit(self, node: AST) -> AST:
510
+ has_non_empty_body_initially = bool(getattr(node, "body", None))
511
+ initial_type = type(node)
512
+
513
+ node = super().generic_visit(node)
514
+
515
+ if (
516
+ type(node) is initial_type
517
+ and has_non_empty_body_initially
518
+ and hasattr(node, "body")
519
+ and not node.body
520
+ ):
521
+ # If we have still the same node type after transformation
522
+ # but we've optimised it's body away, we add a `pass` statement.
523
+ node.body = [Pass()]
524
+
525
+ return node
526
+
500
527
  @contextmanager
501
528
  def _use_memo(
502
529
  self, node: ClassDef | FunctionDef | AsyncFunctionDef
503
530
  ) -> Generator[None, Any, None]:
504
531
  new_memo = TransformMemo(node, self._memo, self._memo.path + (node.name,))
532
+ old_memo = self._memo
533
+ self._memo = new_memo
534
+
505
535
  if isinstance(node, (FunctionDef, AsyncFunctionDef)):
506
536
  new_memo.should_instrument = (
507
537
  self._target_path is None or new_memo.path == self._target_path
@@ -553,8 +583,6 @@ class TypeguardTransformer(NodeTransformer):
553
583
  if isinstance(node, AsyncFunctionDef):
554
584
  new_memo.is_async = True
555
585
 
556
- old_memo = self._memo
557
- self._memo = new_memo
558
586
  yield
559
587
  self._memo = old_memo
560
588
 
@@ -563,12 +591,10 @@ class TypeguardTransformer(NodeTransformer):
563
591
  return memo.get_import(module, name)
564
592
 
565
593
  @overload
566
- def _convert_annotation(self, annotation: None) -> None:
567
- ...
594
+ def _convert_annotation(self, annotation: None) -> None: ...
568
595
 
569
596
  @overload
570
- def _convert_annotation(self, annotation: expr) -> expr:
571
- ...
597
+ def _convert_annotation(self, annotation: expr) -> expr: ...
572
598
 
573
599
  def _convert_annotation(self, annotation: expr | None) -> expr | None:
574
600
  if annotation is None:
@@ -591,8 +617,9 @@ class TypeguardTransformer(NodeTransformer):
591
617
  return node
592
618
 
593
619
  def visit_Module(self, node: Module) -> Module:
620
+ self._module_memo = self._memo = TransformMemo(node, None, ())
594
621
  self.generic_visit(node)
595
- self._memo.insert_imports(node)
622
+ self._module_memo.insert_imports(node)
596
623
 
597
624
  fix_missing_locations(node)
598
625
  return node
@@ -691,14 +718,12 @@ class TypeguardTransformer(NodeTransformer):
691
718
  if self.target_lineno == first_lineno:
692
719
  assert self.target_node is None
693
720
  self.target_node = node
694
- if node.decorator_list and sys.version_info >= (3, 8):
721
+ if node.decorator_list:
695
722
  self.target_lineno = node.decorator_list[0].lineno
696
723
  else:
697
724
  self.target_lineno = node.lineno
698
725
 
699
- all_args = node.args.args + node.args.kwonlyargs
700
- if sys.version_info >= (3, 8):
701
- all_args.extend(node.args.posonlyargs)
726
+ all_args = node.args.args + node.args.kwonlyargs + node.args.posonlyargs
702
727
 
703
728
  # Ensure that any type shadowed by the positional or keyword-only
704
729
  # argument names are ignored in this function
@@ -895,6 +920,22 @@ class TypeguardTransformer(NodeTransformer):
895
920
 
896
921
  self._memo.insert_imports(node)
897
922
 
923
+ # Special case the __new__() method to create a local alias from the
924
+ # class name to the first argument (usually "cls")
925
+ if (
926
+ isinstance(node, FunctionDef)
927
+ and node.args
928
+ and self._memo.parent is not None
929
+ and isinstance(self._memo.parent.node, ClassDef)
930
+ and node.name == "__new__"
931
+ ):
932
+ first_args_expr = Name(node.args.args[0].arg, ctx=Load())
933
+ cls_name = Name(self._memo.parent.node.name, ctx=Store())
934
+ node.body.insert(
935
+ self._memo.code_inject_index,
936
+ Assign([cls_name], first_args_expr),
937
+ )
938
+
898
939
  # Rmove any placeholder "pass" at the end
899
940
  if isinstance(node.body[-1], Pass):
900
941
  del node.body[-1]
@@ -1176,11 +1217,6 @@ class TypeguardTransformer(NodeTransformer):
1176
1217
  """
1177
1218
  self.generic_visit(node)
1178
1219
 
1179
- # Fix empty node body (caused by removal of classes/functions not on the target
1180
- # path)
1181
- if not node.body:
1182
- node.body.append(Pass())
1183
-
1184
1220
  if (
1185
1221
  self._memo is self._module_memo
1186
1222
  and isinstance(node.test, Name)
@@ -2,6 +2,7 @@
2
2
  Transforms lazily evaluated PEP 604 unions into typing.Unions, for compatibility with
3
3
  Python versions older than 3.10.
4
4
  """
5
+
5
6
  from __future__ import annotations
6
7
 
7
8
  from ast import (
@@ -5,17 +5,27 @@ import sys
5
5
  from importlib import import_module
6
6
  from inspect import currentframe
7
7
  from types import CodeType, FrameType, FunctionType
8
- from typing import TYPE_CHECKING, Any, Callable, ForwardRef, Union, cast
8
+ from typing import TYPE_CHECKING, Any, Callable, ForwardRef, Union, cast, final
9
9
  from weakref import WeakValueDictionary
10
10
 
11
11
  if TYPE_CHECKING:
12
12
  from ._memo import TypeCheckMemo
13
13
 
14
- if sys.version_info >= (3, 10):
14
+ if sys.version_info >= (3, 13):
15
15
  from typing import get_args, get_origin
16
16
 
17
17
  def evaluate_forwardref(forwardref: ForwardRef, memo: TypeCheckMemo) -> Any:
18
- return forwardref._evaluate(memo.globals, memo.locals, frozenset())
18
+ return forwardref._evaluate(
19
+ memo.globals, memo.locals, type_params=(), recursive_guard=frozenset()
20
+ )
21
+
22
+ elif sys.version_info >= (3, 10):
23
+ from typing import get_args, get_origin
24
+
25
+ def evaluate_forwardref(forwardref: ForwardRef, memo: TypeCheckMemo) -> Any:
26
+ return forwardref._evaluate(
27
+ memo.globals, memo.locals, recursive_guard=frozenset()
28
+ )
19
29
 
20
30
  else:
21
31
  from metaflow._vendor.typing_extensions import get_args, get_origin
@@ -47,12 +57,6 @@ else:
47
57
  raise
48
58
 
49
59
 
50
- if sys.version_info >= (3, 8):
51
- from typing import final
52
- else:
53
- from metaflow._vendor.typing_extensions import final
54
-
55
-
56
60
  _functions_map: WeakValueDictionary[CodeType, FunctionType] = WeakValueDictionary()
57
61
 
58
62