Skip to content

Commit

Permalink
gh-89547: Support for nesting special forms like Final (#116096)
Browse files Browse the repository at this point in the history
  • Loading branch information
hmc-cs-mdrissi committed Mar 12, 2024
1 parent 4fa95c6 commit d308d33
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 14 deletions.
40 changes: 28 additions & 12 deletions Lib/test/test_typing.py
Expand Up @@ -4655,8 +4655,6 @@ def test_fail_with_bare_union(self):
List[Union]
with self.assertRaises(TypeError):
Tuple[Optional]
with self.assertRaises(TypeError):
ClassVar[ClassVar[int]]
with self.assertRaises(TypeError):
List[ClassVar[int]]

Expand Down Expand Up @@ -6014,16 +6012,6 @@ class F:
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 "
Expand Down Expand Up @@ -8716,6 +8704,34 @@ class C:
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):
Expand Down
4 changes: 2 additions & 2 deletions Lib/typing.py
Expand Up @@ -653,7 +653,7 @@ class Starship:
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
Expand All @@ -675,7 +675,7 @@ class FastConnector(Connection):
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
Expand Down
1 change: 1 addition & 0 deletions Misc/ACKS
Expand Up @@ -470,6 +470,7 @@ Allen Downey
Cesar Douady
Dean Draayer
Fred L. Drake, Jr.
Mehdi Drissi
Derk Drukker
John DuBois
Paul Dubois
Expand Down
@@ -0,0 +1 @@
Add support for nested typing special forms like Final[ClassVar[int]].

0 comments on commit d308d33

Please sign in to comment.