更新(1)
:在一些编译的stdlib模块中也可以看到同样的问题。这与numpy无关(我将从标题中删除numpy标签和numpy)
我正在编写一个包含嵌入式python解释器的共享对象(软件插件)。共享对象启动解释器,解释器导入要执行的python模块。如果导入的模块包含numpy,我会得到一个未定义的符号错误。实际的未定义符号错误会在python版本或numpy版本的函数中发生变化,但它始终是
PyExc_*
家庭
我已将问题简化为这个最小的示例(它实际上包含两个文件):
// main.cc
#include "pybind11/embed.h"
namespace py = pybind11;
extern "C" {
int main() {
py::scoped_interpreter guard{};
auto py_module = py::module::import("numpy");
auto version = py_module.attr("__version__");
py::print(version);
return 0;
}
}
// load.cc
#include <dlfcn.h>
int main() {
void * lib = dlopen("./libissue.so", RTLD_NOW);
int(*fnc)(void) = (int(*)(void))dlsym(lib, "main");
fnc();
dlclose(lib);
return 0;
}
我正在用这个CMAKE文件编译:
cmake_minimum_required(VERSION 3.14)
include(FetchContent)
FetchContent_Declare(
pybind11
GIT_REPOSITORY https://github.com/pybind/pybind11
GIT_TAG v2.8.1)
FetchContent_MakeAvailable(pybind11)
project(
pybind_issue
LANGUAGES C CXX
VERSION 1.0.0)
add_library(issue SHARED main.cc)
set_target_properties(issue PROPERTIES
POSITION_INDEPENDENT_CODE ON
CXX_STANDARD 11)
target_link_libraries(issue PRIVATE pybind11::embed)
# also tested with
# target_link_libraries(main PRIVATE mylib pybind11::lto pybind11::embed pybind11::module)
add_executable(issue_main main.cc)
set_target_properties(issue_main PROPERTIES
POSITION_INDEPENDENT_CODE ON
CXX_STANDARD 11)
target_link_libraries(issue_main PRIVATE pybind11::embed)
add_executable(loader load.cc)
target_link_libraries(loader PRIVATE ${CMAKE_DL_LIBS})
此CMAKE文件汇编了三个目标:
-
加载解释器、导入numpy并打印其版本的可执行文件
-
一个共享对象,它导出一个C函数,该函数做完全相同的事情
-
一个简单的共享对象加载程序,尝试运行导出的函数
"main"
来自共享对象。
如果我运行
issue_main
可执行文件,我可以正确地在屏幕上看到numpy版本。如果我跑
loader
我得到了这个错误:
terminate called after throwing an instance of 'pybind11::error_already_set'
what(): ImportError:
https://numpy.org/devdocs/user/troubleshooting-importerror.html
* The Python version is: Python3.8 from "/usr/bin/python3"
* The NumPy version is: "1.20.3"
and make sure that they are the versions you expect.
Please carefully study the documentation linked above for further help.
Original error was: /usr/local/lib/python3.8/dist-packages/numpy/core/_multiarray_umath.cpython-38-x86_64-linux-gnu.so: undefined symbol: PyExc_RecursionError
At:
/usr/local/lib/python3.8/dist-packages/numpy/core/__init__.py(51): <module>
<frozen importlib._bootstrap>(219): _call_with_frames_removed
<frozen importlib._bootstrap_external>(848): exec_module
<frozen importlib._bootstrap>(686): _load_unlocked
<frozen importlib._bootstrap>(975): _find_and_load_unlocked
<frozen importlib._bootstrap>(991): _find_and_load
<frozen importlib._bootstrap>(219): _call_with_frames_removed
<frozen importlib._bootstrap>(1050): _handle_fromlist
<frozen importlib._bootstrap>(219): _call_with_frames_removed
<frozen importlib._bootstrap>(961): _find_and_load_unlocked
irb(main):003:1* module TestMain
=> #<FFI::Function address=0x00007f9d0ba43bb6>
irb(main):008:0>
irb(main):009:0> TestMain.main
terminate called after throwing an instance of 'pybind11::error_already_set'
what(): ImportError:
IMPORTANT: PLEASE READ THIS FOR ADVICE ON HOW TO SOLVE THIS ISSUE!
Importing the numpy C-extensions failed. This error can happen for
many reasons, often due to issues with your setup or how NumPy was
installed.
We have compiled some common reasons and troubleshooting tips at:
https://numpy.org/devdocs/user/troubleshooting-importerror.html
Please note and check the following:
* The Python version is: Python3.8 from "/usr/bin/python3"
* The NumPy version is: "1.20.3"
and make sure that they are the versions you expect.
Please carefully study the documentation linked above for further help.
Original error was: /usr/local/lib/python3.8/dist-packages/numpy/core/_multiarray_umath.cpython-38-x86_64-linux-gnu.so: undefined symbol: PyExc_RecursionError
At:
/usr/local/lib/python3.8/dist-packages/numpy/core/__init__.py(51): <module>
<frozen importlib._bootstrap>(219): _call_with_frames_removed
<frozen importlib._bootstrap_external>(848): exec_module
<frozen importlib._bootstrap>(686): _load_unlocked
<frozen importlib._bootstrap>(975): _find_and_load_unlocked
<frozen importlib._bootstrap>(991): _find_and_load
<frozen importlib._bootstrap>(219): _call_with_frames_removed
<frozen importlib._bootstrap>(1050): _handle_fromlist
/usr/local/lib/python3.8/dist-packages/numpy/__init__.py(145): <module>
<frozen importlib._bootstrap>(219): _call_with_frames_removed
<frozen importlib._bootstrap_external>(848): exec_module
<frozen importlib._bootstrap>(686): _load_unlocked
<frozen importlib._bootstrap>(975): _find_and_load_unlocked
<frozen importlib._bootstrap>(991): _find_and_load
<frozen importlib._bootstrap>(219): _call_with_frames_removed
<frozen importlib._bootstrap>(961): _find_and_load_unlocked
<frozen importlib._bootstrap>(991): _find_and_load
这个问题在linux上是特定的(没有在OSX上测试),而在Windows上一切都按照预期进行(代码有点变化,为了完整性,这里报告):
// main.cc
#include "pybind11/embed.h"
namespace py = pybind11;
extern "C" {
__declspec(dllexport) int main() {
py::scoped_interpreter guard{};
auto py_module = py::module::import("numpy");
auto version = py_module.attr("__version__");
py::print(version);
return 0;
}
}
// load.cc
#include <windows.h>
int main() {
HMODULE lib = LoadLibrary("./issue.dll");
int(*fnc)(void) = (int(*)(void))GetProcAddress(lib, "main");
fnc();
FreeLibrary(lib);
return 0;
}
有什么我遗漏的吗?
笔记
:
-
我的第一个想法是pybind cmake中的一个bug,这就是我发布
this bug report
-
我的问题似乎与中描述的类似
this bug report
,但我不确定,我甚至不确定它是否是一个bug
-
问题类似于
one described here
,但我认为在这个最小的示例中,我不会多次加载解释器。我想我已经看到了一个与同一个问题相关的问题,并且使用了相同的解决方案(不要多次加载解释器),但是我现在找不到参考。
-
我已经用几个numpy版本(从1.19到1.22,从Ubuntu存储库安装,从pip安装,并在本地构建)进行了测试,但问题仍然存在。只更改了未定义的符号(但始终为
PyExc_
)
-
用蟒蛇3测试。Ubuntu 18.04和Ubuntu 20.04中的6和3.8
-
在pybind 2.6、2.7、2.8.1上测试
-
我试图链接到python静态库,但它不是用-fPIC编译的,因此编译失败。。。
更新说明(1)
:这似乎不仅仅与numpy有关。如果我进口
decimal
(一个带有c模块组件的stdlib数值类)我得到了一个类似的错误:
#include "pybind11/embed.h"
namespace py = pybind11;
extern "C" {
int main() {
py::scoped_interpreter guard{};
auto py_module = py::module::import("decimal");
auto version = py_module.attr("__name__");
py::print(version);
return 0;
}
}
给我
terminate called after throwing an instance of 'pybind11::error_already_set'
what(): ImportError: /usr/lib/python3.8/lib-dynload/_contextvars.cpython-38-x86_64-linux-gnu.so: undefined symbol: PyContextVar_Type
At:
/usr/lib/python3.8/contextvars.py(1): <module>
<frozen importlib._bootstrap>(219): _call_with_frames_removed
<frozen importlib._bootstrap_external>(848): exec_module
<frozen importlib._bootstrap>(686): _load_unlocked
<frozen importlib._bootstrap>(975): _find_and_load_unlocked
<frozen importlib._bootstrap>(991): _find_and_load
/usr/lib/python3.8/_pydecimal.py(440): <module>
<frozen importlib._bootstrap>(219): _call_with_frames_removed
<frozen importlib._bootstrap_external>(848): exec_module
<frozen importlib._bootstrap>(686): _load_unlocked
<frozen importlib._bootstrap>(975): _find_and_load_unlocked
<frozen importlib._bootstrap>(991): _find_and_load
/usr/lib/python3.8/decimal.py(8): <module>
<frozen importlib._bootstrap>(219): _call_with_frames_removed
<frozen importlib._bootstrap_external>(848): exec_module
<frozen importlib._bootstrap>(686): _load_unlocked
<frozen importlib._bootstrap>(975): _find_and_load_unlocked
<frozen importlib._bootstrap>(991): _find_and_load
[1] 3095287 abort (core dumped) ./loader