Skip to content

Commit f47daa0

Browse files
committed
gh-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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy