Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 47568bc

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

File tree

2 files changed

+28
-10
lines changed
Filter options

2 files changed

+28
-10
lines changed

‎Lib/test/test_typing.py

Copy file name to clipboardExpand all lines: Lib/test/test_typing.py
+16Lines 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

Copy file name to clipboardExpand all lines: Lib/typing.py
+12-10Lines 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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.