Question

PyObject* PyImport_ImportModule( const char *name)

How to specify a full file path instead and a module name?

Like PyImport_SomeFunction(const char *path_to_script, const char *name)

Thanks, Elias

Was it helpful?

Solution 4

Eventually, I ended up using the load_source from imp module:

s.sprintf( 
  "import imp\n" 
  "imp.load_source('%s', r'%s')", modname, script_path); 
PyRun_SimpleString(s.c_str()); 

I think it is the most feasible solution. Other suggestions are welcome.

OTHER TIPS

Another solution for cases when all *.py file are in one directory:

PySys_SetPath("path/with/python/files");
PyObject *pModule = PyImport_ImportModule("filename_without_extension");

I can't give you a full answer, but I think I can give you a place to start. Python provides a built-in module called imp which provides access to import internals. It includes a function load_module() which lets you pass in a path. This is implemented in Python/import.c; just search for imp_load_module.

As pointed out, using the above solution by AlexP, you can't import any modules outside of the specified directory. However, instead of setting the path, you can add a path, which is to be searched for modules. This can be done by appending that path to sys.path. Unfortunately the C API doesn't expose a function that is able to do that directly. Instead you can ask Python itself to do it, using

PyRun_SimpleString( "import sys\nsys.path.append(\"<insert folder path>\")\n" );

or any equivalent like: (warning: untested)

PyObject* sys = PyImport_ImportModule( "sys" );
PyObject* sys_path = PyObject_GetAttrString( sys, "path" );
PyObject* folder_path = PyUnicode_FromString( "<insert folder path>" );
PyList_Append( sys_path, folder_path );

Now you can import any file <insert folder path>/<file>.py using the usual

PyObject* mymodule = PyImport_ImportModule( "<file>" );

To refer to the current directory, simply use "." as the folder path.

CPython will not hesitate to call through the imp module to perform import jobs, so there's no reason that you shouldn't do it yourself.

Here's one way to do it in C++, where modulePath and moduleName are separate variables because of laziness:

PyObject* loadModule(const char* modulePath, const char* moduleName)
{
    auto modules = PyImport_GetModuleDict();
    auto impModule = PyDict_GetItemString(modules, "imp");
    if (impModule)
    {
        // GetItemString returns a borrowed reference, but ImportModule
        // returns a new reference, so INCREF here to even it out
        Py_INCREF(impModule);
    }
    else
    {
        impModule = PyImport_ImportModule("imp");
        if (!impModule)
        {
            // we've tried hard enough, bail out
            PyErr_Print();
            return nullptr;
        }
    }

    // The Python API asks for non-const char pointers :(
    char methodName[] = "load_source";
    char args[] = "ss";
    auto module = PyObject_CallMethod(impModule, methodName, args, moduleName, modulePath);

    Py_XDECREF(impModule);
    Py_XDECREF(modulePath);
    return module;
}

I wrote this based on one of my projects' Python module loader, which uses fancier refcount management, and I haven't tried this one, so use at your own risk.

I verified that the alternative of stesim works.

No matter which solution is used, it is important to mention that on windows it is important to have the full path with slashes instead of backslashes.

path = "C:\\path\\to\\module\\module.py" // wrong
path = "C:/path/to/module/module.py"     // correct

In some other questions and answers (like Windows path in python) it is stated, that the first variant should work too. I couldn't get it to work that way whatever method I tried.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top