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.
Files changed (51) 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/pypi/utils.py +4 -0
  40. metaflow/runner/click_api.py +7 -2
  41. metaflow/vendor.py +1 -0
  42. metaflow/version.py +1 -1
  43. {metaflow-2.15.5.dist-info → metaflow-2.15.6.dist-info}/METADATA +2 -2
  44. {metaflow-2.15.5.dist-info → metaflow-2.15.6.dist-info}/RECORD +51 -25
  45. {metaflow-2.15.5.data → metaflow-2.15.6.data}/data/share/metaflow/devtools/Makefile +0 -0
  46. {metaflow-2.15.5.data → metaflow-2.15.6.data}/data/share/metaflow/devtools/Tiltfile +0 -0
  47. {metaflow-2.15.5.data → metaflow-2.15.6.data}/data/share/metaflow/devtools/pick_services.sh +0 -0
  48. {metaflow-2.15.5.dist-info → metaflow-2.15.6.dist-info}/LICENSE +0 -0
  49. {metaflow-2.15.5.dist-info → metaflow-2.15.6.dist-info}/WHEEL +0 -0
  50. {metaflow-2.15.5.dist-info → metaflow-2.15.6.dist-info}/entry_points.txt +0 -0
  51. {metaflow-2.15.5.dist-info → metaflow-2.15.6.dist-info}/top_level.txt +0 -0
@@ -9,6 +9,7 @@ import warnings
9
9
  from enum import Enum
10
10
  from inspect import Parameter, isclass, isfunction
11
11
  from io import BufferedIOBase, IOBase, RawIOBase, TextIOBase
12
+ from itertools import zip_longest
12
13
  from textwrap import indent
13
14
  from typing import (
14
15
  IO,
@@ -33,10 +34,13 @@ from typing import (
33
34
  )
34
35
  from unittest.mock import Mock
35
36
 
36
- try:
37
- from metaflow._vendor import typing_extensions
38
- except ImportError:
39
- typing_extensions = None # type: ignore[assignment]
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
40
44
 
41
45
  from ._config import ForwardRefPolicy
42
46
  from ._exceptions import TypeCheckError, TypeHintWarning
@@ -46,22 +50,20 @@ from ._utils import evaluate_forwardref, get_stacklevel, get_type_name, qualifie
46
50
  if sys.version_info >= (3, 11):
47
51
  from typing import (
48
52
  Annotated,
53
+ NotRequired,
49
54
  TypeAlias,
50
55
  get_args,
51
56
  get_origin,
52
- get_type_hints,
53
- is_typeddict,
54
57
  )
55
58
 
56
59
  SubclassableAny = Any
57
60
  else:
58
61
  from metaflow._vendor.typing_extensions import (
59
62
  Annotated,
63
+ NotRequired,
60
64
  TypeAlias,
61
65
  get_args,
62
66
  get_origin,
63
- get_type_hints,
64
- is_typeddict,
65
67
  )
66
68
  from metaflow._vendor.typing_extensions import Any as SubclassableAny
67
69
 
@@ -80,7 +82,9 @@ TypeCheckLookupCallback: TypeAlias = Callable[
80
82
  ]
81
83
 
82
84
  checker_lookup_functions: list[TypeCheckLookupCallback] = []
83
-
85
+ generic_alias_types: tuple[type, ...] = (type(List), type(List[Any]))
86
+ if sys.version_info >= (3, 9):
87
+ generic_alias_types += (types.GenericAlias,)
84
88
 
85
89
  # Sentinel
86
90
  _missing = object()
@@ -172,31 +176,29 @@ def check_callable(
172
176
  f'{", ".join(unfulfilled_kwonlyargs)}'
173
177
  )
174
178
 
175
- num_mandatory_args = len(
176
- [
177
- param.name
178
- for param in signature.parameters.values()
179
- if param.kind
180
- in (Parameter.POSITIONAL_ONLY, Parameter.POSITIONAL_OR_KEYWORD)
181
- and param.default is Parameter.empty
182
- ]
183
- )
184
- has_varargs = any(
185
- param
186
- for param in signature.parameters.values()
187
- if param.kind == Parameter.VAR_POSITIONAL
188
- )
189
-
190
- if num_mandatory_args > len(argument_types):
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):
191
193
  raise TypeCheckError(
192
- f"has too many arguments in its declaration; expected "
193
- f"{len(argument_types)} but {num_mandatory_args} argument(s) "
194
- f"declared"
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"
195
197
  )
