[3.13] gh-111495: Add tests for PyTuple C API (GH-118757) by skirpichev · Pull Request #123371 · python/cpython
@@ -0,0 +1,261 @@
import unittest
import sys
from collections import namedtuple
from test.support import import_helper
_testcapi = import_helper.import_module('_testcapi') _testlimitedcapi = import_helper.import_module('_testlimitedcapi')
NULL = None PY_SSIZE_T_MIN = _testcapi.PY_SSIZE_T_MIN PY_SSIZE_T_MAX = _testcapi.PY_SSIZE_T_MAX
class TupleSubclass(tuple): pass
class CAPITest(unittest.TestCase): def test_check(self): # Test PyTuple_Check() check = _testlimitedcapi.tuple_check
self.assertTrue(check((1, 2))) self.assertTrue(check(())) self.assertTrue(check(TupleSubclass((1, 2)))) self.assertFalse(check({1: 2})) self.assertFalse(check([1, 2])) self.assertFalse(check(42)) self.assertFalse(check(object()))
# CRASHES check(NULL)
def test_tuple_checkexact(self): # Test PyTuple_CheckExact() check = _testlimitedcapi.tuple_checkexact
self.assertTrue(check((1, 2))) self.assertTrue(check(())) self.assertFalse(check(TupleSubclass((1, 2)))) self.assertFalse(check({1: 2})) self.assertFalse(check([1, 2])) self.assertFalse(check(42)) self.assertFalse(check(object()))
# CRASHES check(NULL)
def test_tuple_new(self): # Test PyTuple_New() tuple_new = _testlimitedcapi.tuple_new size = _testlimitedcapi.tuple_size checknull = _testcapi._check_tuple_item_is_NULL
tup1 = tuple_new(0) self.assertEqual(tup1, ()) self.assertEqual(size(tup1), 0) self.assertIs(type(tup1), tuple) tup2 = tuple_new(1) self.assertIs(type(tup2), tuple) self.assertEqual(size(tup2), 1) self.assertIsNot(tup2, tup1) self.assertTrue(checknull(tup2, 0))
self.assertRaises(SystemError, tuple_new, -1) self.assertRaises(SystemError, tuple_new, PY_SSIZE_T_MIN) self.assertRaises(MemoryError, tuple_new, PY_SSIZE_T_MAX)
def test_tuple_pack(self): # Test PyTuple_Pack() pack = _testlimitedcapi.tuple_pack
self.assertEqual(pack(0), ()) self.assertEqual(pack(1, [1]), ([1],)) self.assertEqual(pack(2, [1], [2]), ([1], [2]))
self.assertRaises(SystemError, pack, PY_SSIZE_T_MIN) self.assertRaises(SystemError, pack, -1) self.assertRaises(MemoryError, pack, PY_SSIZE_T_MAX)
# CRASHES pack(1, NULL) # CRASHES pack(2, [1])
def test_tuple_size(self): # Test PyTuple_Size() size = _testlimitedcapi.tuple_size
self.assertEqual(size(()), 0) self.assertEqual(size((1, 2)), 2) self.assertEqual(size(TupleSubclass((1, 2))), 2)
self.assertRaises(SystemError, size, []) self.assertRaises(SystemError, size, 42) self.assertRaises(SystemError, size, object())
# CRASHES size(NULL)
def test_tuple_get_size(self): # Test PyTuple_GET_SIZE() size = _testcapi.tuple_get_size
self.assertEqual(size(()), 0) self.assertEqual(size((1, 2)), 2) self.assertEqual(size(TupleSubclass((1, 2))), 2)
def test_tuple_getitem(self): # Test PyTuple_GetItem() getitem = _testlimitedcapi.tuple_getitem
tup = ([1], [2], [3]) self.assertEqual(getitem(tup, 0), [1]) self.assertEqual(getitem(tup, 2), [3])
tup2 = TupleSubclass(([1], [2], [3])) self.assertEqual(getitem(tup2, 0), [1]) self.assertEqual(getitem(tup2, 2), [3])
self.assertRaises(IndexError, getitem, tup, PY_SSIZE_T_MIN) self.assertRaises(IndexError, getitem, tup, -1) self.assertRaises(IndexError, getitem, tup, len(tup)) self.assertRaises(IndexError, getitem, tup, PY_SSIZE_T_MAX) self.assertRaises(SystemError, getitem, [1, 2, 3], 1) self.assertRaises(SystemError, getitem, 42, 1)
# CRASHES getitem(NULL, 0)
def test_tuple_get_item(self): # Test PyTuple_GET_ITEM() get_item = _testcapi.tuple_get_item
tup = ([1], [2], [3]) self.assertEqual(get_item(tup, 0), [1]) self.assertEqual(get_item(tup, 2), [3])
tup2 = TupleSubclass(([1], [2], [3])) self.assertEqual(get_item(tup2, 0), [1]) self.assertEqual(get_item(tup2, 2), [3])
# CRASHES get_item(NULL, 0)
def test_tuple_getslice(self): # Test PyTuple_GetSlice() getslice = _testlimitedcapi.tuple_getslice
# empty tup = ([1], [2], [3]) self.assertEqual(getslice(tup, PY_SSIZE_T_MIN, 0), ()) self.assertEqual(getslice(tup, -1, 0), ()) self.assertEqual(getslice(tup, 3, PY_SSIZE_T_MAX), ()) self.assertEqual(getslice(tup, 1, 1), ()) self.assertEqual(getslice(tup, 2, 1), ()) tup = TupleSubclass(([1], [2], [3])) self.assertEqual(getslice(tup, PY_SSIZE_T_MIN, 0), ()) self.assertEqual(getslice(tup, -1, 0), ()) self.assertEqual(getslice(tup, 3, PY_SSIZE_T_MAX), ()) self.assertEqual(getslice(tup, 1, 1), ()) self.assertEqual(getslice(tup, 2, 1), ())
# slice tup = ([1], [2], [3], [4]) self.assertEqual(getslice(tup, 1, 3), ([2], [3])) tup = TupleSubclass(([1], [2], [3], [4])) self.assertEqual(getslice(tup, 1, 3), ([2], [3]))
# whole tup = ([1], [2], [3]) self.assertEqual(getslice(tup, 0, 3), tup) self.assertEqual(getslice(tup, 0, 100), tup) self.assertEqual(getslice(tup, -100, 100), tup) tup = TupleSubclass(([1], [2], [3])) self.assertEqual(getslice(tup, 0, 3), tup) self.assertEqual(getslice(tup, 0, 100), tup) self.assertEqual(getslice(tup, -100, 100), tup)
self.assertRaises(SystemError, getslice, [[1], [2], [3]], 0, 0) self.assertRaises(SystemError, getslice, 42, 0, 0)
# CRASHES getslice(NULL, 0, 0)
def test_tuple_setitem(self): # Test PyTuple_SetItem() setitem = _testlimitedcapi.tuple_setitem checknull = _testcapi._check_tuple_item_is_NULL
tup = ([1], [2]) self.assertEqual(setitem(tup, 0, []), ([], [2])) self.assertEqual(setitem(tup, 1, []), ([1], []))
tup2 = setitem(tup, 1, NULL) self.assertTrue(checknull(tup2, 1))
tup2 = TupleSubclass(([1], [2])) self.assertRaises(SystemError, setitem, tup2, 0, [])
self.assertRaises(IndexError, setitem, tup, PY_SSIZE_T_MIN, []) self.assertRaises(IndexError, setitem, tup, -1, []) self.assertRaises(IndexError, setitem, tup, len(tup), []) self.assertRaises(IndexError, setitem, tup, PY_SSIZE_T_MAX, []) self.assertRaises(SystemError, setitem, [1], 0, []) self.assertRaises(SystemError, setitem, 42, 0, [])
# CRASHES setitem(NULL, 0, [])
def test_tuple_set_item(self): # Test PyTuple_SET_ITEM() set_item = _testcapi.tuple_set_item checknull = _testcapi._check_tuple_item_is_NULL
tup = ([1], [2]) self.assertEqual(set_item(tup, 0, []), ([], [2])) self.assertEqual(set_item(tup, 1, []), ([1], []))
tup2 = set_item(tup, 1, NULL) self.assertTrue(checknull(tup2, 1))
tup2 = TupleSubclass(([1], [2])) self.assertIs(set_item(tup2, 0, []), tup2) self.assertEqual(tup2, ([], [2]))
# CRASHES set_item(tup, -1, []) # CRASHES set_item(tup, len(tup), []) # CRASHES set_item([1], 0, []) # CRASHES set_item(NULL, 0, [])
def test__tuple_resize(self): # Test _PyTuple_Resize() resize = _testcapi._tuple_resize checknull = _testcapi._check_tuple_item_is_NULL
a = () b = resize(a, 0, False) self.assertEqual(len(a), 0) self.assertEqual(len(b), 0) b = resize(a, 2, False) self.assertEqual(len(a), 0) self.assertEqual(len(b), 2) self.assertTrue(checknull(b, 0)) self.assertTrue(checknull(b, 1))
a = ([1], [2], [3]) b = resize(a, 3) self.assertEqual(b, a) b = resize(a, 2) self.assertEqual(b, a[:2]) b = resize(a, 5) self.assertEqual(len(b), 5) self.assertEqual(b[:3], a) self.assertTrue(checknull(b, 3)) self.assertTrue(checknull(b, 4))
a = () self.assertRaises(MemoryError, resize, a, PY_SSIZE_T_MAX) self.assertRaises(SystemError, resize, a, -1) self.assertRaises(SystemError, resize, a, PY_SSIZE_T_MIN) # refcount > 1 a = (1, 2, 3) self.assertRaises(SystemError, resize, a, 3, False) self.assertRaises(SystemError, resize, a, 0, False) # non-tuple self.assertRaises(SystemError, resize, [1, 2, 3], 0, False) self.assertRaises(SystemError, resize, NULL, 0, False)
if __name__ == "__main__": unittest.main()
_testcapi = import_helper.import_module('_testcapi') _testlimitedcapi = import_helper.import_module('_testlimitedcapi')
NULL = None PY_SSIZE_T_MIN = _testcapi.PY_SSIZE_T_MIN PY_SSIZE_T_MAX = _testcapi.PY_SSIZE_T_MAX
class TupleSubclass(tuple): pass
class CAPITest(unittest.TestCase): def test_check(self): # Test PyTuple_Check() check = _testlimitedcapi.tuple_check
self.assertTrue(check((1, 2))) self.assertTrue(check(())) self.assertTrue(check(TupleSubclass((1, 2)))) self.assertFalse(check({1: 2})) self.assertFalse(check([1, 2])) self.assertFalse(check(42)) self.assertFalse(check(object()))
# CRASHES check(NULL)
def test_tuple_checkexact(self): # Test PyTuple_CheckExact() check = _testlimitedcapi.tuple_checkexact
self.assertTrue(check((1, 2))) self.assertTrue(check(())) self.assertFalse(check(TupleSubclass((1, 2)))) self.assertFalse(check({1: 2})) self.assertFalse(check([1, 2])) self.assertFalse(check(42)) self.assertFalse(check(object()))
# CRASHES check(NULL)
def test_tuple_new(self): # Test PyTuple_New() tuple_new = _testlimitedcapi.tuple_new size = _testlimitedcapi.tuple_size checknull = _testcapi._check_tuple_item_is_NULL
tup1 = tuple_new(0) self.assertEqual(tup1, ()) self.assertEqual(size(tup1), 0) self.assertIs(type(tup1), tuple) tup2 = tuple_new(1) self.assertIs(type(tup2), tuple) self.assertEqual(size(tup2), 1) self.assertIsNot(tup2, tup1) self.assertTrue(checknull(tup2, 0))
self.assertRaises(SystemError, tuple_new, -1) self.assertRaises(SystemError, tuple_new, PY_SSIZE_T_MIN) self.assertRaises(MemoryError, tuple_new, PY_SSIZE_T_MAX)
def test_tuple_pack(self): # Test PyTuple_Pack() pack = _testlimitedcapi.tuple_pack
self.assertEqual(pack(0), ()) self.assertEqual(pack(1, [1]), ([1],)) self.assertEqual(pack(2, [1], [2]), ([1], [2]))
self.assertRaises(SystemError, pack, PY_SSIZE_T_MIN) self.assertRaises(SystemError, pack, -1) self.assertRaises(MemoryError, pack, PY_SSIZE_T_MAX)
# CRASHES pack(1, NULL) # CRASHES pack(2, [1])
def test_tuple_size(self): # Test PyTuple_Size() size = _testlimitedcapi.tuple_size
self.assertEqual(size(()), 0) self.assertEqual(size((1, 2)), 2) self.assertEqual(size(TupleSubclass((1, 2))), 2)
self.assertRaises(SystemError, size, []) self.assertRaises(SystemError, size, 42) self.assertRaises(SystemError, size, object())
# CRASHES size(NULL)
def test_tuple_get_size(self): # Test PyTuple_GET_SIZE() size = _testcapi.tuple_get_size
self.assertEqual(size(()), 0) self.assertEqual(size((1, 2)), 2) self.assertEqual(size(TupleSubclass((1, 2))), 2)
def test_tuple_getitem(self): # Test PyTuple_GetItem() getitem = _testlimitedcapi.tuple_getitem
tup = ([1], [2], [3]) self.assertEqual(getitem(tup, 0), [1]) self.assertEqual(getitem(tup, 2), [3])
tup2 = TupleSubclass(([1], [2], [3])) self.assertEqual(getitem(tup2, 0), [1]) self.assertEqual(getitem(tup2, 2), [3])
self.assertRaises(IndexError, getitem, tup, PY_SSIZE_T_MIN) self.assertRaises(IndexError, getitem, tup, -1) self.assertRaises(IndexError, getitem, tup, len(tup)) self.assertRaises(IndexError, getitem, tup, PY_SSIZE_T_MAX) self.assertRaises(SystemError, getitem, [1, 2, 3], 1) self.assertRaises(SystemError, getitem, 42, 1)
# CRASHES getitem(NULL, 0)
def test_tuple_get_item(self): # Test PyTuple_GET_ITEM() get_item = _testcapi.tuple_get_item
tup = ([1], [2], [3]) self.assertEqual(get_item(tup, 0), [1]) self.assertEqual(get_item(tup, 2), [3])
tup2 = TupleSubclass(([1], [2], [3])) self.assertEqual(get_item(tup2, 0), [1]) self.assertEqual(get_item(tup2, 2), [3])
# CRASHES get_item(NULL, 0)
def test_tuple_getslice(self): # Test PyTuple_GetSlice() getslice = _testlimitedcapi.tuple_getslice
# empty tup = ([1], [2], [3]) self.assertEqual(getslice(tup, PY_SSIZE_T_MIN, 0), ()) self.assertEqual(getslice(tup, -1, 0), ()) self.assertEqual(getslice(tup, 3, PY_SSIZE_T_MAX), ()) self.assertEqual(getslice(tup, 1, 1), ()) self.assertEqual(getslice(tup, 2, 1), ()) tup = TupleSubclass(([1], [2], [3])) self.assertEqual(getslice(tup, PY_SSIZE_T_MIN, 0), ()) self.assertEqual(getslice(tup, -1, 0), ()) self.assertEqual(getslice(tup, 3, PY_SSIZE_T_MAX), ()) self.assertEqual(getslice(tup, 1, 1), ()) self.assertEqual(getslice(tup, 2, 1), ())
# slice tup = ([1], [2], [3], [4]) self.assertEqual(getslice(tup, 1, 3), ([2], [3])) tup = TupleSubclass(([1], [2], [3], [4])) self.assertEqual(getslice(tup, 1, 3), ([2], [3]))
# whole tup = ([1], [2], [3]) self.assertEqual(getslice(tup, 0, 3), tup) self.assertEqual(getslice(tup, 0, 100), tup) self.assertEqual(getslice(tup, -100, 100), tup) tup = TupleSubclass(([1], [2], [3])) self.assertEqual(getslice(tup, 0, 3), tup) self.assertEqual(getslice(tup, 0, 100), tup) self.assertEqual(getslice(tup, -100, 100), tup)
self.assertRaises(SystemError, getslice, [[1], [2], [3]], 0, 0) self.assertRaises(SystemError, getslice, 42, 0, 0)
# CRASHES getslice(NULL, 0, 0)
def test_tuple_setitem(self): # Test PyTuple_SetItem() setitem = _testlimitedcapi.tuple_setitem checknull = _testcapi._check_tuple_item_is_NULL
tup = ([1], [2]) self.assertEqual(setitem(tup, 0, []), ([], [2])) self.assertEqual(setitem(tup, 1, []), ([1], []))
tup2 = setitem(tup, 1, NULL) self.assertTrue(checknull(tup2, 1))
tup2 = TupleSubclass(([1], [2])) self.assertRaises(SystemError, setitem, tup2, 0, [])
self.assertRaises(IndexError, setitem, tup, PY_SSIZE_T_MIN, []) self.assertRaises(IndexError, setitem, tup, -1, []) self.assertRaises(IndexError, setitem, tup, len(tup), []) self.assertRaises(IndexError, setitem, tup, PY_SSIZE_T_MAX, []) self.assertRaises(SystemError, setitem, [1], 0, []) self.assertRaises(SystemError, setitem, 42, 0, [])
# CRASHES setitem(NULL, 0, [])
def test_tuple_set_item(self): # Test PyTuple_SET_ITEM() set_item = _testcapi.tuple_set_item checknull = _testcapi._check_tuple_item_is_NULL
tup = ([1], [2]) self.assertEqual(set_item(tup, 0, []), ([], [2])) self.assertEqual(set_item(tup, 1, []), ([1], []))
tup2 = set_item(tup, 1, NULL) self.assertTrue(checknull(tup2, 1))
tup2 = TupleSubclass(([1], [2])) self.assertIs(set_item(tup2, 0, []), tup2) self.assertEqual(tup2, ([], [2]))
# CRASHES set_item(tup, -1, []) # CRASHES set_item(tup, len(tup), []) # CRASHES set_item([1], 0, []) # CRASHES set_item(NULL, 0, [])
def test__tuple_resize(self): # Test _PyTuple_Resize() resize = _testcapi._tuple_resize checknull = _testcapi._check_tuple_item_is_NULL
a = () b = resize(a, 0, False) self.assertEqual(len(a), 0) self.assertEqual(len(b), 0) b = resize(a, 2, False) self.assertEqual(len(a), 0) self.assertEqual(len(b), 2) self.assertTrue(checknull(b, 0)) self.assertTrue(checknull(b, 1))
a = ([1], [2], [3]) b = resize(a, 3) self.assertEqual(b, a) b = resize(a, 2) self.assertEqual(b, a[:2]) b = resize(a, 5) self.assertEqual(len(b), 5) self.assertEqual(b[:3], a) self.assertTrue(checknull(b, 3)) self.assertTrue(checknull(b, 4))
a = () self.assertRaises(MemoryError, resize, a, PY_SSIZE_T_MAX) self.assertRaises(SystemError, resize, a, -1) self.assertRaises(SystemError, resize, a, PY_SSIZE_T_MIN) # refcount > 1 a = (1, 2, 3) self.assertRaises(SystemError, resize, a, 3, False) self.assertRaises(SystemError, resize, a, 0, False) # non-tuple self.assertRaises(SystemError, resize, [1, 2, 3], 0, False) self.assertRaises(SystemError, resize, NULL, 0, False)
if __name__ == "__main__": unittest.main()