Index: Include/setobject.h =================================================================== --- Include/setobject.h (revision 43294) +++ Include/setobject.h (working copy) @@ -78,10 +78,13 @@ PyAPI_FUNC(PyObject *) PyFrozenSet_New(PyObject *); PyAPI_FUNC(Py_ssize_t) PySet_Size(PyObject *anyset); #define PySet_GET_SIZE(so) (((PySetObject *)(so))->used) +PyAPI_FUNC(int) PySet_Clear(PyObject *set); PyAPI_FUNC(int) PySet_Contains(PyObject *anyset, PyObject *key); PyAPI_FUNC(int) PySet_Discard(PyObject *set, PyObject *key); PyAPI_FUNC(int) PySet_Add(PyObject *set, PyObject *key); +PyAPI_FUNC(int) PySet_Next(PyObject *set, Py_ssize_t *pos, PyObject **entry); PyAPI_FUNC(PyObject *) PySet_Pop(PyObject *set); +PyAPI_FUNC(int) PySet_Update(PyObject *set, PyObject *iterable); #ifdef __cplusplus } Index: Objects/setobject.c =================================================================== --- Objects/setobject.c (revision 43294) +++ Objects/setobject.c (working copy) @@ -1969,6 +1969,16 @@ } int +PySet_Clear(PyObject *set) +{ + if (!PyType_IsSubtype(set->ob_type, &PySet_Type)) { + PyErr_BadInternalCall(); + return -1; + } + return set_clear_internal((PySetObject *)set); +} + +int PySet_Contains(PyObject *anyset, PyObject *key) { if (!PyAnySet_Check(anyset)) { @@ -1998,6 +2008,21 @@ return set_add_key((PySetObject *)set, key); } +int +PySet_Next(PyObject *set, Py_ssize_t *pos, PyObject **entry) +{ + setentry *entry_ptr; + + if (!PyAnySet_Check(set)) { + PyErr_BadInternalCall(); + return -1; + } + if (set_next((PySetObject *)set, pos, &entry_ptr) == 0) + return 0; + *entry = entry_ptr->key; + return 1; +} + PyObject * PySet_Pop(PyObject *set) { @@ -2008,7 +2033,17 @@ return set_pop((PySetObject *)set); } +int +PySet_Update(PyObject *set, PyObject *iterable) +{ + if (!PyType_IsSubtype(set->ob_type, &PySet_Type)) { + PyErr_BadInternalCall(); + return -1; + } + return set_update_internal((PySetObject *)set, iterable); +} + #ifdef Py_DEBUG /* Test code to be called with any three element set. @@ -2024,7 +2059,11 @@ static PyObject * test_c_api(PySetObject *so) { - PyObject *elem, *dup, *t, *f, *ob = (PyObject *)so; + int count; + char *s; + Py_ssize_t i; + PyObject *elem, *dup, *t, *f, *dup2; + PyObject *ob = (PyObject *)so; /* Verify preconditions and exercise type/size checks */ assert(PyAnySet_Check(ob)); @@ -2055,6 +2094,35 @@ 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); + Py_DECREF(f); + + /* Exercise direct iteration */ + i = 0, count = 0; + while (PySet_Next((PyObject *)dup, &i, &elem)) { + s = PyString_AsString(elem); + 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); Index: Doc/api/concrete.tex =================================================================== --- Doc/api/concrete.tex (revision 43294) +++ Doc/api/concrete.tex (working copy) @@ -3026,8 +3026,6 @@ \cfunction{PyNumber_InPlaceAdd()}, \cfunction{PyNumber_InPlaceSubtract()}, \cfunction{PyNumber_InPlaceOr()}, and \cfunction{PyNumber_InPlaceXor()}). -For example, to clear a set, write: \code{PyObject_CallMethod(s, "clear", NULL)} - \begin{ctypedesc}{PySetObject} This subtype of \ctype{PyObject} is used to hold the internal data for both \class{set} and \class{frozenset} objects. It is like a @@ -3111,7 +3109,34 @@ \class{frozenset}, or an instance of a subtype. \end{cfuncdesc} +\begin{cfuncdesc}{int}{PySet_Next}{PyObject *p, Py_ssize_t *ppos, + PyObject **entry} + Iterate over all entries in the set \var{p}. The \ctype{Py_ssize_t} + referred to by \var{ppos} must be initialized to \code{0} prior to the first + call to this function to start the iteration; the function returns true for + each entry in the set, and false once all entries have been reported. The + parameter \var{entry} should either point to a \ctype{PyObject*} variable + that will be filled in with each entry, or may be \NULL{}. Any reference + returned through it is borrowed. \var{ppos} should not be altered during + iteration. Its value represents offsets within the internal set structure, + and since the structure is sparse, the offsets are not consecutive. + For example: + +\begin{verbatim} +PyObject *entry; +Py_ssize_t pos = 0; + +while (PySet_Next(myset, &pos, &entry)) { + /* do something interesting with the entries... */ + ... +} +\end{verbatim} + + The set \var{p} must not be mutated during iteration. + +\end{cfuncdesc} + The following functions are available for instances of \class{set} or its subtypes but not for instances of \class{frozenset} or its subtypes. @@ -3142,4 +3167,11 @@ of \class{set} or its subtype. \end{cfuncdesc} +\begin{cfuncdesc}{int}{PySet_Clear}{PyObject *set} + Empty an existing set of all elements. +\end{cfuncdesc} +\begin{cfuncdesc}{int}{PySet_Update}{PyObject *a, PyObject *b} + This is the same as \code{\var{a}.update(\var{b})} in Python. Return + \code{0} on success or \code{-1} if an exception was raised. +\end{cfuncdesc} Index: Lib/test/test_set.py =================================================================== --- Lib/test/test_set.py (revision 43294) +++ Lib/test/test_set.py (working copy) @@ -421,7 +421,7 @@ self.assertRaises(ReferenceError, str, p) # C API test only available in a debug build - if hasattr(sys, "gettotalrefcount"): + if hasattr(set, "test_c_api"): def test_c_api(self): self.assertEqual(set('abc').test_c_api(), True)