From 2ad381f6951e866ec5f71bb5f0875def23b34987 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 24 Feb 2017 04:03:28 +0100 Subject: [PATCH 1/2] Update to typing: treat subscripted generics as proxies (#265) (cherry picked from commit abb3b8ad94d699c8560d94ee9bac9c917b382abe) --- Lib/test/test_typing.py | 27 +++++++++++++++++++++++++++ Lib/typing.py | 7 +++++++ 2 files changed, 34 insertions(+) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 64d8276658ee7f..3a82b595da5950 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -674,6 +674,33 @@ class C(B[int]): c.bar = 'abc' self.assertEqual(c.__dict__, {'bar': 'abc'}) + def test_subscripted_generics_as_proxies(self): + T = TypeVar('T') + class C(Generic[T]): + x = 'def' + self.assertEqual(C[int].x, 'def') + self.assertEqual(C[C[int]].x, 'def') + C[C[int]].x = 'changed' + self.assertEqual(C.x, 'changed') + self.assertEqual(C[str].x, 'changed') + C[List[str]].z = 'new' + self.assertEqual(C.z, 'new') + self.assertEqual(C[Tuple[int]].z, 'new') + + self.assertEqual(C().x, 'changed') + self.assertEqual(C[Tuple[str]]().z, 'new') + + class D(C[T]): + pass + self.assertEqual(D[int].x, 'changed') + self.assertEqual(D.z, 'new') + D.z = 'from derived z' + D[int].x = 'from derived x' + self.assertEqual(C.x, 'changed') + self.assertEqual(C[int].z, 'new') + self.assertEqual(D.x, 'from derived x') + self.assertEqual(D[str].z, 'from derived z') + def test_false_subclasses(self): class MyMapping(MutableMapping[str, str]): pass self.assertNotIsInstance({}, MyMapping) diff --git a/Lib/typing.py b/Lib/typing.py index efe358faf20988..fc2ed94cffd656 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1158,6 +1158,13 @@ def __copy__(self): self.__parameters__, self.__args__, self.__origin__, self.__extra__, self.__orig_bases__) + def __setattr__(self, attr, value): + # We consider all the subscripted genrics as proxies for original class + if attr.startswith('__') and attr.endswith('__'): + super(GenericMeta, self).__setattr__(attr, value) + else: + super(GenericMeta, _gorg(self)).__setattr__(attr, value) + # Prevent checks for Generic to crash when defining Generic. Generic = None From 8128d0dfee176e45b816e38b9950206517d2d829 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 24 Feb 2017 18:28:26 +0100 Subject: [PATCH 2/2] bpo-28556: Fix regression that sneaked into recent typing updates (GH-270) (cherry picked from commit 365cb5bb9069273e6970c9d5d17ee2fe5003e7ac) --- Lib/test/test_typing.py | 8 ++++++++ Lib/typing.py | 5 ++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 3a82b595da5950..f0070ec975791a 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -701,6 +701,14 @@ class D(C[T]): self.assertEqual(D.x, 'from derived x') self.assertEqual(D[str].z, 'from derived z') + def test_abc_registry_kept(self): + T = TypeVar('T') + class C(Generic[T]): ... + C.register(int) + self.assertIsInstance(1, C) + C[int] + self.assertIsInstance(1, C) + def test_false_subclasses(self): class MyMapping(MutableMapping[str, str]): pass self.assertNotIsInstance({}, MyMapping) diff --git a/Lib/typing.py b/Lib/typing.py index fc2ed94cffd656..9a0f49099a3114 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1160,7 +1160,10 @@ def __copy__(self): def __setattr__(self, attr, value): # We consider all the subscripted genrics as proxies for original class - if attr.startswith('__') and attr.endswith('__'): + if ( + attr.startswith('__') and attr.endswith('__') or + attr.startswith('_abc_') + ): super(GenericMeta, self).__setattr__(attr, value) else: super(GenericMeta, _gorg(self)).__setattr__(attr, value)