Skip to content

Commit 56f91fe

Browse files
committed
pythongh-111138: Add test_capi.list tests
Test the public PyList C API.
1 parent f6a0232 commit 56f91fe

File tree

7 files changed

+395
-1
lines changed

7 files changed

+395
-1
lines changed

Lib/test/test_capi/test_list.py

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
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_not_list_objects(self):
31+
for obj in (
32+
123,
33+
UserList([2, 3, 5]),
34+
(2, 3, 5), # tuple
35+
object(),
36+
):
37+
with self.subTest(obj=obj):
38+
self.assertFalse(PyList_Check(obj))
39+
self.assertFalse(PyList_CheckExact(obj))
40+
with self.assertRaises(SystemError):
41+
PyList_Size(obj)
42+
with self.assertRaises(SystemError):
43+
PyList_SetItem(obj, 0, "x")
44+
with self.assertRaises(SystemError):
45+
PyList_Insert(obj, 0, "x")
46+
with self.assertRaises(SystemError):
47+
PyList_Append(obj, "x")
48+
with self.assertRaises(SystemError):
49+
PyList_Sort(obj)
50+
with self.assertRaises(SystemError):
51+
PyList_AsTuple(obj)
52+
with self.assertRaises(SystemError):
53+
PyList_Reverse(obj)
54+
with self.assertRaises(SystemError):
55+
PyList_GetSlice(obj, 0, 1)
56+
with self.assertRaises(SystemError):
57+
PyList_SetSlice(obj, 0, 1, ["x"])
58+
59+
def test_list_check(self):
60+
self.assertTrue(PyList_Check([2, 3, 5]))
61+
#self.assertFalse(PyList_Check(NULL))
62+
63+
def test_list_checkexact(self):
64+
self.assertTrue(PyList_CheckExact([2, 3, 5]))
65+
#self.assertFalse(check(NULL))
66+
67+
def test_list_new(self):
68+
expected = [None, None, None]
69+
70+
PyList_New = _testcapi.list_new
71+
lst = PyList_New(3)
72+
self.assertEqual(lst, expected)
73+
self.assertIs(type(lst), list)
74+
lst2 = PyList_New(3)
75+
self.assertIsNot(lst2, lst)
76+
77+
def test_list_size(self):
78+
self.assertEqual(PyList_Size([2, 3, 5]), 3)
79+
80+
def test_list_getitem(self):
81+
lst = list("abc")
82+
self.assertEqual(PyList_GetItem(lst, 0), "a")
83+
self.assertEqual(PyList_GetItem(lst, 1), "b")
84+
self.assertEqual(PyList_GetItem(lst, 2), "c")
85+
86+
for invalid_index in (PY_SSIZE_T_MIN, -1, 3, PY_SSIZE_T_MAX):
87+
with self.subTest(invalid_index=invalid_index):
88+
self.assertRaises(IndexError, PyList_GetItem, lst, invalid_index)
89+
90+
lst2 = ListSubclass(lst)
91+
self.assertEqual(PyList_GetItem(lst2, 1), "b")
92+
self.assertRaises(IndexError, PyList_GetItem, lst2, 3)
93+
94+
# CRASHES PyList_GetItem(NULL, 0)
95+
96+
def test_list_setitem(self):
97+
lst = list("abc")
98+
PyList_SetItem(lst, 1, "X")
99+
self.assertEqual(lst, ["a", "X", "c"])
100+
self.assertRaises(IndexError, PyList_SetItem, lst, 3, "Y")
101+
102+
lst2 = ListSubclass(list("abc"))
103+
PyList_SetItem(lst2, 1, "X")
104+
self.assertEqual(lst2, ["a", "X", "c"])
105+
106+
# CRASHES PyList_SetItem(list("abc"), 0, NULL)
107+
# CRASHES PyList_SetItem(NULL, 0, 'x')
108+
109+
def test_list_insert(self):
110+
lst = list("abc")
111+
PyList_Insert(lst, 1, "X")
112+
self.assertEqual(lst, ["a", "X", "b", "c"])
113+
PyList_Insert(lst, -1, "Y")
114+
self.assertEqual(lst, ["a", "X", "b", "Y", "c"])
115+
PyList_Insert(lst, 100, "Z")
116+
self.assertEqual(lst, ["a", "X", "b", "Y", "c", "Z"])
117+
PyList_Insert(lst, -100, "0")
118+
self.assertEqual(lst, ["0", "a", "X", "b", "Y", "c", "Z"])
119+
120+
lst2 = ListSubclass(list("abc"))
121+
PyList_Insert(lst2, 1, "X")
122+
self.assertEqual(lst2, ["a", "X", "b", "c"])
123+
124+
with self.assertRaises(SystemError):
125+
PyList_Insert(list("abc"), 0, NULL)
126+
127+
# CRASHES PyList_Insert(NULL, 0, 'x')
128+
129+
def test_list_append(self):
130+
131+
lst = []
132+
PyList_Append(lst, "a")
133+
self.assertEqual(lst, ["a"])
134+
PyList_Append(lst, "b")
135+
self.assertEqual(lst, ["a", "b"])
136+
137+
lst2 = ListSubclass()
138+
PyList_Append(lst2, "X")
139+
self.assertEqual(lst2, ["X"])
140+
141+
self.assertRaises(SystemError, PyList_Append, [], NULL)
142+
143+
# CRASHES PyList_Append(NULL, 'a')
144+
145+
def test_list_sort(self):
146+
lst = [4, 6, 7, 3, 1, 5, 9, 2, 0, 8]
147+
PyList_Sort(lst)
148+
self.assertEqual(lst, list(range(10)))
149+
150+
lst2 = ListSubclass([4, 6, 7, 3, 1, 5, 9, 2, 0, 8])
151+
PyList_Sort(lst2)
152+
self.assertEqual(lst2, list(range(10)))
153+
154+
self.assertRaises(SystemError, PyList_Sort, NULL)
155+
156+
def test_list_astuple(self):
157+
self.assertEqual(PyList_AsTuple([]), ())
158+
self.assertEqual(PyList_AsTuple([2, 5, 10]), (2, 5, 10))
159+
160+
def test_list_reverse(self):
161+
def list_reverse(lst):
162+
self.assertEqual(PyList_Reverse(lst), 0)
163+
return lst
164+
165+
self.assertEqual(list_reverse([]), [])
166+
self.assertEqual(list_reverse([2, 5, 10]), [10, 5, 2])
167+
168+
def test_list_getslice(self):
169+
lst = list("abcdef")
170+
self.assertEqual(PyList_GetSlice(lst, -100, 0), [])
171+
self.assertEqual(PyList_GetSlice(lst, 0, 0), [])
172+
173+
self.assertEqual(PyList_GetSlice(lst, 1, 3), list("bc"))
174+
self.assertEqual(PyList_GetSlice(lst, 0, len(lst)), lst)
175+
self.assertEqual(PyList_GetSlice(lst, 0, 100), lst)
176+
self.assertEqual(PyList_GetSlice(lst, -100, 100), lst)
177+
178+
self.assertEqual(PyList_GetSlice(lst, 100, 0), [])
179+
180+
def test_list_setslice(self):
181+
def set_slice(lst, low, high, value):
182+
lst = lst.copy()
183+
self.assertEqual(PyList_SetSlice(lst, low, high, value), 0)
184+
return lst
185+
186+
# insert items
187+
self.assertEqual(set_slice([], 0, 0, list("abc")), list("abc"))
188+
lst = list("abc")
189+
self.assertEqual(set_slice(lst, 0, 0, ["X"]), list("Xabc"))
190+
self.assertEqual(set_slice(lst, 1, 1, list("XY")), list("aXYbc"))
191+
self.assertEqual(set_slice(lst, len(lst), len(lst), ["X"]), list("abcX"))
192+
self.assertEqual(set_slice(lst, 100, 100, ["X"]), list("abcX"))
193+
194+
# replace items
195+
lst = list("abc")
196+
self.assertEqual(set_slice(lst, -100, 1, list("X")), list("Xbc"))
197+
self.assertEqual(set_slice(lst, 1, 2, list("X")), list("aXc"))
198+
self.assertEqual(set_slice(lst, 1, 3, list("XY")), list("aXY"))
199+
self.assertEqual(set_slice(lst, 0, 3, list("XYZ")), list("XYZ"))
200+
201+
# delete items
202+
lst = list("abcdef")
203+
self.assertEqual(set_slice(lst, 0, len(lst), []), [])
204+
self.assertEqual(set_slice(lst, -100, 100, []), [])
205+
self.assertEqual(set_slice(lst, 1, 5, []), list("af"))
206+
self.assertEqual(set_slice(lst, 3, len(lst), []), list("abc"))
207+
208+
209+
if __name__ == "__main__":
210+
unittest.main()

