[pypy-commit] pypy py3.5: hg merge default

rlamy pypy.commits at gmail.com
Fri Sep 29 11:15:01 EDT 2017


Author: Ronan Lamy <ronan.lamy at 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):


More information about the pypy-commit mailing list