Skip to content

Commit 46102f0

Browse files
authored
Annotated: backport bpo-46491 (#1049)
1 parent 95cf94b commit 46102f0

File tree

3 files changed

+34
-4
lines changed

3 files changed

+34
-4
lines changed

CHANGELOG

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Release 4.x.x
22

3+
- `Annotated` can now wrap `ClassVar` and `Final`. Backport from
4+
bpo-46491. Patch by Gregory Beauregard (@GBeauregard).
35
- Add missed `Required` and `NotRequired` to `__all__`. Patch by
46
Yuri Karabas (@uriyyo).
57
- The `@final` decorator now sets the `__final__` attribute on the

src/test_typing_extensions.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1799,6 +1799,24 @@ class C:
17991799
A.x = 5
18001800
self.assertEqual(C.x, 5)
18011801

1802+
@skipIf(sys.version_info[:2] in ((3, 9), (3, 10)), "Waiting for bpo-46491 bugfix.")
1803+
def test_special_form_containment(self):
1804+
class C:
1805+
classvar: Annotated[ClassVar[int], "a decoration"] = 4
1806+
const: Annotated[Final[int], "Const"] = 4
1807+
1808+
if sys.version_info[:2] >= (3, 7):
1809+
self.assertEqual(get_type_hints(C, globals())["classvar"], ClassVar[int])
1810+
self.assertEqual(get_type_hints(C, globals())["const"], Final[int])
1811+
else:
1812+
self.assertEqual(
1813+
get_type_hints(C, globals())["classvar"],
1814+
Annotated[ClassVar[int], "a decoration"]
1815+
)
1816+
self.assertEqual(
1817+
get_type_hints(C, globals())["const"], Annotated[Final[int], "Const"]
1818+
)
1819+
18021820
def test_hash_eq(self):
18031821
self.assertEqual(len({Annotated[int, 4, 5], Annotated[int, 4, 5]}), 1)
18041822
self.assertNotEqual(Annotated[int, 4, 5], Annotated[int, 5, 4])

src/typing_extensions.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1251,8 +1251,12 @@ def __class_getitem__(cls, params):
12511251
raise TypeError("Annotated[...] should be used "
12521252
"with at least two arguments (a type and an "
12531253
"annotation).")
1254-
msg = "Annotated[t, ...]: t must be a type."
1255-
origin = typing._type_check(params[0], msg)
1254+
allowed_special_forms = (ClassVar, Final)
1255+
if get_origin(params[0]) in allowed_special_forms:
1256+
origin = params[0]
1257+
else:
1258+
msg = "Annotated[t, ...]: t must be a type."
1259+
origin = typing._type_check(params[0], msg)
12561260
metadata = tuple(params[1:])
12571261
return _AnnotatedAlias(origin, metadata)
12581262

@@ -1377,8 +1381,14 @@ def __getitem__(self, params):
13771381
"with at least two arguments (a type and an "
13781382
"annotation).")
13791383
else:
1380-
msg = "Annotated[t, ...]: t must be a type."
1381-
tp = typing._type_check(params[0], msg)
1384+
if (
1385+
isinstance(params[0], typing._TypingBase) and
1386+
type(params[0]).__name__ == "_ClassVar"
1387+
):
1388+
tp = params[0]
1389+
else:
1390+
msg = "Annotated[t, ...]: t must be a type."
1391+
tp = typing._type_check(params[0], msg)
13821392
metadata = tuple(params[1:])
13831393
return self.__class__(
13841394
self.__name__,

0 commit comments

Comments
 (0)