Skip to content

Commit daf82f7

Browse files
Issue python#5322: Fixed setting __new__ to a PyCFunction inside Python code.
Original patch by Andreas Stührk.
1 parent 9bd44d6 commit daf82f7

File tree

3 files changed

+127
-1
lines changed

3 files changed

+127
-1
lines changed

Lib/test/test_descr.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import sys
44
import types
55
import unittest
6+
import warnings
67
import weakref
78

89
from copy import deepcopy
@@ -1550,6 +1551,84 @@ class D(C):
15501551
self.assertEqual(b.foo, 3)
15511552
self.assertEqual(b.__class__, D)
15521553

1554+
def test_bad_new(self):
1555+
self.assertRaises(TypeError, object.__new__)
1556+
self.assertRaises(TypeError, object.__new__, '')
1557+
self.assertRaises(TypeError, list.__new__, object)
1558+
self.assertRaises(TypeError, object.__new__, list)
1559+
class C(object):
1560+
__new__ = list.__new__
1561+
self.assertRaises(TypeError, C)
1562+
class C(list):
1563+
__new__ = object.__new__
1564+
self.assertRaises(TypeError, C)
1565+
1566+
def test_object_new(self):
1567+
class A(object):
1568+
pass
1569+
object.__new__(A)
1570+
self.assertRaises(TypeError, object.__new__, A, 5)
1571+
object.__init__(A())
1572+
self.assertRaises(TypeError, object.__init__, A(), 5)
1573+
1574+
class A(object):
1575+
def __init__(self, foo):
1576+
self.foo = foo
1577+
object.__new__(A)
1578+
object.__new__(A, 5)
1579+
object.__init__(A(3))
1580+
self.assertRaises(TypeError, object.__init__, A(3), 5)
1581+
1582+
class A(object):
1583+
def __new__(cls, foo):
1584+
return object.__new__(cls)
1585+
object.__new__(A)
1586+
self.assertRaises(TypeError, object.__new__, A, 5)
1587+
object.__init__(A(3))
1588+
object.__init__(A(3), 5)
1589+
1590+
class A(object):
1591+
def __new__(cls, foo):
1592+
return object.__new__(cls)
1593+
def __init__(self, foo):
1594+
self.foo = foo
1595+
object.__new__(A)
1596+
with warnings.catch_warnings(record=True) as w:
1597+
warnings.simplefilter('always', DeprecationWarning)
1598+
a = object.__new__(A, 5)
1599+
self.assertEqual(type(a), A)
1600+
self.assertEqual(len(w), 1)
1601+
object.__init__(A(3))
1602+
a = A(3)
1603+
with warnings.catch_warnings(record=True) as w:
1604+
warnings.simplefilter('always', DeprecationWarning)
1605+
object.__init__(a, 5)
1606+
self.assertEqual(a.foo, 3)
1607+
self.assertEqual(len(w), 1)
1608+
1609+
def test_restored_object_new(self):
1610+
class A(object):
1611+
def __new__(cls, *args, **kwargs):
1612+
raise AssertionError
1613+
self.assertRaises(AssertionError, A)
1614+
class B(A):
1615+
__new__ = object.__new__
1616+
def __init__(self, foo):
1617+
self.foo = foo
1618+
with warnings.catch_warnings():
1619+
warnings.simplefilter('error', DeprecationWarning)
1620+
b = B(3)
1621+
self.assertEqual(b.foo, 3)
1622+
self.assertEqual(b.__class__, B)
1623+
del B.__new__
1624+
self.assertRaises(AssertionError, B)
1625+
del A.__new__
1626+
with warnings.catch_warnings():
1627+
warnings.simplefilter('error', DeprecationWarning)
1628+
b = B(3)
1629+
self.assertEqual(b.foo, 3)
1630+
self.assertEqual(b.__class__, B)
1631+
15531632
def test_altmro(self):
15541633
# Testing mro() and overriding it...
15551634
class A(object):
@@ -3756,6 +3835,24 @@ def __init__(self, arg):
37563835
self.assertEqual(isinstance(d, D), True)
37573836
self.assertEqual(d.foo, 1)
37583837

3838+
class C(object):
3839+
@staticmethod
3840+
def __new__(*args):
3841+
return args
3842+
self.assertEqual(C(1, 2), (C, 1, 2))
3843+
class D(C):
3844+
pass
3845+
self.assertEqual(D(1, 2), (D, 1, 2))
3846+
3847+
class C(object):
3848+
@classmethod
3849+
def __new__(*args):
3850+
return args
3851+
self.assertEqual(C(1, 2), (C, C, 1, 2))
3852+
class D(C):
3853+
pass
3854+
self.assertEqual(D(1, 2), (D, D, 1, 2))
3855+
37593856
def test_imul_bug(self):
37603857
# Testing for __imul__ problems...
37613858
# SF bug 544647

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ What's New in Python 2.7.13?
1010
Core and Builtins
1111
-----------------
1212

13+
- Issue #5322: Fixed setting __new__ to a PyCFunction inside Python code.
14+
Original patch by Andreas Stührk.
15+
1316
- Issue #28847: dubmdbm no longer writes the index file in when it is not
1417
changed and supports reading read-only files.
1518

Objects/typeobject.c

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6304,7 +6304,33 @@ update_one_slot(PyTypeObject *type, slotdef *p)
63046304
sanity checks and constructing a new argument
63056305
list. Cut all that nonsense short -- this speeds
63066306
up instance creation tremendously. */
6307-
specific = (void *)type->tp_new;
6307+
PyObject *self = PyCFunction_GET_SELF(descr);
6308+
if (!self || !PyType_Check(self)) {
6309+
/* This should never happen because
6310+
tp_new_wrapper expects a type for self.
6311+
Use slot_tp_new which will call
6312+
tp_new_wrapper which will raise an
6313+
exception. */
6314+
specific = (void *)slot_tp_new;
6315+
}
6316+
else {
6317+
specific = ((PyTypeObject *)self)->tp_new;
6318+
/* Check that the user does not do anything
6319+
silly and unsafe like object.__new__(dict).
6320+
To do this, we check that the most derived
6321+
base that's not a heap type is this type. */
6322+
PyTypeObject *staticbase = type->tp_base;
6323+
while (staticbase &&
6324+
(staticbase->tp_flags & Py_TPFLAGS_HEAPTYPE))
6325+
staticbase = staticbase->tp_base;
6326+
if (staticbase &&
6327+
staticbase->tp_new != specific)
6328+
/* Seems to be unsafe, better use
6329+
slot_tp_new which will call
6330+
tp_new_wrapper which will raise an
6331+
exception if it is unsafe. */
6332+
specific = (void *)slot_tp_new;
6333+
}
63086334
/* XXX I'm not 100% sure that there isn't a hole
63096335
in this reasoning that requires additional
63106336
sanity checks. I'll buy the first person to

0 commit comments

Comments
 (0)