Search code examples
pythoncconstantspython-c-api

How to access constants from objects' methods with the Python C API?


I have a follow up for another question. That question is about how to access constants added to a C extension module for Python.

To paraphrase that question and answer, if you added constants RED and BLUE to your module in the initialization function like this:

PyMODINIT_FUNC
PyInit_mymodule(void)
{
    PyObject* module = PyModule_Create(&Module);

    // ...

    PyModule_AddStringConstant(module, "BLUE", "blue");
    PyModule_AddStringConstant(module, "RED", "red");

    return module;
}

then you can access them, e.g. to return them from a function, like this (bearing in mind that you must increment reference counts if necessary):

// Get module dict. This is a borrowed reference.
PyObject* module_dict = PyModule_GetDict(module);

// Get BLUE constant. This is a borrowed reference.
PyObject* BLUE = PyDict_GetItemString(module_dict, "BLUE");

// Get RED constant. This is a borrowed reference.
PyObject* RED = PyDict_GetItemString(module_dict, "RED");

That works great if you have a pointer to the module object, which was the case in the linked question.

My question is, how do you do this if you don't have a pointer to the module object? For example, if the current function is an object's method, the self pointer you have is for that object, not the module (and I haven't been able to find a way to get the module from the object):

static PyObject * Foo_get_color(FooObject *self) {
    // How do I get the module object here?
    PyObject *module_dict = PyModule_GetDict(/* ??? */);

    PyObject *color = NULL;
    if (self->color == RED) {
        color = PyDict_GetItemString(module_dict, "RED");
        Py_INCREF(color);
    } else if (self->color == BLUE) {
        color = PyDict_GetItemString(module_dict, "BLUE");
        Py_INCREF(color);
    }

    return color;
}

Solution

  • If you have a module definition (struct PyModuleDef) you can just call PyState_FindModule. I am asuming your struct is named Module as in your example, so it will look like this:

    PyObject * your_function() {
        PyObject *module = PyState_FindModule(&Module);
        PyObject* module_dict = PyModule_GetDict(module);
        ...
    }
    

    The FindModule documentation says that you need to add the module to the interpreter state as follows:

    PyMODINIT_FUNC
    PyInit_mymodule(void)
    {
        PyObject* module = PyModule_Create(&Module);
        PyState_AddModule(module, &Module);
        ...