Skip to content

Commit babb787

Browse files
authored
gh-111138: Add PyList_Extend() and PyList_Clear() functions (#111862)
* Split list_extend() into two sub-functions: list_extend_fast() and list_extend_iter(). * list_inplace_concat() no longer has to call Py_DECREF() on the list_extend() result, since list_extend() now returns an int.
1 parent 29af736 commit babb787

File tree

8 files changed

+303
-127
lines changed

8 files changed

+303
-127
lines changed

Doc/c-api/list.rst

+24
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,30 @@ List Objects
128128
list is not supported.
129129
130130
131+
.. c:function:: int PyList_Extend(PyObject *list, PyObject *iterable)
132+
133+
Extend *list* with the contents of *iterable*. This is the same as
134+
``PyList_SetSlice(list, PY_SSIZE_T_MAX, PY_SSIZE_T_MAX, iterable)``
135+
and analogous to ``list.extend(iterable)`` or ``list += iterable``.
136+
137+
Raise an exception and return ``-1`` if *list* is not a :class:`list`
138+
object. Return 0 on success.
139+
140+
.. versionadded:: 3.13
141+
142+
143+
.. c:function:: int PyList_Clear(PyObject *list)
144+
145+
Remove all items from *list*. This is the same as
146+
``PyList_SetSlice(list, 0, PY_SSIZE_T_MAX, NULL)`` and analogous to
147+
``list.clear()`` or ``del list[:]``.
148+
149+
Raise an exception and return ``-1`` if *list* is not a :class:`list`
150+
object. Return 0 on success.
151+
152+
.. versionadded:: 3.13
153+
154+
131155
.. c:function:: int PyList_Sort(PyObject *list)
132156
133157
Sort the items of *list* in place. Return ``0`` on success, ``-1`` on

Doc/whatsnew/3.13.rst

+4
Original file line numberDiff line numberDiff line change
@@ -1164,6 +1164,10 @@ New Features
11641164
:c:func:`PyErr_WriteUnraisable`, but allow to customize the warning mesage.
11651165
(Contributed by Serhiy Storchaka in :gh:`108082`.)
11661166

1167+
* Add :c:func:`PyList_Extend` and :c:func:`PyList_Clear` functions: similar to
1168+
Python ``list.extend()`` and ``list.clear()`` methods.
1169+
(Contributed by Victor Stinner in :gh:`111138`.)
1170+
11671171

11681172
Porting to Python 3.13
11691173
----------------------

Include/cpython/listobject.h

+3
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,6 @@ PyList_SET_ITEM(PyObject *op, Py_ssize_t index, PyObject *value) {
4444
}
4545
#define PyList_SET_ITEM(op, index, value) \
4646
PyList_SET_ITEM(_PyObject_CAST(op), (index), _PyObject_CAST(value))
47+
48+
PyAPI_FUNC(int) PyList_Extend(PyObject *self, PyObject *iterable);
49+
PyAPI_FUNC(int) PyList_Clear(PyObject *self);

Lib/test/test_capi/test_list.py

+72-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
import gc
2+
import weakref
13
import unittest
2-
import sys
34
from test.support import import_helper
45
from collections import UserList
56
_testcapi = import_helper.import_module('_testcapi')
@@ -12,6 +13,15 @@ class ListSubclass(list):
1213
pass
1314

1415

16+
class DelAppend:
17+
def __init__(self, lst, item):
18+
self.lst = lst
19+
self.item = item
20+
21+
def __del__(self):
22+
self.lst.append(self.item)
23+
24+
1525
class CAPITest(unittest.TestCase):
1626
def test_check(self):
1727
# Test PyList_Check()
@@ -196,10 +206,10 @@ def test_list_getslice(self):
196206

197207
def test_list_setslice(self):
198208
# Test PyList_SetSlice()
199-
setslice = _testcapi.list_setslice
209+
list_setslice = _testcapi.list_setslice
200210
def set_slice(lst, low, high, value):
201211
lst = lst.copy()
202-
self.assertEqual(setslice(lst, low, high, value), 0)
212+
self.assertEqual(list_setslice(lst, low, high, value), 0)
203213
return lst
204214

205215
# insert items
@@ -231,8 +241,21 @@ def set_slice(lst, low, high, value):
231241
self.assertEqual(set_slice(lst, 0, len(lst), NULL), [])
232242
self.assertEqual(set_slice(lst, 3, len(lst), NULL), list("abc"))
233243

234-
self.assertRaises(SystemError, setslice, (), 0, 0, [])
235-
self.assertRaises(SystemError, setslice, 42, 0, 0, [])
244+
self.assertRaises(SystemError, list_setslice, (), 0, 0, [])
245+
self.assertRaises(SystemError, list_setslice, 42, 0, 0, [])
246+
247+
# Item finalizer modify the list (clear the list)
248+
lst = []
249+
lst.append(DelAppend(lst, 'zombie'))
250+
self.assertEqual(list_setslice(lst, 0, len(lst), NULL), 0)
251+
self.assertEqual(lst, ['zombie'])
252+
253+
# Item finalizer modify the list (remove an list item)
254+
lst = []
255+
lst.append(DelAppend(lst, 'zombie'))
256+
lst.extend("abc")
257+
self.assertEqual(list_setslice(lst, 0, 1, NULL), 0)
258+
self.assertEqual(lst, ['a', 'b', 'c', 'zombie'])
236259

237260
# CRASHES setslice(NULL, 0, 0, [])
238261

@@ -275,3 +298,47 @@ def test_list_astuple(self):
275298
self.assertRaises(SystemError, astuple, ())
276299
self.assertRaises(SystemError, astuple, object())
277300
self.assertRaises(SystemError, astuple, NULL)
301+
302+
def test_list_clear(self):
303+
# Test PyList_Clear()
304+
list_clear = _testcapi.list_clear
305+
306+
lst = [1, 2, 3]
307+
self.assertEqual(list_clear(lst), 0)
308+
self.assertEqual(lst, [])
309+
310+
lst = []
311+
self.assertEqual(list_clear(lst), 0)
312+
self.assertEqual(lst, [])
313+
314+
self.assertRaises(SystemError, list_clear, ())
315+
self.assertRaises(SystemError, list_clear, object())
316+
317+
# Item finalizer modify the list
318+
lst = []
319+
lst.append(DelAppend(lst, 'zombie'))
320+
list_clear(lst)
321+
self.assertEqual(lst, ['zombie'])
322+
323+
# CRASHES list_clear(NULL)
324+
325+
def test_list_extend(self):
326+
# Test PyList_Extend()
327+
list_extend = _testcapi.list_extend
328+
329+
for other_type in (list, tuple, str, iter):
330+
lst = list("ab")
331+
arg = other_type("def")
332+
self.assertEqual(list_extend(lst, arg), 0)
333+
self.assertEqual(lst, list("abdef"))
334+
335+
# PyList_Extend(lst, lst)
336+
lst = list("abc")
337+
self.assertEqual(list_extend(lst, lst), 0)
338+
self.assertEqual(lst, list("abcabc"))
339+
340+
self.assertRaises(TypeError, list_extend, [], object())
341+
self.assertRaises(SystemError, list_extend, (), list("abc"))
342+
343+
# CRASHES list_extend(NULL, [])
344+
# CRASHES list_extend([], NULL)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add :c:func:`PyList_Extend` and :c:func:`PyList_Clear` functions: similar to
2+
Python ``list.extend()`` and ``list.clear()`` methods. Patch by Victor Stinner.

Modules/_testcapi/list.c

+21
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,25 @@ list_astuple(PyObject* Py_UNUSED(module), PyObject *obj)
162162
}
163163

164164

165+
static PyObject *
166+
list_clear(PyObject* Py_UNUSED(module), PyObject *obj)
167+
{
168+
NULLABLE(obj);
169+
RETURN_INT(PyList_Clear(obj));
170+
}
171+
172+
173+
static PyObject *
174+
list_extend(PyObject* Py_UNUSED(module), PyObject *args)
175+
{
176+
PyObject *obj, *arg;
177+
if (!PyArg_ParseTuple(args, "OO", &obj, &arg)) {
178+
return NULL;
179+
}
180+
NULLABLE(obj);
181+
NULLABLE(arg);
182+
RETURN_INT(PyList_Extend(obj, arg));
183+
}
165184

166185

167186
static PyMethodDef test_methods[] = {
@@ -181,6 +200,8 @@ static PyMethodDef test_methods[] = {
181200
{"list_sort", list_sort, METH_O},
182201
{"list_reverse", list_reverse, METH_O},
183202
{"list_astuple", list_astuple, METH_O},
203+
{"list_clear", list_clear, METH_O},
204+
{"list_extend", list_extend, METH_VARARGS},
184205
{NULL},
185206
};
186207

Objects/clinic/listobject.c.h

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

0 commit comments

Comments
 (0)