diff options
author | Gregory Beauregard <greg@greg.red> | 2022-01-25 14:55:16 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-01-25 14:55:16 -0800 |
commit | 3b53f0167e3d7d22e3199cca85da5ef5c6c6f4cd (patch) | |
tree | 8e53c917cb6d94a7df590501edff7d7c216ef582 | |
parent | 523cf0233edc7a29502fbd30dc6bf641a7408e16 (diff) | |
download | typing-3b53f0167e3d7d22e3199cca85da5ef5c6c6f4cd.tar.gz |
Annotated: backport bpo-46491 (#1049)
-rw-r--r-- | typing_extensions/CHANGELOG | 2 | ||||
-rw-r--r-- | typing_extensions/src/test_typing_extensions.py | 18 | ||||
-rw-r--r-- | typing_extensions/src/typing_extensions.py | 18 |
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__, |