Skip to content

[3.12] gh-110525: Add CAPI tests for set and frozenset objects (GH-110526). #110547

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

Merged
merged 1 commit into from
Oct 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
215 changes: 215 additions & 0 deletions Lib/test/test_capi/test_set.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
import unittest

from test.support import import_helper

# Skip this test if the _testcapi module isn't available.
_testcapi = import_helper.import_module('_testcapi')

class set_subclass(set):
pass

class frozenset_subclass(frozenset):
pass


class TestSetCAPI(unittest.TestCase):
def assertImmutable(self, action, *args):
self.assertRaises(SystemError, action, frozenset(), *args)
self.assertRaises(SystemError, action, frozenset({1}), *args)
self.assertRaises(SystemError, action, frozenset_subclass(), *args)
self.assertRaises(SystemError, action, frozenset_subclass({1}), *args)

def test_set_check(self):
check = _testcapi.set_check
self.assertTrue(check(set()))
self.assertTrue(check({1, 2}))
self.assertFalse(check(frozenset()))
self.assertTrue(check(set_subclass()))
self.assertFalse(check(frozenset_subclass()))
self.assertFalse(check(object()))
# CRASHES: check(NULL)

def test_set_check_exact(self):
check = _testcapi.set_checkexact
self.assertTrue(check(set()))
self.assertTrue(check({1, 2}))
self.assertFalse(check(frozenset()))
self.assertFalse(check(set_subclass()))
self.assertFalse(check(frozenset_subclass()))
self.assertFalse(check(object()))
# CRASHES: check(NULL)

def test_frozenset_check(self):
check = _testcapi.frozenset_check
self.assertFalse(check(set()))
self.assertTrue(check(frozenset()))
self.assertTrue(check(frozenset({1, 2})))
self.assertFalse(check(set_subclass()))
self.assertTrue(check(frozenset_subclass()))
self.assertFalse(check(object()))
# CRASHES: check(NULL)

def test_frozenset_check_exact(self):
check = _testcapi.frozenset_checkexact
self.assertFalse(check(set()))
self.assertTrue(check(frozenset()))
self.assertTrue(check(frozenset({1, 2})))
self.assertFalse(check(set_subclass()))
self.assertFalse(check(frozenset_subclass()))
self.assertFalse(check(object()))
# CRASHES: check(NULL)

def test_anyset_check(self):
check = _testcapi.anyset_check
self.assertTrue(check(set()))
self.assertTrue(check({1, 2}))
self.assertTrue(check(frozenset()))
self.assertTrue(check(frozenset({1, 2})))
self.assertTrue(check(set_subclass()))
self.assertTrue(check(frozenset_subclass()))
self.assertFalse(check(object()))
# CRASHES: check(NULL)

def test_anyset_check_exact(self):
check = _testcapi.anyset_checkexact
self.assertTrue(check(set()))
self.assertTrue(check({1, 2}))
self.assertTrue(check(frozenset()))
self.assertTrue(check(frozenset({1, 2})))
self.assertFalse(check(set_subclass()))
self.assertFalse(check(frozenset_subclass()))
self.assertFalse(check(object()))
# CRASHES: check(NULL)

def test_set_new(self):
set_new = _testcapi.set_new
self.assertEqual(set_new().__class__, set)
self.assertEqual(set_new(), set())
self.assertEqual(set_new((1, 1, 2)), {1, 2})
self.assertEqual(set_new([1, 1, 2]), {1, 2})
with self.assertRaisesRegex(TypeError, 'object is not iterable'):
set_new(object())
with self.assertRaisesRegex(TypeError, 'object is not iterable'):
set_new(1)
with self.assertRaisesRegex(TypeError, "unhashable type: 'dict'"):
set_new((1, {}))

def test_frozenset_new(self):
frozenset_new = _testcapi.frozenset_new
self.assertEqual(frozenset_new().__class__, frozenset)
self.assertEqual(frozenset_new(), frozenset())
self.assertEqual(frozenset_new((1, 1, 2)), frozenset({1, 2}))
self.assertEqual(frozenset_new([1, 1, 2]), frozenset({1, 2}))
with self.assertRaisesRegex(TypeError, 'object is not iterable'):
frozenset_new(object())
with self.assertRaisesRegex(TypeError, 'object is not iterable'):
frozenset_new(1)
with self.assertRaisesRegex(TypeError, "unhashable type: 'dict'"):
frozenset_new((1, {}))

