Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

gh-111138: Add PyList_Extend() and PyList_Clear() functions #111862

Merged
merged 1 commit into from
Nov 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions Doc/c-api/list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,30 @@ List Objects
list is not supported.


.. c:function:: int PyList_Extend(PyObject *list, PyObject *iterable)

Extend *list* with the contents of *iterable*. This is the same as
``PyList_SetSlice(list, PY_SSIZE_T_MAX, PY_SSIZE_T_MAX, iterable)``
and analogous to ``list.extend(iterable)`` or ``list += iterable``.

Raise an exception and return ``-1`` if *list* is not a :class:`list`
object. Return 0 on success.

.. versionadded:: 3.13


.. c:function:: int PyList_Clear(PyObject *list)

Remove all items from *list*. This is the same as
``PyList_SetSlice(list, 0, PY_SSIZE_T_MAX, NULL)`` and analogous to
``list.clear()`` or ``del list[:]``.

Raise an exception and return ``-1`` if *list* is not a :class:`list`
object. Return 0 on success.

.. versionadded:: 3.13


.. c:function:: int PyList_Sort(PyObject *list)

Sort the items of *list* in place. Return ``0`` on success, ``-1`` on
Expand Down
4 changes: 4 additions & 0 deletions Doc/whatsnew/3.13.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1164,6 +1164,10 @@ New Features
:c:func:`PyErr_WriteUnraisable`, but allow to customize the warning mesage.
(Contributed by Serhiy Storchaka in :gh:`108082`.)

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


Porting to Python 3.13
----------------------
Expand Down
3 changes: 3 additions & 0 deletions Include/cpython/listobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,6 @@ PyList_SET_ITEM(PyObject *op, Py_ssize_t index, PyObject *value) {
}
#define PyList_SET_ITEM(op, index, value) \
PyList_SET_ITEM(_PyObject_CAST(op), (index), _PyObject_CAST(value))

PyAPI_FUNC(int) PyList_Extend(PyObject *self, PyObject *iterable);
PyAPI_FUNC(int) PyList_Clear(PyObject *self);
77 changes: 72 additions & 5 deletions Lib/test/test_capi/test_list.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import gc
import weakref
import unittest
import sys
from test.support import import_helper
from collections import UserList
_testcapi = import_helper.import_module('_testcapi')
Expand All @@ -12,6 +13,15 @@ class ListSubclass(list):
pass


class DelAppend:
def __init__(self, lst, item):
self.lst = lst
self.item = item

def __del__(self):
self.lst.append(self.item)


class CAPITest(unittest.TestCase):
def test_check(self):
# Test PyList_Check()
Expand Down Expand Up @@ -196,10 +206,10 @@ def test_list_getslice(self):

def test_list_setslice(self):
# Test PyList_SetSlice()
setslice = _testcapi.list_setslice
list_setslice = _testcapi.list_setslice
def set_slice(lst, low, high, value):
lst = lst.copy()
self.assertEqual(setslice(lst, low, high, value), 0)
self.assertEqual(list_setslice(lst, low, high, value), 0)
return lst

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

self.assertRaises(SystemError, setslice, (), 0, 0, [])
self.assertRaises(SystemError, setslice, 42, 0, 0, [])
self.assertRaises(SystemError, list_setslice, (), 0, 0, [])
self.assertRaises(SystemError, list_setslice, 42, 0, 0, [])

# Item finalizer modify the list (clear the list)
lst = []
lst.append(DelAppend(lst, 'zombie'))
self.assertEqual(list_setslice(lst, 0, len(lst), NULL), 0)
self.assertEqual(lst, ['zombie'])

# Item finalizer modify the list (remove an list item)
lst = []
lst.append(DelAppend(lst, 'zombie'))
lst.extend("abc")
self.assertEqual(list_setslice(lst, 0, 1, NULL), 0)
self.assertEqual(lst, ['a', 'b', 'c', 'zombie'])

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

Expand Down Expand Up @@ -275,3 +298,47 @@ def test_list_astuple(self):
self.assertRaises(SystemError, astuple, ())
self.assertRaises(SystemError, astuple, object())
self.assertRaises(SystemError, astuple, NULL)

def test_list_clear(self):
# Test PyList_Clear()
list_clear = _testcapi.list_clear

lst = [1, 2, 3]
self.assertEqual(list_clear(lst), 0)
self.assertEqual(lst, [])

lst = []
self.assertEqual(list_clear(lst), 0)
self.assertEqual(lst, [])

self.assertRaises(SystemError, list_clear, ())
self.assertRaises(SystemError, list_clear, object())

# Item finalizer modify the list
lst = []
lst.append(DelAppend(lst, 'zombie'))
list_clear(lst)
self.assertEqual(lst, ['zombie'])

# CRASHES list_clear(NULL)

def test_list_extend(self):
# Test PyList_Extend()
list_extend = _testcapi.list_extend

for other_type in (list, tuple, str, iter):
lst = list("ab")
arg = other_type("def")
self.assertEqual(list_extend(lst, arg), 0)
self.assertEqual(lst, list("abdef"))

# PyList_Extend(lst, lst)
lst = list("abc")
self.assertEqual(list_extend(lst, lst), 0)
self.assertEqual(lst, list("abcabc"))

self.assertRaises(TypeError, list_extend, [], object())
self.assertRaises(SystemError, list_extend, (), list("abc"))

# CRASHES list_extend(NULL, [])
# CRASHES list_extend([], NULL)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add :c:func:`PyList_Extend` and :c:func:`PyList_Clear` functions: similar to
Python ``list.extend()`` and ``list.clear()`` methods. Patch by Victor Stinner.
21 changes: 21 additions & 0 deletions Modules/_testcapi/list.c
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,25 @@ list_astuple(PyObject* Py_UNUSED(module), PyObject *obj)
}


static PyObject *
list_clear(PyObject* Py_UNUSED(module), PyObject *obj)
{
NULLABLE(obj);
RETURN_INT(PyList_Clear(obj));
}


static PyObject *
list_extend(PyObject* Py_UNUSED(module), PyObject *args)
{
PyObject *obj, *arg;
if (!PyArg_ParseTuple(args, "OO", &obj, &arg)) {
return NULL;
}
NULLABLE(obj);
NULLABLE(arg);
RETURN_INT(PyList_Extend(obj, arg));
}


static PyMethodDef test_methods[] = {
Expand All @@ -181,6 +200,8 @@ static PyMethodDef test_methods[] = {
{"list_sort", list_sort, METH_O},
{"list_reverse", list_reverse, METH_O},
{"list_astuple", list_astuple, METH_O},
{"list_clear", list_clear, METH_O},
{"list_extend", list_extend, METH_VARARGS},
{NULL},
};

Expand Down
20 changes: 10 additions & 10 deletions Objects/clinic/listobject.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading