Skip to content

Commit 5adfac2

Browse files
Issue python#5322: Fixed setting __new__ to a PyCFunction inside Python code.
Original patch by Andreas Stührk.
1 parent 028ace1 commit 5adfac2

3 files changed

Lines changed: 119 additions & 1 deletion

File tree

Lib/test/test_descr.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import sys
88
import types
99
import unittest
10+
import warnings
1011
import weakref
1112

1213
from copy import deepcopy
@@ -1661,6 +1662,75 @@ class D(C):
16611662
self.assertEqual(b.foo, 3)
16621663
self.assertEqual(b.__class__, D)
16631664

1665+
def test_bad_new(self):
1666+
self.assertRaises(TypeError, object.__new__)
1667+
self.assertRaises(TypeError, object.__new__, '')
1668+
self.assertRaises(TypeError, list.__new__, object)
1669+
self.assertRaises(TypeError, object.__new__, list)
1670+
class C(object):
1671+
__new__ = list.__new__
1672+
self.assertRaises(TypeError, C)
1673+
class C(list):
1674+
__new__ = object.__new__
1675+
self.assertRaises(TypeError, C)
1676+
1677+
def test_object_new(self):
1678+
class A(object):
1679+
pass
1680+
object.__new__(A)
1681+
self.assertRaises(TypeError, object.__new__, A, 5)
1682+
object.__init__(A())
1683+
self.assertRaises(TypeError, object.__init__, A(), 5)
1684+
1685+
class A(object):
1686+
def __init__(self, foo):
1687+
self.foo = foo
1688+
object.__new__(A)
1689+
object.__new__(A, 5)
1690+
object.__init__(A(3))
1691+
self.assertRaises(TypeError, object.__init__, A(3), 5)
1692+
1693+
class A(object):
1694+
def __new__(cls, foo):
1695+
return object.__new__(cls)
1696+
object.__new__(A)
1697+
self.assertRaises(TypeError, object.__new__, A, 5)
1698+
object.__init__(A(3))
1699+
object.__init__(A(3), 5)
1700+
1701+
class A(object):
1702+
def __new__(cls, foo):
1703+
return object.__new__(cls)
1704+
def __init__(self, foo):
1705+
self.foo = foo
1706+
object.__new__(A)
1707+
self.assertRaises(TypeError, object.__new__, A, 5)
1708+
object.__init__(A(3))
1709+
self.assertRaises(TypeError, object.__init__, A(3), 5)
1710+
1711+
def test_restored_object_new(self):
1712+
class A(object):
1713+
def __new__(cls, *args, **kwargs):
1714+
raise AssertionError
1715+
self.assertRaises(AssertionError, A)
1716+
class B(A):
1717+
__new__ = object.__new__
1718+
def __init__(self, foo):
1719+
self.foo = foo
1720+
with warnings.catch_warnings():
1721+
warnings.simplefilter('error', DeprecationWarning)
1722+
b = B(3)
1723+
self.assertEqual(b.foo, 3)
1724+
self.assertEqual(b.__class__, B)
1725+
del B.__new__
1726+
self.assertRaises(AssertionError, B)
1727+
del A.__new__
1728+
with warnings.catch_warnings():
1729+
warnings.simplefilter('error', DeprecationWarning)
1730+
b = B(3)
1731+
self.assertEqual(b.foo, 3)
1732+
self.assertEqual(b.__class__, B)
1733+
16641734
def test_altmro(self):
16651735
# Testing mro() and overriding it...
16661736
class A(object):
@@ -3522,6 +3592,24 @@ def __init__(self, arg):
35223592
self.assertIsInstance(d, D)
35233593
self.assertEqual(d.foo, 1)
35243594

3595+
class C(object):
3596+
@staticmethod
3597+
def __new__(*args):
3598+
return args
3599+
self.assertEqual(C(1, 2), (C, 1, 2))
3600+
class D(C):
3601+
pass
3602+
self.assertEqual(D(1, 2), (D, 1, 2))
3603+
3604+
class C(object):
3605+
@classmethod
3606+
def __new__(*args):
3607+
return args
3608+
self.assertEqual(C(1, 2), (C, C, 1, 2))
3609+
class D(C):
3610+
pass
3611+
self.assertEqual(D(1, 2), (D, D, 1, 2))
3612+
35253613
def test_imul_bug(self):
35263614
# Testing for __imul__ problems...
35273615
# SF bug 544647

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ Release date: TBA
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 #28648: Fixed crash in Py_DecodeLocale() in debug build on Mac OS X
1417
when decode astral characters. Patch by Xiang Zhang.
1518

Objects/typeobject.c

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6798,7 +6798,34 @@ update_one_slot(PyTypeObject *type, slotdef *p)
67986798
sanity checks and constructing a new argument
67996799
list. Cut all that nonsense short -- this speeds
68006800
up instance creation tremendously. */
6801-
specific = (void *)type->tp_new;
6801+
PyObject *self = PyCFunction_GET_SELF(descr);
6802+
if (!self || !PyType_Check(self)) {
6803+
/* This should never happen because
6804+
tp_new_wrapper expects a type for self.
6805+
Use slot_tp_new which will call
6806+
tp_new_wrapper which will raise an
6807+
exception. */
6808+
specific = (void *)slot_tp_new;
6809+
}
6810+
else {
6811+
PyTypeObject *staticbase;
6812+
specific = ((PyTypeObject *)self)->tp_new;
6813+
/* Check that the user does not do anything
6814+
silly and unsafe like object.__new__(dict).
6815+
To do this, we check that the most derived
6816+
base that's not a heap type is this type. */
6817+
staticbase = type->tp_base;
6818+
while (staticbase &&
6819+
(staticbase->tp_flags & Py_TPFLAGS_HEAPTYPE))
6820+
staticbase = staticbase->tp_base;
6821+
if (staticbase &&
6822+
staticbase->tp_new != specific)
6823+
/* Seems to be unsafe, better use
6824+
slot_tp_new which will call
6825+
tp_new_wrapper which will raise an
6826+
exception if it is unsafe. */
6827+
specific = (void *)slot_tp_new;
6828+
}
68026829
/* XXX I'm not 100% sure that there isn't a hole
68036830
in this reasoning that requires additional
68046831
sanity checks. I'll buy the first person to

0 commit comments

Comments
 (0)