More updates from upstream typing.py (3.6->3.7)

This commit is contained in:
Guido van Rossum 2016-10-03 08:42:17 -07:00
commit 441e878e5b
2 changed files with 117 additions and 73 deletions

View File

@ -202,10 +202,13 @@ class UnionTests(BaseTestCase):
def test_union_any(self): def test_union_any(self):
u = Union[Any] u = Union[Any]
self.assertEqual(u, Any) self.assertEqual(u, Any)
u = Union[int, Any] u1 = Union[int, Any]
self.assertEqual(u, Any) u2 = Union[Any, int]
u = Union[Any, int] u3 = Union[Any, object]
self.assertEqual(u, Any) self.assertEqual(u1, u2)
self.assertNotEqual(u1, Any)
self.assertNotEqual(u2, Any)
self.assertNotEqual(u3, Any)
def test_union_object(self): def test_union_object(self):
u = Union[object] u = Union[object]
@ -215,12 +218,6 @@ class UnionTests(BaseTestCase):
u = Union[object, int] u = Union[object, int]
self.assertEqual(u, object) self.assertEqual(u, object)
def test_union_any_object(self):
u = Union[object, Any]
self.assertEqual(u, Any)
u = Union[Any, object]
self.assertEqual(u, Any)
def test_unordered(self): def test_unordered(self):
u1 = Union[int, float] u1 = Union[int, float]
u2 = Union[float, int] u2 = Union[float, int]
@ -600,36 +597,32 @@ class GenericTests(BaseTestCase):
self.assertNotIsInstance({}, MyMapping) self.assertNotIsInstance({}, MyMapping)
self.assertNotIsSubclass(dict, MyMapping) self.assertNotIsSubclass(dict, MyMapping)
def test_multiple_abc_bases(self): def test_abc_bases(self):
class MM(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
# this should just work
MM().update()
self.assertIsInstance(MM(), collections_abc.MutableMapping)
self.assertIsInstance(MM(), MutableMapping)
self.assertNotIsInstance(MM(), List)
self.assertNotIsInstance({}, MM)
def test_multiple_bases(self):
class MM1(MutableMapping[str, str], collections_abc.MutableMapping): class MM1(MutableMapping[str, str], collections_abc.MutableMapping):
def __getitem__(self, k): pass
return None with self.assertRaises(TypeError):
def __setitem__(self, k, v): # consistent MRO not possible
class MM2(collections_abc.MutableMapping, MutableMapping[str, str]):
pass 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): def test_pickle(self):
global C # pickle wants to reference the class by name global C # pickle wants to reference the class by name
@ -1380,12 +1373,28 @@ class CollectionsAbcTests(BaseTestCase):
MMA() MMA()
class MMC(MMA): class MMC(MMA):
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): def __len__(self):
return 0 return 0
self.assertEqual(len(MMC()), 0) self.assertEqual(len(MMC()), 0)
class MMB(typing.MutableMapping[KT, VT]): class MMB(typing.MutableMapping[KT, VT]):
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): def __len__(self):
return 0 return 0

View File

