[Python-checkins] cpython (merge 3.5 -> 3.6): Sync typing.py from upstream (3.5->3.6)

guido.van.rossum python-checkins at python.org
Fri Oct 21 17:32:05 EDT 2016


https://hg.python.org/cpython/rev/7bd7e4018199
changeset:   104624:7bd7e4018199
branch:      3.6
parent:      104621:b0af901b1e2a
parent:      104623:fb03d179f824
user:        Guido van Rossum <guido at python.org>
date:        Fri Oct 21 14:28:29 2016 -0700
summary:
  Sync typing.py from upstream (3.5->3.6)

files:
  Lib/test/test_typing.py |   96 +++++++++++++++++++++++-
  Lib/typing.py           |  107 ++++++++++++++++++++-------
  2 files changed, 170 insertions(+), 33 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
@@ -548,9 +548,9 @@
 
     def test_repr(self):
         self.assertEqual(repr(SimpleMapping),
-                         __name__ + '.' + 'SimpleMapping<~XK, ~XV>')
+                         __name__ + '.' + 'SimpleMapping')
         self.assertEqual(repr(MySimpleMapping),
-                         __name__ + '.' + 'MySimpleMapping<~XK, ~XV>')
+                         __name__ + '.' + 'MySimpleMapping')
 
     def test_chain_repr(self):
         T = TypeVar('T')
@@ -574,7 +574,36 @@
         self.assertNotEqual(Z, Y[T])
 
         self.assertTrue(str(Z).endswith(
-            '.C<~T>[typing.Tuple[~S, ~T]]<~S, ~T>[~T, int]<~T>[str]'))
+            '.C[typing.Tuple[str, int]]'))
+
+    def test_new_repr(self):
+        T = TypeVar('T')
+        U = TypeVar('U', covariant=True)
+        S = TypeVar('S')
+
+        self.assertEqual(repr(List), 'typing.List')
+        self.assertEqual(repr(List[T]), 'typing.List[~T]')
+        self.assertEqual(repr(List[U]), 'typing.List[+U]')
+        self.assertEqual(repr(List[S][T][int]), 'typing.List[int]')
+        self.assertEqual(repr(List[int]), 'typing.List[int]')
+
+    def test_new_repr_complex(self):
+        T = TypeVar('T')
+        TS = TypeVar('TS')
+
+        self.assertEqual(repr(typing.Mapping[T, TS][TS, T]), 'typing.Mapping[~TS, ~T]')
+        self.assertEqual(repr(List[Tuple[T, TS]][int, T]),
+                         'typing.List[typing.Tuple[int, ~T]]')
+        self.assertEqual(repr(List[Tuple[T, T]][List[int]]),
+                 'typing.List[typing.Tuple[typing.List[int], typing.List[int]]]')
+
+    def test_new_repr_bare(self):
+        T = TypeVar('T')
+        self.assertEqual(repr(Generic[T]), 'typing.Generic[~T]')
+        self.assertEqual(repr(typing._Protocol[T]), 'typing.Protocol[~T]')
+        class C(typing.Dict[Any, Any]): ...
+        # this line should just work
+        repr(C.__mro__)
 
     def test_dict(self):
         T = TypeVar('T')
@@ -625,6 +654,63 @@
             class MM2(collections_abc.MutableMapping, MutableMapping[str, str]):
                 pass
 
+    def test_orig_bases(self):
+        T = TypeVar('T')
+        class C(typing.Dict[str, T]): ...
+        self.assertEqual(C.__orig_bases__, (typing.Dict[str, T],))
+
+    def test_naive_runtime_checks(self):
+        def naive_dict_check(obj, tp):
+            # Check if a dictionary conforms to Dict type
+            if len(tp.__parameters__) > 0:
+                raise NotImplementedError
+            if tp.__args__:
+                KT, VT = tp.__args__
+                return all(isinstance(k, KT) and isinstance(v, VT)
+                   for k, v in obj.items())
+        self.assertTrue(naive_dict_check({'x': 1}, typing.Dict[str, int]))
+        self.assertFalse(naive_dict_check({1: 'x'}, typing.Dict[str, int]))
+        with self.assertRaises(NotImplementedError):
+            naive_dict_check({1: 'x'}, typing.Dict[str, T])
+
+        def naive_generic_check(obj, tp):
+            # Check if an instance conforms to the generic class
+            if not hasattr(obj, '__orig_class__'):
+                raise NotImplementedError
+            return obj.__orig_class__ == tp
+        class Node(Generic[T]): ...
+        self.assertTrue(naive_generic_check(Node[int](), Node[int]))
+        self.assertFalse(naive_generic_check(Node[str](), Node[int]))
+        self.assertFalse(naive_generic_check(Node[str](), List))
+        with self.assertRaises(NotImplementedError):
+            naive_generic_check([1,2,3], Node[int])
+
+        def naive_list_base_check(obj, tp):
+            # Check if list conforms to a List subclass
+            return all(isinstance(x, tp.__orig_bases__[0].__args__[0])
+                       for x in obj)
+        class C(List[int]): ...
+        self.assertTrue(naive_list_base_check([1, 2, 3], C))
+        self.assertFalse(naive_list_base_check(['a', 'b'], C))
+
+    def test_multi_subscr_base(self):
+        T = TypeVar('T')
+        U = TypeVar('U')
+        V = TypeVar('V')
+        class C(List[T][U][V]): ...
+        class D(C, List[T][U][V]): ...
+        self.assertEqual(C.__parameters__, (V,))
+        self.assertEqual(D.__parameters__, (V,))
+        self.assertEqual(C[int].__parameters__, ())
+        self.assertEqual(D[int].__parameters__, ())
+        self.assertEqual(C[int].__args__, (int,))
+        self.assertEqual(D[int].__args__, (int,))
+        self.assertEqual(C.__bases__, (List,))
+        self.assertEqual(D.__bases__, (C, List))
+        self.assertEqual(C.__orig_bases__, (List[T][U][V],))
+        self.assertEqual(D.__orig_bases__, (C, List[T][U][V]))
+
+
     def test_pickle(self):
         global C  # pickle wants to reference the class by name
         T = TypeVar('T')
