Pep 593 py3 (#639)
This covers python 3.53 and above. I added a fair amount of tests.
diff --git a/typing_extensions/src_py3/test_typing_extensions.py b/typing_extensions/src_py3/test_typing_extensions.py
index e209a3f..57d77e6 100644
--- a/typing_extensions/src_py3/test_typing_extensions.py
+++ b/typing_extensions/src_py3/test_typing_extensions.py
@@ -11,13 +11,21 @@
from typing import T, KT, VT # Not in __all__.
from typing import Tuple, List, Dict
from typing import Generic
-from typing import get_type_hints
from typing import no_type_check
from typing_extensions import NoReturn, ClassVar, Final, IntVar, Literal, Type, NewType, TypedDict
try:
from typing_extensions import Protocol, runtime
except ImportError:
pass
+try:
+ from typing_extensions import Annotated
+except ImportError:
+ pass
+try:
+ from typing_extensions import get_type_hints
+except ImportError:
+ from typing import get_type_hints
+
import typing
import typing_extensions
import collections.abc as collections_abc
@@ -65,9 +73,6 @@
# Protocols are hard to backport to the original version of typing 3.5.0
HAVE_PROTOCOLS = sys.version_info[:3] != (3, 5, 0)
-# Not backported to older versions yet
-HAVE_ANNOTATED = PEP_560
-
class BaseTestCase(TestCase):
def assertIsSubclass(self, cls, class_or_tuple, msg=None):
if not issubclass(cls, class_or_tuple):
@@ -1474,179 +1479,243 @@
self.assertEqual(Options.__total__, False)
-if HAVE_ANNOTATED:
- from typing_extensions import Annotated, get_type_hints
+@skipUnless(TYPING_3_5_3, "Python >= 3.5.3 required")
+class AnnotatedTests(BaseTestCase):
- class AnnotatedTests(BaseTestCase):
+ def test_repr(self):
+ self.assertEqual(
+ repr(Annotated[int, 4, 5]),
+ "typing_extensions.Annotated[int, 4, 5]"
+ )
+ self.assertEqual(
+ repr(Annotated[List[int], 4, 5]),
+ "typing_extensions.Annotated[typing.List[int], 4, 5]"
+ )
- def test_repr(self):
- self.assertEqual(
- repr(Annotated[int, 4, 5]),
- "typing_extensions.Annotated[int, 4, 5]"
- )
-
- def test_flatten(self):
- A = Annotated[Annotated[int, 4], 5]
- self.assertEqual(A, Annotated[int, 4, 5])
- self.assertEqual(A.__metadata__, (4, 5))
+ def test_flatten(self):
+ A = Annotated[Annotated[int, 4], 5]
+ self.assertEqual(A, Annotated[int, 4, 5])
+ self.assertEqual(A.__metadata__, (4, 5))
+ if PEP_560:
self.assertEqual(A.__origin__, int)
- 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])
- self.assertNotEqual(Annotated[int, 4, 5], Annotated[str, 4, 5])
- self.assertNotEqual(Annotated[int, 4], Annotated[int, 4, 4])
- self.assertEqual(
- {Annotated[int, 4, 5], Annotated[int, 4, 5], Annotated[T, 4, 5]},
- {Annotated[int, 4, 5], Annotated[T, 4, 5]}
- )
+ def test_specialize(self):
+ L = Annotated[List[T], "my decoration"]
+ LI = Annotated[List[int], "my decoration"]
+ self.assertEqual(L[int], Annotated[List[int], "my decoration"])
+ self.assertEqual(L[int].__metadata__, ("my decoration",))
+ if PEP_560:
+ self.assertEqual(L[int].__origin__, List[int])
+ with self.assertRaises(TypeError):
+ LI[int]
+ with self.assertRaises(TypeError):
+ L[int, float]
- def test_instantiate(self):
- class C:
- classvar = 4
+ 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])
+ self.assertNotEqual(Annotated[int, 4, 5], Annotated[str, 4, 5])
+ self.assertNotEqual(Annotated[int, 4], Annotated[int, 4, 4])
+ self.assertEqual(
+ {Annotated[int, 4, 5], Annotated[int, 4, 5], Annotated[T, 4, 5]},
+ {Annotated[int, 4, 5], Annotated[T, 4, 5]}
+ )
- def __init__(self, x):
- self.x = x
+ def test_instantiate(self):
+ class C:
+ classvar = 4
- def __eq__(self, other):
- if not isinstance(other, C):
- return NotImplemented
- return other.x == self.x
+ def __init__(self, x):
+ self.x = x
- A = Annotated[C, "a decoration"]
- a = A(5)
- c = C(5)
- self.assertEqual(a, c)
- self.assertEqual(a.x, c.x)
- self.assertEqual(A.classvar, C.classvar)
+ def __eq__(self, other):
+ if not isinstance(other, C):
+ return NotImplemented
+ return other.x == self.x
- MyCount = Annotated[typing_extensions.Counter[T], "my decoration"]
- self.assertEqual(MyCount([4, 4, 5]), {4: 2, 5: 1})
- self.assertEqual(MyCount[int]([4, 4, 5]), {4: 2, 5: 1})
+ A = Annotated[C, "a decoration"]
+ a = A(5)
+ c = C(5)
+ self.assertEqual(a, c)
+ self.assertEqual(a.x, c.x)
+ self.assertEqual(a.classvar, c.classvar)
+
+ def test_instantiate_generic(self):
+ MyCount = Annotated[typing_extensions.Counter[T], "my decoration"]
+ self.assertEqual(MyCount([4, 4, 5]), {4: 2, 5: 1})
+ self.assertEqual(MyCount[int]([4, 4, 5]), {4: 2, 5: 1})
+
+ def test_cannot_instantiate_forward(self):
+ A = Annotated["int", (5, 6)]
+ with self.assertRaises(TypeError):
+ A(5)
+
+ def test_cannot_instantiate_type_var(self):
+ A = Annotated[T, (5, 6)]
+ with self.assertRaises(TypeError):
+ A(5)
+
+ def test_cannot_getattr_typevar(self):
+ with self.assertRaises(AttributeError):
+ Annotated[T, (5, 7)].x
+
+ def test_attr_passthrough(self):
+ class C:
+ classvar = 4
+
+ A = Annotated[C, "a decoration"]
+ self.assertEqual(A.classvar, 4)
+ A.x = 5
+ self.assertEqual(C.x, 5)
+
+ 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])
+ self.assertNotEqual(Annotated[int, 4, 5], Annotated[str, 4, 5])
+ self.assertNotEqual(Annotated[int, 4], Annotated[int, 4, 4])
+ self.assertEqual(
+ {Annotated[int, 4, 5], Annotated[int, 4, 5], Annotated[T, 4, 5]},
+ {Annotated[int, 4, 5], Annotated[T, 4, 5]}
+ )
+
+ def test_cannot_subclass(self):
+ with self.assertRaisesRegex(TypeError, "Cannot subclass .*Annotated"):
+ class C(Annotated):
+ pass
+
+ def test_cannot_check_instance(self):
+ with self.assertRaises(TypeError):
+ isinstance(5, Annotated[int, "positive"])
+
+ def test_cannot_check_subclass(self):
+ with self.assertRaises(TypeError):
+ issubclass(int, Annotated[int, "positive"])
- def test_cannot_subclass(self):
- with self.assertRaises(TypeError):
- class C(Annotated):
- pass
+ @skipUnless(PEP_560, "pickle support was added with PEP 560")
+ def test_pickle(self):
+ samples = [typing.Any, typing.Union[int, str],
+ typing.Optional[str], Tuple[int, ...],
+ typing.Callable[[str], bytes]]
- def test_pickle(self):
- samples = [typing.Any, typing.Union[int, str],
- typing.Optional[str], Tuple[int, ...],
- typing.Callable[[str], bytes]]
+ for t in samples:
+ x = Annotated[t, "a"]
- for t in samples:
- x = Annotated[t, "a"]
+ for prot in range(pickle.HIGHEST_PROTOCOL + 1):
+ with self.subTest(protocol=prot, type=t):
+ pickled = pickle.dumps(x, prot)
+ restored = pickle.loads(pickled)
+ self.assertEqual(x, restored)
- for prot in range(pickle.HIGHEST_PROTOCOL + 1):
- with self.subTest(protocol=prot, type=t):
- pickled = pickle.dumps(x, prot)
- restored = pickle.loads(pickled)
- self.assertEqual(x, restored)
+ global _Annotated_test_G
- global _Annotated_test_G
+ class _Annotated_test_G(Generic[T]):
+ x = 1
- class _Annotated_test_G(Generic[T]):
- x = 1
+ G = Annotated[_Annotated_test_G[int], "A decoration"]
+ G.foo = 42
+ G.bar = 'abc'
- G = Annotated[_Annotated_test_G[int], "A decoration"]
- G.foo = 42
- G.bar = 'abc'
+ for proto in range(pickle.HIGHEST_PROTOCOL + 1):
+ z = pickle.dumps(G, proto)
+ x = pickle.loads(z)
+ self.assertEqual(x.foo, 42)
+ self.assertEqual(x.bar, 'abc')
+ self.assertEqual(x.x, 1)
- for proto in range(pickle.HIGHEST_PROTOCOL + 1):
- z = pickle.dumps(G, proto)
- x = pickle.loads(z)
- self.assertEqual(x.foo, 42)
- self.assertEqual(x.bar, 'abc')
- self.assertEqual(x.x, 1)
+ def test_subst(self):
+ dec = "a decoration"
+ dec2 = "another decoration"
- def test_subst(self):
- dec = "a decoration"
+ S = Annotated[T, dec2]
+ self.assertEqual(S[int], Annotated[int, dec2])
- S = Annotated[T, dec]
- self.assertEqual(S[int], Annotated[int, dec])
+ self.assertEqual(S[Annotated[int, dec]], Annotated[int, dec, dec2])
+ L = Annotated[List[T], dec]
- L = Annotated[List[T], dec]
- self.assertEqual(L[int], Annotated[List[int], dec])
- with self.assertRaises(TypeError):
- L[int, int]
+ self.assertEqual(L[int], Annotated[List[int], dec])
+ with self.assertRaises(TypeError):
+ L[int, int]
- D = Annotated[Dict[KT, VT], dec]
- self.assertEqual(D[str, int], Annotated[Dict[str, int], dec])
- with self.assertRaises(TypeError):
- D[int]
+ self.assertEqual(S[L[int]], Annotated[List[int], dec, dec2])
- I = Annotated[int, dec]
- with self.assertRaises(TypeError):
- I[None]
+ D = Annotated[Dict[KT, VT], dec]
+ self.assertEqual(D[str, int], Annotated[Dict[str, int], dec])
+ with self.assertRaises(TypeError):
+ D[int]
- LI = L[int]
- with self.assertRaises(TypeError):
- LI[None]
+ I = Annotated[int, dec]
+ with self.assertRaises(TypeError):
+ I[None]
- def test_annotated_in_other_types(self):
- X = List[Annotated[T, 5]]
- self.assertEqual(X[int], List[Annotated[int, 5]])
+ LI = L[int]
+ with self.assertRaises(TypeError):
+ LI[None]
+
+ def test_annotated_in_other_types(self):
+ X = List[Annotated[T, 5]]
+ self.assertEqual(X[int], List[Annotated[int, 5]])
- class GetTypeHintsTests(BaseTestCase):
- def test_get_type_hints(self):
- def foobar(x: List['X']): ...
- X = Annotated[int, (1, 10)]
- self.assertEqual(
- get_type_hints(foobar, globals(), locals()),
- {'x': List[int]}
- )
- self.assertEqual(
- get_type_hints(foobar, globals(), locals(), include_extras=True),
- {'x': List[Annotated[int, (1, 10)]]}
- )
- BA = Tuple[Annotated[T, (1, 0)], ...]
- def barfoo(x: BA): ...
- self.assertEqual(get_type_hints(barfoo, globals(), locals())['x'], Tuple[T, ...])
- self.assertIs(
- get_type_hints(barfoo, globals(), locals(), include_extras=True)['x'],
- BA
- )
- def barfoo2(x: typing.Callable[..., Annotated[List[T], "const"]],
- y: typing.Union[int, Annotated[T, "mutable"]]): ...
- self.assertEqual(
- get_type_hints(barfoo2, globals(), locals()),
- {'x': typing.Callable[..., List[T]], 'y': typing.Union[int, T]}
- )
- BA2 = typing.Callable[..., List[T]]
- def barfoo3(x: BA2): ...
- self.assertIs(
- get_type_hints(barfoo3, globals(), locals(), include_extras=True)["x"],
- BA2
- )
+@skipUnless(PEP_560, "Python 3.7 required")
+class GetTypeHintsTests(BaseTestCase):
+ def test_get_type_hints(self):
+ def foobar(x: List['X']): ...
+ X = Annotated[int, (1, 10)]
+ self.assertEqual(
+ get_type_hints(foobar, globals(), locals()),
+ {'x': List[int]}
+ )
+ self.assertEqual(
+ get_type_hints(foobar, globals(), locals(), include_extras=True),
+ {'x': List[Annotated[int, (1, 10)]]}
+ )
+ BA = Tuple[Annotated[T, (1, 0)], ...]
+ def barfoo(x: BA): ...
+ self.assertEqual(get_type_hints(barfoo, globals(), locals())['x'], Tuple[T, ...])
+ self.assertIs(
+ get_type_hints(barfoo, globals(), locals(), include_extras=True)['x'],
+ BA
+ )
+ def barfoo2(x: typing.Callable[..., Annotated[List[T], "const"]],
+ y: typing.Union[int, Annotated[T, "mutable"]]): ...
+ self.assertEqual(
+ get_type_hints(barfoo2, globals(), locals()),
+ {'x': typing.Callable[..., List[T]], 'y': typing.Union[int, T]}
+ )
+ BA2 = typing.Callable[..., List[T]]
+ def barfoo3(x: BA2): ...
+ self.assertIs(
+ get_type_hints(barfoo3, globals(), locals(), include_extras=True)["x"],
+ BA2
+ )
- def test_get_type_hints_refs(self):
+ def test_get_type_hints_refs(self):
- Const = Annotated[T, "Const"]
+ Const = Annotated[T, "Const"]
- class MySet(Generic[T]):
+ class MySet(Generic[T]):
- def __ior__(self, other: "Const[MySet[T]]") -> "MySet[T]":
- ...
+ def __ior__(self, other: "Const[MySet[T]]") -> "MySet[T]":
+ ...
- def __iand__(self, other: Const["MySet[T]"]) -> "MySet[T]":
- ...
+ def __iand__(self, other: Const["MySet[T]"]) -> "MySet[T]":
+ ...
- self.assertEqual(
- get_type_hints(MySet.__iand__, globals(), locals()),
- {'other': MySet[T], 'return': MySet[T]}
- )
+ self.assertEqual(
+ get_type_hints(MySet.__iand__, globals(), locals()),
+ {'other': MySet[T], 'return': MySet[T]}
+ )
- self.assertEqual(
- get_type_hints(MySet.__iand__, globals(), locals(), include_extras=True),
- {'other': Const[MySet[T]], 'return': MySet[T]}
- )
+ self.assertEqual(
+ get_type_hints(MySet.__iand__, globals(), locals(), include_extras=True),
+ {'other': Const[MySet[T]], 'return': MySet[T]}
+ )
- self.assertEqual(
- get_type_hints(MySet.__ior__, globals(), locals()),
- {'other': MySet[T], 'return': MySet[T]}
- )
+ self.assertEqual(
+ get_type_hints(MySet.__ior__, globals(), locals()),
+ {'other': MySet[T], 'return': MySet[T]}
+ )
@@ -1665,6 +1734,10 @@
self.assertIn('overload', a)
self.assertIn('Text', a)
self.assertIn('TYPE_CHECKING', a)
+ if TYPING_3_5_3:
+ self.assertIn('Annotated', a)
+ if PEP_560:
+ self.assertIn('get_type_hints', a)
if ASYNCIO:
self.assertIn('Awaitable', a)
@@ -1680,9 +1753,6 @@
self.assertIn('Protocol', a)
self.assertIn('runtime', a)
- if HAVE_ANNOTATED:
- self.assertIn('Annotated', a)
- self.assertIn('get_type_hints', a)
def test_typing_extensions_defers_when_possible(self):
exclude = {'overload', 'Text', 'TYPE_CHECKING', 'Final', 'get_type_hints'}
diff --git a/typing_extensions/src_py3/typing_extensions.py b/typing_extensions/src_py3/typing_extensions.py
index 81f60ac..dc5d2db 100644
--- a/typing_extensions/src_py3/typing_extensions.py
+++ b/typing_extensions/src_py3/typing_extensions.py
@@ -25,6 +25,11 @@
except ImportError:
OLD_GENERICS = True
try:
+ from typing import _subs_tree
+ SUBS_TREE = True
+except ImportError:
+ SUBS_TREE = False
+try:
from typing import _tp_cache
except ImportError:
_tp_cache = lambda x: x
@@ -135,19 +140,22 @@
'TYPE_CHECKING',
]
+# Annotated relies on substitution trees of pep 560. It will not work for
+# versions of typing older than 3.5.3
+HAVE_ANNOTATED = PEP_560 or SUBS_TREE
+
+if PEP_560:
+ __all__.append("get_type_hints")
+
+if HAVE_ANNOTATED:
+ __all__.append("Annotated")
+
# Protocols are hard to backport to the original version of typing 3.5.0
HAVE_PROTOCOLS = sys.version_info[:3] != (3, 5, 0)
if HAVE_PROTOCOLS:
__all__.extend(['Protocol', 'runtime'])
-# Annotations were implemented under tight time constraints; this keeps the
-# implementation simple for now
-HAVE_ANNOTATED = PEP_560
-
-if HAVE_ANNOTATED:
- __all__.extend(['Annotated', 'get_type_hints'])
-
# TODO
if hasattr(typing, 'NoReturn'):
@@ -1611,8 +1619,8 @@
"""
-if HAVE_ANNOTATED:
- class _Annotated(typing._GenericAlias, _root=True):
+if PEP_560:
+ class _AnnotatedAlias(typing._GenericAlias, _root=True):
"""Runtime representation of an annotated type.
At its core 'Annotated[t, dec1, dec2, ...]' is an alias for the type 't'
@@ -1621,7 +1629,7 @@
it to types is also the same.
"""
def __init__(self, origin, metadata):
- if isinstance(origin, _Annotated):
+ if isinstance(origin, _AnnotatedAlias):
metadata = origin.__metadata__ + metadata
origin = origin.__origin__
super().__init__(origin, origin)
@@ -1630,7 +1638,7 @@
def copy_with(self, params):
assert len(params) == 1
new_type = params[0]
- return _Annotated(new_type, self.__metadata__)
+ return _AnnotatedAlias(new_type, self.__metadata__)
def __repr__(self):
return "typing_extensions.Annotated[{}, {}]".format(
@@ -1644,7 +1652,7 @@
)
def __eq__(self, other):
- if not isinstance(other, _Annotated):
+ if not isinstance(other, _AnnotatedAlias):
return NotImplemented
if self.__origin__ != other.__origin__:
return False
@@ -1653,7 +1661,6 @@
def __hash__(self):
return hash((self.__origin__, self.__metadata__))
-
class Annotated:
"""Add context specific metadata to a type.
@@ -1680,11 +1687,11 @@
- Annotated can be used as a generic type alias::
- Optimized = Annotated[T, runtime.Optimize]
- Optimized[int] == Annotated[int, runtime.Optimize]
+ Optimized = Annotated[T, runtime.Optimize()]
+ Optimized[int] == Annotated[int, runtime.Optimize()]
- OptimizedList = Annotated[List[T], runtime.Optimize]
- OptimizedList[int] == Annotated[List[int], runtime.Optimize]
+ OptimizedList = Annotated[List[T], runtime.Optimize()]
+ OptimizedList[int] == Annotated[List[int], runtime.Optimize()]
"""
__slots__ = ()
@@ -1692,7 +1699,7 @@
def __new__(cls, *args, **kwargs):
raise TypeError("Type Annotated cannot be instantiated.")
- @typing._tp_cache
+ @_tp_cache
def __class_getitem__(cls, params):
if not isinstance(params, tuple) or len(params) < 2:
raise TypeError("Annotated[...] should be used "
@@ -1701,15 +1708,17 @@
msg = "Annotated[t, ...]: t must be a type."
origin = typing._type_check(params[0], msg)
metadata = tuple(params[1:])
- return _Annotated(origin, metadata)
+ return _AnnotatedAlias(origin, metadata)
def __init_subclass__(cls, *args, **kwargs):
- raise TypeError("Cannot inherit from Annotated")
+ raise TypeError(
+ "Cannot subclass {}.Annotated".format(cls.__module__)
+ )
def _strip_annotations(t):
"""Strips the annotations from a given type.
"""
- if isinstance(t, _Annotated):
+ if isinstance(t, _AnnotatedAlias):
return _strip_annotations(t.__origin__)
if isinstance(t, typing._GenericAlias):
stripped_args = tuple(_strip_annotations(a) for a in t.__args__)
@@ -1755,3 +1764,148 @@
if include_extras:
return hint
return {k: _strip_annotations(t) for k, t in hint.items()}
+
+elif HAVE_ANNOTATED:
+
+ # Prior to Python 3.7 types did not have `copy_with`. A lot of the equality
+ # checks, argument expansion etc. are done on the _subs_tre. As a result we
+ # can't provide a get_type_hints function that strips out annotations.
+
+ class AnnotatedMeta(typing.GenericMeta):
+ """Metaclass for Annotated"""
+
+ def __new__(cls, name, bases, namespace, **kwargs):
+ if any(b is not object for b in bases):
+ raise TypeError("Cannot subclass " + str(Annotated))
+ return super().__new__(cls, name, bases, namespace, **kwargs)
+
+ @property
+ def __metadata__(self):
+ return self._subs_tree()[2]
+
+ def _tree_repr(self, tree):
+ assert len(tree) == 3
+ # First argument is this class, second is __origin__, 3rd is __metadata__
+ tp_tree = tree[1]
+ if not isinstance(tp_tree, tuple):
+ tp_repr = typing._type_repr(tp_tree)
+ else:
+ tp_repr = tp_tree[0]._tree_repr(tp_tree)
+ metadata_reprs = ", ".join(repr(arg) for arg in tree[2])
+ return repr(tree[0]) + '[%s, %s]' % (tp_repr, metadata_reprs)
+
+ def _subs_tree(self, tvars=None, args=None):
+ if self is Annotated:
+ return Annotated
+ res = super()._subs_tree(tvars=tvars, args=args)
+ # Flatten nested Annotated
+ if isinstance(res[1], tuple) and res[1][0] is Annotated:
+ sub_tp = res[1][1]
+ sub_annot = res[1][2]
+ return (Annotated, sub_tp, sub_annot + res[2])
+ return res
+
+ def _get_cons(self):
+ """Return the class used to create instance of this type."""
+ if self.__origin__ is None:
+ raise TypeError("Cannot get the underlying type of a "
+ "non-specialized Annotated type.")
+ tree = self._subs_tree()
+ while isinstance(tree, tuple) and tree[0] is Annotated:
+ tree = tree[1]
+ if isinstance(tree, tuple):
+ return tree[0]
+ else:
+ return tree
+
+ @_tp_cache
+ def __getitem__(self, params):
+ if not isinstance(params, tuple):
+ params = (params,)
+ if self.__origin__ is not None: # specializing an instantiated type
+ return super().__getitem__(params)
+ elif not isinstance(params, tuple) or len(params) < 2:
+ raise TypeError("Annotated[...] should be instantiated "
+ "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)
+ metadata = tuple(params[1:])
+ return self.__class__(
+ self.__name__,
+ self.__bases__,
+ _no_slots_copy(self.__dict__),
+ tvars=_type_vars((tp,)),
+ # Metadata is a tuple so it won't be touched by _replace_args et al.
+ args=(tp, metadata),
+ origin=self,
+ )
+
+ def __call__(self, *args, **kwargs):
+ cons = self._get_cons()
+ result = cons(*args, **kwargs)
+ try:
+ result.__orig_class__ = self
+ except AttributeError:
+ pass
+ return result
+
+ def __getattr__(self, attr):
+ # For simplicity we just don't relay all dunder names
+ if self.__origin__ is not None and not (
+ attr.startswith('__') and attr.endswith('__')
+ ):
+ return getattr(self._get_cons(), attr)
+ raise AttributeError(attr)
+
+ def __setattr__(self, attr, value):
+ if (
+ attr.startswith('__') and attr.endswith('__')
+ or attr.startswith('_abc_')
+ ):
+ super().__setattr__(attr, value)
+ elif self.__origin__ is None:
+ raise AttributeError(attr)
+ else:
+ setattr(self._get_cons(), attr, value)
+
+ # We overload this because the super() error message is confusing:
+ # Parametrized generics cannot be used ...
+ def __instancecheck__(self, obj):
+ raise TypeError("Annotated cannot be used with isinstance().")
+
+ def __subclasscheck__(self, cls):
+ raise TypeError("Annotated cannot be used with issubclass().")
+
+ class Annotated(metaclass=AnnotatedMeta):
+ """Add context specific metadata to a type.
+
+ Example: Annotated[int, runtime_check.Unsigned] indicates to the
+ hypothetical runtime_check module that this type is an unsigned int.
+ Every other consumer of this type can ignore this metadata and treat
+ this type as int.
+
+ The first argument to Annotated must be a valid type, the remaining
+ arguments are kept as a tuple in the __metadata__ field.
+
+ Details:
+
+ - It's an error to call `Annotated` with less than two arguments.
+ - Nested Annotated are flattened::
+
+ Annotated[Annotated[int, Ann1, Ann2], Ann3] == Annotated[int, Ann1, Ann2, Ann3]
+
+ - Instantiating an annotated type is equivalent to instantiating the
+ underlying type::
+
+ Annotated[C, Ann1](5) == C(5)
+
+ - Annotated can be used as a generic type alias::
+
+ Optimized = Annotated[T, runtime.Optimize()]
+ Optimized[int] == Annotated[int, runtime.Optimize()]
+
+ OptimizedList = Annotated[List[T], runtime.Optimize()]
+ OptimizedList[int] == Annotated[List[int], runtime.Optimize()]
+ """