Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

gh-111495: Add tests for PyTuple C API #111606

Closed
wants to merge 11 commits into from
Closed
160 changes: 160 additions & 0 deletions Lib/test/test_capi/test_tuple.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import unittest
import sys
from collections import namedtuple
from test.support import import_helper
_testcapi = import_helper.import_module('_testcapi')

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 = _testcapi.tuple_check
self.assertTrue(check((1, 2)))
self.assertTrue(check(()))
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 = _testcapi.tuple_checkexact
self.assertTrue(check((1, 2)))
self.assertTrue(check(()))
self.assertFalse(check(TupleSubclass((1, 2))))
self.assertFalse(check({1: 2}))
self.assertFalse(check(object()))

# CRASHES check(NULL)

def test_tuple_new(self):
# Test PyTuple_New()
tuple_new = _testcapi.tuple_new
tup = tuple_new(0)
self.assertEqual(tup, ())
self.assertIs(type(tup), tuple)
tup2 = tuple_new(1)
self.assertIsNot(tup2, tup)
self.assertRaises(SystemError, tuple_new, NULL)
self.assertRaises(SystemError, tuple_new, -1)


def test_tuple_pack(self):
# Test PyTuple_Pack()
pass

def test_tuple_size(self):
# Test PyTuple_Size()
size = _testcapi.tuple_size
self.assertEqual(size((1, 2)), 2)
self.assertEqual(size(TupleSubclass((1, 2))), 2)
self.assertRaises(SystemError, size, {})
self.assertRaises(SystemError, size, 23)
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)
# CRASHES size(object())
# CRASHES size(23)
# CRASHES size({})
# CRASHES size(UserList())
# CRASHES size(NULL)


def test_tuple_getitem(self):
# Test PyTuple_GetItem()
getitem = _testcapi.tuple_getitem
tup = (1, 2, 3)
self.assertEqual(getitem(tup, 0), 1)
self.assertEqual(getitem(tup, len(tup)-1), 3)
self.assertRaises(IndexError, getitem, tup, -1)
self.assertRaises(IndexError, getitem, tup, PY_SSIZE_T_MIN)
self.assertRaises(IndexError, getitem, tup, PY_SSIZE_T_MAX)
self.assertRaises(IndexError, getitem, tup, len(tup))
self.assertRaises(SystemError, getitem, 42, 1)

# # CRASHES getitem(NULL, 1)

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)

# CRASHES for get_item(tup, -1)
# CRASHES for get_item(tup, PY_SSIZE_T_MIN)
# CRASHES for get_item(tup, PY_SSIZE_T_MAX)
# CRASHES for out of index: get_item(tup, 3)
# CRASHES get_item(21, 2)
# CRASHES get_item(Null,1)

def test_tuple_getslice(self):
# Test PyTuple_GetSlice()
getslice = _testcapi.tuple_getslice
tup = (1,2,3)

# empty
self.assertEqual(getslice(tup, PY_SSIZE_T_MIN, 0), ())
self.assertEqual(getslice(tup, -1, 0), ())
self.assertEqual(getslice(tup, 3, PY_SSIZE_T_MAX), ())

# slice
self.assertEqual(getslice(tup, 1, 3), (2, 3))

# whole
self.assertEqual(getslice(tup, 0, len(tup)), tup)
self.assertEqual(getslice(tup, 0, 100), tup)
self.assertEqual(getslice(tup, -100, 100), tup)

self.assertRaises(TypeError, tup, 'a', '2')

# CRASHES getslice(NULL, 0, 0)


def test_tuple_setitem(self):
# Test PyTuple_SetItem()
setitem = _testcapi.tuple_setitem
tup = (1, 2, 3)
self.assertRaises(SystemError, setitem, tup, 1, 0)
self.assertRaises(SystemError, setitem, {}, 0, 5)
self.assertRaises(SystemError, setitem, tup, PY_SSIZE_T_MIN, 5)
self.assertRaises(SystemError, setitem, tup, PY_SSIZE_T_MAX, 5)
self.assertRaises(SystemError, setitem, tup, -1, 5)
self.assertRaises(SystemError, setitem, tup, len(tup) , 5)
self.assertRaises(TypeError, setitem, 23, 'a', 5)
self.assertRaises(TypeError, setitem, tup, 1.5, 10)

# CRASHES setitem(NULL, 'a', 5)

def test_tuple_set_item(self):
# Test PyTuple_SET_ITEM()
set_item = _testcapi.tuple_set_item
tup = (1, 2, 3)
set_item(tup, 1, (1, 2))
self.assertEqual(tup, (1, (1, 2), 3))

