diff --git a/typing_extensions/src_py3/test_typing_extensions.py b/typing_extensions/src_py3/test_typing_extensions.py index e209a3f06..57d77e65d 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 @@ def test_total(self): 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_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_repr(self): - self.assertEqual( - repr(Annotated[int, 4, 5]), - "typing_extensions.Annotated[int, 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_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_instantiate(self): + class C: + classvar = 4 + + def __init__(self, x): + self.x = x + + def __eq__(self, other): + if not isinstance(other, C): + return NotImplemented + return other.x == self.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 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_flatten(self): - A = Annotated[Annotated[int, 4], 5] - self.assertEqual(A, Annotated[int, 4, 5]) - self.assertEqual(A.__metadata__, (4, 5)) - self.assertEqual(A.__origin__, int) + 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_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_instantiate(self): - class C: - classvar = 4 + def test_cannot_subclass(self): + with self.assertRaisesRegex(TypeError, "Cannot subclass .*Annotated"): + class C(Annotated): + pass - def __init__(self, x): - self.x = x + def test_cannot_check_instance(self): + with self.assertRaises(TypeError): + isinstance(5, Annotated[int, "positive"]) - def __eq__(self, other): - if not isinstance(other, C): - return NotImplemented - return other.x == self.x + def test_cannot_check_subclass(self): + with self.assertRaises(TypeError): + issubclass(int, Annotated[int, "positive"]) - 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) - 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}) + @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]] + for t in samples: + x = Annotated[t, "a"] - def test_cannot_subclass(self): - with self.assertRaises(TypeError): - class C(Annotated): - pass + 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) - def test_pickle(self): - samples = [typing.Any, typing.Union[int, str], - typing.Optional[str], Tuple[int, ...], - typing.Callable[[str], bytes]] + global _Annotated_test_G - for t in samples: - x = Annotated[t, "a"] + class _Annotated_test_G(Generic[T]): + x = 1 - 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) + G = Annotated[_Annotated_test_G[int], "A decoration"] + G.foo = 42 + G.bar = 'abc' - global _Annotated_test_G + 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) - class _Annotated_test_G(Generic[T]): - x = 1 + def test_subst(self): + dec = "a decoration" + dec2 = "another decoration" - G = Annotated[_Annotated_test_G[int], "A decoration"] - G.foo = 42 - G.bar = 'abc' + S = Annotated[T, dec2] + self.assertEqual(S[int], Annotated[int, dec2]) - 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) + self.assertEqual(S[Annotated[int, dec]], Annotated[int, dec, dec2]) + L = Annotated[List[T], dec] - def test_subst(self): - dec = "a decoration" + self.assertEqual(L[int], Annotated[List[int], dec]) + with self.assertRaises(TypeError): + L[int, int] - S = Annotated[T, dec] - self.assertEqual(S[int], Annotated[int, dec]) + self.assertEqual(S[L[int]], Annotated[List[int], dec, dec2]) - L = Annotated[List[T], dec] - 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] - D = Annotated[Dict[KT, VT], dec] - self.assertEqual(D[str, int], Annotated[Dict[str, int], dec]) - with self.assertRaises(TypeError): - D[int] + I = Annotated[int, dec] + with self.assertRaises(TypeError): + I[None] - I = Annotated[int, dec] - with self.assertRaises(TypeError): - I[None] + 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]]) + + +@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 + ) - 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 - ) - - def test_get_type_hints_refs(self): - - Const = Annotated[T, "Const"] - - class MySet(Generic[T]): - - def __ior__(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(), 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]} - ) + def test_get_type_hints_refs(self): + + Const = Annotated[T, "Const"] + + class MySet(Generic[T]): + + def __ior__(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(), 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]} + ) @@ -1665,6 +1734,10 @@ def test_typing_extensions_includes_standard(self): 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 @@ def test_typing_extensions_includes_standard(self): 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 81f60ac96..dc5d2db9c 100644 --- a/typing_extensions/src_py3/typing_extensions.py +++ b/typing_extensions/src_py3/typing_extensions.py @@ -24,6 +24,11 @@ from typing import _type_vars, _next_in_mro, _type_check 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: @@ -135,19 +140,22 @@ def _check_methods_in_mro(C, *methods): '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 @@ class Point2D(TypedDict): """ -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 @@ class _Annotated(typing._GenericAlias, _root=True): 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 __init__(self, origin, metadata): 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 __reduce__(self): ) 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 __eq__(self, other): def __hash__(self): return hash((self.__origin__, self.__metadata__)) - class Annotated: """Add context specific metadata to a type. @@ -1680,11 +1687,11 @@ class Annotated: - 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 @@ class Annotated: 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 @@ def __class_getitem__(cls, params): 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 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False): 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()] + """