196
- elif not has_varargs and num_mandatory_args < len(argument_types):
198
+ elif not has_varargs and num_positional_args < len(argument_types):
197
199
  raise TypeCheckError(
198
200
  f"has too few arguments in its declaration; expected "
199
- f"{len(argument_types)} but {num_mandatory_args} argument(s) "
201
+ f"{len(argument_types)} but {num_positional_args} argument(s) "
200
202
  f"declared"
201
203
  )
202
204
 
@@ -247,22 +249,33 @@ def check_typed_dict(
247
249
 
248
250
  declared_keys = frozenset(origin_type.__annotations__)
249
251
  if hasattr(origin_type, "__required_keys__"):
250
- required_keys = origin_type.__required_keys__
252
+ required_keys = set(origin_type.__required_keys__)
251
253
  else: # py3.8 and lower
252
- required_keys = declared_keys if origin_type.__total__ else frozenset()
254
+ required_keys = set(declared_keys) if origin_type.__total__ else set()
253
255
 
254
- existing_keys = frozenset(value)
256
+ existing_keys = set(value)
255
257
  extra_keys = existing_keys - declared_keys
256
258
  if extra_keys:
257
259
  keys_formatted = ", ".join(f'"{key}"' for key in sorted(extra_keys, key=repr))
258
260
  raise TypeCheckError(f"has unexpected extra key(s): {keys_formatted}")
259
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
+
260
273
  missing_keys = required_keys - existing_keys
261
274
  if missing_keys:
262
275
  keys_formatted = ", ".join(f'"{key}"' for key in sorted(missing_keys, key=repr))
263
276
  raise TypeCheckError(f"is missing required key(s): {keys_formatted}")
264
277
 
265
- for key, argtype in get_type_hints(origin_type).items():
278
+ for key, argtype in type_hints.items():
266
279
  argvalue = value.get(key, _missing)
267
280
  if argvalue is not _missing:
268
281
  try:
@@ -339,11 +352,7 @@ def check_tuple(
339
352
  memo: TypeCheckMemo,
340
353
  ) -> None:
341
354
  # Specialized check for NamedTuples
342
- field_types = getattr(origin_type, "__annotations__", None)
343
- if field_types is None and sys.version_info < (3, 8):
344
- field_types = getattr(origin_type, "_field_types", None)
345
-
346
- if field_types:
355
+ if field_types := getattr(origin_type, "__annotations__", None):
347
356
  if not isinstance(value, origin_type):
348
357
  raise TypeCheckError(
349
358
  f"is not a named tuple of type {qualified_name(origin_type)}"
@@ -361,7 +370,6 @@ def check_tuple(
361
370
  raise TypeCheckError("is not a tuple")
362
371
 
363
372
  if args:
364
- # Python 3.6+
365
373
  use_ellipsis = args[-1] is Ellipsis
366
374
  tuple_params = args[: -1 if use_ellipsis else None]
367
375
  else:
@@ -402,16 +410,19 @@ def check_union(
402
410
  memo: TypeCheckMemo,
403
411
  ) -> None:
404
412
  errors: dict[str, TypeCheckError] = {}
405
- for type_ in args:
406
- try:
407
- check_type_internal(value, type_, memo)
408
- return
409
- except TypeCheckError as exc:
410
- errors[get_type_name(type_)] = exc
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
411
420
 
412
- formatted_errors = indent(
413
- "\n".join(f"{key}: {error}" for key, error in errors.items()), " "
414
- )
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
415
426
  raise TypeCheckError(f"did not match any element in the union:\n{formatted_errors}")
416
427
 
417
428
 
@@ -441,10 +452,9 @@ def check_class(
441
452
  args: tuple[Any, ...],
442
453
  memo: TypeCheckMemo,
443
454
  ) -> None:
444
- if not isclass(value):
455
+ if not isclass(value) and not isinstance(value, generic_alias_types):
445
456
  raise TypeCheckError("is not a class")
446
457
 
447
- # Needed on Python 3.7+
448
458
  if not args:
449
459
  return
450
460
 
@@ -477,7 +487,7 @@ def check_class(
477
487
  raise TypeCheckError(
478
488
  f"did not match any element in the union:\n{formatted_errors}"
479
489
  )
480
- elif not issubclass(value, expected_class):
490
+ elif not issubclass(value, expected_class): # type: ignore[arg-type]
481
491
  raise TypeCheckError(f"is not a subclass of {qualified_name(expected_class)}")
482
492
 
483
493
 
@@ -531,21 +541,8 @@ def check_typevar(
531
541
  )
532
542
 
533
543
 
534
- if sys.version_info >= (3, 8):
535
- if typing_extensions is None:
536
-
537
- def _is_literal_type(typ: object) -> bool:
538
- return typ is typing.Literal
539
-
540
- else:
541
-
542
- def _is_literal_type(typ: object) -> bool:
543
- return typ is typing.Literal or typ is typing_extensions.Literal
544
-
545
- else:
546
-
547
- def _is_literal_type(typ: object) -> bool:
548
- return typ is typing_extensions.Literal
544
+ def _is_literal_type(typ: object) -> bool:
545
+ return typ is typing.Literal or typ is typing_extensions.Literal
549
546
 
550
547
 
551
548
  def check_literal(
@@ -558,7 +555,6 @@ def check_literal(
558
555
  retval: list[Any] = []
559
556
  for arg in literal_args:
560
557
  if _is_literal_type(get_origin(arg)):
561
- # The first check works on py3.6 and lower, the second one on py3.7+
562
558
  retval.extend(get_literal_args(arg.__args__))
563
559
  elif arg is None or isinstance(arg, (int, str, bytes, bool, Enum)):
564
560
  retval.append(arg)
@@ -638,25 +634,196 @@ def check_io(
638
634
  raise TypeCheckError("is not an I/O object")
639
635
 
640
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
+
641
779
  def check_protocol(
642
780
  value: Any,
643
781
  origin_type: Any,
644
782
  args: tuple[Any, ...],
645
783
  memo: TypeCheckMemo,
646
784
  ) -> None:
647
- # TODO: implement proper compatibility checking and support non-runtime protocols
648
- if getattr(origin_type, "_is_runtime_protocol", False):
649
- if not isinstance(value, origin_type):
650
- raise TypeCheckError(
651
- f"is not compatible with the {origin_type.__qualname__} protocol"
652
- )
653
- else:
654
- warnings.warn(
655
- f"Typeguard cannot check the {origin_type.__qualname__} protocol because "
656
- f"it is a non-runtime protocol. If you would like to type check this "
657
- f"protocol, please use @typing.runtime_checkable",
658
- stacklevel=get_stacklevel(),
659
- )
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
660
827
 
661
828
 
662
829
  def check_byteslike(
@@ -777,7 +944,7 @@ def check_type_internal(
777
944
  if isclass(origin_type):
778
945
  if not isinstance(value, origin_type):
779
946
  raise TypeCheckError(f"is not an instance of {qualified_name(origin_type)}")
780
- elif type(origin_type) is str:
947
+ elif type(origin_type) is str: # noqa: E721
781
948
  warnings.warn(
782
949
  f"Skipping type check against {origin_type!r}; this looks like a "
783
950
  f"string-form forward reference imported from another module",
@@ -801,6 +968,7 @@ origin_type_checkers = {
801
968
  IO: check_io,
802
969
  list: check_list,
803
970
  List: check_list,
971
+ typing.Literal: check_literal,
804
972
  Mapping: check_mapping,
805
973
  MutableMapping: check_mapping,
806
974
  None: check_none,
@@ -817,9 +985,14 @@ origin_type_checkers = {
817
985
  type: check_class,
818
986
  Type: check_class,
819
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,
820
995
  }
821
- if sys.version_info >= (3, 8):
822
- origin_type_checkers[typing.Literal] = check_literal
823
996
  if sys.version_info >= (3, 10):
824
997
  origin_type_checkers[types.UnionType] = check_uniontype
825
998
  origin_type_checkers[typing.TypeGuard] = check_typeguard
@@ -827,16 +1000,6 @@ if sys.version_info >= (3, 11):
827
1000
  origin_type_checkers.update(
828
1001
  {typing.LiteralString: check_literal_string, typing.Self: check_self}
829
1002
  )
830
- if typing_extensions is not None:
831
- # On some Python versions, these may simply be re-exports from typing,
832
- # but exactly which Python versions is subject to change,
833
- # so it's best to err on the safe side
834
- # and update the dictionary on all Python versions
835
- # if typing_extensions is installed
836
- origin_type_checkers[typing_extensions.Literal] = check_literal
837
- origin_type_checkers[typing_extensions.LiteralString] = check_literal_string
838
- origin_type_checkers[typing_extensions.Self] = check_self
839
- origin_type_checkers[typing_extensions.TypeGuard] = check_typeguard
840
1003
 
841
1004
 
842
1005
  def builtin_checker_lookup(
@@ -848,7 +1011,8 @@ def builtin_checker_lookup(
848
1011
  elif is_typeddict(origin_type):
849
1012
  return check_typed_dict
850
1013
  elif isclass(origin_type) and issubclass(
851
- origin_type, Tuple # type: ignore[arg-type]
1014
+ origin_type,
1015
+ Tuple, # type: ignore[arg-type]
852
1016
  ):
853
1017
  # NamedTuple
854
1018
  return check_tuple
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from collections.abc import Collection
3
+ from collections.abc import Iterable
4
4
  from dataclasses import dataclass
5
5
  from enum import Enum, auto
6
6
  from typing import TYPE_CHECKING, TypeVar
@@ -49,11 +49,11 @@ class CollectionCheckStrategy(Enum):
49
49
  FIRST_ITEM = auto()
50
50
  ALL_ITEMS = auto()
51
51
 
52
- def iterate_samples(self, collection: Collection[T]) -> Collection[T]:
52
+ def iterate_samples(self, collection: Iterable[T]) -> Iterable[T]:
53
53
  if self is CollectionCheckStrategy.FIRST_ITEM:
54
- if len(collection):
54
+ try:
55
55
  return [next(iter(collection))]
56
- else:
56
+ except StopIteration:
57
57
  return ()
58
58
  else:
59
59
  return collection
@@ -16,20 +16,18 @@ from ._functions import TypeCheckFailCallback
16
16
  from ._transformer import TypeguardTransformer
17
17
  from ._utils import Unset, function_name, get_stacklevel, is_method_of, unset
18
18
 
19
+ T_CallableOrType = TypeVar("T_CallableOrType", bound=Callable[..., Any])
20
+
19
21
  if TYPE_CHECKING:
20
22
  from typeshed.stdlib.types import _Cell
21
23
 
22
- _F = TypeVar("_F")
23
-
24
- def typeguard_ignore(f: _F) -> _F:
24
+ def typeguard_ignore(f: T_CallableOrType) -> T_CallableOrType:
25
25
  """This decorator is a noop during static type-checking."""
26
26
  return f
27
27
 
28
28
  else:
29
29
  from typing import no_type_check as typeguard_ignore # noqa: F401
30
30
 
31
- T_CallableOrType = TypeVar("T_CallableOrType", bound=Callable[..., Any])
32
-
33
31
 
34
32
  def make_cell(value: object) -> _Cell:
35
33
  return (lambda: value).__closure__[0] # type: ignore[index]
@@ -133,13 +131,11 @@ def typechecked(
133
131
  typecheck_fail_callback: TypeCheckFailCallback | Unset = unset,
134
132
  collection_check_strategy: CollectionCheckStrategy | Unset = unset,
135
133
  debug_instrumentation: bool | Unset = unset,
136
- ) -> Callable[[T_CallableOrType], T_CallableOrType]:
137
- ...
134
+ ) -> Callable[[T_CallableOrType], T_CallableOrType]: ...
138
135
 
139
136
 
140
137
  @overload
141
- def typechecked(target: T_CallableOrType) -> T_CallableOrType:
142
- ...
138
+ def typechecked(target: T_CallableOrType) -> T_CallableOrType: ...
143
139
 
144
140
 
145
141
  def typechecked(
@@ -215,9 +211,9 @@ def typechecked(
215
211
  return target
216
212
 
217
213
  # 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
214
+ wrapper_class: (
215
+ type[classmethod[Any, Any, Any]] | type[staticmethod[Any, Any]] | None
216
+ ) = None
221
217
  if isinstance(target, (classmethod, staticmethod)):
222
218
  wrapper_class = target.__class__
223
219
  target = target.__func__