How to import a file by its full path using C api?

可紊 提交于 2019-12-12 11:56:23

问题


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


回答1:


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");



回答2:


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.




回答3:


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.




回答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.




回答5:


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.




回答6:


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.



来源:https://stackoverflow.com/questions/1796925/how-to-import-a-file-by-its-full-path-using-c-api

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!