Coverage Report

Created: 2026-10-17 06:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cpython/Objects/capsule.c
Line
Count
Source
1
/* Wrap void * pointers to be passed between C modules */
2
3
#include "Python.h"
4
#include "pycore_capsule.h"       // export _PyCapsule_SetTraverse()
5
#include "pycore_gc.h"            // _PyObject_GC_IS_TRACKED()
6
#include "pycore_object.h"        // _PyObject_GC_TRACK()
7
#include "pycore_pymem.h"         // _PyMem_Strdup()
8
9
10
/* Internal structure of PyCapsule */
11
typedef struct {
12
    PyObject_HEAD
13
    void *pointer;
14
    const char *name;
15
    void *context;
16
    PyCapsule_Destructor destructor;
17
    traverseproc traverse_func;
18
    inquiry clear_func;
19
} PyCapsule;
20
21
22
3.83k
#define _PyCapsule_CAST(op)     ((PyCapsule *)(op))
23
24
25
static int
26
_is_legal_capsule(PyObject *op, const char *invalid_capsule)
27
6.81k
{
28
6.81k
    if (!op || !PyCapsule_CheckExact(op)) {
29
0
        goto error;
30
0
    }
31
6.81k
    PyCapsule *capsule = (PyCapsule *)op;
32
33
6.81k
    if (capsule->pointer == NULL) {
34
0
        goto error;
35
0
    }
36
6.81k
    return 1;
37
38
0
error:
39
0
    PyErr_SetString(PyExc_ValueError, invalid_capsule);
40
0
    return 0;
41
6.81k
}
42
43
#define is_legal_capsule(capsule, name) \
44
6.81k
    (_is_legal_capsule(capsule, \
45
6.81k
     name " called with invalid PyCapsule object"))
