summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2016-12-07 09:26:49 (GMT)
committerSerhiy Storchaka <storchaka@gmail.com>2016-12-07 09:26:49 (GMT)
commit0c78634d785361dcde0c2c138e8bf5f9bb744d3d (patch)
tree3b4b16b83fd8b377fa0c61c21be9695890c21250
parent520348e5c0284668738e0ba98b86e7d1a5fd8600 (diff)
parent5adfac2c1b7fb4f0782d097e7e0e6c5614965634 (diff)
downloadcpython-0c78634d785361dcde0c2c138e8bf5f9bb744d3d.zip
cpython-0c78634d785361dcde0c2c138e8bf5f9bb744d3d.tar.gz
cpython-0c78634d785361dcde0c2c138e8bf5f9bb744d3d.tar.bz2
Issue #5322: Fixed setting __new__ to a PyCFunction inside Python code.
Original patch by Andreas Stührk.
-rw-r--r--Lib/test/test_descr.py88
-rw-r--r--Misc/NEWS3
-rw-r--r--Objects/typeobject.c29
3 files changed, 119 insertions, 1 deletions
diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py
index 1e08ed9..ebfcb77 100644
--- a/Lib/test/test_descr.py
+++ b/Lib/test/test_descr.py
@@ -7,6 +7,7 @@ import pickle
import sys
import types
import unittest
+import warnings
import weakref
from copy import deepcopy
@@ -1661,6 +1662,75 @@ order (MRO) for bases """
self.assertEqual(b.foo, 3)
self.assertEqual(b.__class__, D)
+ def test_bad_new(self):
+ self.assertRaises(TypeError, object.__new__)
+ self.assertRaises(TypeError, object.__new__, '')
+ self.assertRaises(TypeError, list.__new__, object)
+ self.assertRaises(TypeError, object.__new__, list)
+ class C(object):
+ __new__ = list.__new__
+ self.assertRaises(TypeError, C)
+ class C(list):
+ __new__ = object.__new__
+ self.assertRaises(TypeError, C)
+
+ def test_object_new(self):
+ class A(object):
+ pass
+ object.__new__(A)
+ self.assertRaises(TypeError, object.__new__, A, 5)
+ object.__init__(A())
+ self.assertRaises(TypeError, object.__init__, A(), 5)
+
+ class A(object):
+ def __init__(self, foo):
+ self.foo = foo
+ object.__new__(A)
+ object.__new__(A, 5)
+ object.__init__(A(3))
+ self.assertRaises(TypeError, object.__init__, A(3), 5)
+
+ class A(object):
+ def __new__(cls, foo):
+ return object.__new__(cls)
+ object.__new__(A)
+ self.assertRaises(TypeError, object.__new__, A, 5)
+ object.__init__(A(3))
+ object.__init__(A(3), 5)
+
+ class A(object):
+ def __new__(cls, foo):
+ return object.__new__(cls)
+ def __init__(self, foo):
+ self.foo = foo
+ object.__new__(A)
+ self.assertRaises(TypeError, object.__new__, A, 5)
+ object.__init__(A(3))
+ self.assertRaises(TypeError, object.__init__, A(3), 5)
+
+ def test_restored_object_new(self):
+ class A(object):
+ def __new__(cls, *args, **kwargs):
+ raise AssertionError
+ self.assertRaises(AssertionError, A)
+ class B(A):
+ __new__ = object.__new__
+ def __init__(self, foo):
+ self.foo = foo
+ with warnings.catch_warnings():
+ warnings.simplefilter('error', DeprecationWarning)
+ b = B(3)
+ self.assertEqual(b.foo, 3)
+ self.assertEqual(b.__class__, B)
+ del B.__new__
+ self.assertRaises(AssertionError, B)
+ del A.__new__
+ with warnings.catch_warnings():
+ warnings.simplefilter('error', DeprecationWarning)
+ b = B(3)
+ self.assertEqual(b.foo, 3)
+ self.assertEqual(b.__class__, B)
+
def test_altmro(self):
# Testing mro() and overriding it...
class A(object):
@@ -3522,6 +3592,24 @@ order (MRO) for bases """
self.assertIsInstance(d, D)
self.assertEqual(d.foo, 1)
+ class C(object):
+ @staticmethod
+ def __new__(*args):
+ return args
+ self.assertEqual(C(1, 2), (C, 1, 2))
+ class D(C):
+ pass
+ self.assertEqual(D(1, 2), (D, 1, 2))
+
+ class C(object):
+ @classmethod
+ def __new__(*args):
+ return args
+ self.assertEqual(C(1, 2), (C, C, 1, 2))
+ class D(C):
+ pass
+ self.assertEqual(D(1, 2), (D, D, 1, 2))
+
def test_imul_bug(self):
# Testing for __imul__ problems...
# SF bug 544647
diff --git a/Misc/NEWS b/Misc/NEWS
index 7277b2c..c625435 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,9 @@ What's New in Python 3.6.1 release candidate 1
Core and Builtins
-----------------
+- Issue #5322: Fixed setting __new__ to a PyCFunction inside Python code.
+ Original patch by Andreas Stührk.
+
Library
-------
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 329261b..96444ae 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -6878,7 +6878,34 @@ update_one_slot(PyTypeObject *type, slotdef *p)
sanity checks and constructing a new argument
list. Cut all that nonsense short -- this speeds
up instance creation tremendously. */
- specific = (void *)type->tp_new;
+ PyObject *self = PyCFunction_GET_SELF(descr);
+ if (!self || !PyType_Check(self)) {
+ /* This should never happen because
+ tp_new_wrapper expects a type for self.
+ Use slot_tp_new which will call
+ tp_new_wrapper which will raise an
+ exception. */
+ specific = (void *)slot_tp_new;
+ }
+ else {
+ PyTypeObject *staticbase;
+ specific = ((PyTypeObject *)self)->tp_new;
+ /* Check that the user does not do anything
+ silly and unsafe like object.__new__(dict).
+ To do this, we check that the most derived
+ base that's not a heap type is this type. */
+ staticbase = type->tp_base;
+ while (staticbase &&
+ (staticbase->tp_flags & Py_TPFLAGS_HEAPTYPE))
+ staticbase = staticbase->tp_base;
+ if (staticbase &&
+ staticbase->tp_new != specific)
+ /* Seems to be unsafe, better use
+ slot_tp_new which will call
+ tp_new_wrapper which will raise an
+ exception if it is unsafe. */
+ specific = (void *)slot_tp_new;
+ }
/* XXX I'm not 100% sure that there isn't a hole
in this reasoning that requires additional
sanity checks. I'll buy the first person to