Skip to content

Commit d5fd400

Browse files
committed
gh-111138: Test the public PyList C API
Add test_capi.test_list tests.
1 parent 102685c commit d5fd400

File tree

7 files changed

+412
-1
lines changed

7 files changed

+412
-1
lines changed

Lib/test/test_capi/test_list.py

+233
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
import unittest
2+
from collections import UserList
3+
import _testcapi
4+
5+
6+
NULL = None
7+
PY_SSIZE_T_MIN = _testcapi.PY_SSIZE_T_MIN
8+
PY_SSIZE_T_MAX = _testcapi.PY_SSIZE_T_MAX
9+
10+
PyList_Check = _testcapi.list_check
11+
PyList_CheckExact = _testcapi.list_checkexact
12+
PyList_Size = _testcapi.list_size
13+
PyList_GetItem = _testcapi.list_getitem
14+
PyList_SetItem = _testcapi.list_setitem
15+
PyList_Insert = _testcapi.list_insert
16+
PyList_Append = _testcapi.list_append
17+
PyList_Sort = _testcapi.list_sort
18+
PyList_AsTuple = _testcapi.list_astuple
19+
PyList_Reverse = _testcapi.list_reverse
20+
PyList_GetSlice = _testcapi.list_getslice
21+
PyList_SetSlice = _testcapi.list_setslice
22+
23+
24+
class ListSubclass(list):
25+
pass
26+
27+
28+
class CAPITest(unittest.TestCase):
29+
30+
def test_list_check(self):
31+
self.assertTrue(PyList_Check([2, 3, 5]))
32+
# CRASHES self.assertFalse(PyList_Check(NULL))
33+
34+
def test_list_checkexact(self):
35+
self.assertTrue(PyList_CheckExact([2, 3, 5]))
36+
# CRASHES self.assertFalse(PyList_CheckExact(NULL))
37+
38+
def test_list_new(self):
39+
expected = [None, None, None]
40+
41+
PyList_New = _testcapi.list_new
42+
lst = PyList_New(3)
43+
self.assertEqual(lst, expected)
44+
self.assertIs(type(lst), list)
45+
lst2 = PyList_New(3)
46+
self.assertIsNot(lst2, lst)
47+
48+
with self.assertRaises(SystemError):
49+
PyList_New(-1)
50+
51+
def test_list_size(self):
52+
self.assertEqual(PyList_Size([]), 0)
53+
self.assertEqual(PyList_Size([2, 3, 5]), 3)
54+
55+
def test_list_getitem(self):
56+
lst = list("abc")
57+
self.assertEqual(PyList_GetItem(lst, 0), "a")
58+
self.assertEqual(PyList_GetItem(lst, 1), "b")
59+
self.assertEqual(PyList_GetItem(lst, 2), "c")
60+
61+
for invalid_index in (PY_SSIZE_T_MIN, -1, len(lst), 10, PY_SSIZE_T_MAX):
62+
with self.subTest(invalid_index=invalid_index):
63+
self.assertRaises(IndexError, PyList_GetItem, lst, invalid_index)
64+
65+
lst2 = ListSubclass(lst)
66+
self.assertEqual(PyList_GetItem(lst2, 1), "b")
67+
self.assertRaises(IndexError, PyList_GetItem, lst2, len(lst2))
68+
69+
# CRASHES PyList_GetItem(NULL, 0)
70+
71+
def test_list_setitem(self):
72+
lst = list("abc")
73+
PyList_SetItem(lst, 1, "X")
74+
self.assertEqual(lst, ["a", "X", "c"])
75+
self.assertRaises(IndexError, PyList_SetItem, lst, 3, "Y")
76+
77+
lst2 = ListSubclass(list("abc"))
78+
PyList_SetItem(lst2, 1, "X")
79+
self.assertEqual(lst2, ["a", "X", "c"])
80+
81+
# CRASHES PyList_SetItem(list("abc"), 0, NULL)
82+
# CRASHES PyList_SetItem(NULL, 0, 'x')
83+
84+
def test_list_insert(self):
85+
lst = list("abc")
86+
PyList_Insert(lst, 1, "X")
87+
self.assertEqual(lst, ["a", "X", "b", "c"])
88+
PyList_Insert(lst, -1, "Y")
89+
self.assertEqual(lst, ["a", "X", "b", "Y", "c"])
90+
PyList_Insert(lst, 100, "Z")
91+
self.assertEqual(lst, ["a", "X", "b", "Y", "c", "Z"])
92+
PyList_Insert(lst, -100, "0")
93+
self.assertEqual(lst, ["0", "a", "X", "b", "Y", "c", "Z"])
94+
95+
lst2 = ListSubclass(list("abc"))
96+
PyList_Insert(lst2, 1, "X")
97+
self.assertEqual(lst2, ["a", "X", "b", "c"])
98+
99+
with self.assertRaises(SystemError):
100+
PyList_Insert(list("abc"), 0, NULL)
101+
102+
# CRASHES PyList_Insert(NULL, 0, 'x')
103+
104+
def test_list_append(self):
105+
lst = []
106+
PyList_Append(lst, "a")
107+
self.assertEqual(lst, ["a"])
108+
PyList_Append(lst, "b")
109+
self.assertEqual(lst, ["a", "b"])
110+
111+
lst2 = ListSubclass()
112+
PyList_Append(lst2, "X")
113+
self.assertEqual(lst2, ["X"])
114+
115+
self.assertRaises(SystemError, PyList_Append, [], NULL)
116+
117+
# CRASHES PyList_Append(NULL, 'a')
118+
119+
def test_list_sort(self):
120+
lst = [4, 6, 7, 3, 1, 5, 9, 2, 0, 8]
121+
PyList_Sort(lst)
122+
self.assertEqual(lst, list(range(10)))
123+
124+
lst2 = ListSubclass([4, 6, 7, 3, 1, 5, 9, 2, 0, 8])
125+
PyList_Sort(lst2)
126+
self.assertEqual(lst2, list(range(10)))
127+
128+
with self.assertRaises(SystemError):
129+
PyList_Sort(NULL)
130+
131+
def test_list_astuple(self):
132+
self.assertEqual(PyList_AsTuple([]), ())
133+
self.assertEqual(PyList_AsTuple([2, 5, 10]), (2, 5, 10))
134+
135+
with self.assertRaises(SystemError):
136+
PyList_AsTuple(NULL)
137+
138+
def test_list_reverse(self):
139+
def list_reverse(lst):
140+
self.assertEqual(PyList_Reverse(lst), 0)
141+
return lst
142+
143+
self.assertEqual(list_reverse([]), [])
144+
self.assertEqual(list_reverse([2, 5, 10]), [10, 5, 2])
145+
146+
with self.assertRaises(SystemError):
147+
PyList_Reverse(NULL)
148+
149+
def test_list_getslice(self):
150+
lst = list("abcdef")
151+
152+
# empty
153+
self.assertEqual(PyList_GetSlice(lst, -100, 0), [])
154+
self.assertEqual(PyList_GetSlice(lst, 0, 0), [])
155+
self.assertEqual(PyList_GetSlice(lst, 3, 0), [])
156+
157+
# slice
158+
self.assertEqual(PyList_GetSlice(lst, 1, 3), list("bc"))
159+
160+
# whole
161+
self.assertEqual(PyList_GetSlice(lst, 0, len(lst)), lst)
162+
self.assertEqual(PyList_GetSlice(lst, 0, 100), lst)
163+
self.assertEqual(PyList_GetSlice(lst, -100, 100), lst)
164+
165+
# CRASHES PyList_GetSlice(NULL, 0, 0)
166+
167+
def test_list_setslice(self):
168+
def set_slice(lst, low, high, value):
169+
lst = lst.copy()
170+
self.assertEqual(PyList_SetSlice(lst, low, high, value), 0)
171+
return lst
172+
173+
# insert items
174+
self.assertEqual(set_slice([], 0, 0, list("abc")), list("abc"))
175+
lst = list("abc")
176+
self.assertEqual(set_slice(lst, 0, 0, ["X"]), list("Xabc"))
177+
self.assertEqual(set_slice(lst, 1, 1, list("XY")), list("aXYbc"))
178+
self.assertEqual(set_slice(lst, len(lst), len(lst), ["X"]), list("abcX"))
179+
self.assertEqual(set_slice(lst, 100, 100, ["X"]), list("abcX"))
180+
181+
# replace items
182+
lst = list("abc")
183+
self.assertEqual(set_slice(lst, -100, 1, list("X")), list("Xbc"))
184+
self.assertEqual(set_slice(lst, 1, 2, list("X")), list("aXc"))
185+
self.assertEqual(set_slice(lst, 1, 3, list("XY")), list("aXY"))
186+
self.assertEqual(set_slice(lst, 0, 3, list("XYZ")), list("XYZ"))
187+
188+
# delete items
189+
lst = list("abcdef")
190+
self.assertEqual(set_slice(lst, 0, len(lst), []), [])
191+
self.assertEqual(set_slice(lst, -100, 100, []), [])
192+
self.assertEqual(set_slice(lst, 1, 5, []), list("af"))
193+
self.assertEqual(set_slice(lst, 3, len(lst), []), list("abc"))
194+
195+
# delete items with NULL
196+
lst = list("abcdef")
197+
self.assertEqual(set_slice(lst, 0, len(lst), NULL), [])
198+
self.assertEqual(set_slice(lst, 3, len(lst), NULL), list("abc"))
199+
200+
# CRASHES PyList_SetSlice(NULL, 0, 0, ["x"])
201+
202+
def test_not_list_objects(self):
203+
for obj in (
204+
123,
205+
UserList([2, 3, 5]),
206+
(2, 3, 5), # tuple
207+
object(),
208+
):
209+
with self.subTest(obj=obj):
210+
self.assertFalse(PyList_Check(obj))
211+
self.assertFalse(PyList_CheckExact(obj))
212+
with self.assertRaises(SystemError):
213+
PyList_Size(obj)
214+
with self.assertRaises(SystemError):
215+
PyList_SetItem(obj, 0, "x")
216+
with self.assertRaises(SystemError):
217+
PyList_Insert(obj, 0, "x")
218+
with self.assertRaises(SystemError):
219+
PyList_Append(obj, "x")
220+
with self.assertRaises(SystemError):
221+
PyList_Sort(obj)
222+
with self.assertRaises(SystemError):
223+
PyList_AsTuple(obj)
224+
with self.assertRaises(SystemError):
225+
PyList_Reverse(obj)
226+
with self.assertRaises(SystemError):
227+
PyList_GetSlice(obj, 0, 1)
228+
with self.assertRaises(SystemError):
229+
PyList_SetSlice(obj, 0, 1, ["x"])
230+
231+
232+
if __name__ == "__main__":
233+
unittest.main()

Modules/Setup.stdlib.in

+1-1
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@
159159
@MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c
160160
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
161161
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c
162-
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/pyos.c _testcapi/immortal.c _testcapi/heaptype_relative.c _testcapi/gc.c _testcapi/sys.c
162+
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/list.c _testcapi/set.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/pyos.c _testcapi/immortal.c _testcapi/heaptype_relative.c _testcapi/gc.c _testcapi/sys.c
163163
@MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
164164
@MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c
165165

0 commit comments

Comments
 (0)