How to instantiate a custom Python class inside a C extension?

MRAB python at mrabarnett.plus.com
Wed Apr 1 15:43:09 EDT 2020


On 2020-04-01 13:42, Musbur wrote:
> 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:
> 
[snip]

Try this instead:

#include <Python.h>

typedef struct {
     PyObject_HEAD
     void* data;
} SeriesObject;

static PyTypeObject SeriesType = {
     PyVarObject_HEAD_INIT(NULL, 0)
     .tp_name = "_Series",
     .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
     .tp_doc = "SeriesObject (msec, value) object",
};

PyObject* make_series(PyObject* unused) {
     SeriesObject* series;

     series = PyObject_New(SeriesObject, &SeriesType);
     series->data = NULL;
     fprintf(stderr, "New SeriesObject at %p\n", series);

     return (PyObject*)series;
}

static void Series_dealloc(PyObject* self_) {
     SeriesObject* self;

     self = (SeriesObject*)self_;
     fprintf(stderr, "Deallocate SeriesObject at %p\n", self);

     PyObject_DEL(self);
}

PyObject* Series_new(PyTypeObject* type, PyObject* args, PyObject* kwargs) {
     SeriesObject* self;

     self = (SeriesObject*)type->tp_alloc(type, 0);
     self->data = NULL;
     fprintf(stderr, "New Series at %p\n", self);

     return (PyObject*)self;
}

static PyMethodDef Series_methods[] = {
     {NULL, NULL, 0, NULL}
};

static PyMethodDef module_methods[] = {
     {"make_series", (PyCFunction)make_series, METH_NOARGS,
       "Instantiate and return a new SeriesObject object."},
     {NULL, NULL, 0, NULL}
};

static PyModuleDef series_module = {
     PyModuleDef_HEAD_INIT,
     "series",
     "Defines the SeriesObject (time, value) class"
     ,
     -1,
     module_methods
};

PyMODINIT_FUNC PyInit_series(void) {
     PyObject* m;

     m = PyModule_Create(&series_module);

     SeriesType.tp_dealloc = Series_dealloc;
     SeriesType.tp_new = Series_new;
     SeriesType.tp_methods = Series_methods;
     if (PyType_Ready(&SeriesType) < 0)
         return NULL;

     PyModule_AddObject(m, "Series", (PyObject*)&SeriesType);

     return m;
}


More information about the Python-list mailing list