Skip to content

Commit f47daa0

Browse files
committed
pythongh-130947: Add again PySequence_Fast() to the limited C API
Add again PySequence_Fast(), PySequence_Fast_GET_SIZE() and PySequence_Fast_GET_ITEM() to the limited C API Add an unit tests.
1 parent e5527f2 commit f47daa0

File tree

8 files changed

+100
-13
lines changed

8 files changed

+100
-13
lines changed

Doc/data/stable_abi.dat

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Doc/whatsnew/3.14.rst

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1576,9 +1576,8 @@ Limited C API changes
15761576
implementation details.
15771577
(Contributed by Victor Stinner in :gh:`120600` and :gh:`124127`.)
15781578

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

15841583

Include/abstract.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,25 @@ PyAPI_FUNC(PyObject *) PySequence_Tuple(PyObject *o);
726726
This is equivalent to the Python expression: list(o) */
727727
PyAPI_FUNC(PyObject *) PySequence_List(PyObject *o);
728728

729+
/* Return the sequence 'o' as a list, unless it's already a tuple or list.
730+
731+
Use PySequence_Fast_GET_ITEM to access the members of this list, and
732+
PySequence_Fast_GET_SIZE to get its length.
733+
734+
Returns NULL on failure. If the object does not support iteration, raises a
735+
TypeError exception with 'm' as the message text. */
736+
PyAPI_FUNC(PyObject *) PySequence_Fast(PyObject *o, const char* m);
737+
738+
/* Return the size of the sequence 'o', assuming that 'o' was returned by
739+
PySequence_Fast and is not NULL. */
740+
#define PySequence_Fast_GET_SIZE(o) \
741+
(PyList_Check(o) ? PyList_Size(o) : PyTuple_Size(o))
742+
743+
/* Return the 'i'-th element of the sequence 'o', assuming that o was returned
744+
by PySequence_Fast, and that i is within bounds. */
745+
#define PySequence_Fast_GET_ITEM(o, i)\
746+
(PyList_Check(o) ? PyList_GetItem((o), (i)) : PyTuple_GetItem((o), (i)))
747+
729748
/* Return the number of occurrences on value on 'o', that is, return
730749
the number of keys for which o[key] == value.
731750

Include/cpython/abstract.h

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -86,22 +86,15 @@ PyAPI_FUNC(Py_ssize_t) PyObject_LengthHint(PyObject *o, Py_ssize_t);
8686
#define PySequence_ITEM(o, i)\
8787
( Py_TYPE(o)->tp_as_sequence->sq_item((o), (i)) )
8888

89-
/* Return the sequence 'o' as a list, unless it's already a tuple or list.
90-
91-
Use PySequence_Fast_GET_ITEM to access the members of this list, and
92-
PySequence_Fast_GET_SIZE to get its length.
93-
94-
Returns NULL on failure. If the object does not support iteration, raises a
95-
TypeError exception with 'm' as the message text. */
96-
PyAPI_FUNC(PyObject *) PySequence_Fast(PyObject *o, const char* m);
97-
9889
/* Return the size of the sequence 'o', assuming that 'o' was returned by
9990
PySequence_Fast and is not NULL. */
91+
#undef PySequence_Fast_GET_SIZE // limited C API implementation
10092
#define PySequence_Fast_GET_SIZE(o) \
10193
(PyList_Check(o) ? PyList_GET_SIZE(o) : PyTuple_GET_SIZE(o))
10294

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

Lib/test/test_capi/test_abstract.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -994,6 +994,42 @@ def test_sequence_tuple(self):
994994
self.assertRaises(TypeError, xtuple, 42)
995995
self.assertRaises(SystemError, xtuple, NULL)
996996

997+
def test_sequence_fast(self):
998+
# Tets PySequence_Fast()
999+
sequence_fast = _testlimitedcapi.sequence_fast
1000+
sequence_fast_get_size = _testlimitedcapi.sequence_fast_get_size
1001+
sequence_fast_get_item = _testlimitedcapi.sequence_fast_get_item
1002+
1003+
tpl = ('a', 'b', 'c')
1004+
fast = sequence_fast(tpl, "err_msg")
1005+
self.assertIs(fast, tpl)
1006+
self.assertEqual(sequence_fast_get_size(fast), 3)
1007+
self.assertEqual(sequence_fast_get_item(fast, 2), 'c')
1008+
1009+
lst = ['a', 'b', 'c']
1010+
fast = sequence_fast(lst, "err_msg")
1011+
self.assertIs(fast, lst)
1012+
self.assertEqual(sequence_fast_get_size(fast), 3)
1013+
self.assertEqual(sequence_fast_get_item(fast, 2), 'c')
1014+
1015+
it = iter(['A', 'B'])
1016+
fast = sequence_fast(it, "err_msg")
1017+
self.assertEqual(fast, ['A', 'B'])
1018+
self.assertEqual(sequence_fast_get_size(fast), 2)
1019+
self.assertEqual(sequence_fast_get_item(fast, 1), 'B')
1020+
1021+
text = 'fast'
1022+
fast = sequence_fast(text, "err_msg")
1023+
self.assertEqual(fast, ['f', 'a', 's', 't'])
1024+
self.assertEqual(sequence_fast_get_size(fast), 4)
1025+
self.assertEqual(sequence_fast_get_item(fast, 0), 'f')
1026+
1027+
self.assertRaises(TypeError, sequence_fast, 42, "err_msg")
1028+
self.assertRaises(SystemError, sequence_fast, NULL, "err_msg")
1029+
1030+
# CRASHES sequence_fast_get_size(NULL)
1031+
# CRASHES sequence_fast_get_item(NULL, 0)
1032+
9971033
def test_object_generichash(self):
9981034
# Test PyObject_GenericHash()
9991035
generichash = _testcapi.object_generichash
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Add again :c:func:`PySequence_Fast`, :c:macro:`PySequence_Fast_GET_SIZE()`
2+
and :c:macro:`PySequence_Fast_GET_ITEM()` to the limited C API. Patch by
3+
Victor Stinner.

Misc/stable_abi.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1253,7 +1253,6 @@
12531253
added = '3.2'
12541254
[function.PySequence_Fast]
12551255
added = '3.2'
1256-
abi_only = true
12571256
[function.PySequence_GetItem]
12581257
added = '3.2'
12591258
[function.PySequence_GetSlice]

Modules/_testlimitedcapi/abstract.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,40 @@ sequence_tuple(PyObject *self, PyObject *obj)
516516
}
517517

518518

519+
static PyObject *
520+
sequence_fast(PyObject *self, PyObject *args)
521+
{
522+
PyObject *obj;
523+
const char *err_msg;
524+
if (!PyArg_ParseTuple(args, "Os", &obj, &err_msg)) {
525+
return NULL;
526+
}
527+
NULLABLE(obj);
528+
return PySequence_Fast(obj, err_msg);
529+
}
530+
531+
532+
static PyObject *
533+
sequence_fast_get_size(PyObject *self, PyObject *obj)
534+
{
535+
NULLABLE(obj);
536+
return PyLong_FromSsize_t(PySequence_Fast_GET_SIZE(obj));
537+
}
538+
539+
540+
static PyObject *
541+
sequence_fast_get_item(PyObject *self, PyObject *args)
542+
{
543+
PyObject *obj;
544+
Py_ssize_t index;
545+
if (!PyArg_ParseTuple(args, "On", &obj, &index)) {
546+
return NULL;
547+
}
548+
NULLABLE(obj);
549+
return PySequence_Fast_GET_ITEM(obj, index);
550+
}
551+
552+
519553
static PyMethodDef test_methods[] = {
520554
{"object_repr", object_repr, METH_O},
521555
{"object_ascii", object_ascii, METH_O},
@@ -567,6 +601,9 @@ static PyMethodDef test_methods[] = {
567601
{"sequence_index", sequence_index, METH_VARARGS},
568602
{"sequence_list", sequence_list, METH_O},
569603
{"sequence_tuple", sequence_tuple, METH_O},
604+
{"sequence_fast", sequence_fast, METH_VARARGS},
605+
{"sequence_fast_get_size", sequence_fast_get_size, METH_O},
606+
{"sequence_fast_get_item", sequence_fast_get_item, METH_VARARGS},
570607

571608
{NULL},
572609
};

0 commit comments

Comments
 (0)