https://github.com/python/cpython/commit/10cbd1fe88d1095a03cce24fb126d479668a67c3
commit: 10cbd1fe88d1095a03cce24fb126d479668a67c3
branch: main
author: Victor Stinner <vstin...@python.org>
committer: vstinner <vstin...@python.org>
date: 2025-03-13T13:00:57+01:00
summary:

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

Add again PySequence_Fast() to the limited C API.

Add unit tests.

files:
A Misc/NEWS.d/next/C_API/2025-03-07-14-49-06.gh-issue-130947._Pw0IX.rst
M Doc/data/stable_abi.dat
M Doc/whatsnew/3.14.rst
M Include/abstract.h
M Include/cpython/abstract.h
M Lib/test/test_capi/test_abstract.py
M Misc/stable_abi.toml
M Modules/_testcapi/abstract.c
M Modules/_testlimitedcapi/abstract.c

diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat
index 59e7a31bc2ef06..c15f82603aa944 100644
--- a/Doc/data/stable_abi.dat
+++ b/Doc/data/stable_abi.dat
@@ -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,,
diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst
index 28879c4a4b5fed..8f58901b28e1b0 100644
--- a/Doc/whatsnew/3.14.rst
+++ b/Doc/whatsnew/3.14.rst
@@ -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`.)
 
 
diff --git a/Include/abstract.h b/Include/abstract.h
index 4efe4fcb014903..b9199fc03a399a 100644
--- a/Include/abstract.h
+++ b/Include/abstract.h
@@ -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.
 
diff --git a/Include/cpython/abstract.h b/Include/cpython/abstract.h
index 8fed1d3110988b..ffd19ccd3500fa 100644
--- a/Include/cpython/abstract.h
+++ b/Include/cpython/abstract.h
@@ -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) \
diff --git a/Lib/test/test_capi/test_abstract.py 
b/Lib/test/test_capi/test_abstract.py
index 3de251bc5c241e..912c2de2b69930 100644
--- a/Lib/test/test_capi/test_abstract.py
+++ b/Lib/test/test_capi/test_abstract.py
@@ -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
diff --git 
a/Misc/NEWS.d/next/C_API/2025-03-07-14-49-06.gh-issue-130947._Pw0IX.rst 
b/Misc/NEWS.d/next/C_API/2025-03-07-14-49-06.gh-issue-130947._Pw0IX.rst
new file mode 100644
index 00000000000000..ff983d4a180f57
--- /dev/null
+++ b/Misc/NEWS.d/next/C_API/2025-03-07-14-49-06.gh-issue-130947._Pw0IX.rst
@@ -0,0 +1,2 @@
+Add again :c:func:`PySequence_Fast` to the limited C API.
+Patch by Victor Stinner.
diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml
index 9317be605f0065..276526a1b6908e 100644
--- a/Misc/stable_abi.toml
+++ b/Misc/stable_abi.toml
@@ -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]
diff --git a/Modules/_testcapi/abstract.c b/Modules/_testcapi/abstract.c
index 8c2c7137cdce40..d4045afd515909 100644
--- a/Modules/_testcapi/abstract.c
+++ b/Modules/_testcapi/abstract.c
@@ -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},
 };
 
diff --git a/Modules/_testlimitedcapi/abstract.c 
b/Modules/_testlimitedcapi/abstract.c
index 6056dd100d6069..e107e53fea8c33 100644
--- a/Modules/_testlimitedcapi/abstract.c
+++ b/Modules/_testlimitedcapi/abstract.c
@@ -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},
 };

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com

Reply via email to