]> granicus.if.org Git - python/commitdiff
Add a C API for sets and frozensets.
authorRaymond Hettinger <python@rcn.com>
Tue, 16 Aug 2005 03:47:52 +0000 (03:47 +0000)
committerRaymond Hettinger <python@rcn.com>
Tue, 16 Aug 2005 03:47:52 +0000 (03:47 +0000)
Doc/api/concrete.tex
Include/setobject.h
Misc/NEWS
Objects/setobject.c
Python/marshal.c

index e174beed086c8600132a11474eca8decedd07fda..2f37be5936aab18c2044e96172eb7d96aa7108f4 100644 (file)
@@ -2897,3 +2897,128 @@ Macros for the convenience of modules implementing the DB API:
   tuple suitable for passing to \code{datetime.date.fromtimestamp()}.
   \versionadded{2.4}
 \end{cfuncdesc}
+
+
+\subsection{Set Objects \label{setObjects}}
+\sectionauthor{Raymond D. Hettinger}{python@rcn.com}                     
+
+\obindex{set}
+\obindex{frozenset}
+\versionadded{2.5}
+
+This section details the public API for \class{set} and \class{frozenset}
+objects.  Any functionality not listed below is best accessed using the
+abstract object API (including
+\cfunction{PyObject_CallMethod()}, \cfunction{PyObject_RichCompareBool()}, 
+\cfunction{PyObject_Hash()}, \cfunction{PyObject_Repr()}, 
+\cfunction{PyObject_IsTrue()}, \cfunction{PyObject_Print()}, and
+\cfunction{PyObject_GetIter()}).                  
+                      
+\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
+  \ctype{PyDictObject} in that it is a fixed size for small sets
+  (much like tuple storage) and will point to a separate, variable sized
+  block of memory for medium and large sized sets (much like list storage).
+  None of the fields of this structure should be considered public and
+  are subject to change.  All access should be done through the
+  documented API.  
+
+\end{ctypedesc}
+
+\begin{cvardesc}{PyTypeObject}{PySet_Type}
+  This is an instance of \ctype{PyTypeObject} representing the Python
+  \class{set} type.
+\end{cvardesc}
+
+\begin{cvardesc}{PyTypeObject}{PyFrozenSet_Type}
+  This is an instance of \ctype{PyTypeObject} representing the Python
+  \class{frozenset} type.
+\end{cvardesc}
+
+
+The following type check macros work on pointers to any Python object.
+Likewise, the constructor functions work with any iterable Python object.
+
+\begin{cfuncdesc}{int}{PyAnySet_Check}{PyObject *p}
+  Returns true if \var{p} is a \class{set} object, a \class{frozenset} 
+  object, or an instance of a subtype.
+\end{cfuncdesc}
+
+\begin{cfuncdesc}{int}{PyAnySet_CheckExact}{PyObject *p}
+  Returns true if \var{p} is a \class{set} object or a \class{frozenset}
+  object but not an instance of a subtype.
+\end{cfuncdesc}
+
+\begin{cfuncdesc}{int}{PyFrozenSet_CheckExact}{PyObject *p}
+  Returns true if \var{p} is a \class{frozenset} object
+  but not an instance of a subtype.
+\end{cfuncdesc}
+
+\begin{cfuncdesc}{PyObject*}{PySet_New}{PyObject *iterable}
+  Returns a new \class{set} containing objects returned by the
+  \var{iterable}.  The \var{iterable} may be \NULL{} to create a
+  new empty set.  Returns the new set on success or \NULL{} on
+  failure.
+\end{cfuncdesc}
+
+\begin{cfuncdesc}{PyObject*}{PyFrozenSet_New}{PyObject *iterable}
+  Returns a new \class{frozenset} containing objects returned by the
+  \var{iterable}.  The \var{iterable} may be \NULL{} to create a
+  new empty frozenset.  Returns the new set on success or \NULL{} on
+  failure.
+\end{cfuncdesc}
+
+
+The following functions and macros are available for instances of
+\class{set} or \class{frozenset} or instances of their subtypes.
+
+\begin{cfuncdesc}{int}{PySet_Size}{PyObject *anyset}
+  Returns the length of a \class{set} or \class{frozenset} object.
+  Equivalent to \samp{len(\var{anyset})}.  Raises a
+  \exception{PyExc_SystemError} if the argument is not a \class{set},
+  \class{frozenset}, or an instance of a subtype.
+  \bifuncindex{len}
+\end{cfuncdesc}
+
+\begin{cfuncdesc}{int}{PySet_GET_SIZE}{PyObject *anyset}
+  Macro form of \cfunction{PySet_Size()} without error checking.
+\end{cfuncdesc}
+
+\begin{cfuncdesc}{int}{PySet_Contains}{PyObject *anyset, PyObject *key}
+  Returns 1 if found, 0 if not found, and -1 if an error is
+  encountered.  Unlike the Python \method{__contains__()} method, this
+  function does not automatically convert unhashable sets into temporary
+  frozensets.  Raises a \exception{TypeError} if the key is unhashable.
+\end{cfuncdesc}
+
+\begin{cfuncdesc}{int}{PySet_Discard}{PyObject *anyset, PyObject *key}
+  Returns 1 if found and removed, 0 if not found (no action taken),
+  and -1 if an error is encountered.  Does not raise \exception{KeyError}
+  for missing keys.  Raises a \exception{TypeError} if the key is unhashable.
+  Unlike the Python \method{discard()} method, this function does
+  not automatically convert unhashable sets into temporary frozensets.  
+\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.
+
+\begin{cfuncdesc}{int}{PySet_Add}{PyObject *set, PyObject *key}
+  Adds \var{key} to a \class{set} instance.  Does not apply to
+  \class{frozenset} instances.  Returns 0 on success or -1 on failure.
+  Raises a \exception{TypeError} if the key is unhashable.
+  Raises a \exception{MemoryError} if there is no room to grow.
+  Raises a \exception{SystemError} if \var{key} is an not an instance
+  of \class{set} or its subtype.
+\end{cfuncdesc}
+
+\begin{cfuncdesc}{PyObject*}{PySet_Pop}{PyObject *set}
+  Returns a new reference to an arbitrary object in the \var{set},
+  and removes the object from the \var{set}.  Returns \NULL{} on
+  failure.  Raises \exception{KeyError} if the set is empty.
+  Raises a \exception{SystemError} if \var{key} is an not an instance
+  of \class{set} or its subtype.                        
+\end{cfuncdesc}
+
+
index 6a829f0cb0913bda716a3d318364732e8dbe8a89..b6849e14c93772000e335464b775a7a0290d15d1 100644 (file)
@@ -74,6 +74,15 @@ PyAPI_DATA(PyTypeObject) PyFrozenSet_Type;
          PyType_IsSubtype((ob)->ob_type, &PySet_Type) || \
          PyType_IsSubtype((ob)->ob_type, &PyFrozenSet_Type))
 