# CRASHES for set_item([1], PY_SSIZE_T_MIN, 5)
# CRASHES for set_item([1], PY_SSIZE_T_MAX, 5)
# CRASHES for set_item([1], -1, 5)
# CRASHES for set_item([], 0, 1)
# CRASHES for set_item(NULL, 0, 1)

def test_tuple_resize(self):
# Test PyTuple_Resize()
pass
133 changes: 133 additions & 0 deletions Modules/_testcapi/tuple.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,140 @@
#include "util.h"


static PyObject *
tuple_check(PyObject* Py_UNUSED(module), PyObject *obj)
{
NULLABLE(obj);
return PyLong_FromLong(PyTuple_Check(obj));
}

static PyObject *
tuple_checkexact(PyObject* Py_UNUSED(module), PyObject *obj)
{
NULLABLE(obj);
return PyLong_FromLong(PyTuple_CheckExact(obj));
}

static PyObject *
tuple_new(PyObject* Py_UNUSED(module), PyObject *obj)
{
return PyTuple_New(PyLong_AsSsize_t(obj));
}

static PyObject *
tuple_pack(PyObject *Py_UNUSED(module), PyObject *args)
{
Py_RETURN_NONE;
}

static PyObject *
tuple_size(PyObject *Py_UNUSED(module), PyObject *obj)
{
NULLABLE(obj);
RETURN_SIZE(PyTuple_Size(obj));
}

static PyObject *
tuple_get_size(PyObject *Py_UNUSED(module), PyObject *obj)
{
NULLABLE(obj);
RETURN_SIZE(PyTuple_GET_SIZE(obj));
}

static PyObject *
tuple_getitem(PyObject *Py_UNUSED(module), PyObject *args)
{
PyObject *obj;
Py_ssize_t i;
if (!PyArg_ParseTuple(args, "On", &obj, &i)){
return NULL;
}
NULLABLE(obj);
return Py_XNewRef(PyTuple_GetItem(obj, i));
}

static PyObject *
tuple_get_item(PyObject *Py_UNUSED(module), PyObject *args)
{
PyObject *obj;
Py_ssize_t i;
if (!PyArg_ParseTuple(args, "On", &obj, &i)){
return NULL;
}
NULLABLE(obj);
return Py_XNewRef(PyTuple_GET_ITEM(obj, i));
}

static PyObject *
tuple_getslice(PyObject *Py_UNUSED(module), PyObject *args)
{
PyObject *obj;
Py_ssize_t ilow, ihigh;
if ( !PyArg_ParseTuple(args, "Onn", &obj, &ilow, &ihigh)){
return NULL;
}
NULLABLE(obj);
return PyTuple_GetSlice(obj, ilow, ihigh);

}

static PyObject *
tuple_setitem(PyObject *Py_UNUSED(module), PyObject *args)
{
PyObject *obj, *value;
Py_ssize_t i;
if ( !PyArg_ParseTuple(args, "OnO", &obj, &i, &value)){
return NULL;
}
NULLABLE(obj);
NULLABLE(value);
RETURN_INT(PyTuple_SetItem(obj, i, Py_XNewRef(value)));

}

static PyObject *
tuple_set_item(PyObject *Py_UNUSED(module), PyObject *args)
{
PyObject *obj, *value;
Py_ssize_t i;
if ( !PyArg_ParseTuple(args, "OnO", &obj, &i, &value)){
return NULL;
}
NULLABLE(obj);
NULLABLE(value);
PyTuple_SET_ITEM(obj, i, Py_XNewRef(value));
Py_RETURN_NONE;

}