def test_set_size(self):
get_size = _testcapi.set_size
self.assertEqual(get_size(set()), 0)
self.assertEqual(get_size(frozenset()), 0)
self.assertEqual(get_size({1, 1, 2}), 2)
self.assertEqual(get_size(frozenset({1, 1, 2})), 2)
self.assertEqual(get_size(set_subclass((1, 2, 3))), 3)
self.assertEqual(get_size(frozenset_subclass((1, 2, 3))), 3)
with self.assertRaises(SystemError):
get_size(object())
# CRASHES: get_size(NULL)

def test_set_get_size(self):
get_size = _testcapi.set_get_size
self.assertEqual(get_size(set()), 0)
self.assertEqual(get_size(frozenset()), 0)
self.assertEqual(get_size({1, 1, 2}), 2)
self.assertEqual(get_size(frozenset({1, 1, 2})), 2)
self.assertEqual(get_size(set_subclass((1, 2, 3))), 3)
self.assertEqual(get_size(frozenset_subclass((1, 2, 3))), 3)
# CRASHES: get_size(NULL)
# CRASHES: get_size(object())

def test_set_contains(self):
contains = _testcapi.set_contains
for cls in (set, frozenset, set_subclass, frozenset_subclass):
with self.subTest(cls=cls):
instance = cls((1, 2))
self.assertTrue(contains(instance, 1))
self.assertFalse(contains(instance, 'missing'))
with self.assertRaisesRegex(TypeError, "unhashable type: 'list'"):
contains(instance, [])
# CRASHES: contains(instance, NULL)
# CRASHES: contains(NULL, object())
# CRASHES: contains(NULL, NULL)

def test_add(self):
add = _testcapi.set_add
for cls in (set, set_subclass):
with self.subTest(cls=cls):
instance = cls((1, 2))
self.assertEqual(add(instance, 1), 0)
self.assertEqual(instance, {1, 2})
self.assertEqual(add(instance, 3), 0)
self.assertEqual(instance, {1, 2, 3})
with self.assertRaisesRegex(TypeError, "unhashable type: 'list'"):
add(instance, [])
with self.assertRaises(SystemError):
add(object(), 1)
self.assertImmutable(add, 1)
# CRASHES: add(NULL, object())
# CRASHES: add(instance, NULL)
# CRASHES: add(NULL, NULL)

def test_discard(self):
discard = _testcapi.set_discard
for cls in (set, set_subclass):
with self.subTest(cls=cls):
instance = cls((1, 2))
self.assertEqual(discard(instance, 3), 0)
self.assertEqual(instance, {1, 2})
self.assertEqual(discard(instance, 1), 1)
self.assertEqual(instance, {2})
self.assertEqual(discard(instance, 2), 1)
self.assertEqual(instance, set())
self.assertEqual(discard(instance, 2), 0)
self.assertEqual(instance, set())
with self.assertRaisesRegex(TypeError, "unhashable type: 'list'"):
discard(instance, [])
with self.assertRaises(SystemError):
discard(object(), 1)
self.assertImmutable(discard, 1)
# CRASHES: discard(NULL, object())
# CRASHES: discard(instance, NULL)
# CRASHES: discard(NULL, NULL)

def test_pop(self):
pop = _testcapi.set_pop
orig = (1, 2)
for cls in (set, set_subclass):
with self.subTest(cls=cls):
instance = cls(orig)
self.assertIn(pop(instance), orig)
self.assertEqual(len(instance), 1)
self.assertIn(pop(instance), orig)
self.assertEqual(len(instance), 0)
with self.assertRaises(KeyError):
pop(instance)
with self.assertRaises(SystemError):
pop(object())
self.assertImmutable(pop)
# CRASHES: pop(NULL)

def test_clear(self):
clear = _testcapi.set_clear
for cls in (set, set_subclass):
with self.subTest(cls=cls):
instance = cls((1, 2))
self.assertEqual(clear(instance), 0)
self.assertEqual(instance, set())
self.assertEqual(clear(instance), 0)
self.assertEqual(instance, set())
with self.assertRaises(SystemError):
clear(object())
self.assertImmutable(clear)
# CRASHES: clear(NULL)
2 changes: 1 addition & 1 deletion Modules/Setup.stdlib.in
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@
@MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c
@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/getargs.c _testcapi/pytime.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/pyos.c _testcapi/immortal.c _testcapi/heaptype_relative.c _testcapi/gc.c
@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/pytime.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/pyos.c _testcapi/immortal.c _testcapi/heaptype_relative.c _testcapi/gc.c
@MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c

