1

I want to create an instance of a Python class with the C API.

My specific use case is the PySerial Object. My Python code would be:

import serial
test = serial.Serial()

I couldn't find any clue or Tutorial for doing so. The only thing I think to know is to declare it as a dependency in my pyproject.toml and import it somehow with a PyImport_* function.

My current code looks somehow like this:

...
/*! Python Module Initialization Function
 * This function will be called when importing the Module to the Python Interpreter.
 * In this function all Python Objects and Constants are added to the Python Module.
 */
PyMODINIT_FUNC PyInit_Module(void)
{
    PyObject *m;
    // Check SubClass Type
    if (PyType_Ready(&SubClassType) < 0) return NULL;

    m = PyModule_Create(&Module_module);
    if (m == NULL) return NULL;

    /* Adding SubClass Class */
    Py_INCREF(&SubClassType);

    if (PyModule_AddObject(m, "SubClass", (PyObject *)&SubClassType) < 0) {
        Py_DECREF(&SubClassType);
        Py_DECREF(m);
        return NULL;
    }

    PySerialObject = PyImport_ImportModule("serial");
    if (PySerialObject == NULL) {
        Py_DECREF(&SubClassType);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
...
/*! SubClass new
 * Creating new SubClass Python Object
 */
static PyObject *SubClass_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    SubClassObject *self;
    self = (SubClassObject *)type->tp_alloc(type, 0);
    self->serial = NULL;
    if (self != NULL) {
                //self->serial = PyObject_CallNoArgs((PyObject*)Py_TYPE(PySerialObject));
        self->serial = PyObject_CallOneArg((PyObject *)Py_TYPE(PySerialObject), Py_BuildValue("serial.Serial()"));
        if (self->serial == NULL) return NULL;
    }
    return (PyObject *)self;
}

My goal is to use the serial.Serial() Python object inside my C code.

5
  • you need to explain more about what the problem is. like does PyImport_ImportModule() returns NULL? stackoverflow.com/help/minimal-reproducible-example Commented Oct 22, 2024 at 13:06
  • It doesn't returns NULL. I'm not sure what it returns. I don't know what to do with it or how to proceed. Commented Oct 22, 2024 at 14:40
  • Side note: Move self->serial = NULL; from above to under if (self != NULL) { because, as you have it, you're dereferencing self before you guarantee that it's non-null. If self were NULL, you'd get a segfault. If you do that change, you can just eliminate the line because you do: self->serial = PyObject_CallOneArg(...); immediately afterwards so self->serial = NULL; is [effectively] a no-op. Commented Oct 22, 2024 at 22:16
  • debug through the app and see who returns what first Commented Oct 23, 2024 at 9:24
  • @CraigEstey yes thank you for the comment. I was too absorbed with my problem and didn't noticed it. Commented Oct 23, 2024 at 16:24

1 Answer 1

1

I found my answer working for my with guidance from the answer Any way to create a NumPy matrix with C API?

The clue is to first import the module and then get the Object from their attributes. With this Object you can instantiate more Instances. In code it would look something like:

/*! Python Module Initialization Function
 * This function will be called when importing the Module to the Python Interpreter.
 * In this function all Python Objects and Constants are added to the Python Module.
 */
PyMODINIT_FUNC PyInit_Module(void)
{
    PyObject *m;
    // Check SubClass Type
    if (PyType_Ready(&SubClassType) < 0) return NULL;

    m = PyModule_Create(&Module_module);
    if (m == NULL) return NULL;

    /* Adding SubClass Class */
    Py_INCREF(&SubClassType);

    if (PyModule_AddObject(m, "SubClass", (PyObject *)&SubClassType) < 0) {
        Py_DECREF(&SubClassType);
        Py_DECREF(m);
        return NULL;
    }

    PySerial = PyImport_ImportModule("serial");
    if (PySerial == NULL) {
        Py_DECREF(&SubClassType);
        Py_DECREF(m);
        return NULL;
    }
    PySerialObject = PyObject_GetAttrString(PySerial, "Serial");
    if (PySerialObject == NULL) {
        Py_DECREF(&SubClassType);
        Py_DECREF(&PySerial);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
...
/*! SubClass new
 * Creating new SubClass Python Object
 */
static PyObject *SubClass_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    SubClassObject *self;
    self = (SubClassObject *)type->tp_alloc(type, 0);
    if (self == NULL) return NULL;
    self->serial = NULL;
    self->serial = PyObject_CallNoArgs(PySerialObject);
    if (self->serial == NULL) return NULL;
    return (PyObject *)self;
}

For a minimal solution I would propose

PyObject *serial = PyImport_ImportModule("serial");
PyObject *serialObject = PyObject_GetAttrString(serial, "Serial");
PyObject *my_serial = PyObject_CallNoArgs(serialObject);

But of course you can use any of the PyObject_Call* functions.

I want to leave it here so future developers have an easy time searching for this exact problem.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.