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;
}
--
https://mail.python.org/mailman/listinfo/python-list

Reply via email to