[Python-checkins] cpython (merge 3.5 -> 3.6): Issue #28556: merge 5 more typing changes from upstream (#340, #344, #348,

guido.van.rossum python-checkins at python.org
Tue Jan 17 23:46:56 EST 2017


https://hg.python.org/cpython/rev/fd889884fe08
changeset:   106206:fd889884fe08
branch:      3.6
parent:      106197:d47c5a8cccec
parent:      106205:1a9e12a852b2
user:        Guido van Rossum <guido at python.org>
date:        Tue Jan 17 20:43:30 2017 -0800
summary:
  Issue #28556: merge 5 more typing changes from upstream (#340, #344, #348, #349, #350) (3.5->3.6)

files:
  Lib/test/test_typing.py |  37 ++++++++++++++++++--
  Lib/typing.py           |  51 ++++++++++++++++++++++++----
  2 files changed, 76 insertions(+), 12 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
@@ -12,7 +12,7 @@
 from typing import Union, Optional
 from typing import Tuple, List, MutableMapping
 from typing import Callable
-from typing import Generic, ClassVar
+from typing import Generic, ClassVar, GenericMeta
 from typing import cast
 from typing import get_type_hints
 from typing import no_type_check, no_type_check_decorator
@@ -23,6 +23,7 @@
 from typing import Pattern, Match
 import abc
 import typing
+import weakref
 try:
     import collections.abc as collections_abc
 except ImportError:
@@ -281,6 +282,15 @@
         self.assertFalse(Union[str, typing.Iterable[int]] == typing.Iterable[int])
         self.assertTrue(Union[str, typing.Iterable] == typing.Iterable)
 
+    def test_union_compare_other(self):
+        self.assertNotEqual(Union, object)
+        self.assertNotEqual(Union, Any)
+        self.assertNotEqual(ClassVar, Union)
+        self.assertNotEqual(Optional, Union)
+        self.assertNotEqual([None], Optional)
+        self.assertNotEqual(Optional, typing.Mapping)
+        self.assertNotEqual(Optional[typing.MutableMapping], Union)
+
     def test_optional(self):
         o = Optional[int]
         u = Union[int, None]
@@ -718,6 +728,12 @@
         self.assertEqual(C.__orig_bases__, (List[T][U][V],))
         self.assertEqual(D.__orig_bases__, (C, List[T][U][V]))
 
+    def test_subscript_meta(self):
+        T = TypeVar('T')
+        self.assertEqual(Type[GenericMeta], Type[GenericMeta])
+        self.assertEqual(Union[T, int][GenericMeta], Union[GenericMeta, int])
+        self.assertEqual(Callable[..., GenericMeta].__args__, (Ellipsis, GenericMeta))
+
     def test_extended_generic_rules_eq(self):
         T = TypeVar('T')
         U = TypeVar('U')
@@ -896,6 +912,14 @@
             self.assertEqual(t, copy(t))
             self.assertEqual(t, deepcopy(t))
 
+    def test_weakref_all(self):
+        T = TypeVar('T')
+        things = [Any, Union[T, int], Callable[..., T], Tuple[Any, Any],
+                  Optional[List[int]], typing.Mapping[int, str],
+                  typing.re.Match[bytes], typing.Iterable['whatever']]
+        for t in things:
+            self.assertEqual(weakref.ref(t)(), t)
+
     def test_parameterized_slots(self):
         T = TypeVar('T')
         class C(Generic[T]):
@@ -1918,7 +1942,9 @@
         self.assertEqual(jim.id, 1)
         self.assertEqual(Emp.__name__, 'Emp')
         self.assertEqual(Emp._fields, ('name', 'id'))
-        self.assertEqual(Emp._field_types, dict(name=str, id=int))
+        self.assertEqual(Emp.__annotations__,
+                         collections.OrderedDict([('name', str), ('id', int)]))
+        self.assertIs(Emp._field_types, Emp.__annotations__)
 
     @skipUnless(PY36, 'Python 3.6 required')
     def test_annotation_usage(self):
@@ -1929,7 +1955,9 @@
         self.assertEqual(tim.cool, 9000)
         self.assertEqual(CoolEmployee.__name__, 'CoolEmployee')
         self.assertEqual(CoolEmployee._fields, ('name', 'cool'))
-        self.assertEqual(CoolEmployee._field_types, dict(name=str, cool=int))
+        self.assertEqual(CoolEmployee.__annotations__,
+                         collections.OrderedDict(name=str, cool=int))
+        self.assertIs(CoolEmployee._field_types, CoolEmployee.__annotations__)
 
     @skipUnless(PY36, 'Python 3.6 required')
     def test_namedtuple_keyword_usage(self):
@@ -1939,7 +1967,8 @@
         self.assertEqual(nick.name, 'Nick')
         self.assertEqual(LocalEmployee.__name__, 'LocalEmployee')
         self.assertEqual(LocalEmployee._fields, ('name', 'age'))
