Skip to content

Commit 10cbd1f

Browse files
authored
gh-130947: Add again PySequence_Fast() to the limited C API (#130948)
Add again PySequence_Fast() to the limited C API. Add unit tests.
1 parent 3a189af commit 10cbd1f

File tree

9 files changed

+90
-13
lines changed

9 files changed

+90
-13
lines changed

Doc/data/stable_abi.dat

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Doc/whatsnew/3.14.rst

+4-3
Original file line numberDiff line numberDiff line change
@@ -1598,9 +1598,10 @@ Limited C API changes
15981598
implementation details.
15991599
(Contributed by Victor Stinner in :gh:`120600` and :gh:`124127`.)
16001600

1601-
* Remove :c:func:`PySequence_Fast` from the limited C API, since this function
1602-
has to be used with :c:macro:`PySequence_Fast_GET_ITEM` which never worked
1603-
in the limited C API.
1601+
* Remove the :c:macro:`PySequence_Fast_GET_SIZE`,
1602+
:c:macro:`PySequence_Fast_GET_ITEM` and :c:macro:`PySequence_Fast_ITEMS`
1603+
macros from the limited C API, since these macros never worked in the limited
1604+
C API. Keep :c:func:`PySequence_Fast` in the limited C API.
16041605
(Contributed by Victor Stinner in :gh:`91417`.)
16051606

16061607

Include/abstract.h

+9
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,15 @@ 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+
729738
/* Return the number of occurrences on value on 'o', that is, return
730739
the number of keys for which o[key] == value.
731740

Include/cpython/abstract.h

-9
Original file line numberDiff line numberDiff line change
@@ -86,15 +86,6 @@ 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. */
10091
#define PySequence_Fast_GET_SIZE(o) \

Lib/test/test_capi/test_abstract.py

+36
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+
# Test PySequence_Fast()
999+
sequence_fast = _testlimitedcapi.sequence_fast
1000+
sequence_fast_get_size = _testcapi.sequence_fast_get_size
1001+
sequence_fast_get_item = _testcapi.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
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add again :c:func:`PySequence_Fast` to the limited C API.
2+
Patch by Victor Stinner.

Misc/stable_abi.toml

-1
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/_testcapi/abstract.c

+24
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,27 @@ pyiter_nextitem(PyObject *self, PyObject *iter)
157157
}
158158

159159

160+
static PyObject *
161+
sequence_fast_get_size(PyObject *self, PyObject *obj)
162+
{
163+
NULLABLE(obj);
164+
return PyLong_FromSsize_t(PySequence_Fast_GET_SIZE(obj));
165+
}
166+
167+
168+
static PyObject *
169+
sequence_fast_get_item(PyObject *self, PyObject *args)
170+
{
171+
PyObject *obj;
172+
Py_ssize_t index;
173+
if (!PyArg_ParseTuple(args, "On", &obj, &index)) {
174+
return NULL;
175+
}
176+
NULLABLE(obj);
177+
return PySequence_Fast_GET_ITEM(obj, index);
178+
}
179+
180+
160181
static PyMethodDef test_methods[] = {
161182
{"object_getoptionalattr", object_getoptionalattr, METH_VARARGS},
162183
{"object_getoptionalattrstring", object_getoptionalattrstring, METH_VARARGS},
@@ -167,6 +188,9 @@ static PyMethodDef test_methods[] = {
167188

168189
{"PyIter_Next", pyiter_next, METH_O},
169190
{"PyIter_NextItem", pyiter_nextitem, METH_O},
191+
192+
{"sequence_fast_get_size", sequence_fast_get_size, METH_O},
193+
{"sequence_fast_get_item", sequence_fast_get_item, METH_VARARGS},
170194
{NULL},
171195
};
172196

Modules/_testlimitedcapi/abstract.c

+14
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,19 @@ 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+
519532
static PyMethodDef test_methods[] = {
520533
{"object_repr", object_repr, METH_O},
521534
{"object_ascii", object_ascii, METH_O},
@@ -567,6 +580,7 @@ static PyMethodDef test_methods[] = {
567580
{"sequence_index", sequence_index, METH_VARARGS},
568581
{"sequence_list", sequence_list, METH_O},
569582
{"sequence_tuple", sequence_tuple, METH_O},
583+
{"sequence_fast", sequence_fast, METH_VARARGS},
570584

571585
{NULL},
572586
};

0 commit comments

Comments
 (0)