+PyAPI_FUNC(PyObject *) PySet_New(PyObject *);
+PyAPI_FUNC(PyObject *) PyFrozenSet_New(PyObject *);
+PyAPI_FUNC(int) PySet_Size(PyObject *anyset);
+#define PySet_GET_SIZE(so) (((PySetObject *)(so))->used)
+PyAPI_FUNC(int) PySet_Contains(PyObject *anyset, PyObject *key);
+PyAPI_FUNC(int) PySet_Discard(PyObject *anyset, PyObject *key);
+PyAPI_FUNC(int) PySet_Add(PyObject *set, PyObject *key);
+PyAPI_FUNC(PyObject *) PySet_Pop(PyObject *set);
+
 #ifdef __cplusplus
 }
 #endif
index 62e6656f868b872d682009bbfcfd7e48d1cf1af8..e3fbbabc5be8a2e776cf9a8a73a31b13aa18483c 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -439,6 +439,8 @@ Build
 C API
 -----
 
+- Added a C API for set and frozenset objects.
+
 - Removed PyRange_New().
 
 
index c52254925c4f2fef5058da3ee8eb38dae7728ed9..978d61359cc0710bbe3b9a04acbf7bfe91b86215 100644 (file)
@@ -843,7 +843,7 @@ frozenset_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
                        return iterable;
                }
                result = make_new_set(type, iterable);
-               if (result == NULL || set_len(result))
+               if (result == NULL || PySet_GET_SIZE(result))
                        return result;
                Py_DECREF(result);
        }
@@ -1052,7 +1052,7 @@ set_intersection(PySetObject *so, PyObject *other)
                int pos = 0;
                setentry *entry;
 
