Skip to content

Commit 6a269c1

Browse files
committed
Document and test finalizer modifying the list
1 parent 50c22cc commit 6a269c1

File tree

2 files changed

+46
-5
lines changed

2 files changed

+46
-5
lines changed

Doc/c-api/list.rst

+12
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ List Objects
5959
Raise an exception and return ``-1`` if *list* is not a :class:`list`
6060
object. Return 0 on success.
6161
62+
.. note::
63+
64+
If a finalizer (:meth:`object.__del__`) of a deleted item modify the
65+
list, it is possible that the list is not empty after calling this
66+
function.
67+
6268
.. versionadded:: 3.13
6369
6470
@@ -149,6 +155,12 @@ List Objects
149155
Return ``0`` on success, ``-1`` on failure. Indexing from the end of the
150156
list is not supported.
151157
158+
.. note::
159+
160+
If *itemlist* is ``NULL`` to delete items and a finalizer
161+
(:meth:`object.__del__`) of a deleted item modify the list, it is
162+
possible that the list is longer than expected.
163+
152164
153165
.. c:function:: int PyList_Sort(PyObject *list)
154166

Lib/test/test_capi/test_list.py

+34-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

@@ -291,6 +314,12 @@ def test_list_clear(self):
291314
self.assertRaises(TypeError, list_clear, ())
292315
self.assertRaises(TypeError, list_clear, object())
293316

317+
# Item finalizer modify the list
318+
lst = []
319+
lst.append(DelAppend(lst, 'zombie'))
320+
list_clear(lst)
321+
self.assertEqual(lst, ['zombie'])
322+
294323
# CRASHES list_clear(NULL)
295324

296325
def test_list_extend(self):

0 commit comments

Comments
 (0)