@@ -662,12 +748,12 @@
         if not PY32:
             self.assertEqual(C.__qualname__,
                              'GenericTests.test_repr_2.<locals>.C')
-        self.assertEqual(repr(C).split('.')[-1], 'C<~T>')
+        self.assertEqual(repr(C).split('.')[-1], 'C')
         X = C[int]
         self.assertEqual(X.__module__, __name__)
         if not PY32:
             self.assertEqual(X.__qualname__, 'C')
-        self.assertEqual(repr(X).split('.')[-1], 'C<~T>[int]')
+        self.assertEqual(repr(X).split('.')[-1], 'C[int]')
 
         class Y(C[int]):
             pass
diff --git a/Lib/typing.py b/Lib/typing.py
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -292,8 +292,8 @@
             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)
+        if isinstance(parameter, TypeVar) and parameter is not self.type_var:
+            raise TypeError("%s cannot be re-parameterized." % self)
         return self.__class__(self.name, parameter,
                               self.impl_type, self.type_checker)
 
@@ -622,9 +622,12 @@
             _get_type_vars(self.__union_params__, tvars)
 
     def __repr__(self):
+        return self._subs_repr([], [])
+
+    def _subs_repr(self, tvars, args):
         r = super().__repr__()
         if self.__union_params__:
-            r += '[%s]' % (', '.join(_type_repr(t)
+            r += '[%s]' % (', '.join(_replace_arg(t, tvars, args)
                                      for t in self.__union_params__))
         return r
 
@@ -706,9 +709,12 @@
             return self.__class__(p, _root=True)
 
     def __repr__(self):
+        return self._subs_repr([], [])
+
+    def _subs_repr(self, tvars, args):
         r = super().__repr__()
         if self.__tuple_params__ is not None:
-            params = [_type_repr(p) for p in self.__tuple_params__]
+            params = [_replace_arg(p, tvars, args) for p in self.__tuple_params__]
             if self.__tuple_use_ellipsis__:
                 params.append('...')
             if not params:
@@ -791,6 +797,8 @@
     def _get_type_vars(self, tvars):
         if self.__args__ and self.__args__ is not Ellipsis:
             _get_type_vars(self.__args__, tvars)
+        if self.__result__:
+            _get_type_vars([self.__result__], tvars)
 
     def _eval_type(self, globalns, localns):
         if self.__args__ is None and self.__result__ is None:
@@ -806,14 +814,17 @@
             return self.__class__(args, result, _root=True)
 
     def __repr__(self):
+        return self._subs_repr([], [])
+
+    def _subs_repr(self, tvars, args):
         r = super().__repr__()
         if self.__args__ is not None or self.__result__ is not None:
             if self.__args__ is Ellipsis:
                 args_r = '...'
             else:
-                args_r = '[%s]' % ', '.join(_type_repr(t)
+                args_r = '[%s]' % ', '.join(_replace_arg(t, tvars, args)
                                             for t in self.__args__)
-            r += '[%s, %s]' % (args_r, _type_repr(self.__result__))
+            r += '[%s, %s]' % (args_r, _replace_arg(self.__result__, tvars, args))
         return r
 
     def __getitem__(self, parameters):
@@ -878,6 +889,16 @@
     return _gorg(a) is _gorg(b)
 
 
+def _replace_arg(arg, tvars, args):
+    if hasattr(arg, '_subs_repr'):
+        return arg._subs_repr(tvars, args)
+    if isinstance(arg, TypeVar):
+       for i, tvar in enumerate(tvars):
+           if arg.__name__ == tvar.__name__:
+               return args[i]
+    return _type_repr(arg)
+
+
 def _next_in_mro(cls):
     """Helper for Generic.__new__.
 
@@ -938,11 +959,7 @@
     """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)
-
+                tvars=None, args=None, origin=None, extra=None, orig_bases=None):
         if tvars is not None:
             # Called from __getitem__() below.
             assert origin is not None
@@ -983,12 +1000,25 @@
                          ", ".join(str(g) for g in gvars)))
                 tvars = gvars
 
