Author: Matti Picus <[email protected]>
Branch:
Changeset: r91436:3e565506d1ed
Date: 2017-05-29 22:33 +0300
http://bitbucket.org/pypy/pypy/changeset/3e565506d1ed/
Log: test, add a failure path, and document differences in
PyTuple_SetItem
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,25 @@
.. __:
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 on PyPy after the tuple is
+used internally, even if its refcount is 1 (whatever that means). On PyPy this
+will 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,38 @@
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_NOARGS",
+ """
+ PyObject *t2, *tuple = PyTuple_New(1);
+ PyObject * one = PyLong_FromLong(1);
+ Py_INCREF(one);
+ int res = PyTuple_SetItem(tuple, 0, one);
+ 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
+ if '__pypy__' in sys.builtin_module_names:
+ raises(SystemError, module.set_after_use)
+ else:
+ module.set_after_use()
+
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,9 +132,6 @@
@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)
@@ -144,6 +141,10 @@
decref(space, py_obj)
raise oefmt(space.w_IndexError, "tuple assignment index out of range")
old_ref = ref.c_ob_item[index]
+ if old_ref and pyobj_has_w_obj(old_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")
ref.c_ob_item[index] = py_obj # consumes a reference
if old_ref:
decref(space, old_ref)
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit