aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Lib
diff options
context:
space:
mode:
Diffstat (limited to 'Lib')
-rw-r--r--Lib/test/test_typing.py28
-rw-r--r--Lib/test/test_zoneinfo/test_zoneinfo_property.py18
-rw-r--r--Lib/test/test_zstd.py9
-rw-r--r--Lib/typing.py30
4 files changed, 62 insertions, 23 deletions
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index ef02e8202fc..bef6773ad6c 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -1605,7 +1605,10 @@ class TypeVarTupleTests(BaseTestCase):
self.assertEqual(gth(func1), {'args': Unpack[Ts]})
def func2(*args: *tuple[int, str]): pass
- self.assertEqual(gth(func2), {'args': Unpack[tuple[int, str]]})
+ hint = gth(func2)['args']
+ self.assertIsInstance(hint, types.GenericAlias)
+ self.assertEqual(hint.__args__[0], int)
+ self.assertIs(hint.__unpacked__, True)
class CustomVariadic(Generic[*Ts]): pass
@@ -1620,7 +1623,10 @@ class TypeVarTupleTests(BaseTestCase):
{'args': Unpack[Ts]})
def func2(*args: '*tuple[int, str]'): pass
- self.assertEqual(gth(func2), {'args': Unpack[tuple[int, str]]})
+ hint = gth(func2)['args']
+ self.assertIsInstance(hint, types.GenericAlias)
+ self.assertEqual(hint.__args__[0], int)
+ self.assertIs(hint.__unpacked__, True)
class CustomVariadic(Generic[*Ts]): pass
@@ -7114,6 +7120,24 @@ class GetTypeHintsTests(BaseTestCase):
right_hints = get_type_hints(t.add_right, globals(), locals())
self.assertEqual(right_hints['node'], Node[T])
+ def test_get_type_hints_preserve_generic_alias_subclasses(self):
+ # https://github.com/python/cpython/issues/130870
+ # A real world example of this is `collections.abc.Callable`. When parameterized,
+ # the result is a subclass of `types.GenericAlias`.
+ class MyAlias(types.GenericAlias):
+ pass
+
+ class MyClass:
+ def __class_getitem__(cls, args):
+ return MyAlias(cls, args)
+
+ # Using a forward reference is important, otherwise it works as expected.
+ # `y` tests that the `GenericAlias` subclass is preserved when stripping `Annotated`.
+ def func(x: MyClass['int'], y: MyClass[Annotated[int, ...]]): ...
+
+ assert isinstance(get_type_hints(func)['x'], MyAlias)
+ assert isinstance(get_type_hints(func)['y'], MyAlias)
+
class GetUtilitiesTestCase(TestCase):
def test_get_origin(self):
diff --git a/Lib/test/test_zoneinfo/test_zoneinfo_property.py b/Lib/test/test_zoneinfo/test_zoneinfo_property.py
index feaa77f3e7f..294c7e9b27a 100644
--- a/Lib/test/test_zoneinfo/test_zoneinfo_property.py
+++ b/Lib/test/test_zoneinfo/test_zoneinfo_property.py
@@ -146,20 +146,24 @@ class ZoneInfoPickleTest(ZoneInfoTestBase):
@add_key_examples
def test_pickle_unpickle_cache(self, key):
zi = self.klass(key)
- pkl_str = pickle.dumps(zi)
- zi_rt = pickle.loads(pkl_str)
+ for proto in range(pickle.HIGHEST_PROTOCOL + 1):
+ with self.subTest(proto=proto):
+ pkl_str = pickle.dumps(zi, proto)
+ zi_rt = pickle.loads(pkl_str)
- self.assertIs(zi, zi_rt)
+ self.assertIs(zi, zi_rt)
@hypothesis.given(key=valid_keys())
@add_key_examples
def test_pickle_unpickle_no_cache(self, key):
zi = self.klass.no_cache(key)
- pkl_str = pickle.dumps(zi)
- zi_rt = pickle.loads(pkl_str)
+ for proto in range(pickle.HIGHEST_PROTOCOL + 1):
+ with self.subTest(proto=proto):
+ pkl_str = pickle.dumps(zi, proto)
+ zi_rt = pickle.loads(pkl_str)
- self.assertIsNot(zi, zi_rt)
- self.assertEqual(str(zi), str(zi_rt))
+ self.assertIsNot(zi, zi_rt)
+ self.assertEqual(str(zi), str(zi_rt))
@hypothesis.given(key=valid_keys())
@add_key_examples
diff --git a/Lib/test/test_zstd.py b/Lib/test/test_zstd.py
index d4c28aed38e..90b2adc9665 100644
--- a/Lib/test/test_zstd.py
+++ b/Lib/test/test_zstd.py
@@ -62,15 +62,18 @@ SAMPLES = None
TRAINED_DICT = None
-SUPPORT_MULTITHREADING = False
+# Cannot be deferred to setup as it is used to check whether or not to skip
+# tests
+try:
+ SUPPORT_MULTITHREADING = CompressionParameter.nb_workers.bounds() != (0, 0)
+except Exception:
+ SUPPORT_MULTITHREADING = False
C_INT_MIN = -(2**31)
C_INT_MAX = (2**31) - 1
def setUpModule():
- global SUPPORT_MULTITHREADING
- SUPPORT_MULTITHREADING = CompressionParameter.nb_workers.bounds() != (0, 0)
# uncompressed size 130KB, more than a zstd block.
# with a frame epilogue, 4 bytes checksum.
global DAT_130K_D
diff --git a/Lib/typing.py b/Lib/typing.py
index ed1dd4fc641..27105838a0a 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -407,6 +407,17 @@ def _tp_cache(func=None, /, *, typed=False):
return decorator
+def _rebuild_generic_alias(alias: GenericAlias, args: tuple[object, ...]) -> GenericAlias:
+ is_unpacked = alias.__unpacked__
+ if _should_unflatten_callable_args(alias, args):
+ t = alias.__origin__[(args[:-1], args[-1])]
+ else:
+ t = alias.__origin__[args]
+ if is_unpacked:
+ t = Unpack[t]
+ return t
+
+
def _deprecation_warning_for_no_type_params_passed(funcname: str) -> None:
import warnings
@@ -454,25 +465,20 @@ def _eval_type(t, globalns, localns, type_params=_sentinel, *, recursive_guard=f
_make_forward_ref(arg) if isinstance(arg, str) else arg
for arg in t.__args__
)
- is_unpacked = t.__unpacked__
- if _should_unflatten_callable_args(t, args):
- t = t.__origin__[(args[:-1], args[-1])]
- else:
- t = t.__origin__[args]
- if is_unpacked:
- t = Unpack[t]
+ else:
+ args = t.__args__
ev_args = tuple(
_eval_type(
a, globalns, localns, type_params, recursive_guard=recursive_guard,
format=format, owner=owner,
)
- for a in t.__args__
+ for a in args
)
if ev_args == t.__args__:
return t
if isinstance(t, GenericAlias):
- return GenericAlias(t.__origin__, ev_args)
+ return _rebuild_generic_alias(t, ev_args)
if isinstance(t, Union):
return functools.reduce(operator.or_, ev_args)
else:
@@ -1860,7 +1866,9 @@ def _allow_reckless_class_checks(depth=2):
The abc and functools modules indiscriminately call isinstance() and
issubclass() on the whole MRO of a user class, which may contain protocols.
"""
- return _caller(depth) in {'abc', 'functools', None}
+ # gh-136047: When `_abc` module is not available, `_py_abc` is required to
+ # allow `_py_abc.ABCMeta` fallback.
+ return _caller(depth) in {'abc', '_py_abc', 'functools', None}
_PROTO_ALLOWLIST = {
@@ -2404,7 +2412,7 @@ def _strip_annotations(t):
stripped_args = tuple(_strip_annotations(a) for a in t.__args__)
if stripped_args == t.__args__:
return t
- return GenericAlias(t.__origin__, stripped_args)
+ return _rebuild_generic_alias(t, stripped_args)
if isinstance(t, Union):
stripped_args = tuple(_strip_annotations(a) for a in t.__args__)
if stripped_args == t.__args__: