]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-114329: Add `PyList_GetItemRef` function (GH-114504)
authorSam Gross <colesbury@gmail.com>
Fri, 2 Feb 2024 13:03:15 +0000 (08:03 -0500)
committerGitHub <noreply@github.com>
Fri, 2 Feb 2024 13:03:15 +0000 (14:03 +0100)
The new `PyList_GetItemRef` is similar to `PyList_GetItem`, but returns
a strong reference instead of a borrowed reference. Additionally, if the
passed "list" object is not a list, the function sets a `TypeError`
instead of calling `PyErr_BadInternalCall()`.

12 files changed:
Doc/c-api/list.rst
Doc/data/refcounts.dat
Doc/data/stable_abi.dat
Doc/whatsnew/3.13.rst
Include/listobject.h
Lib/test/test_capi/test_list.py
Lib/test/test_stable_abi_ctypes.py
Misc/NEWS.d/next/C API/2024-01-23-21-45-02.gh-issue-114329.YRaBoe.rst [new file with mode: 0644]
Misc/stable_abi.toml
Modules/_testcapi/list.c
Objects/listobject.c
PC/python3dll.c

index c8b64bad702f508a241e45c5506a6ae80e8c724d..53eb54d3e1021a3a1b06003a3e2ae1b8549a6921 100644 (file)
@@ -56,13 +56,21 @@ List Objects
    Similar to :c:func:`PyList_Size`, but without error checking.
 
 
-.. c:function:: PyObject* PyList_GetItem(PyObject *list, Py_ssize_t index)
+.. c:function:: PyObject* PyList_GetItemRef(PyObject *list, Py_ssize_t index)
 
    Return the object at position *index* in the list pointed to by *list*.  The
    position must be non-negative; indexing from the end of the list is not
-   supported.  If *index* is out of bounds (<0 or >=len(list)),
+   supported.  If *index* is out of bounds (:code:`<0 or >=len(list)`),
    return ``NULL`` and set an :exc:`IndexError` exception.
 
+   .. versionadded:: 3.13
+
+
+.. c:function:: PyObject* PyList_GetItem(PyObject *list, Py_ssize_t index)
+
+   Like :c:func:`PyList_GetItemRef`, but returns a
+   :term:`borrowed reference` instead of a :term:`strong reference`.
+
 
 .. c:function:: PyObject* PyList_GET_ITEM(PyObject *list, Py_ssize_t i)
 
index f719ce153b239a92c36969c7a834b6c0f6c4544a..62a96146d605ff7282823f20d7b09a280f359091 100644 (file)
@@ -1133,6 +1133,10 @@ PyList_GetItem:PyObject*::0:
 PyList_GetItem:PyObject*:list:0:
 PyList_GetItem:Py_ssize_t:index::
 
+PyList_GetItemRef:PyObject*::+1:
+PyList_GetItemRef:PyObject*:list:0:
+PyList_GetItemRef:Py_ssize_t:index::
+
 PyList_GetSlice:PyObject*::+1:
 PyList_GetSlice:PyObject*:list:0:
 PyList_GetSlice:Py_ssize_t:low::
index da28a2be60bc1bc105023ea35371a50717f403e0..def1903204add7ae5e756dfc76f897956f935833 100644 (file)
@@ -336,6 +336,7 @@ var,PyListRevIter_Type,3.2,,
 function,PyList_Append,3.2,,
 function,PyList_AsTuple,3.2,,
 function,PyList_GetItem,3.2,,
+function,PyList_GetItemRef,3.13,,
 function,PyList_GetSlice,3.2,,
 function,PyList_Insert,3.2,,
 function,PyList_New,3.2,,
index 887c3009f88504152b4f267a92b5c178aee85c66..f17c6ec0775beffaa63dbcca718f9a0c95248e2e 100644 (file)
@@ -1376,6 +1376,10 @@ New Features
   UTF-8 encoded bytes string, rather than a :c:expr:`PyObject*`.
   (Contributed by Victor Stinner in :gh:`108314`.)
 
