From b38f68941c42f021172d489b8ee1b89300367ed3 Mon Sep 17 00:00:00 2001 From: sharktide Date: Wed, 5 Mar 2025 12:27:53 -0500 Subject: [PATCH 01/17] Update typing.py --- Lib/typing.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Lib/typing.py b/Lib/typing.py index 4b3c63b25aeeab..014a1e3fd809c3 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -475,6 +475,19 @@ def _eval_type(t, globalns, localns, type_params=_sentinel, *, recursive_guard=f t = t.__origin__[args] if is_unpacked: t = Unpack[t] + # Ensure the actual subclass is preserved + if ev_args == t.__args__: + return t + if isinstance(t, GenericAlias): + 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: + return t + return t + ev_args = tuple( _eval_type( From bea2a10b4a00dafcf1352c7542484ef491f9ab9e Mon Sep 17 00:00:00 2001 From: sharktide Date: Wed, 5 Mar 2025 14:42:25 -0500 Subject: [PATCH 02/17] gh-130860 finalize pr changes Making pr soon --- Lib/typing.py | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/Lib/typing.py b/Lib/typing.py index 014a1e3fd809c3..25e0576839f74f 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -475,19 +475,6 @@ def _eval_type(t, globalns, localns, type_params=_sentinel, *, recursive_guard=f t = t.__origin__[args] if is_unpacked: t = Unpack[t] - # Ensure the actual subclass is preserved - if ev_args == t.__args__: - return t - if isinstance(t, GenericAlias): - 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: - return t - return t - ev_args = tuple( _eval_type( @@ -499,7 +486,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: From 3af57b4ac891bf58cd5be22c57eecc443b3c016d Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Wed, 5 Mar 2025 21:48:24 +0000 Subject: [PATCH 03/17] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20b?= =?UTF-8?q?lurb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Library/2025-03-05-21-48-22.gh-issue-130870.uDz6AQ.rst | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2025-03-05-21-48-22.gh-issue-130870.uDz6AQ.rst 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..1ac3f7bad27508 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-03-05-21-48-22.gh-issue-130870.uDz6AQ.rst @@ -0,0 +1,6 @@ +Type: Bugfix +Title: Fix _eval_type Handling for GenericAlias with Unflattened Arguments and Union Types +Issue: :gh:`130870` + +Detailed changes: +This change improves the handling of `GenericAlias` and `Union` types in `_eval_type`, ensuring that callable arguments for `GenericAlias` are unflattened and that `Union` types are properly evaluated. It resolves complex annotations, including recursive and generic types. All relevant tests, including those for forward references, generics, `Union` types, and recursion, passed successfully without any issues. From d3a633c006a7bdf5aadfdcadd7f374b7509c53b4 Mon Sep 17 00:00:00 2001 From: sharktide Date: Wed, 5 Mar 2025 16:51:45 -0500 Subject: [PATCH 04/17] Update 2025-03-05-21-48-22.gh-issue-130870.uDz6AQ.rst --- .../Library/2025-03-05-21-48-22.gh-issue-130870.uDz6AQ.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 index 1ac3f7bad27508..7e298fd8e13f34 100644 --- 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 @@ -1,6 +1,6 @@ Type: Bugfix -Title: Fix _eval_type Handling for GenericAlias with Unflattened Arguments and Union Types +Title: Fix ``_eval_type`` Handling for ``GenericAlias`` with Unflattened Arguments and ``Union`` Types Issue: :gh:`130870` Detailed changes: -This change improves the handling of `GenericAlias` and `Union` types in `_eval_type`, ensuring that callable arguments for `GenericAlias` are unflattened and that `Union` types are properly evaluated. It resolves complex annotations, including recursive and generic types. All relevant tests, including those for forward references, generics, `Union` types, and recursion, passed successfully without any issues. +This change improves the handling of ``GenericAlias`` and ``Union`` types in ``_eval_type``, ensuring that callable arguments for ``GenericAlias`` are unflattened and that ``Union`` types are properly evaluated. It resolves complex annotations, including recursive and generic types. All relevant tests, including those for forward references, generics, ``Union`` types, and recursion, passed successfully without any issues. From b85d920d0f77eb6c56eaba05dbc53c4ed9234731 Mon Sep 17 00:00:00 2001 From: sharktide Date: Thu, 6 Mar 2025 07:42:16 -0500 Subject: [PATCH 05/17] Update 2025-03-05-21-48-22.gh-issue-130870.uDz6AQ.rst --- .../Library/2025-03-05-21-48-22.gh-issue-130870.uDz6AQ.rst | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) 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 index 7e298fd8e13f34..5a549cd07f4d96 100644 --- 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 @@ -1,6 +1,4 @@ -Type: Bugfix -Title: Fix ``_eval_type`` Handling for ``GenericAlias`` with Unflattened Arguments and ``Union`` Types Issue: :gh:`130870` Detailed changes: -This change improves the handling of ``GenericAlias`` and ``Union`` types in ``_eval_type``, ensuring that callable arguments for ``GenericAlias`` are unflattened and that ``Union`` types are properly evaluated. It resolves complex annotations, including recursive and generic types. All relevant tests, including those for forward references, generics, ``Union`` types, and recursion, passed successfully without any issues. +This change improves the handling of ``GenericAlias`` and ``Union`` types in ``_eval_type``, ensuring that callable arguments for ``GenericAlias`` are unflattened and that ``Union`` types are properly evaluated. From b0db6a6431104699c02b2909fd1d5019220316b6 Mon Sep 17 00:00:00 2001 From: sharktide Date: Thu, 6 Mar 2025 09:24:54 -0500 Subject: [PATCH 06/17] Update 2025-03-05-21-48-22.gh-issue-130870.uDz6AQ.rst From 596b6f57503f0956e4436740d46a70a955d9d5ef Mon Sep 17 00:00:00 2001 From: sharktide Date: Thu, 6 Mar 2025 13:43:26 -0500 Subject: [PATCH 07/17] gh-130897 Add test cases --- Lib/test/test_typing.py | 63 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index e88c811bfcac52..fe06bd6aa12333 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -10758,6 +10758,69 @@ def test_eq(self): with self.assertWarns(DeprecationWarning): self.assertNotEqual(int, typing._UnionGenericAlias) +class MyType: + pass + +class test_generic_alias_handling(BaseTestCase): + def test_forward_ref(self): + fwd_ref = ForwardRef('MyType') + result = _eval_type(fwd_ref, globals(), locals()) + self.assertIs(result, MyType, f"Expected MyType, got {result}") + + def test_generic_alias(self): + fwd_ref = ForwardRef('MyType') + generic_list = List[fwd_ref] + result = _eval_type(generic_list, globals(), locals()) + self.assertEqual(result, List[MyType], f"Expected List[MyType], got {result}") + + def test_union(self): + fwd_ref_1 = ForwardRef('MyType') + fwd_ref_2 = ForwardRef('int') + union_type = Union[fwd_ref_1, fwd_ref_2] + + result = _eval_type(union_type, globals(), locals()) + self.assertEqual(result, Union[MyType, int], f"Expected Union[MyType, int], got {result}") + + def test_recursive_forward_ref(self): + recursive_ref = ForwardRef('RecursiveType') + globals()['RecursiveType'] = recursive_ref + + recursive_type = Dict[str, List[recursive_ref]] + + result = _eval_type(recursive_type, globals(), locals(), recursive_guard={recursive_ref}) + + self.assertEqual(result, Dict[str, List[recursive_ref]], f"Expected Dict[str, List[RecursiveType]], got {result}") + + def test_callable_unpacking(self): + fwd_ref = ForwardRef('MyType') + callable_type = Callable[[fwd_ref, int], str] + result = _eval_type(callable_type, globals(), locals()) + + self.assertEqual(result, Callable[[MyType, int], str], f"Expected Callable[[MyType, int], str], got {result}") + + def test_unpacked_generic(self): + fwd_ref = ForwardRef('MyType') + generic_type = Tuple[fwd_ref, int] + + result = _eval_type(generic_type, globals(), locals()) + self.assertEqual(result, Tuple[MyType, int], f"Expected Tuple[MyType, int], got {result}") + + def test_preservation_of_type(self): + fwd_ref_1 = ForwardRef('MyType') + fwd_ref_2 = ForwardRef('int') + complex_type = Dict[str, Union[fwd_ref_1, fwd_ref_2]] + + result = _eval_type(complex_type, globals(), locals()) + self.assertEqual(result, Dict[str, Union[MyType, int]], f"Expected Dict[str, Union[MyType, int]], got {result}") + + def test_callable_unflattening(self): + callable_type = Callable[[int, str], bool] + result = _eval_type(callable_type, globals(), locals(), type_params=()) + self.assertEqual(result, Callable[[int, str], bool], f"Expected Callable[[int, str], bool], got {result}") + + callable_type_packed = Callable[[int, str], bool] # Correct format for callable + result = _eval_type(callable_type_packed, globals(), locals(), type_params=()) + self.assertEqual(result, Callable[[int, str], bool], f"Expected Callable[[int, str], bool], got {result}") def load_tests(loader, tests, pattern): import doctest From 37b760c2e96ba8f5c652713f67b8ddaa4e63dfec Mon Sep 17 00:00:00 2001 From: sharktide Date: Thu, 6 Mar 2025 13:56:59 -0500 Subject: [PATCH 08/17] Update test_typing.py --- Lib/test/test_typing.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index fe06bd6aa12333..b0c09510eedf3b 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -41,6 +41,7 @@ from typing import TypeAlias from typing import ParamSpec, Concatenate, ParamSpecArgs, ParamSpecKwargs from typing import TypeGuard, TypeIs, NoDefault +from typing import _eval_type import abc import textwrap import typing @@ -10761,7 +10762,7 @@ def test_eq(self): class MyType: pass -class test_generic_alias_handling(BaseTestCase): +class TestGenericAliasHandling(BaseTestCase): def test_forward_ref(self): fwd_ref = ForwardRef('MyType') result = _eval_type(fwd_ref, globals(), locals()) From aeb602b1742abb50d3a46c149f819e42ebbcfd80 Mon Sep 17 00:00:00 2001 From: sharktide Date: Thu, 6 Mar 2025 14:00:11 -0500 Subject: [PATCH 09/17] Remove whitespace test_typing.py --- Lib/test/test_typing.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index b0c09510eedf3b..bf029fc8413f7e 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -10778,31 +10778,25 @@ def test_union(self): fwd_ref_1 = ForwardRef('MyType') fwd_ref_2 = ForwardRef('int') union_type = Union[fwd_ref_1, fwd_ref_2] - result = _eval_type(union_type, globals(), locals()) self.assertEqual(result, Union[MyType, int], f"Expected Union[MyType, int], got {result}") def test_recursive_forward_ref(self): recursive_ref = ForwardRef('RecursiveType') globals()['RecursiveType'] = recursive_ref - recursive_type = Dict[str, List[recursive_ref]] - result = _eval_type(recursive_type, globals(), locals(), recursive_guard={recursive_ref}) - self.assertEqual(result, Dict[str, List[recursive_ref]], f"Expected Dict[str, List[RecursiveType]], got {result}") def test_callable_unpacking(self): fwd_ref = ForwardRef('MyType') callable_type = Callable[[fwd_ref, int], str] result = _eval_type(callable_type, globals(), locals()) - self.assertEqual(result, Callable[[MyType, int], str], f"Expected Callable[[MyType, int], str], got {result}") def test_unpacked_generic(self): fwd_ref = ForwardRef('MyType') generic_type = Tuple[fwd_ref, int] - result = _eval_type(generic_type, globals(), locals()) self.assertEqual(result, Tuple[MyType, int], f"Expected Tuple[MyType, int], got {result}") @@ -10810,7 +10804,6 @@ def test_preservation_of_type(self): fwd_ref_1 = ForwardRef('MyType') fwd_ref_2 = ForwardRef('int') complex_type = Dict[str, Union[fwd_ref_1, fwd_ref_2]] - result = _eval_type(complex_type, globals(), locals()) self.assertEqual(result, Dict[str, Union[MyType, int]], f"Expected Dict[str, Union[MyType, int]], got {result}") From b31ae23c3db084eafb5aba1f97ff1117eb6c1d24 Mon Sep 17 00:00:00 2001 From: Rihaan Meher Date: Mon, 5 May 2025 07:45:43 -0400 Subject: [PATCH 10/17] Add improved tests for #130897 --- Lib/test/test_typing.py | 81 ++++++++++++++++++++++++++++++++--------- 1 file changed, 63 insertions(+), 18 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 39aacac6cc0b9a..7b76d841be1b7b 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -10732,62 +10732,107 @@ def test_eq(self): with self.assertWarns(DeprecationWarning): self.assertNotEqual(int, typing._UnionGenericAlias) +# Define MyType class MyType: pass class TestGenericAliasHandling(BaseTestCase): + def test_forward_ref(self): fwd_ref = ForwardRef('MyType') - result = _eval_type(fwd_ref, globals(), locals()) - self.assertIs(result, MyType, f"Expected MyType, got {result}") + + def func(arg: fwd_ref): + pass + + result = get_type_hints(func) + self.assertEqual(result['arg'], MyType, f"Expected MyType, got {result['arg']}") def test_generic_alias(self): fwd_ref = ForwardRef('MyType') generic_list = List[fwd_ref] - result = _eval_type(generic_list, globals(), locals()) - self.assertEqual(result, List[MyType], f"Expected List[MyType], got {result}") + + def func(arg: generic_list): + pass + + result = get_type_hints(func) + self.assertEqual(result['arg'], List[MyType], f"Expected List[MyType], got {result['arg']}") def test_union(self): fwd_ref_1 = ForwardRef('MyType') fwd_ref_2 = ForwardRef('int') union_type = Union[fwd_ref_1, fwd_ref_2] - result = _eval_type(union_type, globals(), locals()) - self.assertEqual(result, Union[MyType, int], f"Expected Union[MyType, int], got {result}") + + def func(arg: union_type): + pass + + result = get_type_hints(func) + self.assertEqual(result['arg'], Union[MyType, int], f"Expected Union[MyType, int], got {result['arg']}") def test_recursive_forward_ref(self): recursive_ref = ForwardRef('RecursiveType') globals()['RecursiveType'] = recursive_ref recursive_type = Dict[str, List[recursive_ref]] - result = _eval_type(recursive_type, globals(), locals(), recursive_guard={recursive_ref}) - self.assertEqual(result, Dict[str, List[recursive_ref]], f"Expected Dict[str, List[RecursiveType]], got {result}") + + def func(arg: recursive_type): + pass + + result = get_type_hints(func) + self.assertEqual(result['arg'], Dict[str, List[recursive_ref]], f"Expected Dict[str, List[RecursiveType]], got {result['arg']}") def test_callable_unpacking(self): fwd_ref = ForwardRef('MyType') callable_type = Callable[[fwd_ref, int], str] - result = _eval_type(callable_type, globals(), locals()) - self.assertEqual(result, Callable[[MyType, int], str], f"Expected Callable[[MyType, int], str], got {result}") + + def func(arg1: fwd_ref, arg2: int) -> str: + return "test" + + result = get_type_hints(func) + self.assertEqual(result['arg1'], MyType, f"Expected MyType for arg1, got {result['arg1']}") + self.assertEqual(result['arg2'], int, f"Expected int for arg2, got {result['arg2']}") + self.assertEqual(result['return'], str, f"Expected str for return, got {result['return']}") def test_unpacked_generic(self): fwd_ref = ForwardRef('MyType') generic_type = Tuple[fwd_ref, int] - result = _eval_type(generic_type, globals(), locals()) - self.assertEqual(result, Tuple[MyType, int], f"Expected Tuple[MyType, int], got {result}") + + def func(arg: generic_type): + pass + + result = get_type_hints(func) + self.assertEqual(result['arg'], Tuple[MyType, int], f"Expected Tuple[MyType, int], got {result['arg']}") def test_preservation_of_type(self): fwd_ref_1 = ForwardRef('MyType') fwd_ref_2 = ForwardRef('int') complex_type = Dict[str, Union[fwd_ref_1, fwd_ref_2]] - result = _eval_type(complex_type, globals(), locals()) - self.assertEqual(result, Dict[str, Union[MyType, int]], f"Expected Dict[str, Union[MyType, int]], got {result}") + + def func(arg: complex_type): + pass + + result = get_type_hints(func) + self.assertEqual(result['arg'], Dict[str, Union[MyType, int]], f"Expected Dict[str, Union[MyType, int]], got {result['arg']}") def test_callable_unflattening(self): callable_type = Callable[[int, str], bool] - result = _eval_type(callable_type, globals(), locals(), type_params=()) - self.assertEqual(result, Callable[[int, str], bool], f"Expected Callable[[int, str], bool], got {result}") + + def func(arg1: int, arg2: str) -> bool: + return True + + result = get_type_hints(func) + self.assertEqual(result['arg1'], int, f"Expected int for arg1, got {result['arg1']}") + self.assertEqual(result['arg2'], str, f"Expected str for arg2, got {result['arg2']}") + self.assertEqual(result['return'], bool, f"Expected bool for return, got {result['return']}") callable_type_packed = Callable[[int, str], bool] # Correct format for callable - result = _eval_type(callable_type_packed, globals(), locals(), type_params=()) - self.assertEqual(result, Callable[[int, str], bool], f"Expected Callable[[int, str], bool], got {result}") + + def func_packed(arg1: int, arg2: str) -> bool: + return True + + result = get_type_hints(func_packed) + self.assertEqual(result['arg1'], int, f"Expected int for arg1, got {result['arg1']}") + self.assertEqual(result['arg2'], str, f"Expected str for arg2, got {result['arg2']}") + self.assertEqual(result['return'], bool, f"Expected bool for return, got {result['return']}") + def load_tests(loader, tests, pattern): import doctest From 221cc920fc493c60fc6e4d566500914a54380cd1 Mon Sep 17 00:00:00 2001 From: Rihaan Meher Date: Mon, 5 May 2025 08:08:14 -0400 Subject: [PATCH 11/17] Update test_typing.py --- Lib/test/test_typing.py | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 7b76d841be1b7b..6e03dcc54807fd 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -10740,20 +10740,20 @@ class TestGenericAliasHandling(BaseTestCase): def test_forward_ref(self): fwd_ref = ForwardRef('MyType') - + def func(arg: fwd_ref): pass - + result = get_type_hints(func) self.assertEqual(result['arg'], MyType, f"Expected MyType, got {result['arg']}") def test_generic_alias(self): fwd_ref = ForwardRef('MyType') generic_list = List[fwd_ref] - + def func(arg: generic_list): pass - + result = get_type_hints(func) self.assertEqual(result['arg'], List[MyType], f"Expected List[MyType], got {result['arg']}") @@ -10761,10 +10761,10 @@ def test_union(self): fwd_ref_1 = ForwardRef('MyType') fwd_ref_2 = ForwardRef('int') union_type = Union[fwd_ref_1, fwd_ref_2] - + def func(arg: union_type): pass - + result = get_type_hints(func) self.assertEqual(result['arg'], Union[MyType, int], f"Expected Union[MyType, int], got {result['arg']}") @@ -10772,20 +10772,20 @@ def test_recursive_forward_ref(self): recursive_ref = ForwardRef('RecursiveType') globals()['RecursiveType'] = recursive_ref recursive_type = Dict[str, List[recursive_ref]] - + def func(arg: recursive_type): pass - + result = get_type_hints(func) self.assertEqual(result['arg'], Dict[str, List[recursive_ref]], f"Expected Dict[str, List[RecursiveType]], got {result['arg']}") def test_callable_unpacking(self): fwd_ref = ForwardRef('MyType') callable_type = Callable[[fwd_ref, int], str] - + def func(arg1: fwd_ref, arg2: int) -> str: return "test" - + result = get_type_hints(func) self.assertEqual(result['arg1'], MyType, f"Expected MyType for arg1, got {result['arg1']}") self.assertEqual(result['arg2'], int, f"Expected int for arg2, got {result['arg2']}") @@ -10794,10 +10794,10 @@ def func(arg1: fwd_ref, arg2: int) -> str: def test_unpacked_generic(self): fwd_ref = ForwardRef('MyType') generic_type = Tuple[fwd_ref, int] - + def func(arg: generic_type): pass - + result = get_type_hints(func) self.assertEqual(result['arg'], Tuple[MyType, int], f"Expected Tuple[MyType, int], got {result['arg']}") @@ -10805,29 +10805,29 @@ def test_preservation_of_type(self): fwd_ref_1 = ForwardRef('MyType') fwd_ref_2 = ForwardRef('int') complex_type = Dict[str, Union[fwd_ref_1, fwd_ref_2]] - + def func(arg: complex_type): pass - + result = get_type_hints(func) self.assertEqual(result['arg'], Dict[str, Union[MyType, int]], f"Expected Dict[str, Union[MyType, int]], got {result['arg']}") def test_callable_unflattening(self): callable_type = Callable[[int, str], bool] - + def func(arg1: int, arg2: str) -> bool: return True - + result = get_type_hints(func) self.assertEqual(result['arg1'], int, f"Expected int for arg1, got {result['arg1']}") self.assertEqual(result['arg2'], str, f"Expected str for arg2, got {result['arg2']}") self.assertEqual(result['return'], bool, f"Expected bool for return, got {result['return']}") - callable_type_packed = Callable[[int, str], bool] # Correct format for callable - + callable_type_packed = Callable[[int, str], bool] + def func_packed(arg1: int, arg2: str) -> bool: return True - + result = get_type_hints(func_packed) self.assertEqual(result['arg1'], int, f"Expected int for arg1, got {result['arg1']}") self.assertEqual(result['arg2'], str, f"Expected str for arg2, got {result['arg2']}") From 6950f759034e821319df37785213e60a0566ffd4 Mon Sep 17 00:00:00 2001 From: sharktide Date: Mon, 12 May 2025 12:23:01 -0400 Subject: [PATCH 12/17] Add new test to test_typing.py to demonstrate fixed behaviour in pythongh-130870 --- Lib/test/test_typing.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 1e39752bf1518f..7e1accb3748adc 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -10772,6 +10772,20 @@ def func_packed(arg1: int, arg2: str) -> bool: def test_hashable(self): self.assertEqual(hash(typing._UnionGenericAlias), hash(Union)) +class TestCallableAlias(BaseTestCase): + def test_callable_alias_preserves_subclass(self): + C = Callable[[str, ForwardRef("int")], int] + class A: + c: C + + hints = get_type_hints(A) + + # Ensure evaluated type retains the correct class + self.assertEqual(hints['c'].__class__, C.__class__) + + # 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 From ae5e0e81825f73d0e4be75789e23a8a5fec7b26c Mon Sep 17 00:00:00 2001 From: sharktide Date: Mon, 12 May 2025 12:29:55 -0400 Subject: [PATCH 13/17] Make linter happy --- Lib/test/test_typing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 7e1accb3748adc..305a4c017af8fc 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -10777,7 +10777,7 @@ def test_callable_alias_preserves_subclass(self): C = Callable[[str, ForwardRef("int")], int] class A: c: C - + hints = get_type_hints(A) # Ensure evaluated type retains the correct class From 6278b37749aac85a62462013910f55439cb21c32 Mon Sep 17 00:00:00 2001 From: sharktide Date: Mon, 12 May 2025 16:33:19 -0400 Subject: [PATCH 14/17] Update test import statements and put in the actually correct test. (Ignore previous commit) --- Lib/test/test_typing.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 305a4c017af8fc..d763f09f060655 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 @@ -10774,15 +10775,18 @@ def test_hashable(self): class TestCallableAlias(BaseTestCase): def test_callable_alias_preserves_subclass(self): - C = Callable[[str, ForwardRef("int")], int] + 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()) - hints = get_type_hints(A) - - # Ensure evaluated type retains the correct class + # 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) From f676ac0124c5584579f20b28eddcced32166e2fd Mon Sep 17 00:00:00 2001 From: Rihaan Meher Date: Mon, 12 May 2025 16:40:45 -0400 Subject: [PATCH 15/17] Update 2025-03-05-21-48-22.gh-issue-130870.uDz6AQ.rst --- .../Library/2025-03-05-21-48-22.gh-issue-130870.uDz6AQ.rst | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) 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 index 5a549cd07f4d96..e52af134eeff63 100644 --- 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 @@ -1,4 +1 @@ -Issue: :gh:`130870` - -Detailed changes: -This change improves the handling of ``GenericAlias`` and ``Union`` types in ``_eval_type``, ensuring that callable arguments for ``GenericAlias`` are unflattened and that ``Union`` types are properly evaluated. +Ensure that typing.Callable retains its subclass (_CallableGenericAlias) instead of being incorrectly converted to GenericAlias. From 32c43345feac539b37c9b2cced45ee2edbef4f02 Mon Sep 17 00:00:00 2001 From: Rihaan Meher Date: Sat, 7 Jun 2025 19:42:10 +0530 Subject: [PATCH 16/17] Remove unrelated tests --- Lib/test/test_typing.py | 103 ---------------------------------------- 1 file changed, 103 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index d763f09f060655..90fbb594f2ecf5 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -10670,109 +10670,6 @@ def test_eq(self): with self.assertWarns(DeprecationWarning): self.assertNotEqual(int, typing._UnionGenericAlias) -class MyType: - pass - -class TestGenericAliasHandling(BaseTestCase): - - def test_forward_ref(self): - fwd_ref = ForwardRef('MyType') - - def func(arg: fwd_ref): - pass - - result = get_type_hints(func) - self.assertEqual(result['arg'], MyType, f"Expected MyType, got {result['arg']}") - - def test_generic_alias(self): - fwd_ref = ForwardRef('MyType') - generic_list = List[fwd_ref] - - def func(arg: generic_list): - pass - - result = get_type_hints(func) - self.assertEqual(result['arg'], List[MyType], f"Expected List[MyType], got {result['arg']}") - - def test_union(self): - fwd_ref_1 = ForwardRef('MyType') - fwd_ref_2 = ForwardRef('int') - union_type = Union[fwd_ref_1, fwd_ref_2] - - def func(arg: union_type): - pass - - result = get_type_hints(func) - self.assertEqual(result['arg'], Union[MyType, int], f"Expected Union[MyType, int], got {result['arg']}") - - def test_recursive_forward_ref(self): - recursive_ref = ForwardRef('RecursiveType') - globals()['RecursiveType'] = recursive_ref - recursive_type = Dict[str, List[recursive_ref]] - - def func(arg: recursive_type): - pass - - result = get_type_hints(func) - self.assertEqual(result['arg'], Dict[str, List[recursive_ref]], f"Expected Dict[str, List[RecursiveType]], got {result['arg']}") - - def test_callable_unpacking(self): - fwd_ref = ForwardRef('MyType') - callable_type = Callable[[fwd_ref, int], str] - - def func(arg1: fwd_ref, arg2: int) -> str: - return "test" - - result = get_type_hints(func) - self.assertEqual(result['arg1'], MyType, f"Expected MyType for arg1, got {result['arg1']}") - self.assertEqual(result['arg2'], int, f"Expected int for arg2, got {result['arg2']}") - self.assertEqual(result['return'], str, f"Expected str for return, got {result['return']}") - - def test_unpacked_generic(self): - fwd_ref = ForwardRef('MyType') - generic_type = Tuple[fwd_ref, int] - - def func(arg: generic_type): - pass - - result = get_type_hints(func) - self.assertEqual(result['arg'], Tuple[MyType, int], f"Expected Tuple[MyType, int], got {result['arg']}") - - def test_preservation_of_type(self): - fwd_ref_1 = ForwardRef('MyType') - fwd_ref_2 = ForwardRef('int') - complex_type = Dict[str, Union[fwd_ref_1, fwd_ref_2]] - - def func(arg: complex_type): - pass - - result = get_type_hints(func) - self.assertEqual(result['arg'], Dict[str, Union[MyType, int]], f"Expected Dict[str, Union[MyType, int]], got {result['arg']}") - - def test_callable_unflattening(self): - callable_type = Callable[[int, str], bool] - - def func(arg1: int, arg2: str) -> bool: - return True - - result = get_type_hints(func) - self.assertEqual(result['arg1'], int, f"Expected int for arg1, got {result['arg1']}") - self.assertEqual(result['arg2'], str, f"Expected str for arg2, got {result['arg2']}") - self.assertEqual(result['return'], bool, f"Expected bool for return, got {result['return']}") - - callable_type_packed = Callable[[int, str], bool] - - def func_packed(arg1: int, arg2: str) -> bool: - return True - - result = get_type_hints(func_packed) - self.assertEqual(result['arg1'], int, f"Expected int for arg1, got {result['arg1']}") - self.assertEqual(result['arg2'], str, f"Expected str for arg2, got {result['arg2']}") - self.assertEqual(result['return'], bool, f"Expected bool for return, got {result['return']}") - - 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] From 55ed601e1df12aebd9b044640878fdfcadd89aaf Mon Sep 17 00:00:00 2001 From: Rihaan Meher Date: Sat, 7 Jun 2025 19:48:08 +0530 Subject: [PATCH 17/17] Remove unnecessary imports --- Lib/test/test_typing.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 90fbb594f2ecf5..c23ca933ec908f 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -43,7 +43,6 @@ from typing import TypeAlias from typing import ParamSpec, Concatenate, ParamSpecArgs, ParamSpecKwargs from typing import TypeGuard, TypeIs, NoDefault -from typing import _eval_type import abc import textwrap import typing pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy