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

问题描述 投票:0回答:5

我正在尝试运行 嵌入示例,并且无法从当前工作目录加载模块,除非我明确地将其添加到

sys.path
然后它就可以工作了:

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

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

Edit1:尝试仅导入模块:

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
python linux python-2.7 python-embedding
5个回答
19
投票

您需要调用

PySys_SetArgv(int argc, char **argv, int updatepath)
才能使相关导入工作。如果
sys.path
updatepath
,这会将正在执行的脚本的路径添加到
0
(有关更多信息,请参阅 docs)。

以下应该可以解决问题

#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, sys\n"
                     "print sys.argv, \"\\n\".join(sys.path)\n"
                     "print os.getcwd()\n"
                     "import thing\n" // import a relative module
                     "thing.printer()\n");
  Py_Finalize();
  return 0;
}

0
投票

我对 python 3.5 所做的就是

PySys_SetPath
能够从
cwd
位置导入:

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

里面的

Q
就是Qt。


0
投票

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

  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. 单行选项是在
    PySys_SetArgv(0, NULL);
    之后运行
    Py_Initialize()
    将当前目录添加到 Python 的路径前面。尽管此选项自 Python 3.11 起已被弃用,但它很简单,并且在许多情况下都是有效的选项。此选项在满足以下条件时利用
    PySys_SetArgvEx(int argc, char **argv, int updatepath)
    的行为:
  • 非零
    updatepath
  • argc = 0
    argv
    not 指向现有文件名。

请参阅文档。从 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 的二进制可执行文件的目录。因此,如果从不同的目录调用应用程序,that目录将被附加到搜索路径。 将二进制可执行应用程序(包含嵌入的 python 解释器)的路径添加到搜索路径并非易事,而且似乎不存在简单的跨平台解决方案。如果需要,我建议将应用程序包装在另一个应用程序或在正确目录中运行的脚本中。


0
投票
实际

答案可能是这样的: 自动将当前目录添加到 sys.path 就是“安全路径”的概念。 但看:

int 安全路径
...

默认值:Python 配置中为 0,独立配置中为 1。

https://docs.python.org/3/c-api/init_config.html#c.PyConfig.safe_path

隔离配置可用于将 Python 嵌入到应用程序中。它将Python与系统隔离。例如,环境变量被忽略,LC_CTYPE 语言环境保持不变,并且没有注册信号处理程序。

https://docs.python.org/3/c-api/init_config.html#init-config

看起来,嵌入模式作为隔离模式运行,默认情况下专门禁用此行为。

这个答案适用于 Python 3,在该版本中,这种行为仍然可以观察到。


-1
投票
Py_Initialize();

Py_Finalize();
 即可解决它

希望可以帮到你

© www.soinside.com 2019 - 2024. All rights reserved.