+* Added :c:func:`PyList_GetItemRef` function: similar to
+  :c:func:`PyList_GetItem` but returns a :term:`strong reference` instead of
+  a :term:`borrowed reference`.
+
 * Add :c:func:`Py_IsFinalizing` function: check if the main Python interpreter is
   :term:`shutting down <interpreter shutdown>`.
   (Contributed by Victor Stinner in :gh:`108014`.)
index 6b7041ba0b05d5974f63cb61aa994b0f52050324..4e4084b43483a2deec573e79e876dd1699173b5e 100644 (file)
@@ -29,6 +29,7 @@ PyAPI_FUNC(PyObject *) PyList_New(Py_ssize_t size);
 PyAPI_FUNC(Py_ssize_t) PyList_Size(PyObject *);
 
 PyAPI_FUNC(PyObject *) PyList_GetItem(PyObject *, Py_ssize_t);
+PyAPI_FUNC(PyObject *) PyList_GetItemRef(PyObject *, Py_ssize_t);
 PyAPI_FUNC(int) PyList_SetItem(PyObject *, Py_ssize_t, PyObject *);
 PyAPI_FUNC(int) PyList_Insert(PyObject *, Py_ssize_t, PyObject *);
 PyAPI_FUNC(int) PyList_Append(PyObject *, PyObject *);
index eb03d51d3def378f0fcecf719c60476a37c03dd5..dceb4fce3c077bf37a72d08c71a1729644f50375 100644 (file)
@@ -82,10 +82,8 @@ class CAPITest(unittest.TestCase):
         # CRASHES size(UserList())
         # CRASHES size(NULL)
 
-
-    def test_list_getitem(self):
-        # Test PyList_GetItem()
-        getitem = _testcapi.list_getitem
+    def check_list_get_item(self, getitem, exctype):
+        # Common test cases for PyList_GetItem() and PyList_GetItemRef()
         lst = [1, 2, 3]
         self.assertEqual(getitem(lst, 0), 1)
         self.assertEqual(getitem(lst, 2), 3)
@@ -93,12 +91,19 @@ class CAPITest(unittest.TestCase):
         self.assertRaises(IndexError, getitem, lst, -1)
         self.assertRaises(IndexError, getitem, lst, PY_SSIZE_T_MIN)
         self.assertRaises(IndexError, getitem, lst, PY_SSIZE_T_MAX)
-        self.assertRaises(SystemError, getitem, 42, 1)
-        self.assertRaises(SystemError, getitem, (1, 2, 3), 1)
-        self.assertRaises(SystemError, getitem, {1: 2}, 1)
-
+        self.assertRaises(exctype, getitem, 42, 1)
+        self.assertRaises(exctype, getitem, (1, 2, 3), 1)
+        self.assertRaises(exctype, getitem, {1: 2}, 1)
         # CRASHES getitem(NULL, 1)
 
+    def test_list_getitem(self):
+        # Test PyList_GetItem()
+        self.check_list_get_item(_testcapi.list_getitem, SystemError)
+
+    def test_list_get_item_ref(self):
+        # Test PyList_GetItemRef()
+        self.check_list_get_item(_testcapi.list_get_item_ref, TypeError)
+
     def test_list_get_item(self):
         # Test PyList_GET_ITEM()
         get_item = _testcapi.list_get_item
@@ -112,7 +117,6 @@ class CAPITest(unittest.TestCase):
         # CRASHES get_item(21, 2)
         # CRASHES get_item(NULL, 1)
 
-
     def test_list_setitem(self):
         # Test PyList_SetItem()
         setitem = _testcapi.list_setitem