-               if (set_len(other) > set_len((PyObject *)so)) {
+               if (PySet_GET_SIZE(other) > PySet_GET_SIZE(so)) {
                        tmp = (PyObject *)so;
                        so = (PySetObject *)other;
                        other = tmp;
@@ -1381,7 +1381,7 @@ set_issubset(PySetObject *so, PyObject *other)
                Py_DECREF(tmp);
                return result;
        }
-       if (set_len((PyObject *)so) > set_len(other)) 
+       if (PySet_GET_SIZE(so) > PySet_GET_SIZE(other)) 
                Py_RETURN_FALSE;
 
        while (set_next(so, &pos, &entry)) {
@@ -1426,11 +1426,11 @@ set_richcompare(PySetObject *v, PyObject *w, int op)
        }
        switch (op) {
        case Py_EQ:
-               if (set_len((PyObject *)v) != set_len(w))
+               if (PySet_GET_SIZE(v) != PySet_GET_SIZE(w))
                        Py_RETURN_FALSE;
                return set_issubset(v, w);
        case Py_NE:
-               if (set_len((PyObject *)v) != set_len(w))
+               if (PySet_GET_SIZE(v) != PySet_GET_SIZE(w))
                        Py_RETURN_TRUE;
                r1 = set_issubset(v, w);
                assert (r1 != NULL);
@@ -1442,11 +1442,11 @@ set_richcompare(PySetObject *v, PyObject *w, int op)
        case Py_GE:
                return set_issuperset(v, w);
        case Py_LT:
-               if (set_len((PyObject *)v) >= set_len(w))
+               if (PySet_GET_SIZE(v) >= PySet_GET_SIZE(w))
                        Py_RETURN_FALSE;                
                return set_issubset(v, w);
        case Py_GT:
-               if (set_len((PyObject *)v) <= set_len(w))
+               if (PySet_GET_SIZE(v) <= PySet_GET_SIZE(w))
                        Py_RETURN_FALSE;
                return set_issuperset(v, w);
        }
@@ -1472,7 +1472,7 @@ frozenset_hash(PyObject *self)
        if (so->hash != -1)
                return so->hash;
 
-       hash *= set_len(self) + 1;
+       hash *= PySet_GET_SIZE(self) + 1;
        while (set_next(so, &pos, &entry)) {
                /* Work to increase the bit dispersion for closely spaced hash
                   values.  The is important because some use cases have many 
@@ -1918,3 +1918,67 @@ PyTypeObject PyFrozenSet_Type = {
        frozenset_new,                  /* tp_new */
        PyObject_GC_Del,                /* tp_free */
 };
+
+
+/***** C API functions *************************************************/
+
+PyObject *
+PySet_New(PyObject *iterable)
+{
+       return make_new_set(&PySet_Type, iterable);
+}
+
+PyObject *
+PyFrozenSet_New(PyObject *iterable)
+{
+       PyObject *args = NULL, *result;
+
+       if (iterable != NULL) {
+               args = PyTuple_Pack(1, iterable);
+               if (args == NULL)
+                       return NULL;
+       }
+       result = frozenset_new(&PyFrozenSet_Type, args, NULL);
+       Py_DECREF(args);
+       return result;
+}
+
+int
+PySet_Contains(PyObject *anyset, PyObject *key)
+{
+       if (!PyAnySet_Check(anyset)) {
+               PyErr_BadInternalCall();
+               return -1;
+       }
+       return set_contains_key((PySetObject *)anyset, key);
+}
+
+int
+PySet_Discard(PyObject *anyset, PyObject *key)
+{
+       if (!PyAnySet_Check(anyset)) {
+               PyErr_BadInternalCall();
+               return -1;
+       }
+       return set_discard_key((PySetObject *)anyset, key);
+}
+
+int
+PySet_Add(PyObject *set, PyObject *key)
+{
+       if (!PyType_IsSubtype(set->ob_type, &PySet_Type)) {
+               PyErr_BadInternalCall();
+               return -1;
+       }
+       return set_add_key((PySetObject *)set, key);
+}
+
+PyObject *
+PySet_Pop(PyObject *set)
+{
+       if (!PyType_IsSubtype(set->ob_type, &PySet_Type)) {
+               PyErr_BadInternalCall();
+               return NULL;
+       }
+       return set_pop((PySetObject *)set);
+}
index 3eb7b1ec7ae11e032424456706c0db5a6e64b90c..20d637d2b2c1392ade775d9d478bc5434ab173b2 100644 (file)
@@ -773,11 +773,9 @@ r_object(RFILE *p)
                if (v == NULL)
                        return v;
                if (type == TYPE_SET)
-                       v3 = PyObject_CallFunctionObjArgs(
-                               (PyObject *)&PySet_Type, v, NULL);
+                       v3 = PySet_New(v);
                else
-                       v3 = PyObject_CallFunctionObjArgs(
-                               (PyObject *)&PyFrozenSet_Type, v, NULL);
+                       v3 = PyFrozenSet_New(v);
                Py_DECREF(v);
                return v3;