@ -143,7 +143,6 @@ class _TypingBase(metaclass=TypingMeta, _root=True):
__slots__ = () __slots__ = ()
def __init__(self, *args, **kwds): def __init__(self, *args, **kwds):
pass pass
@ -158,7 +157,7 @@ class _TypingBase(metaclass=TypingMeta, _root=True):
isinstance(args[1], tuple)): isinstance(args[1], tuple)):
# Close enough. # Close enough.
raise TypeError("Cannot subclass %r" % cls) raise TypeError("Cannot subclass %r" % cls)
return object.__new__(cls) return super().__new__(cls)
# Things that are not classes also need these. # Things that are not classes also need these.
def _eval_type(self, globalns, localns): def _eval_type(self, globalns, localns):
@ -177,7 +176,11 @@ class _TypingBase(metaclass=TypingMeta, _root=True):
class _FinalTypingBase(_TypingBase, _root=True): class _FinalTypingBase(_TypingBase, _root=True):
"""Mix-in class to prevent instantiation.""" """Mix-in class to prevent instantiation.
Prevents instantiation unless _root=True is given in class call.
It is used to create pseudo-singleton instances Any, Union, Tuple, etc.
"""
__slots__ = () __slots__ = ()
@ -273,7 +276,7 @@ class _TypeAlias(_TypingBase, _root=True):
assert isinstance(name, str), repr(name) assert isinstance(name, str), repr(name)
assert isinstance(impl_type, type), repr(impl_type) assert isinstance(impl_type, type), repr(impl_type)
assert not isinstance(impl_type, TypingMeta), repr(impl_type) assert not isinstance(impl_type, TypingMeta), repr(impl_type)
assert isinstance(type_var, (type, _TypingBase)) assert isinstance(type_var, (type, _TypingBase)), repr(type_var)
self.name = name self.name = name
self.type_var = type_var self.type_var = type_var
self.impl_type = impl_type self.impl_type = impl_type
@ -375,9 +378,13 @@ def _type_repr(obj):
class _Any(_FinalTypingBase, _root=True): class _Any(_FinalTypingBase, _root=True):
"""Special type indicating an unconstrained type. """Special type indicating an unconstrained type.
- Any object is an instance of Any. - Any is compatible with every type.
- Any class is a subclass of Any. - Any assumed to have all methods.
- As a special case, Any and object are subclasses of each other. - All values assumed to be instances of Any.
Note that all the above statements are true from the point of view of
static type checkers. At runtime, Any should not be used with instance
or class checks.
""" """
__slots__ = () __slots__ = ()
@ -502,7 +509,7 @@ def _tp_cache(func):
try: try:
return cached(*args, **kwds) return cached(*args, **kwds)
except TypeError: except TypeError:
pass # Do not duplicate real errors. pass # All real errors (not unhashable args) are raised below.
return func(*args, **kwds) return func(*args, **kwds)
return inner return inner
@ -542,16 +549,10 @@ class _Union(_FinalTypingBase, _root=True):
Union[Manager, int, Employee] == Union[int, Employee] Union[Manager, int, Employee] == Union[int, Employee]
Union[Employee, Manager] == Employee Union[Employee, Manager] == Employee
- Corollary: if Any is present it is the sole survivor, e.g.::
Union[int, Any] == Any
- Similar for object:: - Similar for object::
Union[int, object] == object Union[int, object] == object
- To cut a tie: Union[object, Any] == Union[Any, object] == Any.
- You cannot subclass or instantiate a union. - You cannot subclass or instantiate a union.
- You cannot write Union[X][Y] (what would it mean?). - You cannot write Union[X][Y] (what would it mean?).
@ -589,14 +590,11 @@ class _Union(_FinalTypingBase, _root=True):
assert not all_params, all_params assert not all_params, all_params
# Weed out subclasses. # Weed out subclasses.
# E.g. Union[int, Employee, Manager] == Union[int, Employee]. # E.g. Union[int, Employee, Manager] == Union[int, Employee].
# If Any or object is present it will be the sole survivor. # If object is present it will be sole survivor among proper classes.
# If both Any and object are present, Any wins. # Never discard type variables.
# Never discard type variables, except against Any.
# (In particular, Union[str, AnyStr] != AnyStr.) # (In particular, Union[str, AnyStr] != AnyStr.)
all_params = set(params) all_params = set(params)
for t1 in params: for t1 in params:
if t1 is Any:
return Any
if not isinstance(t1, type): if not isinstance(t1, type):
continue continue
if any(isinstance(t2, type) and issubclass(t1, t2) if any(isinstance(t2, type) and issubclass(t1, t2)
@ -662,7 +660,7 @@ Union = _Union(_root=True)
class _Optional(_FinalTypingBase, _root=True): class _Optional(_FinalTypingBase, _root=True):
"""Optional type. """Optional type.
Optional[X] is equivalent to Union[X, type(None)]. Optional[X] is equivalent to Union[X, None].
""" """
__slots__ = () __slots__ = ()
@ -894,11 +892,55 @@ def _next_in_mro(cls):
return next_in_mro return next_in_mro
def _valid_for_check(cls):
if cls is Generic:
raise TypeError("Class %r cannot be used with class "
"or instance checks" % cls)
if (cls.__origin__ is not None and
sys._getframe(3).f_globals['__name__'] not in ['abc', 'functools']):
raise TypeError("Parameterized generics cannot be used with class "
"or instance checks")
def _make_subclasshook(cls):
"""Construct a __subclasshook__ callable that incorporates
the associated __extra__ class in subclass checks performed
against cls.
"""
if isinstance(cls.__extra__, abc.ABCMeta):
# The logic mirrors that of ABCMeta.__subclasscheck__.
# Registered classes need not be checked here because
# cls and its extra share the same _abc_registry.
def __extrahook__(subclass):
_valid_for_check(cls)
res = cls.__extra__.__subclasshook__(subclass)
if res is not NotImplemented:
return res
if cls.__extra__ in subclass.__mro__:
return True
for scls in cls.__extra__.__subclasses__():
if isinstance(scls, GenericMeta):
continue
if issubclass(subclass, scls):
return True
return NotImplemented
else:
# For non-ABC extras we'll just call issubclass().
def __extrahook__(subclass):
_valid_for_check(cls)
if cls.__extra__ and issubclass(subclass, cls.__extra__):
return True
return NotImplemented
return __extrahook__
class GenericMeta(TypingMeta, abc.ABCMeta): class GenericMeta(TypingMeta, abc.ABCMeta):
"""Metaclass for generic types.""" """Metaclass for generic types."""
def __new__(cls, name, bases, namespace, def __new__(cls, name, bases, namespace,
tvars=None, args=None, origin=None, extra=None): tvars=None, args=None, origin=None, extra=None):
if extra is not None and type(extra) is abc.ABCMeta and extra not in bases:
bases = (extra,) + bases
self = super().__new__(cls, name, bases, namespace, _root=True) self = super().__new__(cls, name, bases, namespace, _root=True)
if tvars is not None: if tvars is not None:
@ -947,6 +989,13 @@ class GenericMeta(TypingMeta, abc.ABCMeta):
self.__extra__ = extra self.__extra__ = extra
# Speed hack (https://github.com/python/typing/issues/196). # Speed hack (https://github.com/python/typing/issues/196).
self.__next_in_mro__ = _next_in_mro(self) self.__next_in_mro__ = _next_in_mro(self)
# This allows unparameterized generic collections to be used
# with issubclass() and isinstance() in the same way as their
# collections.abc counterparts (e.g., isinstance([], Iterable)).
self.__subclasshook__ = _make_subclasshook(self)
if isinstance(extra, abc.ABCMeta):
self._abc_registry = extra._abc_registry
return self return self
def _get_type_vars(self, tvars): def _get_type_vars(self, tvars):
@ -1032,21 +1081,7 @@ class GenericMeta(TypingMeta, abc.ABCMeta):
# latter, we must extend __instancecheck__ too. For simplicity # latter, we must extend __instancecheck__ too. For simplicity
# we just skip the cache check -- instance checks for generic # we just skip the cache check -- instance checks for generic
# classes are supposed to be rare anyways. # classes are supposed to be rare anyways.
return self.__subclasscheck__(instance.__class__) return issubclass(instance.__class__, self)
def __subclasscheck__(self, cls):
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 not None:
return issubclass(cls, self.__extra__)
return False
# Prevent checks for Generic to crash when defining Generic. # Prevent checks for Generic to crash when defining Generic.