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