index 054e7f0feb1a19db517daa978fcda3a94f5cb085..8bd373976426ef3227e13f005cc6ccb2cff87f1e 100644 (file)
@@ -372,6 +372,7 @@ SYMBOL_NAMES = (
     "PyList_Append",
     "PyList_AsTuple",
     "PyList_GetItem",
+    "PyList_GetItemRef",
     "PyList_GetSlice",
     "PyList_Insert",
     "PyList_New",
diff --git a/Misc/NEWS.d/next/C API/2024-01-23-21-45-02.gh-issue-114329.YRaBoe.rst b/Misc/NEWS.d/next/C API/2024-01-23-21-45-02.gh-issue-114329.YRaBoe.rst
new file mode 100644 (file)
index 0000000..62d4ce0
--- /dev/null
@@ -0,0 +1,3 @@
+Add :c:func:`PyList_GetItemRef`, which is similar to
+:c:func:`PyList_GetItem` but returns a :term:`strong reference` instead of a
+:term:`borrowed reference`.
index ae19d25809ec86741b9f8a3e96aefa94e5d2970c..a9875f6ffd1a56a062dc3c3f7f6d2e3962382a18 100644 (file)
     abi_only = true
 [data.PyExc_IncompleteInputError]
     added = '3.13'
+[function.PyList_GetItemRef]
+    added = '3.13'
index 10e18699f01bc1f469181d4a2c1a1c0afac86661..2cb6499e28336dfcb0e3e5b41f3b9c57b3d14662 100644 (file)
@@ -59,6 +59,18 @@ list_get_item(PyObject *Py_UNUSED(module), PyObject *args)
     return Py_XNewRef(PyList_GET_ITEM(obj, i));
 }
 
+static PyObject *
+list_get_item_ref(PyObject *Py_UNUSED(module), PyObject *args)
+{
+    PyObject *obj;
+    Py_ssize_t i;
+    if (!PyArg_ParseTuple(args, "On", &obj, &i)) {
+        return NULL;
+    }
+    NULLABLE(obj);
+    return PyList_GetItemRef(obj, i);
+}
+
 static PyObject *
 list_setitem(PyObject *Py_UNUSED(module), PyObject *args)
 {
@@ -191,6 +203,7 @@ static PyMethodDef test_methods[] = {
     {"list_get_size", list_get_size, METH_O},
     {"list_getitem", list_getitem, METH_VARARGS},
     {"list_get_item", list_get_item, METH_VARARGS},
+    {"list_get_item_ref", list_get_item_ref, METH_VARARGS},
     {"list_setitem", list_setitem, METH_VARARGS},
     {"list_set_item", list_set_item, METH_VARARGS},
     {"list_insert", list_insert, METH_VARARGS},
index da2b9cc32697ddaf3bb5d71356d220558a38ef12..82a4ba952de07dcc7913af16023daf0e013bcf7d 100644 (file)
@@ -257,6 +257,21 @@ PyList_GetItem(PyObject *op, Py_ssize_t i)
     return ((PyListObject *)op) -> ob_item[i];
 }
 
+PyObject *
+PyList_GetItemRef(PyObject *op, Py_ssize_t i)
+{
+    if (!PyList_Check(op)) {
+        PyErr_SetString(PyExc_TypeError, "expected a list");
+        return NULL;
+    }
+    if (!valid_index(i, Py_SIZE(op))) {
+        _Py_DECLARE_STR(list_err, "list index out of range");
+        PyErr_SetObject(PyExc_IndexError, &_Py_STR(list_err));
+        return NULL;
+    }
+    return Py_NewRef(PyList_GET_ITEM(op, i));
+}
+
 int
 PyList_SetItem(PyObject *op, Py_ssize_t i,
                PyObject *newitem)
index 09ecf98fe56ea62b33c07f56fd596a3c153382b9..aa6bfe2c4022db0bbcbbd7139dd2a7960f515aa9 100755 (executable)
@@ -324,6 +324,7 @@ EXPORT_FUNC(PyIter_Send)
 EXPORT_FUNC(PyList_Append)
 EXPORT_FUNC(PyList_AsTuple)
 EXPORT_FUNC(PyList_GetItem)
+EXPORT_FUNC(PyList_GetItemRef)
 EXPORT_FUNC(PyList_GetSlice)
 EXPORT_FUNC(PyList_Insert)
 EXPORT_FUNC(PyList_New)