CPython's PyCapsule Implementation in C: A void* Wrapper for Extension Modules

PyCapsule carries a C void* through the interpreter: the struct, name matching, destructors, context, optional GC traversal and PyCapsule_Import traced through CPython 3.15.

TL;DR: A PyCapsule is a tiny Python object whose only job is to carry a C void * through the interpreter. It adds a name (so you can’t grab the wrong pointer), a destructor (so the C side can clean up), and optional GC hooks. The whole thing lives in Objects/capsule.c.

Extension modules use capsules to export a C API to other extension modules. The exporting module stores a pointer — often to a struct of function pointers — in a capsule and attaches it to a module attribute. The importing module calls PyCapsule_Import to retrieve it.

The implementation lives entirely in Objects/capsule.c.

The struct: PyCapsule

Internally a capsule is a plain struct: an object header followed by six fields.

c
// Objects/capsule.c
typedef struct {
    PyObject_HEAD
    void *pointer;
    const char *name;
    void *context;
    PyCapsule_Destructor destructor;
    traverseproc traverse_func;
    inquiry clear_func;
} PyCapsule;

What each field is for:

  • pointer is the wrapped void * (never NULL for a legal capsule).
  • name is an optional, conventionally dotted attribute path such as "socket.CAPI" (or NULL).
  • context is an arbitrary void * for the owner’s use.
  • destructor is called exactly once when the capsule is deallocated.
  • traverse_func / clear_func are only set by _PyCapsule_SetTraverse and enable GC participation (see below).

The public type is declared in Include/pycapsule.h:

c
// Include/pycapsule.h
PyAPI_DATA(PyTypeObject) PyCapsule_Type;
typedef void (*PyCapsule_Destructor)(PyObject *);
#define PyCapsule_CheckExact(op) Py_IS_TYPE((op), &PyCapsule_Type)

The type: PyCapsule_Type

PyCapsule_Type is a GC-capable type with tp_basicsize = sizeof(PyCapsule):

c
// Objects/capsule.c
PyTypeObject PyCapsule_Type = {
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
    .tp_name = "PyCapsule",
    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
    .tp_basicsize = sizeof(PyCapsule),
    .tp_dealloc = capsule_dealloc,
    .tp_repr = capsule_repr,
    .tp_doc = PyCapsule_Type__doc__,
    .tp_traverse = capsule_traverse,
    .tp_clear = capsule_clear,
};

The key points:

  • Py_TPFLAGS_HAVE_GC is set because a capsule may own references to Python objects through its context/pointer (via the optional traverse callbacks).
  • However, a freshly created capsule is not GC-tracked. Tracking is opt-in through _PyCapsule_SetTraverse. That keeps the common case — no owned Python objects — cheap and skips unnecessary GC work.
  • tp_clear is capsule_clear, which only runs when traverse was registered.

Construction: PyCapsule_New

c
// Objects/capsule.c
PyObject *
PyCapsule_New(void *pointer, const char *name, PyCapsule_Destructor destructor)
{
    PyCapsule *capsule;

    if (!pointer) {
        PyErr_SetString(PyExc_ValueError, "PyCapsule_New called with null pointer");
        return NULL;
    }

    capsule = PyObject_GC_New(PyCapsule, &PyCapsule_Type);
    if (capsule == NULL) {
        return NULL;
    }

    capsule->pointer = pointer;
    capsule->name = name;
    capsule->context = NULL;
    capsule->destructor = destructor;
    capsule->traverse_func = NULL;
    capsule->clear_func = NULL;
    // Only track the object by the GC when _PyCapsule_SetTraverse() is called

    return (PyObject *)capsule;
}

Things to note:

  • A NULL pointer is rejected up front with ValueError.
  • The name is stored by pointer, not copied. The caller owns that string memory for the capsule’s lifetime — which is why module-level capsule names are typically string literals.
  • The destructor may be NULL, in which case nothing runs at dealloc.
  • context starts as NULL.

Legality and name matching

Most accessors begin with the is_legal_capsule guard:

c
// Objects/capsule.c
static int
_is_legal_capsule(PyObject *op, const char *invalid_capsule)
{
    if (!op || !PyCapsule_CheckExact(op)) {
        goto error;
    }
    PyCapsule *capsule = (PyCapsule *)op;

    if (capsule->pointer == NULL) {
        goto error;
    }
    return 1;

error:
    PyErr_SetString(PyExc_ValueError, invalid_capsule);
    return 0;
}

A capsule becomes “illegal” once its pointer is cleared (set to NULL), which lets a destructor mark it as consumed.

Name comparison is done by name_matches, which treats two NULL names as equal and otherwise uses strcmp:

c
// Objects/capsule.c
static int
name_matches(const char *name1, const char *name2) {
    if (!name1 || !name2) {
        return name1 == name2;
    }
    return !strcmp(name1, name2);
}

PyCapsule_IsValid combines the legality and name checks:

c
// Objects/capsule.c
int
PyCapsule_IsValid(PyObject *op, const char *name)
{
    PyCapsule *capsule = (PyCapsule *)op;

    return (capsule != NULL &&
            PyCapsule_CheckExact(capsule) &&
            capsule->pointer != NULL &&
            name_matches(capsule->name, name));
}

The name is a safety tag

The name is a const char *, and it’s compared (not copied) by every getter that takes a name argument.

PyCapsule_GetPointer requires the supplied name to match the stored one:

c
// Objects/capsule.c
void *
PyCapsule_GetPointer(PyObject *op, const char *name)
{
    if (!is_legal_capsule(op, "PyCapsule_GetPointer")) {
        return NULL;
    }
    PyCapsule *capsule = (PyCapsule *)op;

    if (!name_matches(name, capsule->name)) {
        PyErr_SetString(PyExc_ValueError, "PyCapsule_GetPointer called with incorrect name");
        return NULL;
    }

    return capsule->pointer;
}

PyCapsule_GetName / PyCapsule_SetName read and overwrite the pointer (again, no copy):

c
// Objects/capsule.c
const char *
PyCapsule_GetName(PyObject *op)
{
    if (!is_legal_capsule(op, "PyCapsule_GetName")) {
        return NULL;
    }
    PyCapsule *capsule = (PyCapsule *)op;
    return capsule->name;
}

int
PyCapsule_SetName(PyObject *op, const char *name)
{
    if (!is_legal_capsule(op, "PyCapsule_SetName")) {
        return -1;
    }
    PyCapsule *capsule = (PyCapsule *)op;
    capsule->name = name;
    return 0;
}

Here’s the thing: the name’s purpose is purely defensive. It guards against accidentally retrieving a pointer from the wrong capsule, and it’s the key used by PyCapsule_Import.

The destructor gets one shot

The destructor is a PyCapsule_Destructor (void (*)(PyObject *)). It’s invoked from capsule_dealloc before the object memory is freed:

c
// Objects/capsule.c
static void
capsule_dealloc(PyObject *op)
{
    PyCapsule *capsule = _PyCapsule_CAST(op);
    PyObject_GC_UnTrack(op);
    if (capsule->destructor) {
        capsule->destructor(op);
    }
    PyObject_GC_Del(op);
}

Important details:

  • PyObject_GC_UnTrack runs first, so the object is out of the GC list before the destructor (which may manipulate other objects) runs.
  • The destructor receives the capsule object itself, so it can call PyCapsule_GetPointer/GetContext to recover its payload.
  • The destructor is responsible for releasing whatever the pointer owns (e.g. PyMem_Free, Py_DECREF on referenced types). capsule_dealloc then frees the capsule struct via PyObject_GC_Del.
  • PyCapsule_GetDestructor / PyCapsule_SetDestructor expose the field:
c
// Objects/capsule.c
PyCapsule_Destructor
PyCapsule_GetDestructor(PyObject *op)
{
    if (!is_legal_capsule(op, "PyCapsule_GetDestructor")) {
        return NULL;
    }
    PyCapsule *capsule = (PyCapsule *)op;
    return capsule->destructor;
}

int
PyCapsule_SetDestructor(PyObject *op, PyCapsule_Destructor destructor)
{
    if (!is_legal_capsule(op, "PyCapsule_SetDestructor")) {
        return -1;
    }
    PyCapsule *capsule = (PyCapsule *)op;
    capsule->destructor = destructor;
    return 0;
}