static PyObject *
tuple_resize(PyObject *Py_UNUSED(module), PyObject *args)
{
PyObject *obj;
Py_ssize_t newsize;
if ( !PyArg_ParseTuple(args, "On", &obj, &newsize)){
return NULL;
}
NULLABLE(obj);
RETURN_INT(_PyTuple_Resize(obj, newsize));

Check warning on line 120 in Modules/_testcapi/tuple.c

View workflow job for this annotation

GitHub Actions / Address sanitizer

passing argument 1 of ‘_PyTuple_Resize’ from incompatible pointer type [-Wincompatible-pointer-types]

Check warning on line 120 in Modules/_testcapi/tuple.c

View workflow job for this annotation

GitHub Actions / Ubuntu SSL tests with OpenSSL (1.1.1w)

passing argument 1 of ‘_PyTuple_Resize’ from incompatible pointer type [-Wincompatible-pointer-types]

Check warning on line 120 in Modules/_testcapi/tuple.c

View workflow job for this annotation

GitHub Actions / Ubuntu SSL tests with OpenSSL (3.0.13)

passing argument 1 of ‘_PyTuple_Resize’ from incompatible pointer type [-Wincompatible-pointer-types]

Check warning on line 120 in Modules/_testcapi/tuple.c

View workflow job for this annotation

GitHub Actions / Hypothesis tests on Ubuntu

passing argument 1 of ‘_PyTuple_Resize’ from incompatible pointer type [-Wincompatible-pointer-types]

Check warning on line 120 in Modules/_testcapi/tuple.c

View workflow job for this annotation

GitHub Actions / Ubuntu SSL tests with OpenSSL (3.1.5)

passing argument 1 of ‘_PyTuple_Resize’ from incompatible pointer type [-Wincompatible-pointer-types]

Check warning on line 120 in Modules/_testcapi/tuple.c

View workflow job for this annotation

GitHub Actions / Ubuntu SSL tests with OpenSSL (3.2.1)

passing argument 1 of ‘_PyTuple_Resize’ from incompatible pointer type [-Wincompatible-pointer-types]

Check warning on line 120 in Modules/_testcapi/tuple.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / build and test (x64)

'function': 'PyObject **' differs in levels of indirection from 'PyObject *' [D:\a\cpython\cpython\PCbuild\_testcapi.vcxproj]

Check warning on line 120 in Modules/_testcapi/tuple.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / build and test (x64)

'_PyTuple_Resize': different types for formal and actual parameter 1 [D:\a\cpython\cpython\PCbuild\_testcapi.vcxproj]

Check warning on line 120 in Modules/_testcapi/tuple.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / build (arm64)

'function': 'PyObject **' differs in levels of indirection from 'PyObject *' [D:\a\cpython\cpython\PCbuild\_testcapi.vcxproj]

Check warning on line 120 in Modules/_testcapi/tuple.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / build (arm64)

'_PyTuple_Resize': different types for formal and actual parameter 1 [D:\a\cpython\cpython\PCbuild\_testcapi.vcxproj]

Check warning on line 120 in Modules/_testcapi/tuple.c

View workflow job for this annotation

GitHub Actions / Windows / build and test (x64)

'function': 'PyObject **' differs in levels of indirection from 'PyObject *' [D:\a\cpython\cpython\PCbuild\_testcapi.vcxproj]

Check warning on line 120 in Modules/_testcapi/tuple.c

View workflow job for this annotation

GitHub Actions / Windows / build and test (x64)

'_PyTuple_Resize': different types for formal and actual parameter 1 [D:\a\cpython\cpython\PCbuild\_testcapi.vcxproj]

Check warning on line 120 in Modules/_testcapi/tuple.c

View workflow job for this annotation

GitHub Actions / Windows / build (arm64)

'function': 'PyObject **' differs in levels of indirection from 'PyObject *' [D:\a\cpython\cpython\PCbuild\_testcapi.vcxproj]

Check warning on line 120 in Modules/_testcapi/tuple.c

View workflow job for this annotation

GitHub Actions / Windows / build (arm64)

'_PyTuple_Resize': different types for formal and actual parameter 1 [D:\a\cpython\cpython\PCbuild\_testcapi.vcxproj]

Check warning on line 120 in Modules/_testcapi/tuple.c

View workflow job for this annotation

GitHub Actions / Ubuntu / build and test

passing argument 1 of ‘_PyTuple_Resize’ from incompatible pointer type [-Wincompatible-pointer-types]

Check warning on line 120 in Modules/_testcapi/tuple.c

View workflow job for this annotation

GitHub Actions / Ubuntu (free-threading) / build and test

passing argument 1 of ‘_PyTuple_Resize’ from incompatible pointer type [-Wincompatible-pointer-types]

}



static PyMethodDef test_methods[] = {
{"tuple_check", tuple_check, METH_O},
{"tuple_checkexact", tuple_checkexact, METH_O},
{"tuple_new", tuple_new, METH_O},
{"tuple_pack", tuple_pack, METH_VARARGS},
{"tuple_size", tuple_size, METH_O},
{"tuple_get_size", tuple_get_size, METH_O},
{"tuple_getitem", tuple_getitem, METH_VARARGS},
{"tuple_get_item", tuple_get_item, METH_VARARGS},
{"tuple_getslice", tuple_getslice, METH_VARARGS},
{"tuple_setitem", tuple_setitem, METH_VARARGS},
{"tuple_set_item", tuple_set_item, METH_VARARGS},
{"tuple_resize", tuple_resize, METH_VARARGS},
{NULL},
};

Expand Down
Loading