]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-130947: Add again PySequence_Fast() to the limited C API (#130948)
authorVictor Stinner <vstinner@python.org>
Thu, 13 Mar 2025 12:00:57 +0000 (13:00 +0100)
committerGitHub <noreply@github.com>
Thu, 13 Mar 2025 12:00:57 +0000 (13:00 +0100)
Add again PySequence_Fast() to the limited C API.

Add unit tests.

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

index 59e7a31bc2ef06613ab49498193e46e9d4d52dc1..c15f82603aa94432369077660214dcf75c1ee8c0 100644 (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,,
index 28879c4a4b5fed1ea0d828e1555269e36fe64ab7..8f58901b28e1b095463679d82497e26069cfc6e5 100644 (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`.)
 
 
index 4efe4fcb01490351b4ae42c85433edfc4c248f95..b9199fc03a399a82dcb5b42fca6f36332f64f605 100644 (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.
 
index 8fed1d3110988b1ebe984dc203ff852a537993b8..ffd19ccd3500fac847eb379eaed953aa67d12bec 100644 (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) \
index 3de251bc5c241e42b7a9d4c26902b0565a84f577..912c2de2b6993094f80d0292d059bf505c65d2be 100644 (file)
@@ -994,6 +994,42 @@ class CAPITest(unittest.TestCase):
         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 (file)
index 0000000..ff983d4
--- /dev/null
@@ -0,0 +1,2 @@
+Add again :c:func:`PySequence_Fast` to the limited C API.
+Patch by Victor Stinner.
index 9317be605f00659f18c696f4697779c1615ad202..276526a1b6908e54a1fcd2c8bc4a091d5ea1ef2f 100644 (file)
     added = '3.2'
 [function.PySequence_Fast]
     added = '3.2'
-    abi_only = true
 [function.PySequence_GetItem]
     added = '3.2'
 [function.PySequence_GetSlice]
index 8c2c7137cdce40a5ce97b619dba532db2bdaa3b9..d4045afd515909f3f6c0aeaefc7659d8800bdd7b 100644 (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},
 };
 
index 6056dd100d6069a95d8de855fea49cd409a2d7ec..e107e53fea8c33fe28624e49691e00585bb6158e 100644 (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},
 };