Skip to content

Commit d0f1307

Browse files
authored
gh-114329: Add PyList_GetItemRef function (GH-114504)
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 0e71a29 commit d0f1307

File tree

12 files changed

+68
-11
lines changed

12 files changed

+68
-11
lines changed

Doc/c-api/list.rst

+10-2
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,21 @@ List Objects
5656
Similar to :c:func:`PyList_Size`, but without error checking.
5757
5858
59-
.. c:function:: PyObject* PyList_GetItem(PyObject *list, Py_ssize_t index)
59+
.. c:function:: PyObject* PyList_GetItemRef(PyObject *list, Py_ssize_t index)
6060
6161
Return the object at position *index* in the list pointed to by *list*. The
6262
position must be non-negative; indexing from the end of the list is not
63-
supported. If *index* is out of bounds (<0 or >=len(list)),
63+
supported. If *index* is out of bounds (:code:`<0 or >=len(list)`),
6464
return ``NULL`` and set an :exc:`IndexError` exception.
6565
66+
.. versionadded:: 3.13
67+
68+
69+
.. c:function:: PyObject* PyList_GetItem(PyObject *list, Py_ssize_t index)
70+
71+
Like :c:func:`PyList_GetItemRef`, but returns a
72+
:term:`borrowed reference` instead of a :term:`strong reference`.
73+
6674
6775
.. c:function:: PyObject* PyList_GET_ITEM(PyObject *list, Py_ssize_t i)
6876

Doc/data/refcounts.dat

+4
Original file line numberDiff line numberDiff line change
@@ -1133,6 +1133,10 @@ PyList_GetItem:PyObject*::0:
11331133
PyList_GetItem:PyObject*:list:0:
11341134
PyList_GetItem:Py_ssize_t:index::
11351135

1136+
PyList_GetItemRef:PyObject*::+1:
1137+
PyList_GetItemRef:PyObject*:list:0:
1138+
PyList_GetItemRef:Py_ssize_t:index::
1139+
11361140
PyList_GetSlice:PyObject*::+1:
11371141
PyList_GetSlice:PyObject*:list:0:
11381142
PyList_GetSlice:Py_ssize_t:low::

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
@@ -1376,6 +1376,10 @@ New Features
13761376
UTF-8 encoded bytes string, rather than a :c:expr:`PyObject*`.
13771377
(Contributed by Victor Stinner in :gh:`108314`.)
13781378

1379+
* Added :c:func:`PyList_GetItemRef` function: similar to
1380+
:c:func:`PyList_GetItem` but returns a :term:`strong reference` instead of
1381+
a :term:`borrowed reference`.
1382+
13791383
* Add :c:func:`Py_IsFinalizing` function: check if the main Python interpreter is
13801384
:term:`shutting down <interpreter shutdown>`.
13811385
(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

+13-9
Original file line numberDiff line numberDiff line change
@@ -82,23 +82,28 @@ def test_list_get_size(self):
8282
# CRASHES size(UserList())
8383
# CRASHES size(NULL)
8484

85-
86-
def test_list_getitem(self):
87-
# Test PyList_GetItem()
88-
getitem = _testcapi.list_getitem
85+
def check_list_get_item(self, getitem, exctype):
86+
# Common test cases for PyList_GetItem() and PyList_GetItemRef()
8987
lst = [1, 2, 3]
9088
self.assertEqual(getitem(lst, 0), 1)
9189
self.assertEqual(getitem(lst, 2), 3)
9290
self.assertRaises(IndexError, getitem, lst, 3)
9391
self.assertRaises(IndexError, getitem, lst, -1)
9492
self.assertRaises(IndexError, getitem, lst, PY_SSIZE_T_MIN)
9593
self.assertRaises(IndexError, getitem, lst, PY_SSIZE_T_MAX)
96-
self.assertRaises(SystemError, getitem, 42, 1)
97-
self.assertRaises(SystemError, getitem, (1, 2, 3), 1)
98-
self.assertRaises(SystemError, getitem, {1: 2}, 1)
99-
94+
self.assertRaises(exctype, getitem, 42, 1)
95+
self.assertRaises(exctype, getitem, (1, 2, 3), 1)
96+
self.assertRaises(exctype, getitem, {1: 2}, 1)
10097
# CRASHES getitem(NULL, 1)
10198

99+
def test_list_getitem(self):
100+
# Test PyList_GetItem()
101+
self.check_list_get_item(_testcapi.list_getitem, SystemError)
102+
103+
def test_list_get_item_ref(self):
104+
# Test PyList_GetItemRef()
105+
self.check_list_get_item(_testcapi.list_get_item_ref, TypeError)
106+
102107
def test_list_get_item(self):
103108
# Test PyList_GET_ITEM()
104109
get_item = _testcapi.list_get_item
@@ -112,7 +117,6 @@ def test_list_get_item(self):
112117
# CRASHES get_item(21, 2)
113118
# CRASHES get_item(NULL, 1)
114119

115-
116120
def test_list_setitem(self):
117121
# Test PyList_SetItem()
118122
setitem = _testcapi.list_setitem

Lib/test/test_stable_abi_ctypes.py

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
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
@@ -2487,3 +2487,5 @@
24872487
abi_only = true
24882488
[data.PyExc_IncompleteInputError]
24892489
added = '3.13'
2490+
[function.PyList_GetItemRef]
2491+
added = '3.13'

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
@@ -257,6 +257,21 @@ PyList_GetItem(PyObject *op, Py_ssize_t i)
257257
return ((PyListObject *)op) -> ob_item[i];
258258
}
259259

260+
PyObject *
261+
PyList_GetItemRef(PyObject *op, Py_ssize_t i)
262+
{
263+
if (!PyList_Check(op)) {
264+
PyErr_SetString(PyExc_TypeError, "expected a list");
265+
return NULL;
266+
}
267+
if (!valid_index(i, Py_SIZE(op))) {
268+
_Py_DECLARE_STR(list_err, "list index out of range");
269+
PyErr_SetObject(PyExc_IndexError, &_Py_STR(list_err));
270+
return NULL;
271+
}
272+
return Py_NewRef(PyList_GET_ITEM(op, i));
273+
}
274+
260275
int
261276
PyList_SetItem(PyObject *op, Py_ssize_t i,
262277
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)