Commit 791efb93 authored by Matti Picus's avatar Matti Picus
Browse files

test, fix PySequence_Fast getslice; remove outdated document

parent 1900672c0757
Reference Count
Borrowed References
PyStringObject support
The problem
PyString_AsString() returns a (non-movable) pointer to the underlying
buffer, whereas pypy strings are movable. C code may temporarily
store this address and use it, as long as it owns a reference to the
PyObject. There is no "release" function to specify that the pointer
is not needed any more.
Note that the pointer may be used to fill the initial value of
string. This is valid only when the string was just allocated, and is
not used elsewhere.
Proposed solution
Our emulation of the PyStringObject contains an additional member: a
pointer to a char buffer; it may be NULL.
- A string allocated by pypy will be converted into a PyStringObject
with a NULL buffer. When PyString_AsString() is called, memory is
allocated (with flavor='raw') and content is copied.
- A string allocated with PyString_FromStringAndSize(NULL, size) will
allocate a buffer with the specified size, but the reference won't
be stored in the global map py_objects_r2w; there won't be a
corresponding object in pypy. When from_ref() or Py_INCREF() is
called, the pypy string is created, and added in py_objects_r2w.
The buffer is then supposed to be immutable.
- _PyString_Resize works only on not-yet-pypy'd strings, and returns a
similar object.
- PyString_Size don't need to force the object. (in this case, another
"size" member is needed)
- There could be an (expensive!) check in from_ref() that the buffer
still corresponds to the pypy gc-managed string.
PySequence_Fast support
There are five functions for fast sequence access offered by the CPython API:
PyObject* PySequence_Fast(PyObject *o, const char *m)
PyObject* PySequence_Fast_GET_ITEM( PyObject *o, int i)
PyObject** PySequence_Fast_ITEMS( PyObject *o)
PyObject* PySequence_ITEM( PyObject *o, int i)
int PySequence_Fast_GET_SIZE( PyObject *o)
PyPy supports four of these, but does not support PySequence_Fast_ITEMS.
(Various ways to support PySequence_Fast_ITEMS were considered. They all had
two things in common: they would have taken a lot of work, and they would have
resulted in incomplete semantics or in poor performance. We decided that a slow
implementation of PySequence_Fast_ITEMS was not very useful.)
......@@ -10,7 +10,7 @@ from rpython.rtyper.lltypesystem import rffi, lltype
from pypy.objspace.std import tupleobject
from pypy.module.cpyext.tupleobject import PyTuple_Check, PyTuple_SetItem
from pypy.module.cpyext.object import Py_IncRef, Py_DecRef
from pypy.module.cpyext.pyobject import decref
from pypy.module.cpyext.dictobject import PyDict_Check
......@@ -252,7 +252,7 @@ class CPyListStrategy(ListStrategy):
def setitem(self, w_list, index, w_obj):
storage = self.unerase(w_list.lstorage)
index = self._check_index(index, storage._length)
Py_DecRef(, storage._elems[index])
decref(, storage._elems[index])
storage._elems[index] = make_ref(, w_obj)
def length(self, w_list):
......@@ -264,9 +264,8 @@ class CPyListStrategy(ListStrategy):
return storage._elems
def getslice(self, w_list, start, stop, step, length):
#storage = self.unerase(w_list.lstorage)
raise oefmt(,
"settting a slice of a PySequence_Fast is not supported")
return w_list.strategy.getslice(w_list, start, stop, step, length)
def getitems(self, w_list):
# called when switching list strategy, so convert storage
......@@ -389,5 +388,5 @@ class CPyListStorage(object):
def __del__(self):
for i in range(self._length):
Py_DecRef(, self._elems[i])
decref(, self._elems[i]), flavor='raw')
......@@ -78,6 +78,17 @@ class TestSequence(BaseApiTest):
assert api.PySequence_SetSlice(w_t, 1, 1, space.wrap((3,))) == 0
assert space.eq_w(w_t, space.wrap([1, 3, 5]))
def test_get_slice_fast(self, space, api):
w_t = space.wrap([1, 2, 3, 4, 5])
api.PySequence_Fast(w_t, "foo") # converts
assert space.unwrap(api.PySequence_GetSlice(w_t, 2, 4)) == [3, 4]
assert space.unwrap(api.PySequence_GetSlice(w_t, 1, -1)) == [2, 3, 4]
assert api.PySequence_DelSlice(w_t, 1, 4) == 0
assert space.eq_w(w_t, space.wrap([1, 5]))
assert api.PySequence_SetSlice(w_t, 1, 1, space.wrap((3,))) == 0
assert space.eq_w(w_t, space.wrap([1, 3, 5]))
def test_iter(self, space, api):
w_t = space.wrap((1, 2))
w_iter = api.PySeqIter_New(w_t)
......@@ -226,18 +237,33 @@ class TestCPyListStrategy(BaseApiTest):
assert space.int_w(space.len(w_l)) == 10
class XAppTestSequenceObject(AppTestCpythonExtensionBase):
def test_sequenceobject(self):
class AppTestSequenceObject(AppTestCpythonExtensionBase):
def test_fast(self):
module = self.import_extension('foo', [
("test_fast_sequence", "METH_VARARGS",
PyObject * o = PyTuple_GetItem(args, 0);
int size, i;
PyTypeObject * common_type;
PyObject *foo, **objects;
PyObject * seq = PyTuple_GetItem(args, 0);
/* XXX assert it is a tuple */
PyObject *foo = PySequence_Fast(o, "some string");
PyObject ** res = PySequence_Fast_ITEMS(foo);
/* XXX do some kind of test on res */
/* XXX now what? who manages res's refcount? */
if (seq == NULL)
foo = PySequence_Fast(seq, "some string");
objects = PySequence_Fast_ITEMS(foo);
size = PySequence_Fast_GET_SIZE(seq);
common_type = size > 0 ? Py_TYPE(objects[0]) : NULL;
for (i = 1; i < size; ++i) {
if (Py_TYPE(objects[i]) != common_type) {
common_type = NULL;
return PyBool_FromLong(1);
assert module.test_fast_sequence([1, 2, 3, 4])
s = [1, 2, 3, 4]
assert module.test_fast_sequence(s[0:-1])
assert module.test_fast_sequence(s[::-1])
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment