Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-130947: Add again PySequence_Fast() to the limited C API #130948

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Doc/data/stable_abi.dat

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1576,9 +1576,8 @@ Limited C API changes
implementation details.
(Contributed by Victor Stinner in :gh:`120600` and :gh:`124127`.)

* Remove :c:func:`PySequence_Fast` from the limited C API, since this function
has to be used with :c:macro:`PySequence_Fast_GET_ITEM` which never worked
in the limited C API.
* Remove the :c:macro:`PySequence_Fast_ITEMS` macro from the limited C API,
since this macro never worked in the limited C API.
(Contributed by Victor Stinner in :gh:`91417`.)


Expand Down
19 changes: 19 additions & 0 deletions Include/abstract.h
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,25 @@ PyAPI_FUNC(PyObject *) PySequence_Tuple(PyObject *o);
This is equivalent to the Python expression: list(o) */
PyAPI_FUNC(PyObject *) PySequence_List(PyObject *o);

/* Return the sequence 'o' as a list, unless it's already a tuple or list.

Use PySequence_Fast_GET_ITEM to access the members of this list, and
PySequence_Fast_GET_SIZE to get its length.

Returns NULL on failure. If the object does not support iteration, raises a
TypeError exception with 'm' as the message text. */
PyAPI_FUNC(PyObject *) PySequence_Fast(PyObject *o, const char* m);

/* Return the size of the sequence 'o', assuming that 'o' was returned by
PySequence_Fast and is not NULL. */
#define PySequence_Fast_GET_SIZE(o) \
(PyList_Check(o) ? PyList_Size(o) : PyTuple_Size(o))

/* Return the 'i'-th element of the sequence 'o', assuming that o was returned
by PySequence_Fast, and that i is within bounds. */
#define PySequence_Fast_GET_ITEM(o, i)\
(PyList_Check(o) ? PyList_GetItem((o), (i)) : PyTuple_GetItem((o), (i)))

/* Return the number of occurrences on value on 'o', that is, return
the number of keys for which o[key] == value.

Expand Down
11 changes: 2 additions & 9 deletions Include/cpython/abstract.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,22 +86,15 @@ PyAPI_FUNC(Py_ssize_t) PyObject_LengthHint(PyObject *o, Py_ssize_t);
#define PySequence_ITEM(o, i)\
( Py_TYPE(o)->tp_as_sequence->sq_item((o), (i)) )

/* Return the sequence 'o' as a list, unless it's already a tuple or list.

Use PySequence_Fast_GET_ITEM to access the members of this list, and
PySequence_Fast_GET_SIZE to get its length.

Returns NULL on failure. If the object does not support iteration, raises a
TypeError exception with 'm' as the message text. */
PyAPI_FUNC(PyObject *) PySequence_Fast(PyObject *o, const char* m);

/* Return the size of the sequence 'o', assuming that 'o' was returned by
PySequence_Fast and is not NULL. */
#undef PySequence_Fast_GET_SIZE // limited C API implementation
#define PySequence_Fast_GET_SIZE(o) \
(PyList_Check(o) ? PyList_GET_SIZE(o) : PyTuple_GET_SIZE(o))

/* Return the 'i'-th element of the sequence 'o', assuming that o was returned
by PySequence_Fast, and that i is within bounds. */
#undef PySequence_Fast_GET_ITEM // limited C API implementation
#define PySequence_Fast_GET_ITEM(o, i)\
(PyList_Check(o) ? PyList_GET_ITEM((o), (i)) : PyTuple_GET_ITEM((o), (i)))

Expand Down
36 changes: 36 additions & 0 deletions Lib/test/test_capi/test_abstract.py
Original file line number Diff line number Diff line change
Expand Up @@ -994,6 +994,42 @@ def test_sequence_tuple(self):
self.assertRaises(TypeError, xtuple, 42)
self.assertRaises(SystemError, xtuple, NULL)

