当从 C++ 应用程序中的嵌入式 Python 调用时,Numpy 导入在多数组扩展库上失败

Numpy import fails on multiarray extension library when called from embedded Python within a C++ application

我正在 运行ning 一个 C++ 应用程序,它试图 运行 python 使用 https://docs.python.org/3.5/extending/embedding.html 函数调用。这是应用程序错误消息管道给我的错误。

class 'ImportError': Importing the multiarray numpy extension module failed. Most likely you are trying to import a failed build of numpy. If you're working with a numpy git repo, try git clean -xdf (removes all files not under version control). Otherwise reinstall numpy.

Original error was: /usr/local/lib/python3.5/site-packages/numpy/core/multiarray.cpython-35m-x86_64-linux-gnu.so: undefined symbol: PyExc_UserWarning

我很困惑,因为只有在 C++ 中嵌入 Python 时才会出现这种情况,因为当我通过解释器使用它时,导入工作正常。我对增加我的理解的答案比快速做这个或做那个修复更感兴趣。我在下面列出了一些 system/problem 信息,以及我正在考虑发布的关于同一主题的其他一些问题。任何指导表示赞赏!

System/Problem信息:

我 could/might 尝试通过不同的方式重新安装 numpy,但我无法跟踪为什么这可能有效。

在这一点上,我假设我的知识存在一些漏洞。我看过很多关于在 C++ 中嵌入 Python 时无法导入多数组组件和 numpy 的类似帖子;但是,其中 none 符合我的具体情况,或者如我所说,存在漏洞。以下是我可能会问的子问题列表,如果没有人在这个设置中看到任何明显令人担忧的内容。我可能会用 links when/if 更新问题 我问他们(在我完善它们之后)。

此时我不是在寻求上述问题列表的答案,而是提供更多线索,说明我的知识差距可能在哪里。

感谢您今天抽出时间阅读这个问题。任何帮助表示赞赏。

编辑:2018 年 4 月 17 日:

好吧,我找到了解决方法,目前正在使用它。沙丘问题开始让我更仔细地思考未定义的符号,以及它如何成为 linker/compiler 错误,或者 numpy 导入总是期望一个已经将这些符号加载到内存中的环境。这让我尝试安装不同版本的 numpy 以查看是否有任何旧版本有所不同。他们没有,但它确实使抛出的错误略有不同。当我用谷歌搜索时,这个 。接受的答案通过将这两行添加到 pythonInterface.cpp:

来解决我的问题

这些命令添加要加载的共享库并可供 cpython.multiarray.so 使用。

这不是指向特定 .so 的理想解决方案,它可能因机器而异。它暂时解决了这个问题,但它也可能导致错误,如果 linked 库到 pythonInterface.so 发生变化,在 python 调用过程中可能会出现共享库不匹配的错误,并且这行没有得到更新。我相信如果这个 得到回答,可以获得更好的答案,所以我目前正在推迟提交或接受答案,直到那时。谢谢!

根本原因

出现此错误是因为 numpy 中的 multiarray.cpython-35m-x86_64-linux-gnu.so 模块依赖于 libpythonx.x.so,但它不是显式的 link libpythonx.x.so。因此,如果您使用 ldd -d multiarray.cpython-35m-x86_64-linux-gnu.so,您将不会在列表中看到 python。

Python 没有问题,因为 python 二进制依赖于 libpython.x.x.so,所以当 numpy 使用 dlopen 加载 multiarray.cpython-35m-x86_64-linux-gnu.so 时。 libdl.so 将尝试通过检查主程序的依赖共享库 python 来解析未定义的符号。它会在 libpython.x.x.so.

中找到它

解决方案

知道根本原因后,解决方法很简单,只需帮助 libdl.so 即可找到 libpython.x.x.so。至少有两种方法可以实现:

  1. 使用dlopen("libpythonx.x.so", RTLD_GLOBAL)。打开此 so 使用 RTLD_GLOBAL 标志后,它使 libpythonx.x.so 中的符号可用于随后加载的共享对象的符号解析。
  2. 在嵌入 python 的主程序中,将 libpythonx.x.so 添加到其依赖库中。

我在将应用程序链接到 libpython3 时遇到了类似的错误。5m.a(存档,非动态)。一旦它加载了像 multiarray.cpython-35m-x86_64-linux-gnu.so 这样的东西,它就会期望像 PyFloat_Type 这样的符号存在。

在诊断为什么 Python 可以直接调用并且它可以工作,但我的应用程序不会,我注意到 readelf -s myapplication.symtab table 中有一个 PyFloat_Type 符号,但在 .dynsym table.

中没有

但是,readelf -s /asb/path/to/python3 在两个 table 中都有一个 PyFloat_Type 符号。

添加: target_link_options(myapplication PUBLIC "LINKER:-export-dynamic") 在 CMake 中确保所需的符号在 .dynsym table 中也可用。在此之后,应用程序正常运行。