From d308d33e098d8e176f1e5169225d3cf800ed6aa1 Mon Sep 17 00:00:00 2001 From: Mehdi Drissi Date: Mon, 11 Mar 2024 23:11:56 -0700 Subject: gh-89547: Support for nesting special forms like Final (#116096) --- Lib/test/test_typing.py | 40 +++++++++++++++------- Lib/typing.py | 4 +-- Misc/ACKS | 1 + .../2024-02-28-17-50-42.gh-issue-89547.GetF38.rst | 1 + 4 files changed, 32 insertions(+), 14 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-02-28-17-50-42.gh-issue-89547.GetF38.rst diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 912384a..a9942b4 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -4656,8 +4656,6 @@ class GenericTests(BaseTestCase): with self.assertRaises(TypeError): Tuple[Optional] with self.assertRaises(TypeError): - ClassVar[ClassVar[int]] - with self.assertRaises(TypeError): List[ClassVar[int]] def test_fail_with_bare_generic(self): @@ -6014,16 +6012,6 @@ class ForwardRefTests(BaseTestCase): for clazz in [C, D, E, F]: self.assertEqual(get_type_hints(clazz), expected_result) - def test_nested_classvar_fails_forward_ref_check(self): - class E: - foo: 'typing.ClassVar[typing.ClassVar[int]]' = 7 - class F: - foo: ClassVar['ClassVar[int]'] = 7 - - for clazz in [E, F]: - with self.assertRaises(TypeError): - get_type_hints(clazz) - def test_meta_no_type_check(self): depr_msg = ( "'typing.no_type_check_decorator' is deprecated " @@ -8716,6 +8704,34 @@ class AnnotatedTests(BaseTestCase): self.assertEqual(get_type_hints(C, globals())['classvar'], ClassVar[int]) self.assertEqual(get_type_hints(C, globals())['const'], Final[int]) + def test_special_forms_nesting(self): + # These are uncommon types and are to ensure runtime + # is lax on validation. See gh-89547 for more context. + class CF: + x: ClassVar[Final[int]] + + class FC: + x: Final[ClassVar[int]] + + class ACF: + x: Annotated[ClassVar[Final[int]], "a decoration"] + + class CAF: + x: ClassVar[Annotated[Final[int], "a decoration"]] + + class AFC: + x: Annotated[Final[ClassVar[int]], "a decoration"] + + class FAC: + x: Final[Annotated[ClassVar[int], "a decoration"]] + + self.assertEqual(get_type_hints(CF, globals())['x'], ClassVar[Final[int]]) + self.assertEqual(get_type_hints(FC, globals())['x'], Final[ClassVar[int]]) + self.assertEqual(get_type_hints(ACF, globals())['x'], ClassVar[Final[int]]) + self.assertEqual(get_type_hints(CAF, globals())['x'], ClassVar[Final[int]]) + self.assertEqual(get_type_hints(AFC, globals())['x'], Final[ClassVar[int]]) + self.assertEqual(get_type_hints(FAC, globals())['x'], Final[ClassVar[int]]) + def test_cannot_subclass(self): with self.assertRaisesRegex(TypeError, "Cannot subclass .*Annotated"): class C(Annotated): diff --git a/Lib/typing.py b/Lib/typing.py index cca9525..b235043 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -653,7 +653,7 @@ def ClassVar(self, parameters): Note that ClassVar is not a class itself, and should not be used with isinstance() or issubclass(). """ - item = _type_check(parameters, f'{self} accepts only single type.') + item = _type_check(parameters, f'{self} accepts only single type.', allow_special_forms=True) return _GenericAlias(self, (item,)) @_SpecialForm @@ -675,7 +675,7 @@ def Final(self, parameters): There is no runtime checking of these properties. """ - item = _type_check(parameters, f'{self} accepts only single type.') + item = _type_check(parameters, f'{self} accepts only single type.', allow_special_forms=True) return _GenericAlias(self, (item,)) @_SpecialForm diff --git a/Misc/ACKS b/Misc/ACKS index f01c7a7..03e458d 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -470,6 +470,7 @@ Allen Downey Cesar Douady Dean Draayer Fred L. Drake, Jr. +Mehdi Drissi Derk Drukker John DuBois Paul Dubois diff --git a/Misc/NEWS.d/next/Library/2024-02-28-17-50-42.gh-issue-89547.GetF38.rst b/Misc/NEWS.d/next/Library/2024-02-28-17-50-42.gh-issue-89547.GetF38.rst new file mode 100644 index 0000000..7be4591 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-02-28-17-50-42.gh-issue-89547.GetF38.rst @@ -0,0 +1 @@ +Add support for nested typing special forms like Final[ClassVar[int]]. -- cgit v0.12