def test_sequence_fast(self):
# Tets PySequence_Fast()
sequence_fast = _testlimitedcapi.sequence_fast
sequence_fast_get_size = _testlimitedcapi.sequence_fast_get_size
sequence_fast_get_item = _testlimitedcapi.sequence_fast_get_item

tpl = ('a', 'b', 'c')
fast = sequence_fast(tpl, "err_msg")
self.assertIs(fast, tpl)
self.assertEqual(sequence_fast_get_size(fast), 3)
self.assertEqual(sequence_fast_get_item(fast, 2), 'c')

lst = ['a', 'b', 'c']
fast = sequence_fast(lst, "err_msg")
self.assertIs(fast, lst)
self.assertEqual(sequence_fast_get_size(fast), 3)
self.assertEqual(sequence_fast_get_item(fast, 2), 'c')

it = iter(['A', 'B'])
fast = sequence_fast(it, "err_msg")
self.assertEqual(fast, ['A', 'B'])
self.assertEqual(sequence_fast_get_size(fast), 2)
self.assertEqual(sequence_fast_get_item(fast, 1), 'B')

text = 'fast'
fast = sequence_fast(text, "err_msg")
self.assertEqual(fast, ['f', 'a', 's', 't'])
self.assertEqual(sequence_fast_get_size(fast), 4)
self.assertEqual(sequence_fast_get_item(fast, 0), 'f')

self.assertRaises(TypeError, sequence_fast, 42, "err_msg")
self.assertRaises(SystemError, sequence_fast, NULL, "err_msg")

# CRASHES sequence_fast_get_size(NULL)
# CRASHES sequence_fast_get_item(NULL, 0)

def test_object_generichash(self):
# Test PyObject_GenericHash()
generichash = _testcapi.object_generichash
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Add again :c:func:`PySequence_Fast`, :c:macro:`PySequence_Fast_GET_SIZE()`
and :c:macro:`PySequence_Fast_GET_ITEM()` to the limited C API. Patch by
Victor Stinner.
1 change: 0 additions & 1 deletion Misc/stable_abi.toml
Original file line number Diff line number Diff line change
Expand Up @@ -1253,7 +1253,6 @@
added = '3.2'
[function.PySequence_Fast]
added = '3.2'
abi_only = true
[function.PySequence_GetItem]
added = '3.2'
[function.PySequence_GetSlice]
Expand Down
37 changes: 37 additions & 0 deletions Modules/_testlimitedcapi/abstract.c
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,40 @@ sequence_tuple(PyObject *self, PyObject *obj)
}


static PyObject *
sequence_fast(PyObject *self, PyObject *args)
{
PyObject *obj;
const char *err_msg;
if (!PyArg_ParseTuple(args, "Os", &obj, &err_msg)) {
return NULL;
}
NULLABLE(obj);
return PySequence_Fast(obj, err_msg);
}


static PyObject *
sequence_fast_get_size(PyObject *self, PyObject *obj)
{
NULLABLE(obj);
return PyLong_FromSsize_t(PySequence_Fast_GET_SIZE(obj));
}


static PyObject *
sequence_fast_get_item(PyObject *self, PyObject *args)
{
PyObject *obj;
Py_ssize_t index;
if (!PyArg_ParseTuple(args, "On", &obj, &index)) {
return NULL;
}
NULLABLE(obj);
return PySequence_Fast_GET_ITEM(obj, index);
}


static PyMethodDef test_methods[] = {
{"object_repr", object_repr, METH_O},
{"object_ascii", object_ascii, METH_O},
Expand Down Expand Up @@ -567,6 +601,9 @@ static PyMethodDef test_methods[] = {
{"sequence_index", sequence_index, METH_VARARGS},
{"sequence_list", sequence_list, METH_O},
{"sequence_tuple", sequence_tuple, METH_O},
{"sequence_fast", sequence_fast, METH_VARARGS},
{"sequence_fast_get_size", sequence_fast_get_size, METH_O},
{"sequence_fast_get_item", sequence_fast_get_item, METH_VARARGS},

{NULL},
};
Expand Down
Loading