|
#ifdef Py_DEBUG |
|
|
|
/* Test code to be called with any three element set. |
|
Returns True and original set is restored. */ |
|
|
|
#define assertRaises(call_return_value, exception) \ |
|
do { \ |
|
assert(call_return_value); \ |
|
assert(PyErr_ExceptionMatches(exception)); \ |
|
PyErr_Clear(); \ |
|
} while(0) |
|
|
|
static PyObject * |
|
test_c_api(PySetObject *so, PyObject *Py_UNUSED(ignored)) |
|
{ |
|
Py_ssize_t count; |
|
const char *s; |
|
Py_ssize_t i; |
|
PyObject *elem=NULL, *dup=NULL, *t, *f, *dup2, *x=NULL; |
|
PyObject *ob = (PyObject *)so; |
|
Py_hash_t hash; |
|
PyObject *str; |
|
|
|
/* Verify preconditions */ |
|
assert(PyAnySet_Check(ob)); |
|
assert(PyAnySet_CheckExact(ob)); |
|
assert(!PyFrozenSet_CheckExact(ob)); |
|
|
|
/* so.clear(); so |= set("abc"); */ |
|
str = PyUnicode_FromString("abc"); |
|
if (str == NULL) |
|
return NULL; |
|
set_clear_internal(so); |
|
if (set_update_internal(so, str)) { |
|
Py_DECREF(str); |
|
return NULL; |
|
} |
|
Py_DECREF(str); |
|
|
|
/* Exercise type/size checks */ |
|
assert(PySet_Size(ob) == 3); |
|
assert(PySet_GET_SIZE(ob) == 3); |
|
|
|
/* Raise TypeError for non-iterable constructor arguments */ |
|
assertRaises(PySet_New(Py_None) == NULL, PyExc_TypeError); |
|
assertRaises(PyFrozenSet_New(Py_None) == NULL, PyExc_TypeError); |
|
|
|
/* Raise TypeError for unhashable key */ |
|
dup = PySet_New(ob); |
|
assertRaises(PySet_Discard(ob, dup) == -1, PyExc_TypeError); |
|
assertRaises(PySet_Contains(ob, dup) == -1, PyExc_TypeError); |
|
assertRaises(PySet_Add(ob, dup) == -1, PyExc_TypeError); |
|
|
|
/* Exercise successful pop, contains, add, and discard */ |
|
elem = PySet_Pop(ob); |
|
assert(PySet_Contains(ob, elem) == 0); |
|
assert(PySet_GET_SIZE(ob) == 2); |
|
assert(PySet_Add(ob, elem) == 0); |
|
assert(PySet_Contains(ob, elem) == 1); |
|
assert(PySet_GET_SIZE(ob) == 3); |
|
assert(PySet_Discard(ob, elem) == 1); |
|
assert(PySet_GET_SIZE(ob) == 2); |
|
assert(PySet_Discard(ob, elem) == 0); |
|
assert(PySet_GET_SIZE(ob) == 2); |
|
|
|
/* Exercise clear */ |
|
dup2 = PySet_New(dup); |
|
assert(PySet_Clear(dup2) == 0); |
|
assert(PySet_Size(dup2) == 0); |
|
Py_DECREF(dup2); |
|
|
|
/* Raise SystemError on clear or update of frozen set */ |
|
f = PyFrozenSet_New(dup); |
|
assertRaises(PySet_Clear(f) == -1, PyExc_SystemError); |
|
assertRaises(_PySet_Update(f, dup) == -1, PyExc_SystemError); |
|
assert(PySet_Add(f, elem) == 0); |
|
Py_INCREF(f); |
|
assertRaises(PySet_Add(f, elem) == -1, PyExc_SystemError); |
|
Py_DECREF(f); |
|
Py_DECREF(f); |
|
|
|
/* Exercise direct iteration */ |
|
i = 0, count = 0; |
|
while (_PySet_NextEntry((PyObject *)dup, &i, &x, &hash)) { |
|
s = PyUnicode_AsUTF8(x); |
|
assert(s && (s[0] == 'a' || s[0] == 'b' || s[0] == 'c')); |
|
count++; |
|
} |
|
assert(count == 3); |
|
|
|
/* Exercise updates */ |
|
dup2 = PySet_New(NULL); |
|
assert(_PySet_Update(dup2, dup) == 0); |
|
assert(PySet_Size(dup2) == 3); |
|
assert(_PySet_Update(dup2, dup) == 0); |
|
assert(PySet_Size(dup2) == 3); |
|
Py_DECREF(dup2); |
|
|
|
/* Raise SystemError when self argument is not a set or frozenset. */ |
|
t = PyTuple_New(0); |
|
assertRaises(PySet_Size(t) == -1, PyExc_SystemError); |
|
assertRaises(PySet_Contains(t, elem) == -1, PyExc_SystemError); |
|
Py_DECREF(t); |
|
|
|
/* Raise SystemError when self argument is not a set. */ |
|
f = PyFrozenSet_New(dup); |
|
assert(PySet_Size(f) == 3); |
|
assert(PyFrozenSet_CheckExact(f)); |
|
assertRaises(PySet_Discard(f, elem) == -1, PyExc_SystemError); |
|
assertRaises(PySet_Pop(f) == NULL, PyExc_SystemError); |
|
Py_DECREF(f); |
|
|
|
/* Raise KeyError when popping from an empty set */ |
|
assert(PyNumber_InPlaceSubtract(ob, ob) == ob); |
|
Py_DECREF(ob); |
|
assert(PySet_GET_SIZE(ob) == 0); |
|
assertRaises(PySet_Pop(ob) == NULL, PyExc_KeyError); |
|
|
|
/* Restore the set from the copy using the PyNumber API */ |
|
assert(PyNumber_InPlaceOr(ob, dup) == ob); |
|
Py_DECREF(ob); |
|
|
|
/* Verify constructors accept NULL arguments */ |
|
f = PySet_New(NULL); |
|
assert(f != NULL); |
|
assert(PySet_GET_SIZE(f) == 0); |
|
Py_DECREF(f); |
|
f = PyFrozenSet_New(NULL); |
|
assert(f != NULL); |
|
assert(PyFrozenSet_CheckExact(f)); |
|
assert(PySet_GET_SIZE(f) == 0); |
|
Py_DECREF(f); |
|
|
|
Py_DECREF(elem); |
|
Py_DECREF(dup); |
|
Py_RETURN_TRUE; |
|
} |
|
|
|
#undef assertRaises |
|
|
|
#endif |
Feature or enhancement
Right now CAPI tests of
setandfrozensetare quite outdated. They are defined as.test_c_apimethod onsetobjects, whenPy_DEBUGis set:cpython/Objects/setobject.c
Lines 2371 to 2511 in 7e30821
There are several things that I can see as problematic:
PySet_Check), internal CAPI (_PySet_Update), and internal function calls (set_clear_internal)cpython/Lib/test/test_set.py
Lines 638 to 641 in 7e30821
setandfrozensetsubclassesassertI propose a plan to improve it:
Modules/_testcapi/set.cand addLib/test/test_capi/test_set.pyModules/_testinternalcapi/set.cand make sure that they are executed intest_capi.pytest_c_apifromsetobject.cI plan to work on this and already have the PR for
1.:)Linked PRs
setandfrozensetobjects #110526PySet_Addcorner case withfrozensetobjects #110544setCAPI #110630test_c_apimethod fromsetobject #110688