diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 6ef633e4545aef..c23ca933ec908f 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -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 @@ -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 diff --git a/Lib/typing.py b/Lib/typing.py index 3d64480e1431c1..7c546ec7fb8c10 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -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: diff --git a/Misc/NEWS.d/next/Library/2025-03-05-21-48-22.gh-issue-130870.uDz6AQ.rst b/Misc/NEWS.d/next/Library/2025-03-05-21-48-22.gh-issue-130870.uDz6AQ.rst new file mode 100644 index 00000000000000..e52af134eeff63 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-03-05-21-48-22.gh-issue-130870.uDz6AQ.rst @@ -0,0 +1 @@ +Ensure that typing.Callable retains its subclass (_CallableGenericAlias) instead of being incorrectly converted to GenericAlias.