How to instantiate a custom Python class inside a C extension?
Musbur
musbur at posteo.org
Wed Apr 1 08:42:45 EDT 2020
Hi guys,
I'm wondering how to create an instance of an extension class I wrote.
There's a minimal self-contained C module at the bottom of this post
which exports two things: 1) a class Series, and 2) a function
make_series() which is supposed to create a Series object on the C side
and return it. The make_series function uses PyObject_New() and
PyObject_Init() to create the new instance, but all it produces is some
kind of zombie instance which tends to crash the application with a
segfault in real life. When instantiated from Python using Series(), I
get a well-behaved instance.
I've sprinkled the New, Init and Finalize functions with fprintf()s to
see what happens to the object during its lifetime.
When I run this test script:
from series import *
print("From Python")
s1 = Series()
del s1
print("\nFrom C")
s2 = make_series()
del s2
I get this output:
From Python
New Series at 0x7f89313f6660
Init Series at 0x7f89313f6660
Finalize Series at 0x7f89313f6660
From C
Finalize Series at 0x7f89313f6678
So when created from C, neither the "new" nor the "init" functions are
called on the object, only "finalize". No wonder I get segfaults in the
real life application.
So how is this done right? Here's the C module:
#include <Python.h>
typedef struct {
PyObject_HEAD
void *data;
} Series;
static PyObject *Series_new(PyTypeObject *type,
PyObject *args, PyObject *kw) {
Series *self;
self = (Series *) type->tp_alloc(type, 0);
self->data = NULL;
fprintf(stderr, "New Series at %p\n", self);
return (PyObject*)self;
}
static int Series_init(Series *self, PyObject *args, PyObject *kw) {
fprintf(stderr, "Init Series at %p\n", self);
return 0;
}
static void Series_finalize(PyObject *self) {
fprintf(stderr, "Finalize Series at %p\n", self);
}
static PyMethodDef series_methods[] = {
{NULL, NULL, 0, NULL}
};
static PyTypeObject series_type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "_Series",
.tp_basicsize = sizeof(Series),
.tp_flags = 0
| Py_TPFLAGS_DEFAULT
| Py_TPFLAGS_BASETYPE,
.tp_doc = "Series (msec, value) object",
.tp_methods = series_methods,
.tp_new = Series_new,
.tp_init = (initproc) Series_init,
.tp_dealloc = Series_finalize,
};
/* To create a new Series object directly from C */
PyObject *make_series(void *data) {
Series *pyseries;
pyseries = PyObject_New(Series, &series_type);
PyObject_Init((PyObject *)pyseries, &series_type);
pyseries->data = data;
return (PyObject *) pyseries;
}
static PyMethodDef module_methods[] = {
{"make_series", (PyCFunction)make_series, METH_NOARGS,
"Instantiate and return a new Series object."},
{NULL, NULL, 0, NULL}
};
static PyModuleDef series_module = {
PyModuleDef_HEAD_INIT,
"series",
"Defines the Series (time, value) class"
,
-1,
module_methods
};
PyMODINIT_FUNC PyInit_series(void) {
PyObject *m;
m = PyModule_Create(&series_module);
if (PyType_Ready(&series_type) < 0) {
return NULL;
}
PyModule_AddObject(m, "Series", (PyObject*)&series_type);
return m;
}
More information about the Python-list
mailing list