-        self.assertEqual(LocalEmployee._field_types, dict(name=str, age=int))
+        self.assertEqual(LocalEmployee.__annotations__, dict(name=str, age=int))
+        self.assertIs(LocalEmployee._field_types, LocalEmployee.__annotations__)
         with self.assertRaises(TypeError):
             NamedTuple('Name', [('x', int)], y=str)
         with self.assertRaises(TypeError):
diff --git a/Lib/typing.py b/Lib/typing.py
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -27,6 +27,8 @@
 
     # ABCs (from collections.abc).
     'AbstractSet',  # collections.abc.Set.
+    'GenericMeta',  # subclass of abc.ABCMeta and a metaclass
+                    # for 'Generic' and ABCs below.
     'ByteString',
     'Container',
     'Hashable',
@@ -145,7 +147,7 @@
 class _TypingBase(metaclass=TypingMeta, _root=True):
     """Internal indicator of special typing constructs."""
 
-    __slots__ = ()
+    __slots__ = ('__weakref__',)
 
     def __init__(self, *args, **kwds):
         pass
@@ -514,7 +516,7 @@
 
     if tvars is None:
         tvars = []
-    if hasattr(arg, '_subs_tree'):
+    if hasattr(arg, '_subs_tree') and isinstance(arg, (GenericMeta, _TypingBase)):
         return arg._subs_tree(tvars, args)
     if isinstance(arg, TypeVar):
         for i, tvar in enumerate(tvars):
@@ -523,6 +525,16 @@
     return arg
 
 
+# Special typing constructs Union, Optional, Generic, Callable and Tuple
+# use three special attributes for internal bookkeeping of generic types:
+# * __parameters__ is a tuple of unique free type parameters of a generic
+#   type, for example, Dict[T, T].__parameters__ == (T,);
+# * __origin__ keeps a reference to a type that was subscripted,
+#   e.g., Union[T, int].__origin__ == Union;
+# * __args__ is a tuple of all arguments used in subscripting,
+#   e.g., Dict[T, int].__args__ == (T, int).
+
+
 def _subs_tree(cls, tvars=None, args=None):
     """An internal helper function: calculate substitution tree
     for generic cls after replacing its type parameters with
@@ -757,9 +769,12 @@
         return (Union,) + tree_args
 
     def __eq__(self, other):
-        if not isinstance(other, _Union):
+        if isinstance(other, _Union):
+            return self.__tree_hash__ == other.__tree_hash__
+        elif self is not Union:
             return self._subs_tree() == other
-        return self.__tree_hash__ == other.__tree_hash__
+        else:
+            return self is other
 
     def __hash__(self):
         return self.__tree_hash__
@@ -883,10 +898,26 @@
 
 
 class GenericMeta(TypingMeta, abc.ABCMeta):
-    """Metaclass for generic types."""
+    """Metaclass for generic types.
+
+    This is a metaclass for typing.Generic and generic ABCs defined in
+    typing module. User defined subclasses of GenericMeta can override
+    __new__ and invoke super().__new__. Note that GenericMeta.__new__
+    has strict rules on what is allowed in its bases argument:
+    * plain Generic is disallowed in bases;
+    * Generic[...] should appear in bases at most once;
+    * if Generic[...] is present, then it should list all type variables
+      that appear in other bases.
+    In addition, type of all generic bases is erased, e.g., C[int] is
+    stripped to plain C.
+    """
 
     def __new__(cls, name, bases, namespace,
                 tvars=None, args=None, origin=None, extra=None, orig_bases=None):
+        """Create a new generic class. GenericMeta.__new__ accepts
+        keyword arguments that are used for internal bookkeeping, therefore
+        an override should pass unused keyword arguments to super().
+        """
         if tvars is not None:
             # Called from __getitem__() below.
             assert origin is not None
@@ -1906,7 +1937,9 @@
     msg = "NamedTuple('Name', [(f0, t0), (f1, t1), ...]); each t must be a type"
     types = [(n, _type_check(t, msg)) for n, t in types]
     nm_tpl = collections.namedtuple(name, [n for n, t in types])
-    nm_tpl._field_types = dict(types)
+    # Prior to PEP 526, only _field_types attribute was assigned.
+    # Now, both __annotations__ and _field_types are used to maintain compatibility.
+    nm_tpl.__annotations__ = nm_tpl._field_types = collections.OrderedDict(types)
     try:
         nm_tpl.__module__ = sys._getframe(2).f_globals.get('__name__', '__main__')
     except (AttributeError, ValueError):
@@ -1941,8 +1974,10 @@
 
         Employee = collections.namedtuple('Employee', ['name', 'id'])
 
-    The resulting class has one extra attribute: _field_types,
-    giving a dict mapping field names to types.  (The field names
+    The resulting class has extra __annotations__ and _field_types
+    attributes, giving an ordered dict mapping field names to types.
+    __annotations__ should be preferred, while _field_types
+    is kept to maintain pre PEP 526 compatibility. (The field names
     are in the _fields attribute, which is part of the namedtuple
     API.) Alternative equivalent keyword syntax is also accepted::
 

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


More information about the Python-checkins mailing list