46
47
48
static int
49
7.03k
name_matches(const char *name1, const char *name2) {
50
    /* if either is NULL, */
51
7.03k
    if (!name1 || !name2) {
52
        /* they're only the same if they're both NULL. */
53
0
        return name1 == name2;
54
0
    }
55
7.03k
    return !strcmp(name1, name2);
56
7.03k
}
57
58
59
60
PyObject *
61
PyCapsule_New(void *pointer, const char *name, PyCapsule_Destructor destructor)
62
3.67k
{
63
3.67k
    PyCapsule *capsule;
64
65
3.67k
    if (!pointer) {
66
0
        PyErr_SetString(PyExc_ValueError, "PyCapsule_New called with null pointer");
67
0
        return NULL;
68
0
    }
69
70
3.67k
    capsule = PyObject_GC_New(PyCapsule, &PyCapsule_Type);
71
3.67k
    if (capsule == NULL) {
72
0
        return NULL;
73
0
    }
74
75
3.67k
    capsule->pointer = pointer;
76
3.67k
    capsule->name = name;
77
3.67k
    capsule->context = NULL;
78
3.67k
    capsule->destructor = destructor;
79
3.67k
    capsule->traverse_func = NULL;
80
3.67k
    capsule->clear_func = NULL;
81
    // Only track the object by the GC when _PyCapsule_SetTraverse() is called
82
83
3.67k
    return (PyObject *)capsule;
84
3.67k
}
85
86
87
int
88
PyCapsule_IsValid(PyObject *op, const char *name)
89
233
{
90
233
    PyCapsule *capsule = (PyCapsule *)op;
91
92
233
    return (capsule != NULL &&
93
233
            PyCapsule_CheckExact(capsule) &&
94
233
            capsule->pointer != NULL &&
95
233
            name_matches(capsule->name, name));
96
233
}
97
98
99
void *
100
PyCapsule_GetPointer(PyObject *op, const char *name)
101
6.80k
{
102
6.80k
    if (!is_legal_capsule(op, "PyCapsule_GetPointer")) {
103
0
        return NULL;
104
0
    }
105
6.80k
    PyCapsule *capsule = (PyCapsule *)op;
106
107
6.80k
    if (!name_matches(name, capsule->name)) {
108
0
        PyErr_SetString(PyExc_ValueError, "PyCapsule_GetPointer called with incorrect name");
109
0
        return NULL;
110
0
    }
111
112
6.80k
    return capsule->pointer;
113
6.80k
}
114
115
116
const char *
117
PyCapsule_GetName(PyObject *op)
118
0
{
119
0
    if (!is_legal_capsule(op, "PyCapsule_GetName")) {
120
0
        return NULL;
121
0
    }
122
0
    PyCapsule *capsule = (PyCapsule *)op;
123
0
    return capsule->name;
124
0
}
125
126
127
PyCapsule_Destructor
128
PyCapsule_GetDestructor(PyObject *op)
129
0
{
130
0
    if (!is_legal_capsule(op, "PyCapsule_GetDestructor")) {
131
0
        return NULL;
132
0
    }
133
0
    PyCapsule *capsule = (PyCapsule *)op;
134
0
    return capsule->destructor;
135
0
}
136
137
138
void *
139
PyCapsule_GetContext(PyObject *op)
140
0
{
141
0
    if (!is_legal_capsule(op, "PyCapsule_GetContext")) {
142
0
        return NULL;
143
0
    }
144
0
    PyCapsule *capsule = (PyCapsule *)op;
145
0
    return capsule->context;
146
0
}
147
148
149
int
150
PyCapsule_SetPointer(PyObject *op, void *pointer)
151
0
{
152
0
    if (!is_legal_capsule(op, "PyCapsule_SetPointer")) {
153
0
        return -1;
154
0
    }
155
0
    PyCapsule *capsule = (PyCapsule *)op;
156
157
0
    if (!pointer) {
158
0
        PyErr_SetString(PyExc_ValueError, "PyCapsule_SetPointer called with null pointer");
159
0
        return -1;
160
0
    }
161
162
0
    capsule->pointer = pointer;
163
0
    return 0;
164
0
}
165
166
167
int
168
PyCapsule_SetName(PyObject *op, const char *name)
169
0
{
170
0
    if (!is_legal_capsule(op, "PyCapsule_SetName")) {
171
0
        return -1;
172
0
    }
173
0
    PyCapsule *capsule = (PyCapsule *)op;
174
175
0
    capsule->name = name;
176
0
    return 0;
177
0
}
178
179
180
int
181
PyCapsule_SetDestructor(PyObject *op, PyCapsule_Destructor destructor)
182
0
{
183
0
    if (!is_legal_capsule(op, "PyCapsule_SetDestructor")) {
184
0
        return -1;
185
0
    }
186
0
    PyCapsule *capsule = (PyCapsule *)op;
187
188
0
    capsule->destructor = destructor;
189
0
    return 0;
190
0
}
191
192
193
int
194
PyCapsule_SetContext(PyObject *op, void *context)
195
0
{
196
0
    if (!is_legal_capsule(op, "PyCapsule_SetContext")) {
197
0
        return -1;
198
0
    }
199
0
    PyCapsule *capsule = (PyCapsule *)op;
200
201
0
    capsule->context = context;
202
0
    return 0;
203
0
}
204
205
206
int
207
_PyCapsule_SetTraverse(PyObject *op, traverseproc traverse_func, inquiry clear_func)
208
6
{
209
6
    if (!is_legal_capsule(op, "_PyCapsule_SetTraverse")) {
210
0
        return -1;
211
0
    }
212
6
    PyCapsule *capsule = (PyCapsule *)op;
213
214
6
    if (traverse_func == NULL || clear_func == NULL) {
215
0
        PyErr_SetString(PyExc_ValueError,
216
0
                        "_PyCapsule_SetTraverse() called with NULL callback");
217
0
        return -1;
218
0
    }
219
220
6
    if (!_PyObject_GC_IS_TRACKED(op)) {
221
6
        _PyObject_GC_TRACK(op);
222
6
    }
223
224
6
    capsule->traverse_func = traverse_func;
225
6
    capsule->clear_func = clear_func;
226
6
    return 0;
227
6
}
228
229
230
void *
231
PyCapsule_Import(const char *name, int Py_UNUSED(no_block))
232
14
{
233
14
    PyObject *object = NULL;
234
14
    void *return_value = NULL;
235
14
    char *name_dup = _PyMem_Strdup(name);
236
237
14
    if (!name_dup) {
238
0
        return PyErr_NoMemory();
239
0
    }
240
241
14
    char *trace = name_dup;
242
28
    while (1) {
243
28
        char *dot = strchr(trace, '.');
244
28
        if (dot) {
245
14
            *dot = '\0';
246
14
        }
247
28
        if (object) {
248
14
            PyObject *attr;
249
14
            if (PyObject_GetOptionalAttrString(object, trace, &attr) < 0) {
250
0
                Py_CLEAR(object);
251
0
                break;
252
0
            }
253
14
            Py_SETREF(object, attr);
254
14
        }
255
28
        if (!dot) {
256
            // We are done
257
14
            break;
258
14
        }
259
260
14
        if (!object) {
261
14
            object = PyImport_ImportModule(name_dup);
262
14
            if (!object) {
263
0
                break;
264
0
            }
265
14
        }
266
14
        *dot = '.';
267
14
        trace = dot + 1;
268
14
    }
269
270
    /* compare attribute name to module.name by hand */
271
14
    if (PyCapsule_IsValid(object, name)) {
272
14
        PyCapsule *capsule = (PyCapsule *)object;
273
14
        return_value = capsule->pointer;
274
14
    }
275
0
    else if (!PyErr_Occurred()) {
276
0
        PyErr_Format(PyExc_AttributeError,
277
0
            "PyCapsule_Import \"%s\" is not valid",
278
0
            name);
279
0
    }
280
281
14
    Py_XDECREF(object);
282
14
    PyMem_Free(name_dup);
283
14
    return return_value;
284
14
}
285
286
287
static void
288
capsule_dealloc(PyObject *op)
289
3.58k
{
290
3.58k
    PyCapsule *capsule = _PyCapsule_CAST(op);
291
3.58k
    PyObject_GC_UnTrack(op);
292
3.58k
    if (capsule->destructor) {
293
52
        capsule->destructor(op);
294
52
    }
295
3.58k
    PyObject_GC_Del(op);
296
3.58k
}
297
298
299
static PyObject *
300
capsule_repr(PyObject *o)
301
0
{
302
0
    PyCapsule *capsule = _PyCapsule_CAST(o);
303
0
    const char *name;
304
0
    const char *quote;
305
306
0
    if (capsule->name) {
307
0
        quote = "\"";
308
0
        name = capsule->name;
309
0
    } else {
310
0
        quote = "";
311
0
        name = "NULL";
312
0
    }
313
314
0
    return PyUnicode_FromFormat("<capsule object %s%s%s at %p>",
315
0
        quote, name, quote, capsule);
316
0
}
317
318
319
static int
320
capsule_traverse(PyObject *self, visitproc visit, void *arg)
321
250
{
322
    // Capsule object is only tracked by the GC
323
    // if _PyCapsule_SetTraverse() is called, but
324
    // this can still be manually triggered by gc.get_referents()
325
250
    PyCapsule *capsule = _PyCapsule_CAST(self);
326
250
    if (capsule->traverse_func != NULL) {
327
250
        return capsule->traverse_func(self, visit, arg);
328
250
    }
329
0
    return 0;
330
250
}
331
332
333
static int
334
capsule_clear(PyObject *self)
335
0
{
336
    // Capsule object is only tracked by the GC
337
    // if _PyCapsule_SetTraverse() is called
338
0
    PyCapsule *capsule = _PyCapsule_CAST(self);
339
    assert(capsule->clear_func != NULL);
340
0
    return capsule->clear_func(self);
341
0
}
342
343
344
PyDoc_STRVAR(PyCapsule_Type__doc__,
345
"Capsule objects let you wrap a C \"void *\" pointer in a Python\n\
346
object.  They're a way of passing data through the Python interpreter\n\
347
without creating your own custom type.\n\
348
\n\
349
Capsules are used for communication between extension modules.\n\
350
They provide a way for an extension module to export a C interface\n\
351
to other extension modules, so that extension modules can use the\n\
352
Python import mechanism to link to one another.\n\
353
");
354
355
PyTypeObject PyCapsule_Type = {
356
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
357
    .tp_name = "PyCapsule",
358
    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
359
    .tp_basicsize = sizeof(PyCapsule),
360
    .tp_dealloc = capsule_dealloc,
361
    .tp_repr = capsule_repr,
362
    .tp_doc = PyCapsule_Type__doc__,
363
    .tp_traverse = capsule_traverse,
364
    .tp_clear = capsule_clear,
365
};
366
367