Modules/Setup.stdlib.in

Lines changed: 1 addition & 1 deletion
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

Modules/_testcapi/list.c

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
// clinic/list.c.h uses internal pycore_abstract.h API
2+
#define PYTESTCAPI_NEED_INTERNAL_API
3+
4+
#include "parts.h"
5+
#include "util.h"
6+
7+
static PyObject *
8+
list_check(PyObject *self, PyObject *obj)
9+
{
10+
NULLABLE(obj);
11+
return PyLong_FromLong(PyList_Check(obj));
12+
}
13+
14+
static PyObject *
15+
list_checkexact(PyObject *self, PyObject *obj)
16+
{
17+
NULLABLE(obj);
18+
return PyLong_FromLong(PyList_CheckExact(obj));
19+
}
20+
21+
static PyObject *
22+
list_new(PyObject *self, PyObject *args)
23+
{
24+
Py_ssize_t n;
25+
if (!PyArg_ParseTuple(args, "n", &n)) {
26+
return NULL;
27+
}
28+
29+
PyObject *list = PyList_New(n);
30+
if (list == NULL) {
31+
return NULL;
32+
}
33+
34+
for (Py_ssize_t i=0; i < n; i++) {
35+
PyList_SET_ITEM(list, i, Py_NewRef(Py_None));
36+
}
37+
return list;
38+
}
39+
40+
static PyObject *
41+
list_size(PyObject *self, PyObject *obj)
42+
{
43+
NULLABLE(obj);
44+
RETURN_SIZE(PyList_Size(obj));
45+
}
46+
47+
static PyObject *
48+
list_getitem(PyObject *self, PyObject *args)
49+
{
50+
PyObject *list;
51+
Py_ssize_t index;
52+
if (!PyArg_ParseTuple(args, "On", &list, &index)) {
53+
return NULL;
54+
}
55+
NULLABLE(list);
56+
57+
PyObject *value = PyList_GetItem(list, index);
58+
if (value == NULL) {
59+
if (PyErr_Occurred()) {
60+
return NULL;
61+
}
62+
return Py_NewRef(PyExc_IndexError);
63+
}
64+
return Py_NewRef(value);
65+
}
66+
67+
static PyObject *
68+
list_setitem(PyObject *self, PyObject *args)
69+
{
70+
PyObject *list, *item;
71+
Py_ssize_t index;
72+
if (!PyArg_ParseTuple(args, "OnO", &list, &index, &item)) {
73+
return NULL;
74+
}
75+
NULLABLE(list);
76+
NULLABLE(item);
77+
RETURN_INT(PyList_SetItem(list, index, item));
78+
}
79+
80+
static PyObject *
81+
list_insert(PyObject *self, PyObject *args)
82+
{
83+
PyObject *list, *item;
84+
Py_ssize_t index;
85+
if (!PyArg_ParseTuple(args, "OnO", &list, &index, &item)) {
86+
return NULL;
87+
}
88+
NULLABLE(list);
89+
NULLABLE(item);
90+
RETURN_INT(PyList_Insert(list, index, item));
91+
}
92+
93+
static PyObject *
94+
list_append(PyObject *self, PyObject *args)
95+
{
96+
PyObject *list, *item;
97+
if (!PyArg_ParseTuple(args, "OO", &list, &item)) {
98+
return NULL;
99+
}
100+
NULLABLE(list);
101+
NULLABLE(item);
102+
RETURN_INT(PyList_Append(list, item));
103+
}
104+
105+
static PyObject *
106+
list_sort(PyObject *self, PyObject *obj)
107+
{
108+
NULLABLE(obj);
109+
RETURN_INT(PyList_Sort(obj));
110+
}
111+
112+
static PyObject *
113+
list_astuple(PyObject *self, PyObject *obj)
114+
{
115+
NULLABLE(obj);
116+
return PyList_AsTuple(obj);
117+
}
118+
119+
static PyObject *
120+
list_reverse(PyObject *self, PyObject *obj)
121+
{
122+
NULLABLE(obj);
123+
RETURN_INT(PyList_Reverse(obj));
124+
}
125+
126+
static PyObject *
127+
list_getslice(PyObject *self, PyObject *args)
128+
{
129+
PyObject *list;
130+
Py_ssize_t low, high;
131+
if (!PyArg_ParseTuple(args, "Onn", &list, &low, &high)) {
132+
return NULL;
133+
}
134+
NULLABLE(list);
135+
return PyList_GetSlice(list, low, high);
136+
}
137+
138+
static PyObject *
139+
list_setslice(PyObject *self, PyObject *args)
140+
{
141+
PyObject *list, *value;
142+
Py_ssize_t low, high;
143+
if (!PyArg_ParseTuple(args, "OnnO", &list, &low, &high, &value)) {
144+
return NULL;
145+
}
146+
NULLABLE(list);
147+
RETURN_INT(PyList_SetSlice(list, low, high, value));
148+
}
149+
150+
151+
static PyMethodDef test_methods[] = {
152+
{"list_check", list_check, METH_O},
153+
{"list_checkexact", list_checkexact, METH_O},
154+
{"list_new", list_new, METH_VARARGS},
155+
{"list_size", list_size, METH_O},
156+
{"list_getitem", list_getitem, METH_VARARGS},
157+
{"list_setitem", list_setitem, METH_VARARGS},
158+
{"list_insert", list_insert, METH_VARARGS},
159+
{"list_append", list_append, METH_VARARGS},
160+
{"list_sort", list_sort, METH_O},
161+
{"list_astuple", list_astuple, METH_O},
162+
{"list_reverse", list_reverse, METH_O},
163+
{"list_getslice", list_getslice, METH_VARARGS},
164+
{"list_setslice", list_setslice, METH_VARARGS},
165+
{NULL},
166+
};
167+
168+
int
169+
_PyTestCapi_Init_List(PyObject *m)
170+
{
171+
if (PyModule_AddFunctions(m, test_methods) < 0) {
172+
return -1;
173+
}
174+
175+
return 0;
176+
}

0 commit comments

Comments
 (0)