#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_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 PyObject * test_frozenset_add_in_capi(PyObject *self, PyObject *Py_UNUSED(obj)) { // Test that `frozenset` can be used with `PySet_Add`, // when frozenset is just created in CAPI. PyObject *fs = PyFrozenSet_New(NULL); if (fs == NULL) { return NULL; } PyObject *num = PyLong_FromLong(1); if (num == NULL) { goto error; } if (PySet_Add(fs, num) < 0) { goto error; } int contains = PySet_Contains(fs, num); if (contains < 0) { goto error; } else if (contains == 0) { goto unexpected; } Py_DECREF(fs); Py_DECREF(num); Py_RETURN_NONE; unexpected: PyErr_SetString(PyExc_ValueError, "set does not contain expected value"); error: Py_DECREF(fs); Py_XDECREF(num); return NULL; } static PyObject * raiseTestError(const char* test_name, const char* msg) { PyErr_Format(PyExc_AssertionError, "%s: %s", test_name, msg); return NULL; } static PyObject * test_frozenset_add_in_capi_tracking_immutable(PyObject *self, PyObject *Py_UNUSED(ignored)) { // Test: GC tracking - frozenset with only immutable items should not be tracked PyObject *frozenset = PyFrozenSet_New(NULL); if (frozenset == NULL) { return NULL; } if (PySet_Add(frozenset, Py_True) < 0) { Py_DECREF(frozenset); return NULL; } if (PyObject_GC_IsTracked(frozenset)) { Py_DECREF(frozenset); return raiseTestError("test_frozenset_add_in_capi_tracking_immutable", "frozenset with only bool should not be GC tracked"); } Py_DECREF(frozenset); Py_RETURN_NONE; } static PyObject * test_frozenset_add_in_capi_tracking(PyObject *self, PyObject *Py_UNUSED(ignored)) { // Test: GC tracking - frozenset with tracked object should be tracked PyObject *frozenset = PyFrozenSet_New(NULL); if (frozenset == NULL) { return NULL; } PyObject *tracked_obj = PyErr_NewException("_testlimitedcapi.py_set_add", NULL, NULL); if (tracked_obj == NULL) { goto error; } if (!PyObject_GC_IsTracked(tracked_obj)) { Py_DECREF(frozenset); Py_DECREF(tracked_obj); return raiseTestError("test_frozenset_add_in_capi_tracking", "test object should be tracked"); } if (PySet_Add(frozenset, tracked_obj) < 0) { goto error; } Py_DECREF(tracked_obj); if (!PyObject_GC_IsTracked(frozenset)) { Py_DECREF(frozenset); return raiseTestError("test_frozenset_add_in_capi_tracking", "frozenset with with GC tracked object should be tracked"); } Py_DECREF(frozenset); Py_RETURN_NONE; error: Py_DECREF(frozenset); Py_XDECREF(tracked_obj); return NULL; } static PyObject * test_set_contains_does_not_convert_unhashable_key(PyObject *self, PyObject *Py_UNUSED(obj)) { // See https://docs.python.org/3/c-api/set.html#c.PySet_Contains PyObject *outer_set = PySet_New(NULL); PyObject *needle = PySet_New(NULL); if (needle == NULL) { Py_DECREF(outer_set); return NULL; } PyObject *num = PyLong_FromLong(42); if (num == NULL) { Py_DECREF(outer_set); Py_DECREF(needle); return NULL; } if (PySet_Add(needle, num) < 0) { Py_DECREF(outer_set); Py_DECREF(needle); Py_DECREF(num); return NULL; } int result = PySet_Contains(outer_set, needle); Py_DECREF(num); Py_DECREF(needle); Py_DECREF(outer_set); if (result < 0) { if (PyErr_ExceptionMatches(PyExc_TypeError)) { PyErr_Clear(); Py_RETURN_NONE; } return NULL; } PyErr_SetString(PyExc_AssertionError, "PySet_Contains should have raised TypeError for unhashable key"); return NULL; } 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_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}, {"test_frozenset_add_in_capi", test_frozenset_add_in_capi, METH_NOARGS}, {"test_frozenset_add_in_capi_tracking", test_frozenset_add_in_capi_tracking, METH_NOARGS}, {"test_frozenset_add_in_capi_tracking_immutable", test_frozenset_add_in_capi_tracking_immutable, METH_NOARGS}, {"test_set_contains_does_not_convert_unhashable_key", test_set_contains_does_not_convert_unhashable_key, METH_NOARGS}, {NULL}, }; int _PyTestLimitedCAPI_Init_Set(PyObject *m) { if (PyModule_AddFunctions(m, test_methods) < 0) { return -1; } return 0; }