Skip to content

Commit 7d7ffcd

Browse files
chadrikilevkivskyi
authored andcommitted
Pass *args and **kwargs to superclass in Generic.__new__ (python#517)
In cases with multiple inheritance, Generic's super may not be object, and so its __new__ requires the same args as __init__. Note that there's special treatment required for the case where the next class in the mro is object, otherwise every subclass of Generic that takes an argument would need to override __new__.
1 parent ae9f5ed commit 7d7ffcd

4 files changed

Lines changed: 149 additions & 4 deletions

File tree

python2/test_typing.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1229,6 +1229,73 @@ class D(C):
12291229
with self.assertRaises(Exception):
12301230
D[T]
12311231

1232+
def test_new_with_args(self):
1233+
1234+
class A(Generic[T]):
1235+
pass
1236+
1237+
class B(object):
1238+
def __new__(cls, arg):
1239+
# call object.__new__
1240+
obj = super(B, cls).__new__(cls)
1241+
obj.arg = arg
1242+
return obj
1243+
1244+
# mro: C, A, Generic, B, object
1245+
class C(A, B):
1246+
pass
1247+
1248+
c = C('foo')
1249+
self.assertEqual(c.arg, 'foo')
1250+
1251+
def test_new_with_args2(self):
1252+
1253+
class A(object):
1254+
def __init__(self, arg):
1255+
self.from_a = arg
1256+
# call object
1257+
super(A, self).__init__()
1258+
1259+
# mro: C, Generic, A, object
1260+
class C(Generic[T], A):
1261+
def __init__(self, arg):
1262+
self.from_c = arg
1263+
# call Generic
1264+
super(C, self).__init__(arg)
1265+
1266+
c = C('foo')
1267+
self.assertEqual(c.from_a, 'foo')
1268+
self.assertEqual(c.from_c, 'foo')
1269+
1270+
def test_new_no_args(self):
1271+
1272+
class A(Generic[T]):
1273+
pass
1274+
1275+
class B(object):
1276+
def __new__(cls):
1277+
# call object
1278+
obj = super(B, cls).__new__(cls)
1279+
obj.from_b = 'b'
1280+
return obj
1281+
1282+
# mro: C, A, Generic, B, object
1283+
class C(A, B):
1284+
def __init__(self, arg):
1285+
self.arg = arg
1286+
1287+
def __new__(cls, arg):
1288+
# call A
1289+
obj = super(C, cls).__new__(cls)
1290+
obj.from_c = 'c'
1291+
return obj
1292+
1293+
c = C('foo')
1294+
self.assertEqual(c.arg, 'foo')
1295+
self.assertEqual(c.from_b, 'b')
1296+
self.assertEqual(c.from_c, 'c')
1297+
1298+
12321299

12331300
class ClassVarTests(BaseTestCase):
12341301

python2/typing.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1287,10 +1287,16 @@ def _generic_new(base_cls, cls, *args, **kwds):
12871287
# Assure type is erased on instantiation,
12881288
# but attempt to store it in __orig_class__
12891289
if cls.__origin__ is None:
1290-
return base_cls.__new__(cls)
1290+
if base_cls.__new__ is object.__new__:
1291+
return base_cls.__new__(cls)
1292+
else:
1293+
return base_cls.__new__(cls, *args, **kwds)
12911294
else:
12921295
origin = cls._gorg
1293-
obj = base_cls.__new__(origin)
1296+
if base_cls.__new__ is object.__new__:
1297+
obj = base_cls.__new__(origin)
1298+
else:
1299+
obj = base_cls.__new__(origin, *args, **kwds)
12941300
try:
12951301
obj.__orig_class__ = cls
12961302
except AttributeError:

src/test_typing.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1310,6 +1310,72 @@ class D(C):
13101310
with self.assertRaises(Exception):
13111311
D[T]
13121312

1313+
def test_new_with_args(self):
1314+
1315+
class A(Generic[T]):
1316+
pass
1317+
1318+
class B:
1319+
def __new__(cls, arg):
1320+
# call object
1321+
obj = super().__new__(cls)
1322+
obj.arg = arg
1323+
return obj
1324+
1325+
# mro: C, A, Generic, B, object
1326+
class C(A, B):
1327+
pass
1328+
1329+
c = C('foo')
1330+
self.assertEqual(c.arg, 'foo')
1331+
1332+
def test_new_with_args2(self):
1333+
1334+
class A:
1335+
def __init__(self, arg):
1336+
self.from_a = arg
1337+
# call object
1338+
super().__init__()
1339+
1340+
# mro: C, Generic, A, object
1341+
class C(Generic[T], A):
1342+
def __init__(self, arg):
1343+
self.from_c = arg
1344+
# call Generic
1345+
super().__init__(arg)
1346+
1347+
c = C('foo')
1348+
self.assertEqual(c.from_a, 'foo')
1349+
self.assertEqual(c.from_c, 'foo')
1350+
1351+
def test_new_no_args(self):
1352+
1353+
class A(Generic[T]):
1354+
pass
1355+
1356+
class B:
1357+
def __new__(cls):
1358+
# call object
1359+
obj = super().__new__(cls)
1360+
obj.from_b = 'b'
1361+
return obj
1362+
1363+
# mro: C, A, Generic, B, object
1364+
class C(A, B):
1365+
def __init__(self, arg):
1366+
self.arg = arg
1367+
1368+
def __new__(cls, arg):
1369+
# call A
1370+
obj = super().__new__(cls)
1371+
obj.from_c = 'c'
1372+
return obj
1373+
1374+
c = C('foo')
1375+
self.assertEqual(c.arg, 'foo')
1376+
self.assertEqual(c.from_b, 'b')
1377+
self.assertEqual(c.from_c, 'c')
1378+
13131379

13141380
class ClassVarTests(BaseTestCase):
13151381

src/typing.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1181,10 +1181,16 @@ def _generic_new(base_cls, cls, *args, **kwds):
11811181
# Assure type is erased on instantiation,
11821182
# but attempt to store it in __orig_class__
11831183
if cls.__origin__ is None:
1184-
return base_cls.__new__(cls)
1184+
if base_cls.__new__ is object.__new__:
1185+
return base_cls.__new__(cls)
1186+
else:
1187+
return base_cls.__new__(cls, *args, **kwds)
11851188
else:
11861189
origin = cls._gorg
1187-
obj = base_cls.__new__(origin)
1190+
if base_cls.__new__ is object.__new__:
1191+
obj = base_cls.__new__(origin)
1192+
else:
1193+
obj = base_cls.__new__(origin, *args, **kwds)
11881194
try:
11891195
obj.__orig_class__ = cls
11901196
except AttributeError:

0 commit comments

Comments
 (0)