Skip to content

Commit 41e0aea

Browse files
bpo-46491: Allow Annotated on outside of Final/ClassVar (GH-30864)
We treat Annotated type arg as class-level annotation. This exempts it from checks against Final and ClassVar in order to allow using them in any nesting order. Automerge-Triggered-By: GH:gvanrossum (cherry picked from commit e1abffc) Co-authored-by: Gregory Beauregard <[email protected]>
1 parent 367a37a commit 41e0aea

File tree

3 files changed

+13
-4
lines changed

3 files changed

+13
-4
lines changed

Lib/test/test_typing.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4582,6 +4582,14 @@ class C:
45824582
A.x = 5
45834583
self.assertEqual(C.x, 5)
45844584

4585+
def test_special_form_containment(self):
4586+
class C:
4587+
classvar: Annotated[ClassVar[int], "a decoration"] = 4
4588+
const: Annotated[Final[int], "Const"] = 4
4589+
4590+
self.assertEqual(get_type_hints(C, globals())['classvar'], ClassVar[int])
4591+
self.assertEqual(get_type_hints(C, globals())['const'], Final[int])
4592+
45854593
def test_hash_eq(self):
45864594
self.assertEqual(len({Annotated[int, 4, 5], Annotated[int, 4, 5]}), 1)
45874595
self.assertNotEqual(Annotated[int, 4, 5], Annotated[int, 5, 4])

Lib/typing.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ def _type_convert(arg, module=None):
143143
return arg
144144

145145

146-
def _type_check(arg, msg, is_argument=True, module=None, *, is_class=False):
146+
def _type_check(arg, msg, is_argument=True, module=None, *, allow_special_forms=False):
147147
"""Check that the argument is a type, and return it (internal helper).
148148
149149
As a special case, accept None and return type(None) instead. Also wrap strings
@@ -156,7 +156,7 @@ def _type_check(arg, msg, is_argument=True, module=None, *, is_class=False):
156156
We append the repr() of the actual value (truncated to 100 chars).
157157
"""
158158
invalid_generic_forms = (Generic, Protocol)
159-
if not is_class:
159+
if not allow_special_forms:
160160
invalid_generic_forms += (ClassVar,)
161161
if is_argument:
162162
invalid_generic_forms += (Final,)
@@ -691,7 +691,7 @@ def _evaluate(self, globalns, localns, recursive_guard):
691691
eval(self.__forward_code__, globalns, localns),
692692
"Forward references must evaluate to types.",
693693
is_argument=self.__forward_is_argument__,
694-
is_class=self.__forward_is_class__,
694+
allow_special_forms=self.__forward_is_class__,
695695
)
696696
self.__forward_value__ = _eval_type(
697697
type_, globalns, localns, recursive_guard | {self.__forward_arg__}
@@ -1677,7 +1677,7 @@ def __class_getitem__(cls, params):
16771677
"with at least two arguments (a type and an "
16781678
"annotation).")
16791679
msg = "Annotated[t, ...]: t must be a type."
1680-
origin = _type_check(params[0], msg)
1680+
origin = _type_check(params[0], msg, allow_special_forms=True)
16811681
metadata = tuple(params[1:])
16821682
return _AnnotatedAlias(origin, metadata)
16831683

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Allow :data:`typing.Annotated` to wrap :data:`typing.Final` and :data:`typing.ClassVar`. Patch by Gregory Beauregard.

0 commit comments

Comments
 (0)