[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