A common pattern is to clear the pointer inside the destructor, marking the capsule consumed:

c
static void my_destructor(PyObject *capsule) {
    void *p = PyCapsule_GetPointer(capsule, "my.module.API");
    /* release p ... */
    PyCapsule_SetPointer(capsule, NULL);  /* mark consumed */
}

Context: the owner’s scratchpad

context is a free-form void * slot, accessed via:

c
// Objects/capsule.c
void *
PyCapsule_GetContext(PyObject *op)
{
    if (!is_legal_capsule(op, "PyCapsule_GetContext")) {
        return NULL;
    }
    PyCapsule *capsule = (PyCapsule *)op;
    return capsule->context;
}

int
PyCapsule_SetContext(PyObject *op, void *context)
{
    if (!is_legal_capsule(op, "PyCapsule_SetContext")) {
        return -1;
    }
    PyCapsule *capsule = (PyCapsule *)op;
    capsule->context = context;
    return 0;
}

Unlike pointer, context may be NULL and isn’t subject to the legality check’s non-null requirement.

It’s typically used to stash auxiliary data the destructor or traverse callback needs (for example, a reference to an owning object), since the destructor only receives the capsule.

Optional GC traversal

By default a capsule owns no Python references as far as the GC is concerned.

When the wrapped pointer does refer to Python objects (say, a C API struct holding PyObject * fields), the owner can register traverse/clear callbacks:

c
// Objects/capsule.c
int
_PyCapsule_SetTraverse(PyObject *op, traverseproc traverse_func, inquiry clear_func)
{
    if (!is_legal_capsule(op, "_PyCapsule_SetTraverse")) {
        return -1;
    }
    PyCapsule *capsule = (PyCapsule *)op;

    if (traverse_func == NULL || clear_func == NULL) {
        PyErr_SetString(PyExc_ValueError,
                        "_PyCapsule_SetTraverse() called with NULL callback");
        return -1;
    }

    if (!_PyObject_GC_IS_TRACKED(op)) {
        _PyObject_GC_TRACK(op);
    }

    capsule->traverse_func = traverse_func;
    capsule->clear_func = clear_func;
    return 0;
}

This is an internal API (declared in Include/internal/pycore_capsule.h), used by modules such as socket and _curses:

c
// Modules/socketmodule.c
PyObject *capsule = PyCapsule_New(capi, PySocket_CAPSULE_NAME, sock_capi_destroy);
if (_PyCapsule_SetTraverse(capsule,
                           sock_capi_traverse, sock_capi_clear) < 0) { ... }

The callbacks receive the capsule and use PyCapsule_GetPointer to reach the wrapped struct, then Py_VISIT/Py_CLEAR its Python members:

c
// Modules/socketmodule.c
static int
sock_capi_traverse(PyObject *capsule, visitproc visit, void *arg)
{
    PySocketModule_APIObject *capi = PyCapsule_GetPointer(capsule, PySocket_CAPSULE_NAME);
    assert(capi != NULL);
    Py_VISIT(capi->Sock_Type);
    return 0;
}

static int
sock_capi_clear(PyObject *capsule)
{
    PySocketModule_APIObject *capi = PyCapsule_GetPointer(capsule, PySocket_CAPSULE_NAME);
    assert(capi != NULL);
    Py_CLEAR(capi->Sock_Type);
    return 0;
}

The generic traverse/clear in capsule.c just dispatch to the registered callbacks (and capsule_clear asserts that a clear callback exists):

c
// Objects/capsule.c
static int
capsule_traverse(PyObject *self, visitproc visit, void *arg)
{
    PyCapsule *capsule = _PyCapsule_CAST(self);
    if (capsule->traverse_func != NULL) {
        return capsule->traverse_func(self, visit, arg);
    }
    return 0;
}

static int
capsule_clear(PyObject *self)
{
    PyCapsule *capsule = _PyCapsule_CAST(self);
    assert(capsule->clear_func != NULL);
    return capsule->clear_func(self);
}

PyCapsule_Import: the headline use case

This is how one extension module imports a capsule from another module’s attribute:

