From 29bc19321018ec6e58f9f4da9c18c42e9a9c580e Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Tue, 5 Dec 2017 03:43:58 +0100 Subject: [bpo-28556] Minor fixes for typing module (#4710) --- Lib/test/test_typing.py | 50 +++++++++++++++++++++- Lib/typing.py | 12 ++---- .../2017-12-05-02-03-07.bpo-28556.9Z_PsJ.rst | 3 ++ 3 files changed, 55 insertions(+), 10 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2017-12-05-02-03-07.bpo-28556.9Z_PsJ.rst diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index a3b6eb9..4ae1ebf 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -37,6 +37,9 @@ except ImportError: 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 @@ class GenericTests(BaseTestCase): 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 GenericTests(BaseTestCase): 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 @@ else: 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 c00a3a1..b5564cc 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -973,7 +973,8 @@ class GenericMeta(TypingMeta, abc.ABCMeta): # 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 @@ class GenericMeta(TypingMeta, abc.ABCMeta): # 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 0000000..8f3a895 --- /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. -- cgit v0.12