Skip to content

gh-130870 Fix _eval_type Handling for GenericAlias with Unflattened Arguments and Union Types #130897

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions Lib/test/test_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import collections
import collections.abc
from collections import defaultdict
from collections.abc import Callable as ABCallable
from functools import lru_cache, wraps, reduce
import gc
import inspect
Expand Down Expand Up @@ -10668,9 +10669,23 @@ def test_eq(self):
with self.assertWarns(DeprecationWarning):
self.assertNotEqual(int, typing._UnionGenericAlias)

def test_hashable(self):
self.assertEqual(hash(typing._UnionGenericAlias), hash(Union))
class TestCallableAlias(BaseTestCase):
def test_callable_alias_preserves_subclass(self):
C = ABCallable[[str, ForwardRef('int')], int]
class A:
c: C
# Explicitly pass global namespace to ensure correct resolution
hints = get_type_hints(A, globalns=globals())

# Ensure evaluated type retains the correct subclass (_CallableGenericAlias)
self.assertEqual(hints['c'].__class__, C.__class__)

# Ensure evaluated type retains correct origin
self.assertEqual(hints['c'].__origin__, C.__origin__)

# Instead of comparing raw ForwardRef, check if the resolution is correct
expected_args = tuple(int if isinstance(arg, ForwardRef) else arg for arg in C.__args__)
self.assertEqual(hints['c'].__args__, expected_args)

def load_tests(loader, tests, pattern):
import doctest
Expand Down
4 changes: 3 additions & 1 deletion Lib/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,9 @@ def _eval_type(t, globalns, localns, type_params=_sentinel, *, recursive_guard=f
if ev_args == t.__args__:
return t
if isinstance(t, GenericAlias):
return GenericAlias(t.__origin__, ev_args)
if _should_unflatten_callable_args(t, ev_args):
return t.__class__(t.__origin__, (ev_args[:-1], ev_args[-1]))
return t.__class__(t.__origin__, ev_args)
if isinstance(t, Union):
return functools.reduce(operator.or_, ev_args)
else:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Ensure that typing.Callable retains its subclass (_CallableGenericAlias) instead of being incorrectly converted to GenericAlias.
Loading