c
// Objects/capsule.c
void *
PyCapsule_Import(const char *name, int no_block)
{
    PyObject *object = NULL;
    void *return_value = NULL;
    char *trace;
    size_t name_length = (strlen(name) + 1) * sizeof(char);
    char *name_dup = (char *)PyMem_Malloc(name_length);
    ...
    memcpy(name_dup, name, name_length);

    trace = name_dup;
    while (trace) {
        char *dot = strchr(trace, '.');
        if (dot) {
            *dot++ = '\0';
        }

        if (object == NULL) {
            object = PyImport_ImportModule(trace);
            ...
        } else {
            PyObject *object2 = PyObject_GetAttrString(object, trace);
            Py_SETREF(object, object2);
        }
        ...
        trace = dot;
    }

    if (PyCapsule_IsValid(object, name)) {
        PyCapsule *capsule = (PyCapsule *)object;
        return_value = capsule->pointer;
    } else {
        PyErr_Format(PyExc_AttributeError,
            "PyCapsule_Import \"%s\" is not valid", name);
    }

EXIT:
    Py_XDECREF(object);
    if (name_dup) {
        PyMem_Free(name_dup);
    }
    return return_value;
}

It walks the dotted name ("module.sub.attr"), importing the first segment and then getattr-ing the rest, and finally validates that the resolved object is a capsule whose name matches the full name.

The no_block parameter is accepted for API compatibility, but the implementation imports normally.

On success it returns the borrowed pointer — the capsule (and thus the pointer) stays alive as long as the module holds it.

Repr

c
// Objects/capsule.c
static PyObject *
capsule_repr(PyObject *o)
{
    PyCapsule *capsule = _PyCapsule_CAST(o);
    const char *name;
    const char *quote;

    if (capsule->name) {
        quote = "\"";
        name = capsule->name;
    } else {
        quote = "";
        name = "NULL";
    }

    return PyUnicode_FromFormat("<capsule object %s%s%s at %p>",
        quote, name, quote, capsule);
}

So repr(capsule) looks like <capsule object "socket.CAPI" at 0x...> or <capsule object NULL at 0x...>.

The memory layout

text
PyCapsule  (sizeof = 7 words on 64-bit)
+----------------------------+
| ob_refcnt                  |
+----------------------------+
| ob_flags                   |
+----------------------------+
| ob_type  -> &PyCapsule_Type|
+----------------------------+
| pointer  (void *)          |
+----------------------------+
| name     (const char *)    |
+----------------------------+
| context  (void *)          |
+----------------------------+
| destructor                 |
+----------------------------+
| traverse_func              |
+----------------------------+
| clear_func                 |
+----------------------------+

Key takeaways

  • A PyCapsule is a GC-capable object wrapping a void *, plus a name, context, destructor, and optional traverse/clear callbacks.
  • PyCapsule_New rejects NULL pointers; the name is stored by pointer (not copied) and context starts NULL.
  • Legality requires a non-null pointer; setting it to NULL (often in the destructor) marks the capsule consumed.
  • Names are compared with strcmp (with NULL == NULL), and getters like PyCapsule_GetPointer require a matching name.
  • The destructor runs during dealloc, before PyObject_GC_Del, and receives the capsule so it can recover its payload.
  • context is a free-form slot not subject to the non-null legality check.
  • GC tracking is opt-in via the internal _PyCapsule_SetTraverse; otherwise capsules are untracked.
  • PyCapsule_Import walks a dotted path, validates the final capsule by name, and returns its pointer.

The one thing to remember: a capsule is a typed handle, not a container. The name is a cheap tag that catches “wrong pointer”, the destructor is your one guaranteed cleanup hook, and everything else is convention.

Further reading

  • Objects/capsule.c – PyCapsule struct, PyCapsule_Type, PyCapsule_New, accessors, _PyCapsule_SetTraverse, PyCapsule_Import, capsule_dealloc, capsule_repr, capsule_traverse, capsule_clear.
  • Include/pycapsule.h – public API and PyCapsule_CheckExact.
  • Include/internal/pycore_capsule.h – _PyCapsule_SetTraverse.
  • Modules/socketmodule.c, Modules/_cursesmodule.c – real users of traverse.
  • Lib/test/test_capi/test_capsule.py – capsule test suite.
  • Doc/c-api/capsule.rst – public documentation.

PythonCpythonInternalsCapsule