Skip to content

Commit 47568bc

Browse files
committed
gh-130870: Preserve GenericAlias subclasses in typing.get_type_hints()
1 parent 18249d9 commit 47568bc

File tree

2 files changed

+28
-10
lines changed

2 files changed

+28
-10
lines changed

Lib/test/test_typing.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7178,6 +7178,22 @@ def func(x: undefined) -> undefined: ...
71787178
self.assertEqual(get_type_hints(func, format=annotationlib.Format.STRING),
71797179
{'x': 'undefined', 'return': 'undefined'})
71807180

7181+
def test_get_type_hints_preserve_generic_alias_subclasses(self):
7182+
# https://github.com/python/cpython/issues/130870
7183+
# An real world example of this is `collections.abc.Callable`. When parameterized,
7184+
# the result is a subclass of `types.GenericAlias`.
7185+
class MyAlias(GenericAlias):
7186+
pass
7187+
7188+
class MyClass:
7189+
def __class_getitem__(cls, args):
7190+
return MyAlias(cls, args)
7191+
7192+
# Using a forward reference is important, otherwise it works as expected.
7193+
def func(x: MyClass['int']): ...
7194+
7195+
assert isinstance(get_type_hints(func)['x'], MyAlias)
7196+
71817197

71827198
class GetUtilitiesTestCase(TestCase):
71837199
def test_get_origin(self):

Lib/typing.py

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -468,25 +468,27 @@ def _eval_type(t, globalns, localns, type_params=_sentinel, *, recursive_guard=f
468468
_make_forward_ref(arg) if isinstance(arg, str) else arg
469469
for arg in t.__args__
470470
)
471-
is_unpacked = t.__unpacked__
472-
if _should_unflatten_callable_args(t, args):
473-
t = t.__origin__[(args[:-1], args[-1])]
474-
else:
475-
t = t.__origin__[args]
476-
if is_unpacked:
477-
t = Unpack[t]
471+
else:
472+
args = t.__args__
478473

479474
ev_args = tuple(
480475
_eval_type(
481476
a, globalns, localns, type_params, recursive_guard=recursive_guard,
482477
format=format, owner=owner,
483478
)
484-
for a in t.__args__
479+
for a in args
485480
)
486-
if ev_args == t.__args__:
481+
if ev_args == args:
487482
return t
488483
if isinstance(t, GenericAlias):
489-
return GenericAlias(t.__origin__, ev_args)
484+
is_unpacked = t.__unpacked__
485+
if _should_unflatten_callable_args(t, ev_args):
486+
t = t.__origin__[(ev_args[:-1], ev_args[-1])]
487+
else:
488+
t = t.__origin__[ev_args]
489+
if is_unpacked:
490+
t = Unpack[t]
491+
return t
490492
if isinstance(t, Union):
491493
return functools.reduce(operator.or_, ev_args)
492494
else:

0 commit comments

Comments
 (0)