Search code examples
python-c-api

How to add a built-in module to a C-Python API after Py_Initialize?


I have a module defined in my C code like so:

static struct PyModuleDef module_def = {
    PyModuleDef_HEAD_INIT,
    "the_module_name",  /* m_name */
    module_documentation,  /* m_doc */
    //....
};

and a function to initialize it:

PyMODINIT_FUNC init_the_module(void)
{
    PyObject *mod, *submodule;
    PyObject *sys_modules = PyThreadState_GET()->interp->modules;

    mod = PyModule_Create(&module_def);

    PyModule_AddObject(mod, "some_submodule", (submodule = init_the_submodule()));
    PyDict_SetItemString(sys_modules, PyModule_GetName(submodule), submodule);
    Py_INCREF(submodule);

    // more submodules..

    return mod;
}

The application that I am embedding python into is quite big and I can not change the workflow much. At this point Py_Initialize has already been called, so I can not call PyImport_ExtendInittabor PyImport_AppendInittab . How can I create and add the module to the system modules? Maybe I can manipulate the modules dictionary directly? Like so:

PyObject *modules, *the_module;
modules = PyImport_GetModuleDict();

PyDict_SetItemString(modules, "the_module_name", init_the_module());

the_module = PyDict_GetItemString(modules, "the_module_name"); //this is getting something back
std::cout << PyDict_Size(the_module) << std::endl; // this prints -1

Solution

  • The easiest way to handle this is to statically initialize your statically-linked modules by directly calling initspam() after the call to Py_Initialize() or PyMac_Initialize():

    int main(int argc, char **argv)
    {
        /* Pass argv[0] to the Python interpreter */
        Py_SetProgramName(argv[0]);
    
        /* Initialize the Python interpreter.  Required. */
        Py_Initialize();
    
        /* Add a static module */
        initspam();
    

    An example may be found in the file Demo/embed/demo.c in the Python source distribution.