为什么PyImport_Import无法从当前目录加载模块



我正在尝试运行嵌入示例,但我无法从当前工作目录加载模块,除非我明确地将其添加到sys.path,否则它可以工作:

PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append(".")"); 

Python不应该在当前目录中查找模块吗?

第1版:尝试使用导入模块

Py_Initialize();
PyRun_SimpleString("import multiply"); 

它仍然失败,并出现以下错误:

Traceback (most recent call last):
File "<string>", line 1, in <module>
ImportError: No module named multiply

第2版:来自sys.path文档:

如果脚本目录不可用(例如,如果解释器交互调用或者如果脚本是从标准输入读取的),path[0]是一个空字符串,用于引导Python搜索模块首先在当前目录中

不确定不可用是什么意思,但如果我打印sys.path[0],它不是空的:

/usr/lib/pymodules/python2.7

您需要调用PySys_SetArgv(int argc, char **argv, int updatepath)才能使相对导入工作。如果updatepath0,则会将正在执行的脚本的路径添加到sys.path(有关更多信息,请参阅文档)。

应采用以下方法

#include <Python.h>
int
main(int argc, char *argv[])
{
Py_SetProgramName(argv[0]);  /* optional but recommended */
Py_Initialize();
PySys_SetArgv(argc, argv); // must call this to get sys.argv and relative imports
PyRun_SimpleString("import os, sysn"
"print sys.argv, "\n".join(sys.path)n"
"print os.getcwd()n"
"import thingn" // import a relative module
"thing.printer()n");
Py_Finalize();
return 0;
}

我对python 3.5所做的是PySys_SetPath,以便能够从cwd位置导入:

QString qs = QDir::currentPath();
std::wstring ws = qs.toStdWString();
PySys_SetPath(ws.data());

其中的Qs是Qt。

要从当前目录加载模块,有很多选项。

  1. 您需要通过初始化结构来配置Python(包括路径配置)。请参见Python初始化配置
Py_Initialize():
//...
PyConfig config;
PyConfig_InitPythonConfig(&config);
config.module_search_paths_set = 1;
PyWideStringList_Append(&config.module_search_paths, L"."); // append local dir to path
Py_InitializeFromConfig(&config);
//...
pModule = PyImport_Import(pName); // import module
  1. 一个单行选项是在Py_Initialize()之后运行PySys_SetArgv(0, NULL);,以将当前目录预先设置为Python的路径。尽管自Python 3.11以来,该选项已被弃用,但它很简单,在许多情况下都是有效的选项。当满足以下条件时,此选项会利用PySys_SetArgvEx(int argc, char **argv, int updatepath)的行为:
  • 非零updatepath
  • 指向现有文件名的argc = 0argv不是

请参阅文档。从Python 2.7起有效。对于添加特定目录,argv[0]应该包含该目录中Python脚本的路径(例如"NULL")。PySys_SetArgv为您正确设置updatepath值。尽管如前所述,该选项目前已被弃用


示例项目

加载当前目录中Python脚本中定义的模块,并从该模块中调用一个函数,所有这些都是用C应用程序的输入参数指定的。为简便起见,已删除支票。此示例与文档页面中提供的在C/C++应用程序中嵌入Python的示例密切相关。

/你好.py

def sayHello1():
print("Hello from sayHello1!")
return 1
def sayHello2():
print("Hello from sayHello2!")
return 2

/main.c

(为简便起见,删除了检查)

#define PY_SSIZE_T_CLEAN
#include <Python.h>
int main(int argc, char *argv[])
{
PyObject *pName = NULL;
PyObject *pModule = NULL;
PyObject *pFunc = NULL;
PyObject *pValue = NULL;
Py_Initialize();
// PySys_SetArgv(0, NULL); // Option 2 [deprecated]
// Option 1 - Python Initialization api
PyConfig config;
PyConfig_InitPythonConfig(&config);
config.module_search_paths_set = 1;
PyWideStringList_Append(&config.module_search_paths, L".");
Py_InitializeFromConfig(&config);
// ...
pName = PyUnicode_FromString(argv[1]);
pModule = PyImport_Import(pName);
Py_XDECREF(pName);
pFunc = PyObject_GetAttrString(pModule, argv[2]);
pValue = PyObject_CallNoArgs(pFunc); // actual call to the fcn
Py_XDECREF(pValue);
Py_XDECREF(pFunc);
Py_XDECREF(pModule);

Py_Finalize();
return 0;
}

编译(cmake):

cmake_minimum_required(VERSION 3.2)
project(python_c_api)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
find_package(Python COMPONENTS Interpreter Development)
cmake_path(GET Python_EXECUTABLE PARENT_PATH Python_BIN_DIR) 
set(config_cmd_flags "--cflags")
execute_process(COMMAND ${Python_BIN_DIR}/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}-config ${config_cmd_flags} 
OUTPUT_VARIABLE Python_FLAGS)
add_executable(python_extension main.c)
target_link_libraries(python_extension PUBLIC python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR})
target_link_directories(python_extension PUBLIC ${Python_LIBRARY_DIRS})
target_include_directories(python_extension PUBLIC ${Python_INCLUDE_DIRS})
set_target_properties(python_extension PROPERTIES 
OUTPUT_NAME "call"
RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}
)
separate_arguments(Python_FLAGS_NORM UNIX_COMMAND "${Python_FLAGS}")
target_compile_options(python_extension PUBLIC ${Python_FLAGS_NORM})
cmake .
cmake --build .

执行:(为简单起见,已删除复选框。按此处所示调用应用程序)。

./call hello sayHello1
Hello from sayHello1!
./call hello sayHello2
Hello from sayHello2!

编辑:此解决方案将当前工作目录附加到搜索路径,而不是存储包含嵌入式python的二进制可执行文件的目录。因此,如果从不同的目录调用应用程序,目录将被附加到搜索路径中。将二进制可执行应用程序的路径(包含嵌入的python解释器)添加到搜索路径并非易事,而且似乎不存在简单的跨平台解决方案。如果需要,我建议将应用程序包装在另一个运行在正确目录中的应用程序或脚本中。

我遇到了完全相同的问题,我只需添加Py_Initialize();Py_Finalize();就解决了它

希望能帮助你

最新更新