Search code examples
pythonc++python-embedding

How is it possible to access a Python module in C++?


I'm trying to import a module in C++; the module resides in a package and should be accessed like:

from x.y import class1,func1, const1, etc 

I'm on Python 3.6 and for this version what I have found so far is to use PyRun_SimpleString to do the import and then use PyImport_AddModuleObject to have a handle to my module. that is:

PyRun_SimpleString("from PacKage1 import Module1 as Module1");
auto module = PyImport_AddModuleObject(PyUnicode_DecodeFSDefault("Module1"));

so that down the road I can access its different attributes, something like this:

auto args = Py_BuildValue("sOOOOONNiN", model_name, model_checkpoint_path, align_fn,
                          bank_folder_root, cache_folder, postfix, 
                          rebuild_cache, use_jit, threshold, device);

if (module != nullptr) {
    // dict is a borrowed reference.
    auto pdict = PyModule_GetDict(module);
    if (pdict == nullptr) {
        cout << "Fails to get the dictionary.\n";
        return 1;
    }
    //Py_DECREF(module);
    PyObject *pKeys = PyDict_Keys(pdict);
    PyObject *pValues = PyDict_Values(pdict);

    map<string, string> my_map;
    //cout << "size: " << PyDict_Size(pdict)<<endl;

    char* cstr_key = new char[100];
    char* cstr_value = new char[500];

    for (Py_ssize_t i = 0; i < PyDict_Size(pdict); ++i) {
        PyArg_Parse(PyList_GetItem(pKeys, i), "s", &cstr_key);
        PyArg_Parse(PyList_GetItem(pValues, i), "s", &cstr_value);
        //cout << cstr<< "  "<< cstr2 <<endl;
        my_map.emplace(cstr_key, cstr_value);
    }
    for (auto x : my_map)
    {
        cout << x.first << " : " << x.second << endl;
    }
    system("pause");

    // Builds the name of a callable class
    auto python_class = PyDict_GetItemString(pdict, "MyClass1");

    system("pause");
    if (python_class == nullptr) {
        cout << "Fails to get the Python class.\n";
        return 1;
    }
    //Py_DECREF(pdict);


    cout << python_class;
    PyObject* object;

    // Creates an instance of the class
    if (PyCallable_Check(python_class)) {
        object = PyObject_CallObject(python_class, args);
        Py_DECREF(python_class);
    }
    else {
        std::cout << "Cannot instantiate the Python class" << endl;
        Py_DECREF(python_class);
        return 1;
    }

    auto val = PyObject_CallMethod(object, "is_jit_model_available", NULL);
    if (!val)
        cout << "error!";

    cout << val;

When I try to run this code, I get this output which shows the maps contents:

size: 5
__doc__ : Module1
__loader__ : Module1
__name__ : Module1
__package__ : Module1
__spec__ : Module1

So this is the result of PyModule_GetDict(module); However, when it comes to the class extraction from this module, it fails, that is, the PyDict_GetItemString(pdict, "MyClass1"); returns null.

It seems to me the module handler itself is not right and that is probably because maybe it doesn't point to the actual module which means I completely failed at importing and getting a handle to that module.

Therefore I can't think of any other way that allows me to import a module and use it like this.

What am I missing here?


Solution

  • As pointed in the comments, my initial attempt for importing the module is completely wrong, and instead I should have done this :

    auto module = PyImport_ImportModule("Package1.Module1");
    

    so I did so :

    auto module = PyImport_ImportModule("FV.F_V");
    

    After doing this, now I get much more meaningful output as you can see:

    module: 0000021306FF9B38
    size: 30
    FaceVerification: C:\Users\Master\Anaconda3\Lib\site-packages\FV\__pycache__\F_V.cpython-36.pyc
    Image: C:\Users\Master\Anaconda3\Lib\site-packages\FV\__pycache__\F_V.cpython-36.pyc
    Path: C:\Users\Master\Anaconda3\Lib\site-packages\FV\__pycache__\F_V.cpython-36.pyc
    __builtins__: C:\Users\Master\Anaconda3\Lib\site-packages\FV\__pycache__\F_V.cpython-36.pyc
    __cached__: C:\Users\Master\Anaconda3\Lib\site-packages\FV\__pycache__\F_V.cpython-36.pyc
    __doc__: FV.F_V
    __file__: C:\Users\Master\Anaconda3\Lib\site-packages\FV\F_V.py
    __loader__: FV
    __name__: FV.F_V
    __package__: FV
    __spec__: FV
    __warningregistry__ : C:\Users\Master\Anaconda3\Lib\site-packages\FV\__pycache__\F_V.cpython-36.pyc
    align_face: C:\Users\Master\Anaconda3\Lib\site-packages\FV\__pycache__\F_V.cpython-36.pyc
    cv2: C:\Users\Master\Anaconda3\Lib\site-packages\FV\__pycache__\F_V.cpython-36.pyc
    data_transforms: C:\Users\Master\Anaconda3\Lib\site-packages\FV\__pycache__\F_V.cpython-36.pyc
    get_central_face_attributes: C:\Users\Master\Anaconda3\Lib\site-packages\FV\__pycache__\F_V.cpython-36.pyc
    math: C:\Users\Master\Anaconda3\Lib\site-packages\FV\__pycache__\F_V.cpython-36.pyc
    nn: C:\Users\Master\Anaconda3\Lib\site-packages\FV\__pycache__\F_V.cpython-36.pyc
    np: C:\Users\Master\Anaconda3\Lib\site-packages\FV\__pycache__\F_V.cpython-36.pyc
    os: C:\Users\Master\Anaconda3\Lib\site-packages\FV\__pycache__\F_V.cpython-36.pyc
    pickle: C:\Users\Master\Anaconda3\Lib\site-packages\FV\__pycache__\F_V.cpython-36.pyc
    plt: C:\Users\Master\Anaconda3\Lib\site-packages\FV\__pycache__\F_V.cpython-36.pyc
    resnet101: C:\Users\Master\Anaconda3\Lib\site-packages\FV\__pycache__\F_V.cpython-36.pyc
    resnet18: C:\Users\Master\Anaconda3\Lib\site-packages\FV\__pycache__\F_V.cpython-36.pyc
    resnet50: C:\Users\Master\Anaconda3\Lib\site-packages\FV\__pycache__\F_V.cpython-36.pyc
    scipy: C:\Users\Master\Anaconda3\Lib\site-packages\FV\__pycache__\F_V.cpython-36.pyc
    time: C:\Users\Master\Anaconda3\Lib\site-packages\FV\__pycache__\F_V.cpython-36.pyc
    torch: C:\Users\Master\Anaconda3\Lib\site-packages\FV\__pycache__\F_V.cpython-36.pyc
    tqdm: C:\Users\Master\Anaconda3\Lib\site-packages\FV\__pycache__\F_V.cpython-36.pyc
    transforms : C:\Users\Master\Anaconda3\Lib\site-packages\FV\__pycache__\F_V.cpython-36.pyc