Skip to main content

Objects/capsule.c (part 2)

Source:

cpython 3.14 @ ab2d84fe1023/Objects/capsule.c

This annotation covers the full capsule API. Capsules are the mechanism C extensions use to share C-level pointers across module boundaries.

Map

LinesSymbolRole
1-60PyCapsule_NewCreate a capsule wrapping a C pointer
61-140PyCapsule_GetPointerRetrieve the pointer, validating the name
141-200PyCapsule_SetDestructorRegister a cleanup function
201-300PyCapsule_ImportFetch a capsule from another module's attribute

Reading

PyCapsule_New

// CPython: Objects/capsule.c:60 PyCapsule_New
PyObject *
PyCapsule_New(void *pointer, const char *name, PyCapsule_Destructor destructor)
{
PyCapsuleObject *capsule;
if (!pointer) {
PyErr_SetString(PyExc_ValueError, "PyCapsule_New called with null pointer");
return NULL;
}
capsule = PyObject_New(PyCapsuleObject, &PyCapsule_Type);
capsule->pointer = pointer;
capsule->name = name;
capsule->context = NULL;
capsule->destructor = destructor;
return (PyObject *)capsule;
}

The name is a C string literal (typically "module.attribute") used to validate that the pointer came from the right module. The destructor is called from tp_dealloc when the capsule is garbage collected.

PyCapsule_GetPointer

// CPython: Objects/capsule.c:100 PyCapsule_GetPointer
void *
PyCapsule_GetPointer(PyObject *o, const char *name)
{
PyCapsuleObject *capsule = (PyCapsuleObject *)o;
if (!PyCapsule_IsValid(o, name)) {
/* name mismatch: most likely a version skew or wrong capsule */
PyErr_Format(PyExc_ValueError,
"PyCapsule_GetPointer called with incorrect name");
return NULL;
}
return capsule->pointer;
}

PyCapsule_IsValid checks both that o is a capsule and that its name matches. The name check prevents accidentally using a capsule from a different module that happens to expose the same attribute name.

PyCapsule_Import

// CPython: Objects/capsule.c:220 PyCapsule_Import
void *
PyCapsule_Import(const char *name, int no_block)
{
/* name = "package.module.attribute" */
/* Import "package.module", then getattr("attribute") */
char *p = strrchr(name, '.');
/* module_name = name[0..p-1] */
PyObject *module = PyImport_ImportModule(module_name);
PyObject *object = PyObject_GetAttrString(module, p + 1);
void *result = PyCapsule_GetPointer(object, name);
...
return result;
}

PyCapsule_Import("numpy.core._multiarray_umath._ARRAY_API", 0) imports numpy.core._multiarray_umath and fetches _ARRAY_API. The name passed to GetPointer must match the name the capsule was created with, providing a safe cross-module pointer exchange.

gopy notes

PyCapsule_New is objects.NewCapsule in objects/capsule.go. The pointer is stored as unsafe.Pointer. PyCapsule_GetPointer validates the name before returning. PyCapsule_Import calls objects.Import then objects.GetAttr.