From f5b9c8112b31714daad31ba0ceac83c4e1454b2e Mon Sep 17 00:00:00 2001 From: Chad Dombrova Date: Sun, 10 Dec 2017 09:21:50 -0500 Subject: [PATCH 1/2] Pass *args and **kwargs to superclass in Generic.__new__ In cases with multiple inheritance, Generic's super may not be object, and so its __new__ requires the same args as __init__. --- python2/test_typing.py | 48 ++++++++++++++++++++++++++++++++++++++++++ python2/typing.py | 10 +++++++-- src/test_typing.py | 47 +++++++++++++++++++++++++++++++++++++++++ src/typing.py | 10 +++++++-- 4 files changed, 111 insertions(+), 4 deletions(-) diff --git a/python2/test_typing.py b/python2/test_typing.py index 829cd9de1..c1f7a8e59 100644 --- a/python2/test_typing.py +++ b/python2/test_typing.py @@ -1229,6 +1229,54 @@ class D(C): with self.assertRaises(Exception): D[T] + def test_new_with_args(self): + + class A(Generic[T]): + pass + + class B(object): + def __new__(cls, arg): + # call object.__new__ + obj = super(B, cls).__new__(cls) + obj.arg = arg + return obj + + # mro: C, A, Generic, B, object + class C(A, B): + pass + + c = C('foo') + self.assertEqual(c.arg, 'foo') + + def test_new_no_args(self): + + class A(Generic[T]): + pass + + class B(object): + def __new__(cls): + # call object.__new__ + obj = super(B, cls).__new__(cls) + obj.from_b = 'b' + return obj + + # mro: C, A, Generic, B, object + class C(A, B): + def __init__(self, arg): + self.arg = arg + + def __new__(cls, arg): + # call A.__new__ + obj = super(C, cls).__new__(cls) + obj.from_c = 'c' + return obj + + c = C('foo') + self.assertEqual(c.arg, 'foo') + self.assertEqual(c.from_b, 'b') + self.assertEqual(c.from_c, 'c') + + class ClassVarTests(BaseTestCase): diff --git a/python2/typing.py b/python2/typing.py index 693945365..47baaf386 100644 --- a/python2/typing.py +++ b/python2/typing.py @@ -1287,10 +1287,16 @@ def _generic_new(base_cls, cls, *args, **kwds): # Assure type is erased on instantiation, # but attempt to store it in __orig_class__ if cls.__origin__ is None: - return base_cls.__new__(cls) + if base_cls is object: + return base_cls.__new__(cls) + else: + return base_cls.__new__(cls, *args, **kwds) else: origin = cls._gorg - obj = base_cls.__new__(origin) + if base_cls is object: + obj = base_cls.__new__(origin) + else: + obj = base_cls.__new__(origin, *args, **kwds) try: obj.__orig_class__ = cls except AttributeError: diff --git a/src/test_typing.py b/src/test_typing.py index a2c961171..8eb51d3eb 100644 --- a/src/test_typing.py +++ b/src/test_typing.py @@ -1310,6 +1310,53 @@ class D(C): with self.assertRaises(Exception): D[T] + def test_new_with_args(self): + + class A(Generic[T]): + pass + + class B: + def __new__(cls, arg): + # call object.__new__ + obj = super().__new__(cls) + obj.arg = arg + return obj + + # mro: C, A, Generic, B, object + class C(A, B): + pass + + c = C('foo') + self.assertEqual(c.arg, 'foo') + + def test_new_no_args(self): + + class A(Generic[T]): + pass + + class B: + def __new__(cls): + # call object.__new__ + obj = super().__new__(cls) + obj.from_b = 'b' + return obj + + # mro: C, A, Generic, B, object + class C(A, B): + def __init__(self, arg): + self.arg = arg + + def __new__(cls, arg): + # call A.__new__ + obj = super().__new__(cls) + obj.from_c = 'c' + return obj + + c = C('foo') + self.assertEqual(c.arg, 'foo') + self.assertEqual(c.from_b, 'b') + self.assertEqual(c.from_c, 'c') + class ClassVarTests(BaseTestCase): diff --git a/src/typing.py b/src/typing.py index b5564cc29..b073b3374 100644 --- a/src/typing.py +++ b/src/typing.py @@ -1181,10 +1181,16 @@ def _generic_new(base_cls, cls, *args, **kwds): # Assure type is erased on instantiation, # but attempt to store it in __orig_class__ if cls.__origin__ is None: - return base_cls.__new__(cls) + if base_cls is object: + return base_cls.__new__(cls) + else: + return base_cls.__new__(cls, *args, **kwds) else: origin = cls._gorg - obj = base_cls.__new__(origin) + if base_cls is object: + obj = base_cls.__new__(origin) + else: + obj = base_cls.__new__(origin, *args, **kwds) try: obj.__orig_class__ = cls except AttributeError: From 23674e6908367ff29f9971618efc618b70a23335 Mon Sep 17 00:00:00 2001 From: Chad Dombrova Date: Thu, 14 Dec 2017 19:05:36 -0800 Subject: [PATCH 2/2] Protect against an edge case with multiple parent classes with __init__ --- python2/test_typing.py | 23 +++++++++++++++++++++-- python2/typing.py | 4 ++-- src/test_typing.py | 25 ++++++++++++++++++++++--- src/typing.py | 4 ++-- 4 files changed, 47 insertions(+), 9 deletions(-) diff --git a/python2/test_typing.py b/python2/test_typing.py index c1f7a8e59..8fdfaf9db 100644 --- a/python2/test_typing.py +++ b/python2/test_typing.py @@ -1248,6 +1248,25 @@ class C(A, B): c = C('foo') self.assertEqual(c.arg, 'foo') + def test_new_with_args2(self): + + class A(object): + def __init__(self, arg): + self.from_a = arg + # call object + super(A, self).__init__() + + # mro: C, Generic, A, object + class C(Generic[T], A): + def __init__(self, arg): + self.from_c = arg + # call Generic + super(C, self).__init__(arg) + + c = C('foo') + self.assertEqual(c.from_a, 'foo') + self.assertEqual(c.from_c, 'foo') + def test_new_no_args(self): class A(Generic[T]): @@ -1255,7 +1274,7 @@ class A(Generic[T]): class B(object): def __new__(cls): - # call object.__new__ + # call object obj = super(B, cls).__new__(cls) obj.from_b = 'b' return obj @@ -1266,7 +1285,7 @@ def __init__(self, arg): self.arg = arg def __new__(cls, arg): - # call A.__new__ + # call A obj = super(C, cls).__new__(cls) obj.from_c = 'c' return obj diff --git a/python2/typing.py b/python2/typing.py index 47baaf386..fea387c37 100644 --- a/python2/typing.py +++ b/python2/typing.py @@ -1287,13 +1287,13 @@ def _generic_new(base_cls, cls, *args, **kwds): # Assure type is erased on instantiation, # but attempt to store it in __orig_class__ if cls.__origin__ is None: - if base_cls is object: + if base_cls.__new__ is object.__new__: return base_cls.__new__(cls) else: return base_cls.__new__(cls, *args, **kwds) else: origin = cls._gorg - if base_cls is object: + if base_cls.__new__ is object.__new__: obj = base_cls.__new__(origin) else: obj = base_cls.__new__(origin, *args, **kwds) diff --git a/src/test_typing.py b/src/test_typing.py index 8eb51d3eb..1108950d1 100644 --- a/src/test_typing.py +++ b/src/test_typing.py @@ -1317,7 +1317,7 @@ class A(Generic[T]): class B: def __new__(cls, arg): - # call object.__new__ + # call object obj = super().__new__(cls) obj.arg = arg return obj @@ -1329,6 +1329,25 @@ class C(A, B): c = C('foo') self.assertEqual(c.arg, 'foo') + def test_new_with_args2(self): + + class A: + def __init__(self, arg): + self.from_a = arg + # call object + super().__init__() + + # mro: C, Generic, A, object + class C(Generic[T], A): + def __init__(self, arg): + self.from_c = arg + # call Generic + super().__init__(arg) + + c = C('foo') + self.assertEqual(c.from_a, 'foo') + self.assertEqual(c.from_c, 'foo') + def test_new_no_args(self): class A(Generic[T]): @@ -1336,7 +1355,7 @@ class A(Generic[T]): class B: def __new__(cls): - # call object.__new__ + # call object obj = super().__new__(cls) obj.from_b = 'b' return obj @@ -1347,7 +1366,7 @@ def __init__(self, arg): self.arg = arg def __new__(cls, arg): - # call A.__new__ + # call A obj = super().__new__(cls) obj.from_c = 'c' return obj diff --git a/src/typing.py b/src/typing.py index b073b3374..7827872bd 100644 --- a/src/typing.py +++ b/src/typing.py @@ -1181,13 +1181,13 @@ def _generic_new(base_cls, cls, *args, **kwds): # Assure type is erased on instantiation, # but attempt to store it in __orig_class__ if cls.__origin__ is None: - if base_cls is object: + if base_cls.__new__ is object.__new__: return base_cls.__new__(cls) else: return base_cls.__new__(cls, *args, **kwds) else: origin = cls._gorg - if base_cls is object: + if base_cls.__new__ is object.__new__: obj = base_cls.__new__(origin) else: obj = base_cls.__new__(origin, *args, **kwds)