[pypy-commit] pypy cpyext-avoid-roundtrip: (antocuni, arigo)
arigo
pypy.commits at gmail.com
Tue Oct 10 10:07:38 EDT 2017
Author: Armin Rigo <arigo at tunes.org>
Branch: cpyext-avoid-roundtrip
Changeset: r92705:86616775c934
Date: 2017-10-10 16:02 +0200
http://bitbucket.org/pypy/pypy/changeset/86616775c934/
Log: (antocuni, arigo)
Test and "fix": PySequence_GetItem() should return a new reference
to an *existing* object that is kept alive by the original tuple or
list too. PyArg_ParseTuple() and probably 3rd-party code depends on
that.
diff --git a/pypy/module/cpyext/sequence.py b/pypy/module/cpyext/sequence.py
--- a/pypy/module/cpyext/sequence.py
+++ b/pypy/module/cpyext/sequence.py
@@ -1,11 +1,13 @@
from rpython.rlib import rerased, jit
+from rpython.rlib.objectmodel import keepalive_until_here
from pypy.interpreter.error import OperationError, oefmt
from pypy.objspace.std.listobject import (
ListStrategy, UNROLL_CUTOFF, W_ListObject, ObjectListStrategy)
from pypy.module.cpyext.api import (
cpython_api, CANNOT_FAIL, CONST_STRING, Py_ssize_t, PyObject, PyObjectP)
from pypy.module.cpyext.pyobject import PyObject, make_ref, from_ref
+from pypy.module.cpyext.pyobject import as_pyobj, incref
from rpython.rtyper.lltypesystem import rffi, lltype
from pypy.objspace.std import tupleobject
@@ -47,6 +49,9 @@
converted to a sequence, and raises a TypeError, raise a new TypeError with
m as the message text. If the conversion otherwise, fails, reraise the
original exception"""
+ # CCC test and enable these two lines:
+ #if isinstance(w_obj, tupleobject.W_TupleObject):
+ # return w_obj
if isinstance(w_obj, W_ListObject):
# make sure we can return a borrowed obj from PySequence_Fast_GET_ITEM
w_obj.convert_to_cpy_strategy(space)
@@ -132,7 +137,21 @@
This function used an int type for i. This might require
changes in your code for properly supporting 64-bit systems."""
- return space.getitem(w_obj, space.newint(i))
+ if isinstance(w_obj, tupleobject.W_TupleObject):
+ from pypy.module.cpyext.tupleobject import PyTuple_GetItem
+ py_obj = as_pyobj(space, w_obj)
+ py_res = PyTuple_GetItem(space, py_obj, i)
+ incref(space, py_res)
+ keepalive_until_here(w_obj)
+ return py_res
+ if isinstance(w_obj, W_ListObject):
+ from pypy.module.cpyext.listobject import PyList_GET_ITEM
+ py_obj = as_pyobj(space, w_obj)
+ py_res = PyList_GET_ITEM(space, py_obj, i)
+ incref(space, py_res)
+ keepalive_until_here(w_obj)
+ return py_res
+ return make_ref(space, space.getitem(w_obj, space.newint(i)))
@cpython_api([PyObject, Py_ssize_t], PyObject)
def PySequence_GetItem(space, w_obj, i):
diff --git a/pypy/module/cpyext/test/test_sequence.py b/pypy/module/cpyext/test/test_sequence.py
--- a/pypy/module/cpyext/test/test_sequence.py
+++ b/pypy/module/cpyext/test/test_sequence.py
@@ -156,6 +156,29 @@
result = api.PySequence_Index(w_gen, w_tofind)
assert result == 4
+ def test_sequence_getitem(self, space, api):
+ # PySequence_GetItem() is defined to return a new reference.
+ # When it happens to be called on a list or tuple, it returns
+ # a new reference that is also kept alive by the fact that it
+ # lives in the list/tuple. Some code like PyArg_ParseTuple()
+ # relies on this fact: it decrefs the result of
+ # PySequence_GetItem() but then expects it to stay alive. Meh.
+ # Here, we check that we try hard not to break this kind of
+ # code: if written naively, it could return a fresh PyIntObject,
+ # for example.
+ w1 = space.wrap((41, 42, 43))
+ p1 = api.PySequence_GetItem(w1, 1)
+ p2 = api.PySequence_GetItem(w1, 1)
+ assert p1 == p2
+ assert p1.c_ob_refcnt > 1
+ #
+ w1 = space.wrap([41, 42, 43])
+ p1 = api.PySequence_GetItem(w1, 1)
+ p2 = api.PySequence_GetItem(w1, 1)
+ assert p1 == p2
+ assert p1.c_ob_refcnt > 1
+
+
class AppTestSetObject(AppTestCpythonExtensionBase):
def test_sequence_macro_cast(self):
module = self.import_extension('foo', [
More information about the pypy-commit
mailing list