# Some testing modules MUST be built as shared libraries.
Expand Down
1 change: 1 addition & 0 deletions Modules/_testcapi/parts.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ int _PyTestCapi_Init_Watchers(PyObject *module);
int _PyTestCapi_Init_Long(PyObject *module);
int _PyTestCapi_Init_Float(PyObject *module);
int _PyTestCapi_Init_Dict(PyObject *module);
int _PyTestCapi_Init_Set(PyObject *module);
int _PyTestCapi_Init_Structmember(PyObject *module);
int _PyTestCapi_Init_Exceptions(PyObject *module);
int _PyTestCapi_Init_Code(PyObject *module);
Expand Down
162 changes: 162 additions & 0 deletions Modules/_testcapi/set.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
#include <stddef.h> // ptrdiff_t

#include "parts.h"
#include "util.h"

static PyObject *
set_check(PyObject *self, PyObject *obj)
{
NULLABLE(obj);
RETURN_INT(PySet_Check(obj));
}

static PyObject *
set_checkexact(PyObject *self, PyObject *obj)
{
NULLABLE(obj);
RETURN_INT(PySet_CheckExact(obj));
}

static PyObject *
frozenset_check(PyObject *self, PyObject *obj)
{
NULLABLE(obj);
RETURN_INT(PyFrozenSet_Check(obj));
}

static PyObject *
frozenset_checkexact(PyObject *self, PyObject *obj)
{
NULLABLE(obj);
RETURN_INT(PyFrozenSet_CheckExact(obj));
}

static PyObject *
anyset_check(PyObject *self, PyObject *obj)
{
NULLABLE(obj);
RETURN_INT(PyAnySet_Check(obj));
}

static PyObject *
anyset_checkexact(PyObject *self, PyObject *obj)
{
NULLABLE(obj);
RETURN_INT(PyAnySet_CheckExact(obj));
}

static PyObject *
set_new(PyObject *self, PyObject *args)
{
PyObject *iterable = NULL;
if (!PyArg_ParseTuple(args, "|O", &iterable)) {
return NULL;
}
return PySet_New(iterable);
}

static PyObject *
frozenset_new(PyObject *self, PyObject *args)
{
PyObject *iterable = NULL;
if (!PyArg_ParseTuple(args, "|O", &iterable)) {
return NULL;
}
return PyFrozenSet_New(iterable);
}

static PyObject *
set_size(PyObject *self, PyObject *obj)
{
NULLABLE(obj);
RETURN_SIZE(PySet_Size(obj));
}

static PyObject *
set_get_size(PyObject *self, PyObject *obj)
{
NULLABLE(obj);
RETURN_SIZE(PySet_GET_SIZE(obj));
}

static PyObject *
set_contains(PyObject *self, PyObject *args)
{
PyObject *obj, *item;
if (!PyArg_ParseTuple(args, "OO", &obj, &item)) {
return NULL;
}
NULLABLE(obj);
NULLABLE(item);
RETURN_INT(PySet_Contains(obj, item));
}

static PyObject *
set_add(PyObject *self, PyObject *args)
{
PyObject *obj, *item;
if (!PyArg_ParseTuple(args, "OO", &obj, &item)) {
return NULL;
}
NULLABLE(obj);
NULLABLE(item);
RETURN_INT(PySet_Add(obj, item));
}

static PyObject *
set_discard(PyObject *self, PyObject *args)
{
PyObject *obj, *item;
if (!PyArg_ParseTuple(args, "OO", &obj, &item)) {
return NULL;
}
NULLABLE(obj);
NULLABLE(item);
RETURN_INT(PySet_Discard(obj, item));
}

static PyObject *
set_pop(PyObject *self, PyObject *obj)
{
NULLABLE(obj);
return PySet_Pop(obj);
}

static PyObject *
set_clear(PyObject *self, PyObject *obj)
{
NULLABLE(obj);
RETURN_INT(PySet_Clear(obj));
}

static PyMethodDef test_methods[] = {
{"set_check", set_check, METH_O},
{"set_checkexact", set_checkexact, METH_O},
{"frozenset_check", frozenset_check, METH_O},
{"frozenset_checkexact", frozenset_checkexact, METH_O},
{"anyset_check", anyset_check, METH_O},
{"anyset_checkexact", anyset_checkexact, METH_O},

{"set_new", set_new, METH_VARARGS},
{"frozenset_new", frozenset_new, METH_VARARGS},

{"set_size", set_size, METH_O},
{"set_get_size", set_get_size, METH_O},
{"set_contains", set_contains, METH_VARARGS},
{"set_add", set_add, METH_VARARGS},
{"set_discard", set_discard, METH_VARARGS},
{"set_pop", set_pop, METH_O},
{"set_clear", set_clear, METH_O},

{NULL},
};

int
_PyTestCapi_Init_Set(PyObject *m)
{
if (PyModule_AddFunctions(m, test_methods) < 0) {
return -1;
}

return 0;
}
Loading