aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Beauregard <greg@greg.red>2022-01-25 14:55:16 -0800
committerGitHub <noreply@github.com>2022-01-25 14:55:16 -0800
commit3b53f0167e3d7d22e3199cca85da5ef5c6c6f4cd (patch)
tree8e53c917cb6d94a7df590501edff7d7c216ef582
parent523cf0233edc7a29502fbd30dc6bf641a7408e16 (diff)
downloadtyping-3b53f0167e3d7d22e3199cca85da5ef5c6c6f4cd.tar.gz
Annotated: backport bpo-46491 (#1049)
-rw-r--r--typing_extensions/CHANGELOG2
-rw-r--r--typing_extensions/src/test_typing_extensions.py18
-rw-r--r--typing_extensions/src/typing_extensions.py18
3 files changed, 34 insertions, 4 deletions
diff --git a/typing_extensions/CHANGELOG b/typing_extensions/CHANGELOG
index e8bb7bf..026ba59 100644
--- a/typing_extensions/CHANGELOG
+++ b/typing_extensions/CHANGELOG
@@ -1,5 +1,7 @@
# Release 4.x.x
+- `Annotated` can now wrap `ClassVar` and `Final`. Backport from
+ bpo-46491. Patch by Gregory Beauregard (@GBeauregard).
- Add missed `Required` and `NotRequired` to `__all__`. Patch by
Yuri Karabas (@uriyyo).
- The `@final` decorator now sets the `__final__` attribute on the
diff --git a/typing_extensions/src/test_typing_extensions.py b/typing_extensions/src/test_typing_extensions.py
index ab6a84d..f92bb13 100644
--- a/typing_extensions/src/test_typing_extensions.py
+++ b/typing_extensions/src/test_typing_extensions.py
@@ -1799,6 +1799,24 @@ class AnnotatedTests(BaseTestCase):
A.x = 5
self.assertEqual(C.x, 5)
+ @skipIf(sys.version_info[:2] in ((3, 9), (3, 10)), "Waiting for bpo-46491 bugfix.")
+ def test_special_form_containment(self):
+ class C:
+ classvar: Annotated[ClassVar[int], "a decoration"] = 4
+ const: Annotated[Final[int], "Const"] = 4
+
+ if sys.version_info[:2] >= (3, 7):
+ self.assertEqual(get_type_hints(C, globals())["classvar"], ClassVar[int])
+ self.assertEqual(get_type_hints(C, globals())["const"], Final[int])
+ else:
+ self.assertEqual(
+ get_type_hints(C, globals())["classvar"],
+ Annotated[ClassVar[int], "a decoration"]
+ )
+ self.assertEqual(
+ get_type_hints(C, globals())["const"], Annotated[Final[int], "Const"]
+ )
+
def test_hash_eq(self):
self.assertEqual(len({Annotated[int, 4, 5], Annotated[int, 4, 5]}), 1)
self.assertNotEqual(Annotated[int, 4, 5], Annotated[int, 5, 4])
diff --git a/typing_extensions/src/typing_extensions.py b/typing_extensions/src/typing_extensions.py
index 2e0b3d7..dab53d4 100644
--- a/typing_extensions/src/typing_extensions.py
+++ b/typing_extensions/src/typing_extensions.py
@@ -1251,8 +1251,12 @@ elif PEP_560:
raise TypeError("Annotated[...] should be used "
"with at least two arguments (a type and an "
"annotation).")
- msg = "Annotated[t, ...]: t must be a type."
- origin = typing._type_check(params[0], msg)
+ allowed_special_forms = (ClassVar, Final)
+ if get_origin(params[0]) in allowed_special_forms:
+ origin = params[0]
+ else:
+ msg = "Annotated[t, ...]: t must be a type."
+ origin = typing._type_check(params[0], msg)
metadata = tuple(params[1:])
return _AnnotatedAlias(origin, metadata)
@@ -1377,8 +1381,14 @@ else:
"with at least two arguments (a type and an "
"annotation).")
else:
- msg = "Annotated[t, ...]: t must be a type."
- tp = typing._type_check(params[0], msg)
+ if (
+ isinstance(params[0], typing._TypingBase) and
+ type(params[0]).__name__ == "_ClassVar"
+ ):
+ tp = params[0]
+ else:
+ msg = "Annotated[t, ...]: t must be a type."
+ tp = typing._type_check(params[0], msg)
metadata = tuple(params[1:])
return self.__class__(
self.__name__,