Skip to content

Commit

Permalink
Document C API
Browse files Browse the repository at this point in the history
Expand the documentation for the various classes implemented in the C API.
While we're at it, switch to PyDoc_STR.

Signed-off-by: Sean Anderson <[email protected]>
  • Loading branch information
Forty-Bot committed Nov 22, 2023
1 parent 90f959f commit 09ed1f2
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 16 deletions.
13 changes: 11 additions & 2 deletions _mpmetrics.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,11 @@ PyTypeObject BufferType = {
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
Py_TPFLAGS_HAVE_GC,
.tp_name = "_mpmetrics.Buffer",
.tp_doc = "Shared memory buffer base class",
.tp_doc = PyDoc_STR("Buffer(mem)\n"
"--\n"
"\n"
"Create a buffer backed by 'mem'. This is a base class for other C\n"
"classes in _mpmetrics."),
.tp_new = PyType_GenericNew,
.tp_init = (initproc)Buffer_init,
.tp_dealloc = (destructor)Buffer_dealloc,
Expand Down Expand Up @@ -154,7 +158,12 @@ int PyType_AddDoubleConstant(PyTypeObject *type, const char *name,
static PyModuleDef module = {
PyModuleDef_HEAD_INIT,
.m_name = "_mpmetrics",
.m_doc = "C helpers for multiprocess-safe metrics",
.m_doc = PyDoc_STR("C helpers for multiprocess-safe metrics\n"
"\n"
"This module provides various concurrency primitives, typically backed\n"
"by shared memory. You probably want to use mpmetrics.atomic instead\n"
"of the atomic types in this module, as they may not always be\n"
"available on all architectures."),
.m_size = -1,
};

Expand Down
44 changes: 35 additions & 9 deletions atomic.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@

#ifdef DOUBLE
#define PTYPE double
#define FORMAT "d"
#define FORMAT "f"
#define DOCTYPE "float"
#define NAME AtomicDouble
#define LONGNAME "atomic double"
#define OBJECT AtomicDouble
#define AS PyFloat_AsDouble
#define FROM PyFloat_FromDouble
Expand All @@ -15,13 +17,16 @@
#define FORMAT paste(PRId, WIDTH)
#define PTYPE paste(paste(int, WIDTH), _t)
#define NAME paste(AtomicInt, WIDTH)
#define LONGNAME "atomic " stringify(WIDTH) "-bit signed integer"
#define FROM PyLong_FromLongLong
#else /* SIGNED */
#define PTYPE paste(paste(uint, WIDTH), _t)
#define FORMAT paste(PRIu, WIDTH)
#define NAME paste(AtomicUInt, WIDTH)
#define LONGNAME "atomic " stringify(WIDTH) "-bit unsigned integer"
#define FROM PyLong_FromUnsignedLongLong
#endif /* SIGNED */
#define DOCTYPE "int"
#define OBJECT paste(NAME, OBJECT)
#define AS _Generic((PTYPE)0, \
int: PyLong_AsInt, \
Expand Down Expand Up @@ -118,19 +123,35 @@ static PyMethodDef METHODS[] = {
.ml_name = "get",
.ml_meth = (PyCFunction)GET,
.ml_flags = METH_NOARGS,
.ml_doc = "Get the current value",
.ml_doc = PyDoc_STR("get() -> " DOCTYPE "\n"
"\n"
"Return the current value of the backing " DOCTYPE "."),
},
{
.ml_name = "set",
.ml_meth = (PyCFunction)SET,
.ml_flags = METH_O,
.ml_doc = "Set the current value",
.ml_doc = PyDoc_STR("set(value)\n"
"\n"
"Set the backing " DOCTYPE " to 'value'."),
},
{
.ml_name = "add",
.ml_meth = (PyCFunction)ADD,
.ml_flags = METH_VARARGS | METH_KEYWORDS,
.ml_doc = "Add a number to the value",
.ml_doc = PyDoc_STR("add(amount, raise_on_overflow=True) -> " DOCTYPE "\n"
"\n"
#ifdef DOUBLE
"Add 'amount' to the backing " DOCTYPE " and return the value\n"
"from before the addition. The value of 'raise_on_overflow'\n"
"is ignored."),
#else
"Add 'amount' to the backing " DOCTYPE " and return the value\n"
"from before the addition. If the addition overflows, the\n"
"result will wrap around (using two's complement addition)\n"
"and, if 'raise_on_overflow' is True, an exception will be\n"
"raised."),
#endif
},
{ 0 },
};
Expand All @@ -141,11 +162,14 @@ static PyTypeObject TYPE = {
.tp_basicsize = sizeof(OBJECT),
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
.tp_name = "_mpmetrics." stringify(NAME),
#ifdef DOUBLE
.tp_doc = "Atomic " stringify(PTYPE),
#else
.tp_doc = "Atomic " stringify(WIDTH) "-bit integer",
#endif
.tp_doc = PyDoc_STR(stringify(NAME) "(mem)\n"
"--\n"
"\n"
"Construct a new atomic " LONGNAME " backed by 'mem'.\n"
"All atomic operations use the sequentially-consistent memory order.\n"
"On architectures not supporting " LONGNAME "s, this\n"
"class will be None."),

.tp_new = PyType_GenericNew,
.tp_init = (initproc)INIT,
.tp_methods = METHODS,
Expand Down Expand Up @@ -212,7 +236,9 @@ static int TYPE_ADD(PyObject *m)

#undef PTYPE
#undef FORMAT
#undef DOCTYPE
#undef NAME
#undef LONGNAME
#undef OBJECT
#undef AS
#undef FROM
24 changes: 19 additions & 5 deletions lock.c
Original file line number Diff line number Diff line change
Expand Up @@ -157,25 +157,30 @@ static PyMethodDef Lock_methods[] = {
.ml_name = "acquire",
.ml_meth = (PyCFunction)Lock_acquire,
.ml_flags = METH_VARARGS | METH_KEYWORDS,
.ml_doc = "Acquire the lock",
.ml_doc = PyDoc_STR("acquire(block=True, timeout=None) -> bool\n"
"\n"
"Acquire the lock. If 'block' is False, then try to acquire\n"
"the lock without blocking. If 'timeout' is not None, then\n"
"wait for at most 'timeout' seconds before aborting. Returns\n"
"True if the lock was acquired, or False otherwise."),
},
{
.ml_name = "release",
.ml_meth = (PyCFunction)Lock_release,
.ml_flags = METH_NOARGS,
.ml_doc = "Release the lock",
.ml_doc = PyDoc_STR("release()\n"
"\n"
"Release the lock."),
},
{
.ml_name = "__enter__",
.ml_meth = (PyCFunction)Lock_enter,
.ml_flags = METH_NOARGS,
.ml_doc = "Enter a critical section",
},
{
.ml_name = "__exit__",
.ml_meth = (PyCFunction)Lock_exit,
.ml_flags = METH_FASTCALL,
.ml_doc = "Exit a critical section",
},
{ 0 },
};
Expand All @@ -185,7 +190,16 @@ static PyTypeObject LockType = {
.tp_basicsize = sizeof(LockObject),
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
.tp_name = "_mpmetrics.Lock",
.tp_doc = "Shared memory mutex lock",
.tp_doc = PyDoc_STR("Lock(mem)\n"
"--\n"
"\n"
"Create a shared memory mutex lock backed by 'mem'. On POSIX systems,\n"
"this uses the POSIX threads mutex lock implementation. This lock is\n"
"not reentrant: trying to acquire it multiple times or release it\n"
"without first having acquired it will cause a deadlock.\n"
"\n"
"This class may be used as a context manager. Acquiring the lock is\n"
"blocking when entering a critical section."),
.tp_new = PyType_GenericNew,
.tp_init = (initproc)Lock_init,
.tp_methods = Lock_methods,
Expand Down

0 comments on commit 09ed1f2

Please sign in to comment.