[pypy-commit] pypy default: (antocuni, arigo) Issue #2666
arigo
pypy.commits at gmail.com
Fri Sep 29 09:23:38 EDT 2017
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r92503:3f4fc7771154
Date: 2017-09-29 15:23 +0200
http://bitbucket.org/pypy/pypy/changeset/3f4fc7771154/
Log: (antocuni, arigo) Issue #2666
Mess with cpyext. See comments.
diff --git a/pypy/module/cpyext/test/test_typeobject.py b/pypy/module/cpyext/test/test_typeobject.py
--- a/pypy/module/cpyext/test/test_typeobject.py
+++ b/pypy/module/cpyext/test/test_typeobject.py
@@ -1362,6 +1362,47 @@
assert Asize == Bsize
assert Asize > basesize
+ def test_multiple_inheritance_bug1(self):
+ module = self.import_extension('foo', [
+ ("get_type", "METH_NOARGS",
+ '''
+ Py_INCREF(&Foo_Type);
+ return (PyObject *)&Foo_Type;
+ '''
+ ), ("forty_two", "METH_O",
+ '''
+ return PyInt_FromLong(42);
+ '''
+ )], prologue='''
+ static PyTypeObject Foo_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "foo.foo",
+ };
+ static PyObject *dummy_new(PyTypeObject *t, PyObject *a,
+ PyObject *k)
+ {
+ abort(); /* never actually called in CPython */
+ }
+ ''', more_init = '''
+ Foo_Type.tp_base = (PyTypeObject *)PyExc_Exception;
+ Foo_Type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
+ Foo_Type.tp_new = dummy_new;
+ if (PyType_Ready(&Foo_Type) < 0) INITERROR;
+ ''')
+ Foo = module.get_type()
+ class A(Foo, SyntaxError):
+ pass
+ assert A.__base__ is SyntaxError
+ A(42) # assert is not aborting
+
+ class Bar(Exception):
+ __new__ = module.forty_two
+
+ class B(Bar, SyntaxError):
+ pass
+
+ assert B() == 42
+
class AppTestHashable(AppTestCpythonExtensionBase):
def test_unhashable(self):
diff --git a/pypy/module/cpyext/typeobject.py b/pypy/module/cpyext/typeobject.py
--- a/pypy/module/cpyext/typeobject.py
+++ b/pypy/module/cpyext/typeobject.py
@@ -394,6 +394,9 @@
ptr = get_new_method_def(space)
ptr.c_ml_meth = rffi.cast(PyCFunction, llslot(space, tp_new_wrapper))
+def is_tp_new_wrapper(space, ml):
+ return ml.c_ml_meth == rffi.cast(PyCFunction, llslot(space, tp_new_wrapper))
+
def add_tp_new_wrapper(space, dict_w, pto):
if "__new__" in dict_w:
return
diff --git a/pypy/objspace/std/typeobject.py b/pypy/objspace/std/typeobject.py
--- a/pypy/objspace/std/typeobject.py
+++ b/pypy/objspace/std/typeobject.py
@@ -626,6 +626,12 @@
if w_newdescr is None: # see test_crash_mro_without_object_1
raise oefmt(space.w_TypeError, "cannot create '%N' instances",
self)
+ #
+ # issue #2666
+ if space.config.objspace.usemodules.cpyext:
+ w_newtype, w_newdescr = self.hack_which_new_to_call(
+ w_newtype, w_newdescr)
+ #
w_newfunc = space.get(w_newdescr, self)
if (space.config.objspace.std.newshortcut and
not we_are_jitted() and
@@ -646,6 +652,30 @@
"__init__() should return None")
return w_newobject
+ def hack_which_new_to_call(self, w_newtype, w_newdescr):
+ # issue #2666: for cpyext, we need to hack in order to reproduce
+ # an "optimization" of CPython that actually changes behaviour
+ # in corner cases.
+ #
+ # * Normally, we use the __new__ found in the MRO in the normal way.
+ #
+ # * If by chance this __new__ happens to be implemented as a C
+ # function, then instead, we discard it and use directly
+ # self.__base__.tp_new.
+ #
+ # * Most of the time this is the same (and faster for CPython), but
+ # it can fail if self.__base__ happens not to be the first base.
+ #
+ from pypy.module.cpyext.methodobject import W_PyCFunctionObject
+ from pypy.module.cpyext.typeobject import is_tp_new_wrapper
+
+ if (isinstance(w_newdescr, W_PyCFunctionObject) and
+ is_tp_new_wrapper(self.space, w_newdescr.ml)):
+ w_bestbase = find_best_base(self.bases_w)
+ return w_bestbase.lookup_where('__new__')
+ else:
+ return w_newtype, w_newdescr
+
def descr_repr(self, space):
w_mod = self.get_module()
if w_mod is None or not space.isinstance_w(w_mod, space.w_text):
More information about the pypy-commit
mailing list