Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Improve CAPI tests ofset andfrozenset #110525

Closed
Assignees
sobolevn
Labels
interpreter-core(Objects, Python, Grammar, and Parser dirs)testsTests in the Lib/test dirtopic-C-APItype-featureA feature request or enhancement
@sobolevn

Description

@sobolevn

Feature or enhancement

Right now CAPI tests ofset andfrozenset are quite outdated. They are defined as.test_c_api method onset objects, whenPy_DEBUG is set:

#ifdefPy_DEBUG
/* Test code to be called with any three element set.
Returns True and original set is restored. */
#defineassertRaises(call_return_value,exception) \
do { \
assert(call_return_value); \
assert(PyErr_ExceptionMatches(exception)); \
PyErr_Clear(); \
} while(0)
staticPyObject*
test_c_api(PySetObject*so,PyObject*Py_UNUSED(ignored))
{
Py_ssize_tcount;
constchar*s;
Py_ssize_ti;
PyObject*elem=NULL,*dup=NULL,*t,*f,*dup2,*x=NULL;
PyObject*ob= (PyObject*)so;
Py_hash_thash;
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)
returnNULL;
set_clear_internal(so);
if (set_update_internal(so,str)) {
Py_DECREF(str);
returnNULL;
}
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

There are several things that I can see as problematic:

  • It is stored together with the production code
  • These tests are mixing CAPI (PySet_Check), internal CAPI (_PySet_Update), and internal function calls (set_clear_internal)
  • These tests are hard to parametrize since they are called as

@unittest.skipUnless(hasattr(set,"test_c_api"),
'C API test only available in a debug build')
deftest_c_api(self):
self.assertEqual(set().test_c_api(),True)

  • Multiple things are not tested: likeset andfrozenset subclasses
  • Test failures are not quite informative, because using Cassert
  • This is a single huge test
  • This test is only run under debug builds and not under "release" builds

I propose a plan to improve it:

  • Move CAPI tests toModules/_testcapi/set.c and addLib/test/test_capi/test_set.py
  • Move internal CAPI testsModules/_testinternalcapi/set.c and make sure that they are executed intest_capi.py
  • Deletetest_c_api fromsetobject.c

I plan to work on this and already have the PR for1. :)

Linked PRs

Metadata

Metadata

Assignees

Labels

interpreter-core(Objects, Python, Grammar, and Parser dirs)testsTests in the Lib/test dirtopic-C-APItype-featureA feature request or enhancement

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions


    [8]ページ先頭

    ©2009-2025 Movatter.jp