Skip to content

Commit 8f96d08

Browse files
committed
pythongh-114329: Add PyList_GetItemRef function
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()`.
1 parent ce01ab5 commit 8f96d08

File tree

10 files changed

+64
-0
lines changed

10 files changed

+64
-0
lines changed

Doc/c-api/list.rst

+15
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,21 @@ List Objects
6464
return ``NULL`` and set an :exc:`IndexError` exception.
6565
6666
67+
.. c:function:: PyObject* PyList_GetItemRef(PyObject *list, Py_ssize_t index)
68+
69+
Return a :term:`strong reference` to the object at position *index* in the
70+
list pointed to by *list*. The position must be non-negative; indexing from
71+
the end of the list is not supported. If *index* is out of bounds
72+
(<0 or >=len(list)), return ``NULL`` and set an :exc:`IndexError` exception.
73+
If *list* is not a :class:`list` object, return ``NULL`` and set a
74+
:exc:`TypeError` exception.
75+
76+
This behaves like :c:func:`PyList_GetItem`, but returns a
77+
:term:`strong reference` instead of a :term:`borrowed reference`.
78+
79+
.. versionadded:: 3.13
80+
81+
6782
.. c:function:: PyObject* PyList_GET_ITEM(PyObject *list, Py_ssize_t i)
6883
6984
Similar to :c:func:`PyList_GetItem`, but without error checking.

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.13.rst

+4
Original file line numberDiff line numberDiff line change
@@ -1305,6 +1305,10 @@ New Features
13051305
UTF-8 encoded bytes string, rather than a :c:expr:`PyObject*`.
13061306
(Contributed by Victor Stinner in :gh:`108314`.)
13071307

1308+
* Added :c:func:`PyList_GetItemRef` function: similar to
1309+
:c:func:`PyList_GetItem` but returns a :term:`strong reference` instead of
1310+
a :term:`borrowed reference`.
1311+
13081312
* Add :c:func:`Py_IsFinalizing` function: check if the main Python interpreter is
13091313
:term:`shutting down <interpreter shutdown>`.
13101314
(Contributed by Victor Stinner in :gh:`108014`.)

Include/listobject.h

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ PyAPI_FUNC(PyObject *) PyList_New(Py_ssize_t size);
2929
PyAPI_FUNC(Py_ssize_t) PyList_Size(PyObject *);
3030

3131
PyAPI_FUNC(PyObject *) PyList_GetItem(PyObject *, Py_ssize_t);
32+
PyAPI_FUNC(PyObject *) PyList_GetItemRef(PyObject *, Py_ssize_t);
3233
PyAPI_FUNC(int) PyList_SetItem(PyObject *, Py_ssize_t, PyObject *);
3334
PyAPI_FUNC(int) PyList_Insert(PyObject *, Py_ssize_t, PyObject *);
3435
PyAPI_FUNC(int) PyList_Append(PyObject *, PyObject *);

Lib/test/test_capi/test_list.py

+9
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,15 @@ def test_list_get_item(self):
112112
# CRASHES get_item(21, 2)
113113
# CRASHES get_item(NULL, 1)
114114

115+
def test_list_get_item_ref(self):
116+
# Test PyList_GetItemRef()
117+
get_item_ref = _testcapi.list_get_item_ref
118+
lst = [1, 2, [1, 2, 3]]
119+
self.assertEqual(get_item_ref(lst, 0), 1)
120+
self.assertEqual(get_item_ref(lst, 2), [1, 2, 3])
121+
self.assertRaises(IndexError, get_item_ref, lst, 3)
122+
self.assertRaises(IndexError, get_item_ref, lst, -1)
123+
self.assertRaises(TypeError, get_item_ref, "not a list", 0)
115124

116125
def test_list_setitem(self):
117126
# Test PyList_SetItem()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Add :c:func:`PyList_GetItemRef`, which is similar to
2+
:c:func:`PyList_GetItem` but returns a :term:`strong reference` instead of a
3+
:term:`borrowed reference`.

Misc/stable_abi.toml

+2
Original file line numberDiff line numberDiff line change
@@ -905,6 +905,8 @@
905905
added = '3.2'
906906
[function.PyList_GetItem]
907907
added = '3.2'
908+
[function.PyList_GetItemRef]
909+
added = '3.13'
908910
[function.PyList_GetSlice]
909911
added = '3.2'
910912
[function.PyList_Insert]

Modules/_testcapi/list.c

+13
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,18 @@ list_get_item(PyObject *Py_UNUSED(module), PyObject *args)
5959
return Py_XNewRef(PyList_GET_ITEM(obj, i));
6060
}
6161

62+
static PyObject *
63+
list_get_item_ref(PyObject *Py_UNUSED(module), PyObject *args)
64+
{
65+
PyObject *obj;
66+
Py_ssize_t i;
67+
if (!PyArg_ParseTuple(args, "On", &obj, &i)) {
68+
return NULL;
69+
}
70+
NULLABLE(obj);
71+
return PyList_GetItemRef(obj, i);
72+
}
73+
6274
static PyObject *
6375
list_setitem(PyObject *Py_UNUSED(module), PyObject *args)
6476
{
@@ -191,6 +203,7 @@ static PyMethodDef test_methods[] = {
191203
{"list_get_size", list_get_size, METH_O},
192204
{"list_getitem", list_getitem, METH_VARARGS},
193205
{"list_get_item", list_get_item, METH_VARARGS},
206+
{"list_get_item_ref", list_get_item_ref, METH_VARARGS},
194207
{"list_setitem", list_setitem, METH_VARARGS},
195208
{"list_set_item", list_set_item, METH_VARARGS},
196209
{"list_insert", list_insert, METH_VARARGS},

Objects/listobject.c

+15
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,21 @@ PyList_GetItem(PyObject *op, Py_ssize_t i)
253253
return ((PyListObject *)op) -> ob_item[i];
254254
}
255255

256+
PyObject *
257+
PyList_GetItemRef(PyObject *op, Py_ssize_t i)
258+
{
259+
if (!PyList_Check(op)) {
260+
PyErr_SetString(PyExc_TypeError, "expected a list");
261+
return NULL;
262+
}
263+
if (!valid_index(i, Py_SIZE(op))) {
264+
_Py_DECLARE_STR(list_err, "list index out of range");
265+
PyErr_SetObject(PyExc_IndexError, &_Py_STR(list_err));
266+
return NULL;
267+
}
268+
return Py_NewRef(PyList_GET_ITEM(op, i));
269+
}
270+
256271
int
257272
PyList_SetItem(PyObject *op, Py_ssize_t i,
258273
PyObject *newitem)

PC/python3dll.c

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

0 commit comments

Comments
 (0)