From 4cefe74aef34a86a9eb60cea9d7a775fc288004c Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 27 Sep 2016 15:20:12 -0700 Subject: [PATCH] Update typing.py and test_typing.py from upstream (https://github.com/python/typing) --- Lib/test/test_typing.py | 337 +++++++------------ Lib/typing.py | 703 +++++++++++++++++++--------------------- 2 files changed, 447 insertions(+), 593 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 3b99060fac8..6543005f095 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -9,7 +9,7 @@ from typing import Any from typing import TypeVar, AnyStr from typing import T, KT, VT # Not in __all__. from typing import Union, Optional -from typing import Tuple, List +from typing import Tuple, List, MutableMapping from typing import Callable from typing import Generic, ClassVar from typing import cast @@ -21,6 +21,10 @@ from typing import NamedTuple from typing import IO, TextIO, BinaryIO from typing import Pattern, Match import typing +try: + import collections.abc as collections_abc +except ImportError: + import collections as collections_abc # Fallback for PY3.2. class BaseTestCase(TestCase): @@ -62,18 +66,11 @@ class AnyTests(BaseTestCase): with self.assertRaises(TypeError): isinstance(42, Any) - def test_any_subclass(self): - self.assertTrue(issubclass(Employee, Any)) - self.assertTrue(issubclass(int, Any)) - self.assertTrue(issubclass(type(None), Any)) - self.assertTrue(issubclass(object, Any)) - - def test_others_any(self): - self.assertFalse(issubclass(Any, Employee)) - self.assertFalse(issubclass(Any, int)) - self.assertFalse(issubclass(Any, type(None))) - # However, Any is a subclass of object (this can't be helped). - self.assertTrue(issubclass(Any, object)) + def test_any_subclass_type_error(self): + with self.assertRaises(TypeError): + issubclass(Employee, Any) + with self.assertRaises(TypeError): + issubclass(Any, Employee) def test_repr(self): self.assertEqual(repr(Any), 'typing.Any') @@ -88,32 +85,21 @@ class AnyTests(BaseTestCase): with self.assertRaises(TypeError): class A(Any): pass + with self.assertRaises(TypeError): + class A(type(Any)): + pass def test_cannot_instantiate(self): with self.assertRaises(TypeError): Any() + with self.assertRaises(TypeError): + type(Any)() def test_cannot_subscript(self): with self.assertRaises(TypeError): Any[int] - def test_any_is_subclass(self): - # Any should be considered a subclass of everything. - self.assertIsSubclass(Any, Any) - self.assertIsSubclass(Any, typing.List) - self.assertIsSubclass(Any, typing.List[int]) - self.assertIsSubclass(Any, typing.List[T]) - self.assertIsSubclass(Any, typing.Mapping) - self.assertIsSubclass(Any, typing.Mapping[str, int]) - self.assertIsSubclass(Any, typing.Mapping[KT, VT]) - self.assertIsSubclass(Any, Generic) - self.assertIsSubclass(Any, Generic[T]) - self.assertIsSubclass(Any, Generic[KT, VT]) - self.assertIsSubclass(Any, AnyStr) - self.assertIsSubclass(Any, Union) - self.assertIsSubclass(Any, Union[int, str]) - self.assertIsSubclass(Any, typing.Match) - self.assertIsSubclass(Any, typing.Match[str]) + def test_any_works_with_alias(self): # These expressions must simply not fail. typing.Match[Any] typing.Pattern[Any] @@ -124,13 +110,8 @@ class TypeVarTests(BaseTestCase): def test_basic_plain(self): T = TypeVar('T') - # Every class is a subclass of T. - self.assertIsSubclass(int, T) - self.assertIsSubclass(str, T) # T equals itself. self.assertEqual(T, T) - # T is a subclass of itself. - self.assertIsSubclass(T, T) # T is an instance of TypeVar self.assertIsInstance(T, TypeVar) @@ -139,16 +120,12 @@ class TypeVarTests(BaseTestCase): with self.assertRaises(TypeError): isinstance(42, T) - def test_basic_constrained(self): - A = TypeVar('A', str, bytes) - # Only str and bytes are subclasses of A. - self.assertIsSubclass(str, A) - self.assertIsSubclass(bytes, A) - self.assertNotIsSubclass(int, A) - # A equals itself. - self.assertEqual(A, A) - # A is a subclass of itself. - self.assertIsSubclass(A, A) + def test_typevar_subclass_type_error(self): + T = TypeVar('T') + with self.assertRaises(TypeError): + issubclass(int, T) + with self.assertRaises(TypeError): + issubclass(T, int) def test_constrained_error(self): with self.assertRaises(TypeError): @@ -185,19 +162,6 @@ class TypeVarTests(BaseTestCase): self.assertNotEqual(TypeVar('T'), TypeVar('T')) self.assertNotEqual(TypeVar('T', int, str), TypeVar('T', int, str)) - def test_subclass_as_unions(self): - # None of these are true -- each type var is its own world. - self.assertFalse(issubclass(TypeVar('T', int, str), - TypeVar('T', int, str))) - self.assertFalse(issubclass(TypeVar('T', int, float), - TypeVar('T', int, float, str))) - self.assertFalse(issubclass(TypeVar('T', int, str), - TypeVar('T', str, int))) - A = TypeVar('A', int, str) - B = TypeVar('B', int, str, float) - self.assertFalse(issubclass(A, B)) - self.assertFalse(issubclass(B, A)) - def test_cannot_subclass_vars(self): with self.assertRaises(TypeError): class V(TypeVar('T')): @@ -212,12 +176,6 @@ class TypeVarTests(BaseTestCase): with self.assertRaises(TypeError): TypeVar('A')() - def test_bound(self): - X = TypeVar('X', bound=Employee) - self.assertIsSubclass(Employee, X) - self.assertIsSubclass(Manager, X) - self.assertNotIsSubclass(int, X) - def test_bound_errors(self): with self.assertRaises(TypeError): TypeVar('X', bound=42) @@ -230,8 +188,16 @@ class UnionTests(BaseTestCase): def test_basics(self): u = Union[int, float] self.assertNotEqual(u, Union) - self.assertTrue(issubclass(int, u)) - self.assertTrue(issubclass(float, u)) + + def test_subclass_error(self): + with self.assertRaises(TypeError): + issubclass(int, Union) + with self.assertRaises(TypeError): + issubclass(Union, int) + with self.assertRaises(TypeError): + issubclass(int, Union[int, str]) + with self.assertRaises(TypeError): + issubclass(Union[int, str], int) def test_union_any(self): u = Union[Any] @@ -260,18 +226,6 @@ class UnionTests(BaseTestCase): u2 = Union[float, int] self.assertEqual(u1, u2) - def test_subclass(self): - u = Union[int, Employee] - self.assertTrue(issubclass(Manager, u)) - - def test_self_subclass(self): - self.assertTrue(issubclass(Union[KT, VT], Union)) - self.assertFalse(issubclass(Union, Union[KT, VT])) - - def test_multiple_inheritance(self): - u = Union[int, Employee] - self.assertTrue(issubclass(ManagingFounder, u)) - def test_single_class_disappears(self): t = Union[Employee] self.assertIs(t, Employee) @@ -284,13 +238,6 @@ class UnionTests(BaseTestCase): u = Union[Employee, Manager] self.assertIs(u, Employee) - def test_weird_subclasses(self): - u = Union[Employee, int, float] - v = Union[int, float] - self.assertTrue(issubclass(v, u)) - w = Union[int, Manager] - self.assertTrue(issubclass(w, u)) - def test_union_union(self): u = Union[int, float] v = Union[u, Employee] @@ -307,6 +254,9 @@ class UnionTests(BaseTestCase): with self.assertRaises(TypeError): class C(Union): pass + with self.assertRaises(TypeError): + class C(type(Union)): + pass with self.assertRaises(TypeError): class C(Union[int, str]): pass @@ -314,9 +264,18 @@ class UnionTests(BaseTestCase): def test_cannot_instantiate(self): with self.assertRaises(TypeError): Union() + with self.assertRaises(TypeError): + type(Union)() u = Union[int, float] with self.assertRaises(TypeError): u() + with self.assertRaises(TypeError): + type(u)() + + def test_union_generalization(self): + self.assertFalse(Union[str, typing.Iterable[int]] == str) + self.assertFalse(Union[str, typing.Iterable[int]] == typing.Iterable[int]) + self.assertTrue(Union[str, typing.Iterable] == typing.Iterable) def test_optional(self): o = Optional[int] @@ -327,10 +286,6 @@ class UnionTests(BaseTestCase): with self.assertRaises(TypeError): Union[()] - def test_issubclass_union(self): - self.assertIsSubclass(Union[int, str], Union) - self.assertNotIsSubclass(int, Union) - def test_union_instance_type_error(self): with self.assertRaises(TypeError): isinstance(42, Union[int, str]) @@ -355,43 +310,19 @@ class UnionTests(BaseTestCase): Union[Elem, str] # Nor should this -class TypeVarUnionTests(BaseTestCase): - - def test_simpler(self): - A = TypeVar('A', int, str, float) - B = TypeVar('B', int, str) - self.assertIsSubclass(A, A) - self.assertIsSubclass(B, B) - self.assertNotIsSubclass(B, A) - self.assertIsSubclass(A, Union[int, str, float]) - self.assertNotIsSubclass(Union[int, str, float], A) - self.assertNotIsSubclass(Union[int, str], B) - self.assertIsSubclass(B, Union[int, str]) - self.assertNotIsSubclass(A, B) - self.assertNotIsSubclass(Union[int, str, float], B) - self.assertNotIsSubclass(A, Union[int, str]) - - def test_var_union_subclass(self): - self.assertTrue(issubclass(T, Union[int, T])) - self.assertTrue(issubclass(KT, Union[KT, VT])) - - def test_var_union(self): - TU = TypeVar('TU', Union[int, float], None) - self.assertIsSubclass(int, TU) - self.assertIsSubclass(float, TU) - - class TupleTests(BaseTestCase): def test_basics(self): - self.assertTrue(issubclass(Tuple[int, str], Tuple)) - self.assertTrue(issubclass(Tuple[int, str], Tuple[int, str])) - self.assertFalse(issubclass(int, Tuple)) - self.assertFalse(issubclass(Tuple[float, str], Tuple[int, str])) - self.assertFalse(issubclass(Tuple[int, str, int], Tuple[int, str])) - self.assertFalse(issubclass(Tuple[int, str], Tuple[int, str, int])) + with self.assertRaises(TypeError): + issubclass(Tuple[int, str], Tuple) + with self.assertRaises(TypeError): + issubclass(Tuple, Tuple[int, str]) + with self.assertRaises(TypeError): + issubclass(tuple, Tuple[int, str]) + + class TP(tuple): ... self.assertTrue(issubclass(tuple, Tuple)) - self.assertFalse(issubclass(Tuple, tuple)) # Can't have it both ways. + self.assertTrue(issubclass(TP, Tuple)) def test_equality(self): self.assertEqual(Tuple[int], Tuple[int]) @@ -407,21 +338,7 @@ class TupleTests(BaseTestCase): def test_tuple_instance_type_error(self): with self.assertRaises(TypeError): isinstance((0, 0), Tuple[int, int]) - with self.assertRaises(TypeError): - isinstance((0, 0), Tuple) - - def test_tuple_ellipsis_subclass(self): - - class B: - pass - - class C(B): - pass - - self.assertNotIsSubclass(Tuple[B], Tuple[B, ...]) - self.assertIsSubclass(Tuple[C, ...], Tuple[B, ...]) - self.assertNotIsSubclass(Tuple[C, ...], Tuple[B]) - self.assertNotIsSubclass(Tuple[C], Tuple[B, ...]) + self.assertIsInstance((0, 0), Tuple) def test_repr(self): self.assertEqual(repr(Tuple), 'typing.Tuple') @@ -439,17 +356,9 @@ class TupleTests(BaseTestCase): class CallableTests(BaseTestCase): def test_self_subclass(self): - self.assertTrue(issubclass(Callable[[int], int], Callable)) - self.assertFalse(issubclass(Callable, Callable[[int], int])) - self.assertTrue(issubclass(Callable[[int], int], Callable[[int], int])) - self.assertFalse(issubclass(Callable[[Employee], int], - Callable[[Manager], int])) - self.assertFalse(issubclass(Callable[[Manager], int], - Callable[[Employee], int])) - self.assertFalse(issubclass(Callable[[int], Employee], - Callable[[int], Manager])) - self.assertFalse(issubclass(Callable[[int], Manager], - Callable[[int], Employee])) + with self.assertRaises(TypeError): + self.assertTrue(issubclass(type(lambda x: x), Callable[[int], int])) + self.assertTrue(issubclass(type(lambda x: x), Callable)) def test_eq_hash(self): self.assertEqual(Callable[[int], int], Callable[[int], int]) @@ -466,6 +375,11 @@ class CallableTests(BaseTestCase): class C(Callable): pass + with self.assertRaises(TypeError): + + class C(type(Callable)): + pass + with self.assertRaises(TypeError): class C(Callable[[int], int]): @@ -474,9 +388,13 @@ class CallableTests(BaseTestCase): def test_cannot_instantiate(self): with self.assertRaises(TypeError): Callable() + with self.assertRaises(TypeError): + type(Callable)() c = Callable[[int], str] with self.assertRaises(TypeError): c() + with self.assertRaises(TypeError): + type(c)() def test_callable_instance_works(self): def f(): @@ -616,6 +534,12 @@ class GenericTests(BaseTestCase): with self.assertRaises(TypeError): Y[str, str] + def test_generic_errors(self): + with self.assertRaises(TypeError): + isinstance([], List[int]) + with self.assertRaises(TypeError): + issubclass(list, List[int]) + def test_init(self): T = TypeVar('T') S = TypeVar('S') @@ -671,6 +595,42 @@ class GenericTests(BaseTestCase): c.bar = 'abc' self.assertEqual(c.__dict__, {'bar': 'abc'}) + def test_false_subclasses(self): + class MyMapping(MutableMapping[str, str]): pass + self.assertNotIsInstance({}, MyMapping) + self.assertNotIsSubclass(dict, MyMapping) + + def test_multiple_abc_bases(self): + class MM1(MutableMapping[str, str], collections_abc.MutableMapping): + def __getitem__(self, k): + return None + def __setitem__(self, k, v): + pass + def __delitem__(self, k): + pass + def __iter__(self): + return iter(()) + def __len__(self): + return 0 + class MM2(collections_abc.MutableMapping, MutableMapping[str, str]): + def __getitem__(self, k): + return None + def __setitem__(self, k, v): + pass + def __delitem__(self, k): + pass + def __iter__(self): + return iter(()) + def __len__(self): + return 0 + # these two should just work + MM1().update() + MM2().update() + self.assertIsInstance(MM1(), collections_abc.MutableMapping) + self.assertIsInstance(MM1(), MutableMapping) + self.assertIsInstance(MM2(), collections_abc.MutableMapping) + self.assertIsInstance(MM2(), MutableMapping) + def test_pickle(self): global C # pickle wants to reference the class by name T = TypeVar('T') @@ -853,6 +813,8 @@ class ClassVarTests(BaseTestCase): pass def test_cannot_init(self): + with self.assertRaises(TypeError): + ClassVar() with self.assertRaises(TypeError): type(ClassVar)() with self.assertRaises(TypeError): @@ -865,52 +827,6 @@ class ClassVarTests(BaseTestCase): issubclass(int, ClassVar) -class VarianceTests(BaseTestCase): - - def test_invariance(self): - # Because of invariance, List[subclass of X] is not a subclass - # of List[X], and ditto for MutableSequence. - self.assertNotIsSubclass(typing.List[Manager], typing.List[Employee]) - self.assertNotIsSubclass(typing.MutableSequence[Manager], - typing.MutableSequence[Employee]) - # It's still reflexive. - self.assertIsSubclass(typing.List[Employee], typing.List[Employee]) - self.assertIsSubclass(typing.MutableSequence[Employee], - typing.MutableSequence[Employee]) - - def test_covariance_tuple(self): - # Check covariace for Tuple (which are really special cases). - self.assertIsSubclass(Tuple[Manager], Tuple[Employee]) - self.assertNotIsSubclass(Tuple[Employee], Tuple[Manager]) - # And pairwise. - self.assertIsSubclass(Tuple[Manager, Manager], - Tuple[Employee, Employee]) - self.assertNotIsSubclass(Tuple[Employee, Employee], - Tuple[Manager, Employee]) - # And using ellipsis. - self.assertIsSubclass(Tuple[Manager, ...], Tuple[Employee, ...]) - self.assertNotIsSubclass(Tuple[Employee, ...], Tuple[Manager, ...]) - - def test_covariance_sequence(self): - # Check covariance for Sequence (which is just a generic class - # for this purpose, but using a type variable with covariant=True). - self.assertIsSubclass(typing.Sequence[Manager], - typing.Sequence[Employee]) - self.assertNotIsSubclass(typing.Sequence[Employee], - typing.Sequence[Manager]) - - def test_covariance_mapping(self): - # Ditto for Mapping (covariant in the value, invariant in the key). - self.assertIsSubclass(typing.Mapping[Employee, Manager], - typing.Mapping[Employee, Employee]) - self.assertNotIsSubclass(typing.Mapping[Manager, Employee], - typing.Mapping[Employee, Employee]) - self.assertNotIsSubclass(typing.Mapping[Employee, Manager], - typing.Mapping[Manager, Manager]) - self.assertNotIsSubclass(typing.Mapping[Manager, Employee], - typing.Mapping[Manager, Manager]) - - class CastTests(BaseTestCase): def test_basics(self): @@ -1247,7 +1163,6 @@ class CollectionsAbcTests(BaseTestCase): # path and could fail. So call this a few times. self.assertIsInstance([], typing.Iterable) self.assertIsInstance([], typing.Iterable) - self.assertIsInstance([], typing.Iterable[int]) self.assertNotIsInstance(42, typing.Iterable) # Just in case, also test issubclass() a few times. self.assertIsSubclass(list, typing.Iterable) @@ -1256,7 +1171,6 @@ class CollectionsAbcTests(BaseTestCase): def test_iterator(self): it = iter([]) self.assertIsInstance(it, typing.Iterator) - self.assertIsInstance(it, typing.Iterator[int]) self.assertNotIsInstance(42, typing.Iterator) @skipUnless(PY35, 'Python 3.5 required') @@ -1268,13 +1182,8 @@ class CollectionsAbcTests(BaseTestCase): globals(), ns) foo = ns['foo'] g = foo() - self.assertIsSubclass(type(g), typing.Awaitable[int]) self.assertIsInstance(g, typing.Awaitable) self.assertNotIsInstance(foo, typing.Awaitable) - self.assertIsSubclass(typing.Awaitable[Manager], - typing.Awaitable[Employee]) - self.assertNotIsSubclass(typing.Awaitable[Employee], - typing.Awaitable[Manager]) g.send(None) # Run foo() till completion, to avoid warning. @skipUnless(PY35, 'Python 3.5 required') @@ -1283,8 +1192,6 @@ class CollectionsAbcTests(BaseTestCase): it = AsyncIteratorWrapper(base_it) self.assertIsInstance(it, typing.AsyncIterable) self.assertIsInstance(it, typing.AsyncIterable) - self.assertIsSubclass(typing.AsyncIterable[Manager], - typing.AsyncIterable[Employee]) self.assertNotIsInstance(42, typing.AsyncIterable) @skipUnless(PY35, 'Python 3.5 required') @@ -1292,8 +1199,6 @@ class CollectionsAbcTests(BaseTestCase): base_it = range(10) # type: Iterator[int] it = AsyncIteratorWrapper(base_it) self.assertIsInstance(it, typing.AsyncIterator) - self.assertIsSubclass(typing.AsyncIterator[Manager], - typing.AsyncIterator[Employee]) self.assertNotIsInstance(42, typing.AsyncIterator) def test_sized(self): @@ -1457,10 +1362,6 @@ class CollectionsAbcTests(BaseTestCase): yield 42 g = foo() self.assertIsSubclass(type(g), typing.Generator) - self.assertIsSubclass(typing.Generator[Manager, Employee, Manager], - typing.Generator[Employee, Manager, Employee]) - self.assertNotIsSubclass(typing.Generator[Manager, Manager, Manager], - typing.Generator[Employee, Employee, Employee]) def test_no_generator_instantiation(self): with self.assertRaises(TypeError): @@ -1511,7 +1412,6 @@ class OtherABCTests(BaseTestCase): cm = manager() self.assertIsInstance(cm, typing.ContextManager) - self.assertIsInstance(cm, typing.ContextManager[int]) self.assertNotIsInstance(42, typing.ContextManager) @@ -1653,22 +1553,16 @@ class RETests(BaseTestCase): pat = re.compile('[a-z]+', re.I) self.assertIsSubclass(pat.__class__, Pattern) self.assertIsSubclass(type(pat), Pattern) - self.assertIsSubclass(type(pat), Pattern[str]) + self.assertIsInstance(pat, Pattern) mat = pat.search('12345abcde.....') self.assertIsSubclass(mat.__class__, Match) - self.assertIsSubclass(mat.__class__, Match[str]) - self.assertIsSubclass(mat.__class__, Match[bytes]) # Sad but true. self.assertIsSubclass(type(mat), Match) - self.assertIsSubclass(type(mat), Match[str]) + self.assertIsInstance(mat, Match) + # these should just work p = Pattern[Union[str, bytes]] - self.assertIsSubclass(Pattern[str], Pattern) - self.assertIsSubclass(Pattern[str], p) - m = Match[Union[bytes, str]] - self.assertIsSubclass(Match[bytes], Match) - self.assertIsSubclass(Match[bytes], m) def test_errors(self): with self.assertRaises(TypeError): @@ -1681,9 +1575,6 @@ class RETests(BaseTestCase): with self.assertRaises(TypeError): # Too complicated? m[str] - with self.assertRaises(TypeError): - # We don't support isinstance(). - isinstance(42, Pattern) with self.assertRaises(TypeError): # We don't support isinstance(). isinstance(42, Pattern[str]) @@ -1710,7 +1601,7 @@ class RETests(BaseTestCase): pass self.assertEqual(str(ex.exception), - "A type alias cannot be subclassed") + "Cannot subclass typing._TypeAlias") class AllTests(BaseTestCase): diff --git a/Lib/typing.py b/Lib/typing.py index 4676d28c8e0..925d9e42d3c 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -89,6 +89,13 @@ def _qualname(x): return x.__name__ +def _trim_name(nm): + if nm.startswith('_') and nm not in ('_TypeAlias', + '_ForwardRef', '_TypingBase', '_FinalTypingBase'): + nm = nm[1:] + return nm + + class TypingMeta(type): """Metaclass for every type defined below. @@ -127,22 +134,69 @@ class TypingMeta(type): pass def __repr__(self): - return '%s.%s' % (self.__module__, _qualname(self)) + qname = _trim_name(_qualname(self)) + return '%s.%s' % (self.__module__, qname) -class Final: +class _TypingBase(metaclass=TypingMeta, _root=True): + """Indicator of special typing constructs.""" + + __slots__ = () + + + def __init__(self, *args, **kwds): + pass + + def __new__(cls, *args, **kwds): + """Constructor. + + This only exists to give a better error message in case + someone tries to subclass a special typing object (not a good idea). + """ + if (len(args) == 3 and + isinstance(args[0], str) and + isinstance(args[1], tuple)): + # Close enough. + raise TypeError("Cannot subclass %r" % cls) + return object.__new__(cls) + + # Things that are not classes also need these. + def _eval_type(self, globalns, localns): + return self + + def _get_type_vars(self, tvars): + pass + + def __repr__(self): + cls = type(self) + qname = _trim_name(_qualname(cls)) + return '%s.%s' % (cls.__module__, qname) + + def __call__(self, *args, **kwds): + raise TypeError("Cannot instantiate %r" % type(self)) + + +class _FinalTypingBase(_TypingBase, _root=True): """Mix-in class to prevent instantiation.""" __slots__ = () - def __new__(self, *args, **kwds): - raise TypeError("Cannot instantiate %r" % self.__class__) + def __new__(cls, *args, _root=False, **kwds): + self = super().__new__(cls, *args, **kwds) + if _root is True: + return self + raise TypeError("Cannot instantiate %r" % cls) -class _ForwardRef(TypingMeta): +class _ForwardRef(_TypingBase, _root=True): """Wrapper to hold a forward reference.""" - def __new__(cls, arg): + __slots__ = ('__forward_arg__', '__forward_code__', + '__forward_evaluated__', '__forward_value__', + '__forward_frame__') + + def __init__(self, arg): + super().__init__(arg) if not isinstance(arg, str): raise TypeError('ForwardRef must be a string -- got %r' % (arg,)) try: @@ -150,7 +204,6 @@ class _ForwardRef(TypingMeta): except SyntaxError: raise SyntaxError('ForwardRef must be an expression -- got %r' % (arg,)) - self = super().__new__(cls, arg, (), {}, _root=True) self.__forward_arg__ = arg self.__forward_code__ = code self.__forward_evaluated__ = False @@ -161,7 +214,6 @@ class _ForwardRef(TypingMeta): frame = frame.f_back assert frame is not None self.__forward_frame__ = frame - return self def _eval_type(self, globalns, localns): if not self.__forward_evaluated__: @@ -177,49 +229,36 @@ class _ForwardRef(TypingMeta): self.__forward_evaluated__ = True return self.__forward_value__ + def __eq__(self, other): + if not isinstance(other, _ForwardRef): + return NotImplemented + return (self.__forward_arg__ == other.__forward_arg__ and + self.__forward_frame__ == other.__forward_frame__) + + def __hash__(self): + return hash((self.__forward_arg__, self.__forward_frame__)) + def __instancecheck__(self, obj): raise TypeError("Forward references cannot be used with isinstance().") def __subclasscheck__(self, cls): - if not self.__forward_evaluated__: - globalns = self.__forward_frame__.f_globals - localns = self.__forward_frame__.f_locals - try: - self._eval_type(globalns, localns) - except NameError: - return False # Too early. - return issubclass(cls, self.__forward_value__) + raise TypeError("Forward references cannot be used with issubclass().") def __repr__(self): return '_ForwardRef(%r)' % (self.__forward_arg__,) -class _TypeAlias: +class _TypeAlias(_TypingBase, _root=True): """Internal helper class for defining generic variants of concrete types. - Note that this is not a type; let's call it a pseudo-type. It can - be used in instance and subclass checks, e.g. isinstance(m, Match) - or issubclass(type(m), Match). However, it cannot be itself the - target of an issubclass() call; e.g. issubclass(Match, C) (for - some arbitrary class C) raises TypeError rather than returning - False. + Note that this is not a type; let's call it a pseudo-type. It cannot + be used in instance and subclass checks in parameterized form, i.e. + ``isinstance(42, Match[str])`` raises ``TypeError`` instead of returning + ``False``. """ __slots__ = ('name', 'type_var', 'impl_type', 'type_checker') - def __new__(cls, *args, **kwds): - """Constructor. - - This only exists to give a better error message in case - someone tries to subclass a type alias (not a good idea). - """ - if (len(args) == 3 and - isinstance(args[0], str) and - isinstance(args[1], tuple)): - # Close enough. - raise TypeError("A type alias cannot be subclassed") - return object.__new__(cls) - def __init__(self, name, type_var, impl_type, type_checker): """Initializer. @@ -232,9 +271,9 @@ class _TypeAlias: and returns a value that should be a type_var instance. """ assert isinstance(name, str), repr(name) - assert isinstance(type_var, type), repr(type_var) assert isinstance(impl_type, type), repr(impl_type) assert not isinstance(impl_type, TypingMeta), repr(impl_type) + assert isinstance(type_var, (type, _TypingBase)) self.name = name self.type_var = type_var self.impl_type = impl_type @@ -244,36 +283,41 @@ class _TypeAlias: return "%s[%s]" % (self.name, _type_repr(self.type_var)) def __getitem__(self, parameter): - assert isinstance(parameter, type), repr(parameter) if not isinstance(self.type_var, TypeVar): raise TypeError("%s cannot be further parameterized." % self) - if self.type_var.__constraints__: - if not issubclass(parameter, Union[self.type_var.__constraints__]): + if self.type_var.__constraints__ and isinstance(parameter, type): + if not issubclass(parameter, self.type_var.__constraints__): raise TypeError("%s is not a valid substitution for %s." % (parameter, self.type_var)) + if isinstance(parameter, TypeVar): + raise TypeError("%s cannot be re-parameterized." % self.type_var) return self.__class__(self.name, parameter, self.impl_type, self.type_checker) + def __eq__(self, other): + if not isinstance(other, _TypeAlias): + return NotImplemented + return self.name == other.name and self.type_var == other.type_var + + def __hash__(self): + return hash((self.name, self.type_var)) + def __instancecheck__(self, obj): - raise TypeError("Type aliases cannot be used with isinstance().") + if not isinstance(self.type_var, TypeVar): + raise TypeError("Parameterized type aliases cannot be used " + "with isinstance().") + return isinstance(obj, self.impl_type) def __subclasscheck__(self, cls): - if cls is Any: - return True - if isinstance(cls, _TypeAlias): - # Covariance. For now, we compare by name. - return (cls.name == self.name and - issubclass(cls.type_var, self.type_var)) - else: - # Note that this is too lenient, because the - # implementation type doesn't carry information about - # whether it is about bytes or str (for example). - return issubclass(cls, self.impl_type) + if not isinstance(self.type_var, TypeVar): + raise TypeError("Parameterized type aliases cannot be used " + "with issubclass().") + return issubclass(cls, self.impl_type) def _get_type_vars(types, tvars): for t in types: - if isinstance(t, TypingMeta) or isinstance(t, _ClassVar): + if isinstance(t, TypingMeta) or isinstance(t, _TypingBase): t._get_type_vars(tvars) @@ -284,7 +328,7 @@ def _type_vars(types): def _eval_type(t, globalns, localns): - if isinstance(t, TypingMeta) or isinstance(t, _ClassVar): + if isinstance(t, TypingMeta) or isinstance(t, _TypingBase): return t._eval_type(globalns, localns) else: return t @@ -306,7 +350,7 @@ def _type_check(arg, msg): return type(None) if isinstance(arg, str): arg = _ForwardRef(arg) - if not isinstance(arg, (type, _TypeAlias)) and not callable(arg): + if not isinstance(arg, (type, _TypingBase)) and not callable(arg): raise TypeError(msg + " Got %.100r." % (arg,)) return arg @@ -328,23 +372,7 @@ def _type_repr(obj): return repr(obj) -class AnyMeta(TypingMeta): - """Metaclass for Any.""" - - def __new__(cls, name, bases, namespace, _root=False): - self = super().__new__(cls, name, bases, namespace, _root=_root) - return self - - def __instancecheck__(self, obj): - raise TypeError("Any cannot be used with isinstance().") - - def __subclasscheck__(self, cls): - if not isinstance(cls, type): - return super().__subclasscheck__(cls) # To TypeError. - return True - - -class Any(Final, metaclass=AnyMeta, _root=True): +class _Any(_FinalTypingBase, _root=True): """Special type indicating an unconstrained type. - Any object is an instance of Any. @@ -354,8 +382,17 @@ class Any(Final, metaclass=AnyMeta, _root=True): __slots__ = () + def __instancecheck__(self, obj): + raise TypeError("Any cannot be used with isinstance().") -class TypeVar(TypingMeta, metaclass=TypingMeta, _root=True): + def __subclasscheck__(self, cls): + raise TypeError("Any cannot be used with issubclass().") + + +Any = _Any(_root=True) + + +class TypeVar(_TypingBase, _root=True): """Type variable. Usage:: @@ -400,9 +437,14 @@ class TypeVar(TypingMeta, metaclass=TypingMeta, _root=True): A.__constraints__ == (str, bytes) """ - def __new__(cls, name, *constraints, bound=None, + __slots__ = ('__name__', '__bound__', '__constraints__', + '__covariant__', '__contravariant__') + + def __init__(self, name, *constraints, bound=None, covariant=False, contravariant=False): - self = super().__new__(cls, name, (Final,), {}, _root=True) + super().__init__(name, *constraints, bound=bound, + covariant=covariant, contravariant=contravariant) + self.__name__ = name if covariant and contravariant: raise ValueError("Bivariant types are not supported.") self.__covariant__ = bool(covariant) @@ -417,7 +459,6 @@ class TypeVar(TypingMeta, metaclass=TypingMeta, _root=True): self.__bound__ = _type_check(bound, "Bound must be a type.") else: self.__bound__ = None - return self def _get_type_vars(self, tvars): if self not in tvars: @@ -436,16 +477,7 @@ class TypeVar(TypingMeta, metaclass=TypingMeta, _root=True): raise TypeError("Type variables cannot be used with isinstance().") def __subclasscheck__(self, cls): - # TODO: Make this raise TypeError too? - if cls is self: - return True - if cls is Any: - return True - if self.__bound__ is not None: - return issubclass(cls, self.__bound__) - if self.__constraints__: - return any(issubclass(cls, c) for c in self.__constraints__) - return True + raise TypeError("Type variables cannot be used with issubclass().") # Some unconstrained type variables. These are used by the container types. @@ -463,124 +495,19 @@ T_contra = TypeVar('T_contra', contravariant=True) # Ditto contravariant. AnyStr = TypeVar('AnyStr', bytes, str) -class UnionMeta(TypingMeta): - """Metaclass for Union.""" - - def __new__(cls, name, bases, namespace, parameters=None, _root=False): - if parameters is None: - return super().__new__(cls, name, bases, namespace, _root=_root) - if not isinstance(parameters, tuple): - raise TypeError("Expected parameters=") - # Flatten out Union[Union[...], ...] and type-check non-Union args. - params = [] - msg = "Union[arg, ...]: each arg must be a type." - for p in parameters: - if isinstance(p, UnionMeta): - params.extend(p.__union_params__) - else: - params.append(_type_check(p, msg)) - # Weed out strict duplicates, preserving the first of each occurrence. - all_params = set(params) - if len(all_params) < len(params): - new_params = [] - for t in params: - if t in all_params: - new_params.append(t) - all_params.remove(t) - params = new_params - assert not all_params, all_params - # Weed out subclasses. - # E.g. Union[int, Employee, Manager] == Union[int, Employee]. - # If Any or object is present it will be the sole survivor. - # If both Any and object are present, Any wins. - # Never discard type variables, except against Any. - # (In particular, Union[str, AnyStr] != AnyStr.) - all_params = set(params) - for t1 in params: - if t1 is Any: - return Any - if isinstance(t1, TypeVar): - continue - if isinstance(t1, _TypeAlias): - # _TypeAlias is not a real class. - continue - if not isinstance(t1, type): - assert callable(t1) # A callable might sneak through. - continue - if any(isinstance(t2, type) and issubclass(t1, t2) - for t2 in all_params - {t1} if not isinstance(t2, TypeVar)): - all_params.remove(t1) - # It's not a union if there's only one type left. - if len(all_params) == 1: - return all_params.pop() - # Create a new class with these params. - self = super().__new__(cls, name, bases, {}, _root=True) - self.__union_params__ = tuple(t for t in params if t in all_params) - self.__union_set_params__ = frozenset(self.__union_params__) - return self - - def _eval_type(self, globalns, localns): - p = tuple(_eval_type(t, globalns, localns) - for t in self.__union_params__) - if p == self.__union_params__: - return self - else: - return self.__class__(self.__name__, self.__bases__, {}, - p, _root=True) - - def _get_type_vars(self, tvars): - if self.__union_params__: - _get_type_vars(self.__union_params__, tvars) - - def __repr__(self): - r = super().__repr__() - if self.__union_params__: - r += '[%s]' % (', '.join(_type_repr(t) - for t in self.__union_params__)) - return r - - def __getitem__(self, parameters): - if self.__union_params__ is not None: - raise TypeError( - "Cannot subscript an existing Union. Use Union[u, t] instead.") - if parameters == (): - raise TypeError("Cannot take a Union of no types.") - if not isinstance(parameters, tuple): - parameters = (parameters,) - return self.__class__(self.__name__, self.__bases__, - dict(self.__dict__), parameters, _root=True) - - def __eq__(self, other): - if not isinstance(other, UnionMeta): - return NotImplemented - return self.__union_set_params__ == other.__union_set_params__ - - def __hash__(self): - return hash(self.__union_set_params__) - - def __instancecheck__(self, obj): - raise TypeError("Unions cannot be used with isinstance().") - - def __subclasscheck__(self, cls): - if cls is Any: - return True - if self.__union_params__ is None: - return isinstance(cls, UnionMeta) - elif isinstance(cls, UnionMeta): - if cls.__union_params__ is None: - return False - return all(issubclass(c, self) for c in (cls.__union_params__)) - elif isinstance(cls, TypeVar): - if cls in self.__union_params__: - return True - if cls.__constraints__: - return issubclass(Union[cls.__constraints__], self) - return False - else: - return any(issubclass(cls, t) for t in self.__union_params__) +def _tp_cache(func): + cached = functools.lru_cache()(func) + @functools.wraps(func) + def inner(*args, **kwds): + try: + return cached(*args, **kwds) + except TypeError: + pass # Do not duplicate real errors. + return func(*args, **kwds) + return inner -class Union(Final, metaclass=UnionMeta, _root=True): +class _Union(_FinalTypingBase, _root=True): """Union type; Union[X, Y] means either X or Y. To define a union, use e.g. Union[int, str]. Details: @@ -632,23 +559,107 @@ class Union(Final, metaclass=UnionMeta, _root=True): - You can use Optional[X] as a shorthand for Union[X, None]. """ - # Unsubscripted Union type has params set to None. - __union_params__ = None - __union_set_params__ = None + __slots__ = ('__union_params__', '__union_set_params__') + + def __new__(cls, parameters=None, *args, _root=False): + self = super().__new__(cls, parameters, *args, _root=_root) + if parameters is None: + self.__union_params__ = None + self.__union_set_params__ = None + return self + if not isinstance(parameters, tuple): + raise TypeError("Expected parameters=") + # Flatten out Union[Union[...], ...] and type-check non-Union args. + params = [] + msg = "Union[arg, ...]: each arg must be a type." + for p in parameters: + if isinstance(p, _Union): + params.extend(p.__union_params__) + else: + params.append(_type_check(p, msg)) + # Weed out strict duplicates, preserving the first of each occurrence. + all_params = set(params) + if len(all_params) < len(params): + new_params = [] + for t in params: + if t in all_params: + new_params.append(t) + all_params.remove(t) + params = new_params + assert not all_params, all_params + # Weed out subclasses. + # E.g. Union[int, Employee, Manager] == Union[int, Employee]. + # If Any or object is present it will be the sole survivor. + # If both Any and object are present, Any wins. + # Never discard type variables, except against Any. + # (In particular, Union[str, AnyStr] != AnyStr.) + all_params = set(params) + for t1 in params: + if t1 is Any: + return Any + if not isinstance(t1, type): + continue + if any(isinstance(t2, type) and issubclass(t1, t2) + for t2 in all_params - {t1} + if not (isinstance(t2, GenericMeta) and + t2.__origin__ is not None)): + all_params.remove(t1) + # It's not a union if there's only one type left. + if len(all_params) == 1: + return all_params.pop() + self.__union_params__ = tuple(t for t in params if t in all_params) + self.__union_set_params__ = frozenset(self.__union_params__) + return self + + def _eval_type(self, globalns, localns): + p = tuple(_eval_type(t, globalns, localns) + for t in self.__union_params__) + if p == self.__union_params__: + return self + else: + return self.__class__(p, _root=True) + + def _get_type_vars(self, tvars): + if self.__union_params__: + _get_type_vars(self.__union_params__, tvars) + + def __repr__(self): + r = super().__repr__() + if self.__union_params__: + r += '[%s]' % (', '.join(_type_repr(t) + for t in self.__union_params__)) + return r + + @_tp_cache + def __getitem__(self, parameters): + if self.__union_params__ is not None: + raise TypeError( + "Cannot subscript an existing Union. Use Union[u, t] instead.") + if parameters == (): + raise TypeError("Cannot take a Union of no types.") + if not isinstance(parameters, tuple): + parameters = (parameters,) + return self.__class__(parameters, _root=True) + + def __eq__(self, other): + if not isinstance(other, _Union): + return NotImplemented + return self.__union_set_params__ == other.__union_set_params__ + + def __hash__(self): + return hash(self.__union_set_params__) + + def __instancecheck__(self, obj): + raise TypeError("Unions cannot be used with isinstance().") + + def __subclasscheck__(self, cls): + raise TypeError("Unions cannot be used with issubclass().") -class OptionalMeta(TypingMeta): - """Metaclass for Optional.""" - - def __new__(cls, name, bases, namespace, _root=False): - return super().__new__(cls, name, bases, namespace, _root=_root) - - def __getitem__(self, arg): - arg = _type_check(arg, "Optional[t] requires a single type.") - return Union[arg, type(None)] +Union = _Union(_root=True) -class Optional(Final, metaclass=OptionalMeta, _root=True): +class _Optional(_FinalTypingBase, _root=True): """Optional type. Optional[X] is equivalent to Union[X, type(None)]. @@ -656,16 +667,31 @@ class Optional(Final, metaclass=OptionalMeta, _root=True): __slots__ = () + @_tp_cache + def __getitem__(self, arg): + arg = _type_check(arg, "Optional[t] requires a single type.") + return Union[arg, type(None)] -class TupleMeta(TypingMeta): - """Metaclass for Tuple.""" - def __new__(cls, name, bases, namespace, parameters=None, +Optional = _Optional(_root=True) + + +class _Tuple(_FinalTypingBase, _root=True): + """Tuple type; Tuple[X, Y] is the cross-product type of X and Y. + + Example: Tuple[T1, T2] is a tuple of two elements corresponding + to type variables T1 and T2. Tuple[int, float, str] is a tuple + of an int, a float and a string. + + To specify a variable-length tuple of homogeneous type, use Tuple[T, ...]. + """ + + __slots__ = ('__tuple_params__', '__tuple_use_ellipsis__') + + def __init__(self, parameters=None, use_ellipsis=False, _root=False): - self = super().__new__(cls, name, bases, namespace, _root=_root) self.__tuple_params__ = parameters self.__tuple_use_ellipsis__ = use_ellipsis - return self def _get_type_vars(self, tvars): if self.__tuple_params__: @@ -679,8 +705,7 @@ class TupleMeta(TypingMeta): if p == self.__tuple_params__: return self else: - return self.__class__(self.__name__, self.__bases__, {}, - p, _root=True) + return self.__class__(p, _root=True) def __repr__(self): r = super().__repr__() @@ -694,6 +719,7 @@ class TupleMeta(TypingMeta): ', '.join(params)) return r + @_tp_cache def __getitem__(self, parameters): if self.__tuple_params__ is not None: raise TypeError("Cannot re-parameterize %r" % (self,)) @@ -707,64 +733,50 @@ class TupleMeta(TypingMeta): use_ellipsis = False msg = "Tuple[t0, t1, ...]: each t must be a type." parameters = tuple(_type_check(p, msg) for p in parameters) - return self.__class__(self.__name__, self.__bases__, - dict(self.__dict__), parameters, + return self.__class__(parameters, use_ellipsis=use_ellipsis, _root=True) def __eq__(self, other): - if not isinstance(other, TupleMeta): + if not isinstance(other, _Tuple): return NotImplemented return (self.__tuple_params__ == other.__tuple_params__ and self.__tuple_use_ellipsis__ == other.__tuple_use_ellipsis__) def __hash__(self): - return hash(self.__tuple_params__) + return hash((self.__tuple_params__, self.__tuple_use_ellipsis__)) def __instancecheck__(self, obj): - raise TypeError("Tuples cannot be used with isinstance().") + if self.__tuple_params__ == None: + return isinstance(obj, tuple) + raise TypeError("Parameterized Tuple cannot be used " + "with isinstance().") def __subclasscheck__(self, cls): - if cls is Any: - return True - if not isinstance(cls, type): - return super().__subclasscheck__(cls) # To TypeError. - if issubclass(cls, tuple): - return True # Special case. - if not isinstance(cls, TupleMeta): - return super().__subclasscheck__(cls) # False. - if self.__tuple_params__ is None: - return True - if cls.__tuple_params__ is None: - return False # ??? - if cls.__tuple_use_ellipsis__ != self.__tuple_use_ellipsis__: - return False - # Covariance. - return (len(self.__tuple_params__) == len(cls.__tuple_params__) and - all(issubclass(x, p) - for x, p in zip(cls.__tuple_params__, - self.__tuple_params__))) + if self.__tuple_params__ == None: + return issubclass(cls, tuple) + raise TypeError("Parameterized Tuple cannot be used " + "with issubclass().") -class Tuple(Final, metaclass=TupleMeta, _root=True): - """Tuple type; Tuple[X, Y] is the cross-product type of X and Y. +Tuple = _Tuple(_root=True) - Example: Tuple[T1, T2] is a tuple of two elements corresponding - to type variables T1 and T2. Tuple[int, float, str] is a tuple - of an int, a float and a string. - To specify a variable-length tuple of homogeneous type, use Sequence[T]. +class _Callable(_FinalTypingBase, _root=True): + """Callable type; Callable[[int], str] is a function of (int) -> str. + + The subscription syntax must always be used with exactly two + values: the argument list and the return type. The argument list + must be a list of types; the return type must be a single type. + + There is no syntax to indicate optional or keyword arguments, + such function types are rarely used as callback types. """ - __slots__ = () + __slots__ = ('__args__', '__result__') - -class CallableMeta(TypingMeta): - """Metaclass for Callable.""" - - def __new__(cls, name, bases, namespace, _root=False, - args=None, result=None): + def __init__(self, args=None, result=None, _root=False): if args is None and result is None: - pass # Must be 'class Callable'. + pass else: if args is not Ellipsis: if not isinstance(args, list): @@ -775,10 +787,8 @@ class CallableMeta(TypingMeta): args = tuple(_type_check(arg, msg) for arg in args) msg = "Callable[args, result]: result must be a type." result = _type_check(result, msg) - self = super().__new__(cls, name, bases, namespace, _root=_root) self.__args__ = args self.__result__ = result - return self def _get_type_vars(self, tvars): if self.__args__ and self.__args__ is not Ellipsis: @@ -795,8 +805,7 @@ class CallableMeta(TypingMeta): if args == self.__args__ and result == self.__result__: return self else: - return self.__class__(self.__name__, self.__bases__, {}, - args=args, result=result, _root=True) + return self.__class__(args, result, _root=True) def __repr__(self): r = super().__repr__() @@ -816,12 +825,10 @@ class CallableMeta(TypingMeta): raise TypeError( "Callable must be used as Callable[[arg, ...], result].") args, result = parameters - return self.__class__(self.__name__, self.__bases__, - dict(self.__dict__), _root=True, - args=args, result=result) + return self.__class__(args, result, _root=True) def __eq__(self, other): - if not isinstance(other, CallableMeta): + if not isinstance(other, _Callable): return NotImplemented return (self.__args__ == other.__args__ and self.__result__ == other.__result__) @@ -836,31 +843,18 @@ class CallableMeta(TypingMeta): if self.__args__ is None and self.__result__ is None: return isinstance(obj, collections_abc.Callable) else: - raise TypeError("Callable[] cannot be used with isinstance().") + raise TypeError("Parameterized Callable cannot be used " + "with isinstance().") def __subclasscheck__(self, cls): - if cls is Any: - return True - if not isinstance(cls, CallableMeta): - return super().__subclasscheck__(cls) if self.__args__ is None and self.__result__ is None: - return True - # We're not doing covariance or contravariance -- this is *invariance*. - return self == cls + return issubclass(cls, collections_abc.Callable) + else: + raise TypeError("Parameterized Callable cannot be used " + "with issubclass().") -class Callable(Final, metaclass=CallableMeta, _root=True): - """Callable type; Callable[[int], str] is a function of (int) -> str. - - The subscription syntax must always be used with exactly two - values: the argument list and the return type. The argument list - must be a list of types; the return type must be a single type. - - There is no syntax to indicate optional or keyword arguments, - such function types are rarely used as callback types. - """ - - __slots__ = () +Callable = _Callable(_root=True) def _gorg(a): @@ -985,6 +979,7 @@ class GenericMeta(TypingMeta, abc.ABCMeta): def __hash__(self): return hash((self.__name__, self.__parameters__)) + @_tp_cache def __getitem__(self, params): if not isinstance(params, tuple): params = (params,) @@ -1040,44 +1035,18 @@ class GenericMeta(TypingMeta, abc.ABCMeta): return self.__subclasscheck__(instance.__class__) def __subclasscheck__(self, cls): - if cls is Any: - return True - if isinstance(cls, GenericMeta): - # For a covariant class C(Generic[T]), - # C[X] is a subclass of C[Y] iff X is a subclass of Y. - origin = self.__origin__ - if origin is not None and origin is cls.__origin__: - assert len(self.__args__) == len(origin.__parameters__) - assert len(cls.__args__) == len(origin.__parameters__) - for p_self, p_cls, p_origin in zip(self.__args__, - cls.__args__, - origin.__parameters__): - if isinstance(p_origin, TypeVar): - if p_origin.__covariant__: - # Covariant -- p_cls must be a subclass of p_self. - if not issubclass(p_cls, p_self): - break - elif p_origin.__contravariant__: - # Contravariant. I think it's the opposite. :-) - if not issubclass(p_self, p_cls): - break - else: - # Invariant -- p_cls and p_self must equal. - if p_self != p_cls: - break - else: - # If the origin's parameter is not a typevar, - # insist on invariance. - if p_self != p_cls: - break - else: - return True - # If we break out of the loop, the superclass gets a chance. + if self is Generic: + raise TypeError("Class %r cannot be used with class " + "or instance checks" % self) + if (self.__origin__ is not None and + sys._getframe(1).f_globals['__name__'] != 'abc'): + raise TypeError("Parameterized generics cannot be used with class " + "or instance checks") if super().__subclasscheck__(cls): return True - if self.__extra__ is None or isinstance(cls, GenericMeta): - return False - return issubclass(cls, self.__extra__) + if self.__extra__ is not None: + return issubclass(cls, self.__extra__) + return False # Prevent checks for Generic to crash when defining Generic. @@ -1117,7 +1086,7 @@ class Generic(metaclass=GenericMeta): return obj -class _ClassVar(metaclass=TypingMeta, _root=True): +class _ClassVar(_FinalTypingBase, _root=True): """Special type construct to mark class variables. An annotation wrapped in ClassVar indicates that a given @@ -1134,36 +1103,35 @@ class _ClassVar(metaclass=TypingMeta, _root=True): be used with isinstance() or issubclass(). """ - def __init__(self, tp=None, _root=False): - cls = type(self) - if _root: - self.__type__ = tp - else: - raise TypeError('Cannot initialize {}'.format(cls.__name__[1:])) + __slots__ = ('__type__',) + + def __init__(self, tp=None, **kwds): + self.__type__ = tp def __getitem__(self, item): cls = type(self) if self.__type__ is None: return cls(_type_check(item, - '{} accepts only types.'.format(cls.__name__[1:])), + '{} accepts only single type.'.format(cls.__name__[1:])), _root=True) raise TypeError('{} cannot be further subscripted' .format(cls.__name__[1:])) def _eval_type(self, globalns, localns): - return type(self)(_eval_type(self.__type__, globalns, localns), - _root=True) + new_tp = _eval_type(self.__type__, globalns, localns) + if new_tp == self.__type__: + return self + return type(self)(new_tp, _root=True) def _get_type_vars(self, tvars): if self.__type__: _get_type_vars(self.__type__, tvars) def __repr__(self): - cls = type(self) - if not self.__type__: - return '{}.{}'.format(cls.__module__, cls.__name__[1:]) - return '{}.{}[{}]'.format(cls.__module__, cls.__name__[1:], - _type_repr(self.__type__)) + r = super().__repr__() + if self.__type__ is not None: + r += '[{}]'.format(_type_repr(self.__type__)) + return r def __hash__(self): return hash((type(self).__name__, self.__type__)) @@ -1614,52 +1582,52 @@ if hasattr(collections_abc, 'Collection'): if hasattr(collections_abc, 'Collection'): class AbstractSet(Collection[T_co], extra=collections_abc.Set): - pass + __slots__ = () else: class AbstractSet(Sized, Iterable[T_co], Container[T_co], extra=collections_abc.Set): - pass + __slots__ = () class MutableSet(AbstractSet[T], extra=collections_abc.MutableSet): - pass + __slots__ = () # NOTE: It is only covariant in the value type. if hasattr(collections_abc, 'Collection'): class Mapping(Collection[KT], Generic[KT, VT_co], extra=collections_abc.Mapping): - pass + __slots__ = () else: class Mapping(Sized, Iterable[KT], Container[KT], Generic[KT, VT_co], extra=collections_abc.Mapping): - pass + __slots__ = () class MutableMapping(Mapping[KT, VT], extra=collections_abc.MutableMapping): - pass + __slots__ = () if hasattr(collections_abc, 'Reversible'): if hasattr(collections_abc, 'Collection'): class Sequence(Reversible[T_co], Collection[T_co], extra=collections_abc.Sequence): - pass + __slots__ = () else: class Sequence(Sized, Reversible[T_co], Container[T_co], extra=collections_abc.Sequence): - pass + __slots__ = () else: class Sequence(Sized, Iterable[T_co], Container[T_co], extra=collections_abc.Sequence): - pass + __slots__ = () class MutableSequence(Sequence[T], extra=collections_abc.MutableSequence): - pass + __slots__ = () class ByteString(Sequence[int], extra=collections_abc.ByteString): - pass + __slots__ = () ByteString.register(type(memoryview(b''))) @@ -1667,6 +1635,8 @@ ByteString.register(type(memoryview(b''))) class List(list, MutableSequence[T], extra=list): + __slots__ = () + def __new__(cls, *args, **kwds): if _geqv(cls, List): raise TypeError("Type List cannot be instantiated; " @@ -1676,6 +1646,8 @@ class List(list, MutableSequence[T], extra=list): class Set(set, MutableSet[T], extra=set): + __slots__ = () + def __new__(cls, *args, **kwds): if _geqv(cls, Set): raise TypeError("Type Set cannot be instantiated; " @@ -1683,22 +1655,7 @@ class Set(set, MutableSet[T], extra=set): return set.__new__(cls, *args, **kwds) -class _FrozenSetMeta(GenericMeta): - """This metaclass ensures set is not a subclass of FrozenSet. - - Without this metaclass, set would be considered a subclass of - FrozenSet, because FrozenSet.__extra__ is collections.abc.Set, and - set is a subclass of that. - """ - - def __subclasscheck__(self, cls): - if issubclass(cls, Set): - return False - return super().__subclasscheck__(cls) - - -class FrozenSet(frozenset, AbstractSet[T_co], metaclass=_FrozenSetMeta, - extra=frozenset): +class FrozenSet(frozenset, AbstractSet[T_co], extra=frozenset): __slots__ = () def __new__(cls, *args, **kwds): @@ -1709,23 +1666,23 @@ class FrozenSet(frozenset, AbstractSet[T_co], metaclass=_FrozenSetMeta, class MappingView(Sized, Iterable[T_co], extra=collections_abc.MappingView): - pass + __slots__ = () class KeysView(MappingView[KT], AbstractSet[KT], extra=collections_abc.KeysView): - pass + __slots__ = () class ItemsView(MappingView[Tuple[KT, VT_co]], AbstractSet[Tuple[KT, VT_co]], Generic[KT, VT_co], extra=collections_abc.ItemsView): - pass + __slots__ = () class ValuesView(MappingView[VT_co], extra=collections_abc.ValuesView): - pass + __slots__ = () if hasattr(contextlib, 'AbstractContextManager'): @@ -1736,6 +1693,8 @@ if hasattr(contextlib, 'AbstractContextManager'): class Dict(dict, MutableMapping[KT, VT], extra=dict): + __slots__ = () + def __new__(cls, *args, **kwds): if _geqv(cls, Dict): raise TypeError("Type Dict cannot be instantiated; " @@ -1745,6 +1704,8 @@ class Dict(dict, MutableMapping[KT, VT], extra=dict): class DefaultDict(collections.defaultdict, MutableMapping[KT, VT], extra=collections.defaultdict): + __slots__ = () + def __new__(cls, *args, **kwds): if _geqv(cls, DefaultDict): raise TypeError("Type DefaultDict cannot be instantiated; " @@ -1800,6 +1761,8 @@ class Type(Generic[CT_co], extra=type): At this point the type checker knows that joe has type BasicUser. """ + __slots__ = () + def _make_nmtuple(name, types): nm_tpl = collections.namedtuple(name, [n for n, t in types])