From 392c6d94bdfd4e436d55134bbcc5750f70219b7d Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 23 Feb 2017 00:26:05 +0100 Subject: [PATCH 1/3] Initial implementation (tests and PY2 port needed) --- src/typing.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/typing.py b/src/typing.py index efe358faf..e1bdaf477 100644 --- a/src/typing.py +++ b/src/typing.py @@ -1158,6 +1158,12 @@ def __copy__(self): self.__parameters__, self.__args__, self.__origin__, self.__extra__, self.__orig_bases__) + def __setattr__(self, attr, value): + if attr.startswith('__') and attr.endswith('__'): + type.__setattr__(self, attr, value) + else: + type.__setattr__(_gorg(self), attr, value) + # Prevent checks for Generic to crash when defining Generic. Generic = None From db133c1bfe9895eeec037c30fe72cbc75c190722 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 23 Feb 2017 14:29:34 +0100 Subject: [PATCH 2/3] Tests and PY2 backport --- python2/test_typing.py | 27 +++++++++++++++++++++++++++ python2/typing.py | 7 +++++++ src/test_typing.py | 27 +++++++++++++++++++++++++++ src/typing.py | 1 + 4 files changed, 62 insertions(+) diff --git a/python2/test_typing.py b/python2/test_typing.py index 0c8d1bbba..7f8c3a985 100644 --- a/python2/test_typing.py +++ b/python2/test_typing.py @@ -642,6 +642,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/python2/typing.py b/python2/typing.py index 5cbfd4164..d77b1b2a1 100644 --- a/python2/typing.py +++ b/python2/typing.py @@ -1243,6 +1243,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('__'): + type.__setattr__(self, attr, value) + else: + type.__setattr__(_gorg(self), attr, value) + # Prevent checks for Generic to crash when defining Generic. Generic = None diff --git a/src/test_typing.py b/src/test_typing.py index 64d827665..3a82b595d 100644 --- a/src/test_typing.py +++ b/src/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/src/typing.py b/src/typing.py index e1bdaf477..677b65ea7 100644 --- a/src/typing.py +++ b/src/typing.py @@ -1159,6 +1159,7 @@ def __copy__(self): 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('__'): type.__setattr__(self, attr, value) else: From dce0a273b0f7b076ab40a963b6d8736b35fb2de1 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 23 Feb 2017 19:56:19 +0100 Subject: [PATCH 3/3] Be more cooperative, use super() --- python2/typing.py | 4 ++-- src/typing.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/python2/typing.py b/python2/typing.py index d77b1b2a1..867fa5353 100644 --- a/python2/typing.py +++ b/python2/typing.py @@ -1246,9 +1246,9 @@ 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('__'): - type.__setattr__(self, attr, value) + super(GenericMeta, self).__setattr__(attr, value) else: - type.__setattr__(_gorg(self), attr, value) + super(GenericMeta, _gorg(self)).__setattr__(attr, value) # Prevent checks for Generic to crash when defining Generic. diff --git a/src/typing.py b/src/typing.py index 677b65ea7..fc2ed94cf 100644 --- a/src/typing.py +++ b/src/typing.py @@ -1161,9 +1161,9 @@ 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('__'): - type.__setattr__(self, attr, value) + super(GenericMeta, self).__setattr__(attr, value) else: - type.__setattr__(_gorg(self), attr, value) + super(GenericMeta, _gorg(self)).__setattr__(attr, value) # Prevent checks for Generic to crash when defining Generic.