From a1cbace91b624681dd7bb8eb82ba187bda55d785 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Fri, 1 Sep 2023 21:57:25 +0100 Subject: [PATCH] gh-105509: Simplify implementation of `typing.Annotated` (#105510) --- Lib/test/test_typing.py | 3 +- Lib/typing.py | 38 +++++++------------ ...-06-08-15-56-45.gh-issue-105509.YIG57j.rst | 3 ++ 3 files changed, 17 insertions(+), 27 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-06-08-15-56-45.gh-issue-105509.YIG57j.rst diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 6447ed6078e..69f5ff913c5 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -8189,8 +8189,7 @@ class AnnotatedTests(BaseTestCase): def test_new(self): with self.assertRaisesRegex( - TypeError, - 'Type Annotated cannot be instantiated', + TypeError, 'Cannot instantiate typing.Annotated', ): Annotated() diff --git a/Lib/typing.py b/Lib/typing.py index 387b4c5ad52..8655b756a9f 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -2001,7 +2001,8 @@ def __mro_entries__(self, bases): return (self.__origin__,) -class Annotated: +@_SpecialForm +def Annotated(self, params): """Add context-specific metadata to a type. Example: Annotated[int, runtime_check.Unsigned] indicates to the @@ -2048,30 +2049,17 @@ class Annotated: where T1, T2 etc. are TypeVars, which would be invalid, because only one type should be passed to Annotated. """ - - __slots__ = () - - def __new__(cls, *args, **kwargs): - raise TypeError("Type Annotated cannot be instantiated.") - - @_tp_cache - def __class_getitem__(cls, params): - if not isinstance(params, tuple) or len(params) < 2: - raise TypeError("Annotated[...] should be used " - "with at least two arguments (a type and an " - "annotation).") - if _is_unpacked_typevartuple(params[0]): - raise TypeError("Annotated[...] should not be used with an " - "unpacked TypeVarTuple") - msg = "Annotated[t, ...]: t must be a type." - origin = _type_check(params[0], msg, allow_special_forms=True) - metadata = tuple(params[1:]) - return _AnnotatedAlias(origin, metadata) - - def __init_subclass__(cls, *args, **kwargs): - raise TypeError( - "Cannot subclass {}.Annotated".format(cls.__module__) - ) + if not isinstance(params, tuple) or len(params) < 2: + raise TypeError("Annotated[...] should be used " + "with at least two arguments (a type and an " + "annotation).") + if _is_unpacked_typevartuple(params[0]): + raise TypeError("Annotated[...] should not be used with an " + "unpacked TypeVarTuple") + msg = "Annotated[t, ...]: t must be a type." + origin = _type_check(params[0], msg, allow_special_forms=True) + metadata = tuple(params[1:]) + return _AnnotatedAlias(origin, metadata) def runtime_checkable(cls): diff --git a/Misc/NEWS.d/next/Library/2023-06-08-15-56-45.gh-issue-105509.YIG57j.rst b/Misc/NEWS.d/next/Library/2023-06-08-15-56-45.gh-issue-105509.YIG57j.rst new file mode 100644 index 00000000000..26d8a66c2c4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-06-08-15-56-45.gh-issue-105509.YIG57j.rst @@ -0,0 +1,3 @@ +:data:`typing.Annotated` is now implemented as an instance of +``typing._SpecialForm`` rather than a class. This should have no user-facing +impact for users of the :mod:`typing` module public API.