Search code examples
pythonc++python-c-api

How to list all function names of a Python module in C++?


I have a C++ program, I want to import a Python module and list all function names in this module. How can I do it?

I used the following code to get the dict from a module:

PyDictObject* pDict = (PyDictObject*)PyModule_GetDict(pModule);

But how to list the functions names?


Solution

  • Out of curiosity, I tried to puzzle this out.

    First, a minimal Python module testModule.py:

    def main():
      print("in test.main()")
    
    def funcTest():
      print("in test.funcTest()")
    

    Second, a minimal C++ sample testPyModule.cc to load and evaluate the testModule:

    // standard C++ header:
    #include <iostream>
    
    // Python header:
    #include <Python.h>
    
    int main()
    {
      // initialize Python interpreter
      Py_Initialize();
      // run script
      const char *const script =
        "# tweak sys path to make Python module of cwd locatable\n"
        "import sys\n"
        "sys.path.insert(0, \".\")\n";
      PyRun_SimpleString(script);
      // get module testModule
      PyObject *pModuleTest = PyImport_ImportModule("testModule"); // new reference
      // evaluate dictionary of testModule
      PyObject *const pDict = PyModule_GetDict(pModuleTest); // borrowed
      // find functions
      std::cout << "Functions of testModule:\n";
      PyObject *pKey = nullptr, *pValue = nullptr;
      for (Py_ssize_t i = 0; PyDict_Next(pDict, &i, &pKey, &pValue);) {
        const char *key = PyUnicode_AsUTF8(pKey);
        if (PyFunction_Check(pValue)) {
          std::cout << "function '" << key << "'\n";
        }
      }
      Py_DECREF(pModuleTest);
      // finalize Python interpreter
      Py_Finalize();
    }
    

    Output:

    Functions of testModule:
    function 'main'
    function 'funcTest'
    

    Notes:

    To puzzle this out, I had to dig through the doc. pages. These are the links for the pages I used:

    It's obvious that I didn't check any pointer for NULL (or nullptr) to keep the sample short and compact. Production code should do this, of course.