Author: Ronan Lamy <ronan.l...@gmail.com> Branch: py3.5 Changeset: r92505:a0fe3f649ba8 Date: 2017-09-29 17:14 +0200 http://bitbucket.org/pypy/pypy/changeset/a0fe3f649ba8/
Log: hg merge default diff --git a/pypy/module/cpyext/methodobject.py b/pypy/module/cpyext/methodobject.py --- a/pypy/module/cpyext/methodobject.py +++ b/pypy/module/cpyext/methodobject.py @@ -10,7 +10,8 @@ from pypy.module.cpyext.api import ( CONST_STRING, METH_CLASS, METH_COEXIST, METH_KEYWORDS, METH_NOARGS, METH_O, METH_STATIC, METH_VARARGS, PyObject, bootstrap_function, - cpython_api, generic_cpy_call, CANNOT_FAIL, slot_function, cts) + cpython_api, generic_cpy_call, CANNOT_FAIL, slot_function, cts, + build_type_checkers) from pypy.module.cpyext.pyobject import ( Py_DecRef, from_ref, make_ref, as_pyobj, make_typedescr) @@ -129,6 +130,10 @@ ret = self.call(space, w_instance, w_args, w_kw) return ret +# PyPy addition, for Cython +_, _ = build_type_checkers("MethodDescr", W_PyCMethodObject) + + @cpython_api([PyObject], rffi.INT_real, error=CANNOT_FAIL) def PyCFunction_Check(space, w_obj): from pypy.interpreter.function import BuiltinFunction @@ -155,6 +160,7 @@ (self.name.decode('utf-8'), self.w_objclass.getname(self.space))) + class W_PyCWrapperObject(W_Root): def __init__(self, space, pto, method_name, wrapper_func, wrapper_func_kwds, doc, func, offset=None): diff --git a/pypy/module/cpyext/test/foo.c b/pypy/module/cpyext/test/foo.c --- a/pypy/module/cpyext/test/foo.c +++ b/pypy/module/cpyext/test/foo.c @@ -83,19 +83,32 @@ return cls; } +// for CPython +#ifndef PyMethodDescr_Check +int PyMethodDescr_Check(PyObject* method) +{ + PyObject *meth = PyObject_GetAttrString((PyObject*)&PyList_Type, "append"); + if (!meth) return 0; + int res = PyObject_TypeCheck(method, meth->ob_type); + Py_DECREF(meth); + return res; +} +#endif + PyObject* make_classmethod(PyObject* method) { // adapted from __Pyx_Method_ClassMethod - if (PyObject_TypeCheck(method, &PyWrapperDescr_Type)) { - return PyClassMethod_New(method); + if (PyMethodDescr_Check(method)) { + PyMethodDescrObject *descr = (PyMethodDescrObject *)method; + PyTypeObject *d_type = descr->d_common.d_type; + return PyDescr_NewClassMethod(d_type, descr->d_method); } else if (PyMethod_Check(method)) { return PyClassMethod_New(PyMethod_GET_FUNCTION(method)); } else { - PyMethodDescrObject *descr = (PyMethodDescrObject *)method; - PyTypeObject *d_type = descr->d_common.d_type; - return PyDescr_NewClassMethod(d_type, descr->d_method); + PyErr_SetString(PyExc_TypeError, "unknown method kind"); + return NULL; } } @@ -828,6 +841,8 @@ fake_classmeth = PyDict_GetItemString((PyObject *)fooType.tp_dict, "fake_classmeth"); classmeth = make_classmethod(fake_classmeth); + if (classmeth == NULL) + INITERROR; if (PyDict_SetItemString((PyObject *)fooType.tp_dict, "fake_classmeth", classmeth) < 0) INITERROR; 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 @@ -1321,6 +1321,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 PyLong_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 @@ -395,6 +395,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 @@ -641,6 +641,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, space.w_None, w_type=self) if (space.config.objspace.std.newshortcut and not we_are_jitted() and @@ -661,6 +667,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): _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit