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

时间:2018-04-11 21:14:58

标签: python c++ python-3.x numpy python-embedding

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

  

class' ImportError':   导入多阵列numpy扩展模块失败。最   可能你试图导入一个失败的numpy版本。   如果您正在使用numpy git repo,请尝试git clean -xdf(全部删除   不受版本控制的文件)。否则重新安装numpy。

     

原始错误是:/usr/local/lib/python3.5/site-packages/numpy/core/multiarray.cpython-35m-x86_64-linux-gnu.so:undefined symbol:PyExc_UserWarning

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

系统/问题信息:

  • Ubuntu 16.04,64位
  • 已编译的Python 3.5.5 with enabled-shared
  • numpy import在解释器中工作(python3.exe和python3.5.exe)
  • 我已确保PySys_SetPath()设置与解释器输出相同的sys.path:import syssys.path
  • 我可以导入其他模块,如PIL和datetimeutil;但是,numpy和pandas不可导入(pandas使用numpy或似乎)
  • 嵌入式Python使用以下命令:Py_Import_Import()Py_Initialize()(我确定。它只被调用一次。)等,但它没有获得解释器的全局锁定。
  • 该应用程序使用CMake构建系统构建,该系统为我的系统编译MakeFiles。
  • 使用pip 9.0.0使用pip3.5 install numpy命令
  • 安装numpy-1.14.2
  • 导致此错误的python脚本有一行:import numpy ...
  • 我没有.zip文件,我从中导入文件。
  • 嵌入在C ++中的Python使用的.exe位于/ usr / local / bin / python3(使用Py_GetProgramName()来确定这一点)。此.exe链接到libpython3.5m.so.1.0,缺少的符号位于libpython3.5m.so.1.0(运行nm)
  • ldd on multiarray.cpython-35m-x86_64-linux-gnu.so显示:

    ldd multiarray.cpython-35m-x86_64-linux-gnu.so

      

    linux-vdso.so.1 => (0x00007ffd9e36b000)

         

    libopenblasp-r0-39a31c03.2.18.so => /usr/local/lib/python3.5/site-packages/numpy/core/./../.libs/libopenblasp-r0-39a31c03.2.18.so(0x00007fdbe149b000)

         

    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6(0x00007fdbe1192000)

         

    libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0(0x00007fdbe0f75000)   libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6(0x00007fdbe0bab000)   /lib64/ld-linux-x86-64.so.2(0x00007fdbe3ed5000)

         

    libgfortran-ed201abd.so.3.0.0 => /usr/local/lib/python3.5/site-packages/numpy/core/./../.libs/libgfortran-ed201abd.so.3.0.0(0x00007fdbe08b1000)

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

此时,我假设我的知识中存在一些漏洞。我已经看过很多关于在C ++中嵌入Python时无法导入多阵列组件和numpy的类似帖子;但是,要么它们都不符合我的具体情况,要么就像我说的那样存在漏洞。以下是我可能会问的一个子问题列表,如果没有人在这个设置中看到任何明显有关的内容。如果我问他们(在我擦亮之后),我可能会用链接更新问题。

  • 如何将numpy multiarray.so链接到pythonX.X.so以进行符号解析? ldd似乎没有暗示它曾经做过。在link
  • 问这个问题
  • CMake问题与此相关的问题在question于18/12年4月16日得到解决,并于4/16/18得到解答。
  • 在.bashrc中设置PYTHONPATH似乎没有更新Py_GetPath()返回的内容,我不得不通过与sys.path不同的方法在site-packages中添加导入。它可能只更新不会影响C ++的bash脚本环境变量。

我此时并没有要求上述问题清单的答案,而是我提供了更多关于我的知识差距所在的线索。

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

编辑:4/17/18:

好吧,我找到了一个工作,我现在正在使用它。 Dunes问题开始让我更仔细地考虑未定义的符号以及它如何成为链接器/编译器错误,或者numpy导入总是期望具有已经加载到内存中的符号的环境。这让我试图安装不同版本的numpy,看看是否有任何旧版本有所作为。他们没有,但它确实使错误略有不同。当我用Google搜索时,question appeared。接受的答案通过将这两行添加到pythonInterface.cpp中来解决这个问题:

  • #include <dlfcn.h>
  • dlopen("libpython3.5m.so.1.0", RTLD_LAZY | RTLD_GLOBAL)

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

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

2 个答案:

答案 0 :(得分:2)

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

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

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

补充: target_link_options(myapplication PUBLIC "LINKER:-export-dynamic") 在 CMake 中确保所需的符号也在 .dynsym 表中可用。在此之后,应用程序正常工作。

答案 1 :(得分:1)

根本原因

发生此错误是因为numpy中的multiarray.cpython-35m-x86_64-linux-gnu.so模块取决于libpythonx.x.so,因为它不是显式链接libpythonx.x.so。因此,如果您使用ldd -d multiarray.cpython-35m-x86_64-linux-gnu.so,您将在列表中看不到python。

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

解决方案

了解根本原因后,解决方案非常简单,只需帮助libdl.so就能找到libpython.x.x.so。至少有两种方法可以实现:

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