[Python-checkins] cpython (3.5): More updates from upstream typing.py

guido.van.rossum python-checkins at python.org
Mon Oct 3 11:42:25 EDT 2016


https://hg.python.org/cpython/rev/b24d0f274623
changeset:   104260:b24d0f274623
branch:      3.5
parent:      104255:ac838bf5499d
user:        Guido van Rossum <guido at python.org>
date:        Mon Oct 03 08:40:50 2016 -0700
summary:
  More updates from upstream typing.py

files:
  Lib/test/test_typing.py |   67 ++++++++++-------
  Lib/typing.py           |  105 ++++++++++++++++++---------
  2 files changed, 108 insertions(+), 64 deletions(-)


diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -202,10 +202,13 @@
     def test_union_any(self):
         u = Union[Any]
         self.assertEqual(u, Any)
-        u = Union[int, Any]
-        self.assertEqual(u, Any)
-        u = Union[Any, int]
-        self.assertEqual(u, Any)
+        u1 = Union[int, Any]
+        u2 = Union[Any, int]
+        u3 = Union[Any, object]
+        self.assertEqual(u1, u2)
+        self.assertNotEqual(u1, Any)
+        self.assertNotEqual(u2, Any)
+        self.assertNotEqual(u3, Any)
 
     def test_union_object(self):
         u = Union[object]
@@ -215,12 +218,6 @@
         u = Union[object, int]
         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):
         u1 = Union[int, float]
         u2 = Union[float, int]
@@ -600,8 +597,8 @@
         self.assertNotIsInstance({}, MyMapping)
         self.assertNotIsSubclass(dict, MyMapping)
 
-    def test_multiple_abc_bases(self):
-        class MM1(MutableMapping[str, str], collections_abc.MutableMapping):
+    def test_abc_bases(self):
+        class MM(MutableMapping[str, str]):
             def __getitem__(self, k):
                 return None
             def __setitem__(self, k, v):
@@ -612,24 +609,20 @@
                 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):
+        # 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):
+            pass
+        with self.assertRaises(TypeError):
+            # consistent MRO not possible
+            class MM2(collections_abc.MutableMapping, MutableMapping[str, str]):
                 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
@@ -1380,12 +1373,28 @@
             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):
                 return 0
 
         self.assertEqual(len(MMC()), 0)
 
         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):
                 return 0
 
diff --git a/Lib/typing.py b/Lib/typing.py
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -143,7 +143,6 @@
 
     __slots__ = ()
 
-
     def __init__(self, *args, **kwds):
         pass
 
@@ -158,7 +157,7 @@
                 isinstance(args[1], tuple)):
             # Close enough.
             raise TypeError("Cannot subclass %r" % cls)
-        return object.__new__(cls)
+        return super().__new__(cls)
 
     # Things that are not classes also need these.
     def _eval_type(self, globalns, localns):
@@ -177,7 +176,11 @@
 
 
 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__ = ()
 
@@ -273,7 +276,7 @@
         assert isinstance(name, str), repr(name)
         assert isinstance(impl_type, type), 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.type_var = type_var
         self.impl_type = impl_type
@@ -375,9 +378,13 @@
 class _Any(_FinalTypingBase, _root=True):
     """Special type indicating an unconstrained type.
 
-    - Any object is an instance of Any.
-    - Any class is a subclass of Any.
-    - As a special case, Any and object are subclasses of each other.
+    - Any is compatible with every type.
+    - Any assumed to have all methods.
+    - 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__ = ()
@@ -502,7 +509,7 @@
         try:
             return cached(*args, **kwds)
         except TypeError:
-            pass  # Do not duplicate real errors.
+            pass  # All real errors (not unhashable args) are raised below.
         return func(*args, **kwds)
     return inner
 
@@ -542,16 +549,10 @@
         Union[Manager, int, Employee] == Union[int, Employee]
         Union[Employee, Manager] == Employee
 
-    - Corollary: if Any is present it is the sole survivor, e.g.::
-
-        Union[int, Any] == Any
-
     - Similar for 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 write Union[X][Y] (what would it mean?).
@@ -589,14 +590,11 @@
             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.
+        # If object is present it will be sole survivor among proper classes.
+        # Never discard type variables.
         # (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)
@@ -662,7 +660,7 @@
 class _Optional(_FinalTypingBase, _root=True):
     """Optional type.
 
-    Optional[X] is equivalent to Union[X, type(None)].
+    Optional[X] is equivalent to Union[X, None].
     """
 
     __slots__ = ()
@@ -894,11 +892,55 @@
     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):
     """Metaclass for generic types."""
 
     def __new__(cls, name, bases, namespace,
                 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)
 
         if tvars is not None:
@@ -947,6 +989,13 @@
         self.__extra__ = extra
         # Speed hack (https://github.com/python/typing/issues/196).
         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
 
     def _get_type_vars(self, tvars):
@@ -1032,21 +1081,7 @@
         # latter, we must extend __instancecheck__ too. For simplicity
         # we just skip the cache check -- instance checks for generic
         # classes are supposed to be rare anyways.
-        return self.__subclasscheck__(instance.__class__)
-
-    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
+        return issubclass(instance.__class__, self)
 
 
 # Prevent checks for Generic to crash when defining Generic.

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list