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

Add again PySequence_Fast() to the limited C API.

Add unit tests.
This commit is contained in:
Victor Stinner 2025-03-13 13:00:57 +01:00 committed by GitHub
parent 3a189af4b2
commit 10cbd1fe88
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 90 additions and 13 deletions

View file

@ -582,6 +582,7 @@ func,PySequence_Contains,3.2,,
func,PySequence_Count,3.2,,
func,PySequence_DelItem,3.2,,
func,PySequence_DelSlice,3.2,,
func,PySequence_Fast,3.2,,
func,PySequence_GetItem,3.2,,
func,PySequence_GetSlice,3.2,,
func,PySequence_In,3.2,,

View file

@ -1598,9 +1598,10 @@ 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_GET_SIZE`,
:c:macro:`PySequence_Fast_GET_ITEM` and :c:macro:`PySequence_Fast_ITEMS`
macros from the limited C API, since these macros never worked in the limited
C API. Keep :c:func:`PySequence_Fast` in the limited C API.
(Contributed by Victor Stinner in :gh:`91417`.)

View file

@ -726,6 +726,15 @@ 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 number of occurrences on value on 'o', that is, return
the number of keys for which o[key] == value.

View file

@ -86,15 +86,6 @@ 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. */
#define PySequence_Fast_GET_SIZE(o) \

View file

@ -994,6 +994,42 @@ def test_sequence_tuple(self):
self.assertRaises(TypeError, xtuple, 42)
self.assertRaises(SystemError, xtuple, NULL)
def test_sequence_fast(self):
# Test PySequence_Fast()
sequence_fast = _testlimitedcapi.sequence_fast
sequence_fast_get_size = _testcapi.sequence_fast_get_size
sequence_fast_get_item = _testcapi.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

View file

@ -0,0 +1,2 @@
Add again :c:func:`PySequence_Fast` to the limited C API.
Patch by Victor Stinner.

View file

@ -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]

View file

@ -157,6 +157,27 @@ pyiter_nextitem(PyObject *self, PyObject *iter)
}
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_getoptionalattr", object_getoptionalattr, METH_VARARGS},
{"object_getoptionalattrstring", object_getoptionalattrstring, METH_VARARGS},
@ -167,6 +188,9 @@ static PyMethodDef test_methods[] = {
{"PyIter_Next", pyiter_next, METH_O},
{"PyIter_NextItem", pyiter_nextitem, METH_O},
{"sequence_fast_get_size", sequence_fast_get_size, METH_O},
{"sequence_fast_get_item", sequence_fast_get_item, METH_VARARGS},
{NULL},
};

View file

@ -516,6 +516,19 @@ 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 PyMethodDef test_methods[] = {
{"object_repr", object_repr, METH_O},
{"object_ascii", object_ascii, METH_O},
@ -567,6 +580,7 @@ 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},
{NULL},
};