[pypy-commit] pypy default: merge heads

arigo pypy.commits at gmail.com
Tue May 30 07:37:50 EDT 2017


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r91446:789fb549e0af
Date: 2017-05-30 13:37 +0200
http://bitbucket.org/pypy/pypy/changeset/789fb549e0af/

Log:	merge heads

diff --git a/pypy/doc/cpython_differences.rst b/pypy/doc/cpython_differences.rst
--- a/pypy/doc/cpython_differences.rst
+++ b/pypy/doc/cpython_differences.rst
@@ -357,6 +357,26 @@
 
 .. __: https://bitbucket.org/pypy/pypy/issue/1974/different-behaviour-for-collections-of
 
+C-API Differences
+-----------------
+
+The external C-API has been reimplemented in PyPy as an internal cpyext module.
+We support most of the documented C-API, but sometimes internal C-abstractions
+leak out on CPython and are abused, perhaps even unknowingly. For instance,
+assignment to a ``PyTupleObject`` is not supported after the tuple is
+used internally, even by another C-API function call. On CPython this will
+succeed as long as the refcount is 1.  On PyPy this will always raise a
+``SystemError('PyTuple_SetItem called on tuple after  use of tuple")``
+exception (explicitly listed here for search engines).
+
+Another similar problem is assignment of a new function pointer to any of the
+``tp_as_*`` structures after calling ``PyType_Ready``. For instance, overriding
+``tp_as_number.nb_int`` with a different function after calling ``PyType_Ready``
+on CPython will result in the old function being called for ``x.__int__()``
+(via class ``__dict__`` lookup) and the new function being called for ``int(x)``
+(via slot lookup). On PyPy we will always call the __new__ function, not the
+old, this quirky behaviour is unfortunately necessary to fully support NumPy.
+
 Performance Differences
 -------------------------
 
diff --git a/pypy/module/cpyext/test/test_tupleobject.py b/pypy/module/cpyext/test/test_tupleobject.py
--- a/pypy/module/cpyext/test/test_tupleobject.py
+++ b/pypy/module/cpyext/test/test_tupleobject.py
@@ -161,3 +161,47 @@
         assert list(a) == range(100, 400, 100)
         assert list(a) == range(100, 400, 100)
         assert list(a) == range(100, 400, 100)
+
+    def test_setitem(self):
+        module = self.import_extension('foo', [
+            ("set_after_use", "METH_O",
+             """
+                PyObject *t2, *tuple = PyTuple_New(1);
+                PyObject * one = PyLong_FromLong(1);
+                int res;
+                Py_INCREF(one);
+                res = PyTuple_SetItem(tuple, 0, one);
+                if (res != 0)
+                {
+                    Py_DECREF(tuple);
+                    return NULL;
+                }
+                Py_INCREF(args);
+                res = PyTuple_SetItem(tuple, 0, args);
+                if (res != 0)
+                {
+                    Py_DECREF(tuple);
+                    return NULL;
+                }
+                /* Do something that uses the tuple, but does not incref */
+                t2 = PyTuple_GetSlice(tuple, 0, 1);
+                Py_DECREF(t2);
+                Py_INCREF(one);
+                res = PyTuple_SetItem(tuple, 0, one);
+                Py_DECREF(tuple);
+                if (res != 0)
+                {
+                    Py_DECREF(one);
+                    return NULL;
+                }
+                Py_INCREF(Py_None);
+                return Py_None;
+             """),
+            ])
+        import sys
+        s = 'abc'
+        if '__pypy__' in sys.builtin_module_names:
+            raises(SystemError, module.set_after_use, s)
+        else:
+            module.set_after_use(s)
+
diff --git a/pypy/module/cpyext/tupleobject.py b/pypy/module/cpyext/tupleobject.py
--- a/pypy/module/cpyext/tupleobject.py
+++ b/pypy/module/cpyext/tupleobject.py
@@ -6,7 +6,7 @@
     PyVarObjectFields, cpython_struct, bootstrap_function, slot_function)
 from pypy.module.cpyext.pyobject import (
     PyObject, PyObjectP, make_ref, from_ref, decref, incref,
-    track_reference, make_typedescr, get_typedescr)
+    track_reference, make_typedescr, get_typedescr, pyobj_has_w_obj)
 from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall
 from pypy.objspace.std.tupleobject import W_TupleObject
 
@@ -132,19 +132,20 @@
 
 @cpython_api([PyObject, Py_ssize_t, PyObject], rffi.INT_real, error=-1)
 def PyTuple_SetItem(space, ref, index, py_obj):
-    # XXX this will not complain when changing tuples that have
-    # already been realized as a W_TupleObject, but won't update the
-    # W_TupleObject
     if not tuple_check_ref(space, ref):
         decref(space, py_obj)
         PyErr_BadInternalCall(space)
-    ref = rffi.cast(PyTupleObject, ref)
-    size = ref.c_ob_size
+    tupleobj = rffi.cast(PyTupleObject, ref)
+    size = tupleobj.c_ob_size
     if index < 0 or index >= size:
         decref(space, py_obj)
         raise oefmt(space.w_IndexError, "tuple assignment index out of range")
-    old_ref = ref.c_ob_item[index]
-    ref.c_ob_item[index] = py_obj    # consumes a reference
+    old_ref = tupleobj.c_ob_item[index]
+    if pyobj_has_w_obj(ref):
+        # similar but not quite equal to ref.c_ob_refcnt != 1 on CPython
+        raise oefmt(space.w_SystemError, "PyTuple_SetItem called on tuple after"
+                                        " use of tuple")
+    tupleobj.c_ob_item[index] = py_obj    # consumes a reference
     if old_ref:
         decref(space, old_ref)
     return 0


More information about the pypy-commit mailing list