mirror of https://github.com/python/cpython
361 lines
8.0 KiB
C
361 lines
8.0 KiB
C
/* Wrap void * pointers to be passed between C modules */
|
|
|
|
#include "Python.h"
|
|
#include "pycore_gc.h" // _PyObject_GC_IS_TRACKED()
|
|
#include "pycore_object.h" // _PyObject_GC_TRACK()
|
|
|
|
|
|
/* Internal structure of PyCapsule */
|
|
typedef struct {
|
|
PyObject_HEAD
|
|
void *pointer;
|
|
const char *name;
|
|
void *context;
|
|
PyCapsule_Destructor destructor;
|
|
traverseproc traverse_func;
|
|
inquiry clear_func;
|
|
} PyCapsule;
|
|
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
#define is_legal_capsule(capsule, name) \
|
|
(_is_legal_capsule(capsule, \
|
|
name " called with invalid PyCapsule object"))
|
|
|
|
|
|
static int
|
|
name_matches(const char *name1, const char *name2) {
|
|
/* if either is NULL, */
|
|
if (!name1 || !name2) {
|
|
/* they're only the same if they're both NULL. */
|
|
return name1 == name2;
|
|
}
|
|
return !strcmp(name1, name2);
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
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));
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
const char *
|
|
PyCapsule_GetName(PyObject *op)
|
|
{
|
|
if (!is_legal_capsule(op, "PyCapsule_GetName")) {
|
|
return NULL;
|
|
}
|
|
PyCapsule *capsule = (PyCapsule *)op;
|
|
return capsule->name;
|
|
}
|
|
|
|
|
|
PyCapsule_Destructor
|
|
PyCapsule_GetDestructor(PyObject *op)
|
|
{
|
|
if (!is_legal_capsule(op, "PyCapsule_GetDestructor")) {
|
|
return NULL;
|
|
}
|
|
PyCapsule *capsule = (PyCapsule *)op;
|
|
return capsule->destructor;
|
|
}
|
|
|
|
|
|
void *
|
|
PyCapsule_GetContext(PyObject *op)
|
|
{
|
|
if (!is_legal_capsule(op, "PyCapsule_GetContext")) {
|
|
return NULL;
|
|
}
|
|
PyCapsule *capsule = (PyCapsule *)op;
|
|
return capsule->context;
|
|
}
|
|
|
|
|
|
int
|
|
PyCapsule_SetPointer(PyObject *op, void *pointer)
|
|
{
|
|
if (!is_legal_capsule(op, "PyCapsule_SetPointer")) {
|
|
return -1;
|
|
}
|
|
PyCapsule *capsule = (PyCapsule *)op;
|
|
|
|
if (!pointer) {
|
|
PyErr_SetString(PyExc_ValueError, "PyCapsule_SetPointer called with null pointer");
|
|
return -1;
|
|
}
|
|
|
|
capsule->pointer = pointer;
|
|
return 0;
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
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);
|
|
|
|
if (!name_dup) {
|
|
return PyErr_NoMemory();
|
|
}
|
|
|
|
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);
|
|
if (!object) {
|
|
PyErr_Format(PyExc_ImportError, "PyCapsule_Import could not import module \"%s\"", trace);
|
|
}
|
|
} else {
|
|
PyObject *object2 = PyObject_GetAttrString(object, trace);
|
|
Py_SETREF(object, object2);
|
|
}
|
|
if (!object) {
|
|
goto EXIT;
|
|
}
|
|
|
|
trace = dot;
|
|
}
|
|
|
|
/* compare attribute name to module.name by hand */
|
|
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;
|
|
}
|
|
|
|
|
|
static void
|
|
capsule_dealloc(PyObject *op)
|
|
{
|
|
PyCapsule *capsule = (PyCapsule *)op;
|
|
PyObject_GC_UnTrack(op);
|
|
if (capsule->destructor) {
|
|
capsule->destructor(op);
|
|
}
|
|
PyObject_GC_Del(op);
|
|
}
|
|
|
|
|
|
static PyObject *
|
|
capsule_repr(PyObject *o)
|
|
{
|
|
PyCapsule *capsule = (PyCapsule *)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);
|
|
}
|
|
|
|
|
|
static int
|
|
capsule_traverse(PyCapsule *capsule, visitproc visit, void *arg)
|
|
{
|
|
// Capsule object is only tracked by the GC
|
|
// if _PyCapsule_SetTraverse() is called
|
|
assert(capsule->traverse_func != NULL);
|
|
|
|
return capsule->traverse_func((PyObject*)capsule, visit, arg);
|
|
}
|
|
|
|
|
|
static int
|
|
capsule_clear(PyCapsule *capsule)
|
|
{
|
|
// Capsule object is only tracked by the GC
|
|
// if _PyCapsule_SetTraverse() is called
|
|
assert(capsule->clear_func != NULL);
|
|
|
|
return capsule->clear_func((PyObject*)capsule);
|
|
}
|
|
|
|
|
|
PyDoc_STRVAR(PyCapsule_Type__doc__,
|
|
"Capsule objects let you wrap a C \"void *\" pointer in a Python\n\
|
|
object. They're a way of passing data through the Python interpreter\n\
|
|
without creating your own custom type.\n\
|
|
\n\
|
|
Capsules are used for communication between extension modules.\n\
|
|
They provide a way for an extension module to export a C interface\n\
|
|
to other extension modules, so that extension modules can use the\n\
|
|
Python import mechanism to link to one another.\n\
|
|
");
|
|
|
|
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 = (traverseproc)capsule_traverse,
|
|
.tp_clear = (inquiry)capsule_clear,
|
|
};
|
|
|
|
|