[Python-checkins] [bpo-28556] Minor fixes for typing module (#4710)

Ned Deily webhook-mailer at python.org
Mon Dec 4 21:44:00 EST 2017


https://github.com/python/cpython/commit/29bc19321018ec6e58f9f4da9c18c42e9a9c580e
commit: 29bc19321018ec6e58f9f4da9c18c42e9a9c580e
branch: master
author: Ivan Levkivskyi <levkivskyi at gmail.com>
committer: Ned Deily <nad at python.org>
date: 2017-12-04T21:43:58-05:00
summary:

[bpo-28556] Minor fixes for typing module (#4710)

files:
A Misc/NEWS.d/next/Library/2017-12-05-02-03-07.bpo-28556.9Z_PsJ.rst
M Lib/test/test_typing.py
M Lib/typing.py

diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index a3b6eb93393..4ae1ebf05f3 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -37,6 +37,9 @@
     from test import mod_generics_cache
 
 
+PY36 = sys.version_info[:2] >= (3, 6)
+
+
 class BaseTestCase(TestCase):
 
     def assertIsSubclass(self, cls, class_or_tuple, msg=None):
@@ -633,6 +636,27 @@ def test_init(self):
         with self.assertRaises(TypeError):
             Generic[T, S, T]
 
+    @skipUnless(PY36, "__init_subclass__ support required")
+    def test_init_subclass(self):
+        class X(typing.Generic[T]):
+            def __init_subclass__(cls, **kwargs):
+                super().__init_subclass__(**kwargs)
+                cls.attr = 42
+        class Y(X):
+            pass
+        self.assertEqual(Y.attr, 42)
+        with self.assertRaises(AttributeError):
+            X.attr
+        X.attr = 1
+        Y.attr = 2
+        class Z(Y):
+            pass
+        class W(X[int]):
+            pass
+        self.assertEqual(Y.attr, 2)
+        self.assertEqual(Z.attr, 42)
+        self.assertEqual(W.attr, 42)
+
     def test_repr(self):
         self.assertEqual(repr(SimpleMapping),
                          __name__ + '.' + 'SimpleMapping')
@@ -1080,6 +1104,30 @@ class Node(Generic[T]): ...
                 self.assertTrue(t is copy(t))
                 self.assertTrue(t is deepcopy(t))
 
+    def test_copy_generic_instances(self):
+        T = TypeVar('T')
+        class C(Generic[T]):
+            def __init__(self, attr: T) -> None:
+                self.attr = attr
+
+        c = C(42)
+        self.assertEqual(copy(c).attr, 42)
+        self.assertEqual(deepcopy(c).attr, 42)
+        self.assertIsNot(copy(c), c)
+        self.assertIsNot(deepcopy(c), c)
+        c.attr = 1
+        self.assertEqual(copy(c).attr, 1)
+        self.assertEqual(deepcopy(c).attr, 1)
+        ci = C[int](42)
+        self.assertEqual(copy(ci).attr, 42)
+        self.assertEqual(deepcopy(ci).attr, 42)
+        self.assertIsNot(copy(ci), ci)
+        self.assertIsNot(deepcopy(ci), ci)
+        ci.attr = 1
+        self.assertEqual(copy(ci).attr, 1)
+        self.assertEqual(deepcopy(ci).attr, 1)
+        self.assertEqual(ci.__orig_class__, C[int])
+
     def test_weakref_all(self):
         T = TypeVar('T')
         things = [Any, Union[T, int], Callable[..., T], Tuple[Any, Any],
@@ -1580,8 +1628,6 @@ class ACM:
     asyncio = None
     AwaitableWrapper = AsyncIteratorWrapper = ACM = object
 
-PY36 = sys.version_info[:2] >= (3, 6)
-
 PY36_TESTS = """
 from test import ann_module, ann_module2, ann_module3
 from typing import AsyncContextManager
diff --git a/Lib/typing.py b/Lib/typing.py
index c00a3a10e1f..b5564cc29a2 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -973,7 +973,8 @@ def __new__(cls, name, bases, namespace,
         # 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)
-        namespace.update({'__origin__': origin, '__extra__': extra})
+        namespace.update({'__origin__': origin, '__extra__': extra,
+                          '_gorg': None if not origin else origin._gorg})
         self = super().__new__(cls, name, bases, namespace, _root=True)
         super(GenericMeta, self).__setattr__('_gorg',
                                              self if not origin else origin._gorg)
@@ -1160,17 +1161,12 @@ def __instancecheck__(self, instance):
         # classes are supposed to be rare anyways.
         return issubclass(instance.__class__, self)
 
-    def __copy__(self):
-        return self.__class__(self.__name__, self.__bases__,
-                              _no_slots_copy(self.__dict__),
-                              self.__parameters__, self.__args__, self.__origin__,
-                              self.__extra__, self.__orig_bases__)
-
     def __setattr__(self, attr, value):
         # We consider all the subscripted generics as proxies for original class
         if (
             attr.startswith('__') and attr.endswith('__') or
-            attr.startswith('_abc_')
+            attr.startswith('_abc_') or
+            self._gorg is None  # The class is not fully created, see #typing/506
         ):
             super(GenericMeta, self).__setattr__(attr, value)
         else:
diff --git a/Misc/NEWS.d/next/Library/2017-12-05-02-03-07.bpo-28556.9Z_PsJ.rst b/Misc/NEWS.d/next/Library/2017-12-05-02-03-07.bpo-28556.9Z_PsJ.rst
new file mode 100644
index 00000000000..8f3a8954970
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2017-12-05-02-03-07.bpo-28556.9Z_PsJ.rst
@@ -0,0 +1,3 @@
+Two minor fixes for ``typing`` module: allow shallow copying instances of
+generic classes, improve interaction of ``__init_subclass__`` with generics.
+Original PRs by Ivan Levkivskyi.



More information about the Python-checkins mailing list