+        initial_bases = bases
+        if extra is not None and type(extra) is abc.ABCMeta and extra not in bases:
+            bases = (extra,) + bases
+        bases = tuple(_gorg(b) if isinstance(b, GenericMeta) else b for b in bases)
+
+        # remove bare Generic from bases if there are other generic bases
+        if any(isinstance(b, GenericMeta) and b is not Generic for b in bases):
+            bases = tuple(b for b in bases if b is not Generic)
+        self = super().__new__(cls, name, bases, namespace, _root=True)
+
         self.__parameters__ = tvars
         self.__args__ = args
         self.__origin__ = origin
         self.__extra__ = extra
         # Speed hack (https://github.com/python/typing/issues/196).
         self.__next_in_mro__ = _next_in_mro(self)
+        # Preserve base classes on subclassing (__bases__ are type erased now).
+        if orig_bases is None:
+            self.__orig_bases__ = initial_bases
 
         # This allows unparameterized generic collections to be used
         # with issubclass() and isinstance() in the same way as their
@@ -1006,17 +1036,29 @@
             _get_type_vars(self.__parameters__, tvars)
 
     def __repr__(self):
-        if self.__origin__ is not None:
-            r = repr(self.__origin__)
-        else:
-            r = super().__repr__()
-        if self.__args__:
-            r += '[%s]' % (
-                ', '.join(_type_repr(p) for p in self.__args__))
-        if self.__parameters__:
-            r += '<%s>' % (
-                ', '.join(_type_repr(p) for p in self.__parameters__))
-        return r
+        if self.__origin__ is None:
+            return super().__repr__()
+        return self._subs_repr([], [])
+
+    def _subs_repr(self, tvars, args):
+        assert len(tvars) == len(args)
+        # Construct the chain of __origin__'s.
+        current = self.__origin__
+        orig_chain = []
+        while current.__origin__ is not None:
+            orig_chain.append(current)
+            current = current.__origin__
+        # Replace type variables in __args__ if asked ...
+        str_args = []
+        for arg in self.__args__:
+            str_args.append(_replace_arg(arg, tvars, args))
+        # ... then continue replacing down the origin chain.
+        for cls in orig_chain:
+            new_str_args = []
+            for i, arg in enumerate(cls.__args__):
+                new_str_args.append(_replace_arg(arg, cls.__parameters__, str_args))
+            str_args = new_str_args
+        return super().__repr__() + '[%s]' % ', '.join(str_args)
 
     def __eq__(self, other):
         if not isinstance(other, GenericMeta):
@@ -1049,11 +1091,11 @@
                 raise TypeError(
                     "Parameters to Generic[...] must all be unique")
             tvars = params
-            args = None
+            args = params
         elif self is _Protocol:
             # _Protocol is internal, don't check anything.
             tvars = params
-            args = None
+            args = params
         elif self.__origin__ in (Generic, _Protocol):
             # Can't subscript Generic[...] or _Protocol[...].
             raise TypeError("Cannot subscript already-subscripted %s" %
@@ -1071,12 +1113,13 @@
             tvars = _type_vars(params)
             args = params
         return self.__class__(self.__name__,
-                              (self,) + self.__bases__,
+                              self.__bases__,
                               dict(self.__dict__),
                               tvars=tvars,
                               args=args,
                               origin=self,
-                              extra=self.__extra__)
+                              extra=self.__extra__,
+                              orig_bases=self.__orig_bases__)
 
     def __instancecheck__(self, instance):
         # Since we extend ABC.__subclasscheck__ and
@@ -1120,6 +1163,10 @@
         else:
             origin = _gorg(cls)
             obj = cls.__next_in_mro__.__new__(origin)
+            try:
+                obj.__orig_class__ = cls
+            except AttributeError:
+                pass
             obj.__init__(*args, **kwds)
             return obj
 
@@ -1163,12 +1210,15 @@
 
     def _get_type_vars(self, tvars):
         if self.__type__:
-            _get_type_vars(self.__type__, tvars)
+            _get_type_vars([self.__type__], tvars)
 
     def __repr__(self):
+        return self._subs_repr([], [])
+
+    def _subs_repr(self, tvars, args):
         r = super().__repr__()
         if self.__type__ is not None:
-            r += '[{}]'.format(_type_repr(self.__type__))
+            r += '[{}]'.format(_replace_arg(self.__type__, tvars, args))
         return r
 
     def __hash__(self):
@@ -1485,6 +1535,7 @@
                             attr != '__next_in_mro__' and
                             attr != '__parameters__' and
                             attr != '__origin__' and
+                            attr != '__orig_bases__' and
                             attr != '__extra__' and
                             attr != '__module__'):
                         attrs.add(attr)

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


More information about the Python-checkins mailing list