Python加载C库时,CDLL无法识别Python路径中的库
我正在尝试让一些开源的学术代码运行起来(项目主页在这里)。这个项目是一个庞大的C++代码库,里面有一个(非常)简单的Python封装,它使用CDLL
来加载C++代码,并调用一些可以让Python脚本简单操作这个代码的C函数。
不过,最开始导入代码的时候就崩溃了,因为它找不到和它在一起的.so文件,这些文件在site-packages目录下:
在安装的文件中:
from ctypes import *
try:
self.lib = CDLL("_lammps.so")
except:
try:
self.lib = CDLL("_lammps_serial.so")
except:
raise OSError,"Could not load LAMMPS dynamic library"
在一个脚本或解释器中:
from lammps import lammps
l = lammps()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "lammps.py", line 42, in __init__
raise OSError,"Could not load LAMMPS dynamic library"
OSError: Could not load LAMMPS dynamic library
其他的答案看起来好像已经解决了这个问题,但这只有在CDLL()
在实际调用的脚本中被调用时才有效(或者是在运行解释器的命令行的工作目录下)——也就是说,如果“相对路径”是在用户空间,而不是Python库空间的话。
我们该如何可靠地安装一个自己构建的C/C++库,以便可以导入呢?除了把系统库的位置像/usr/lib
那样弄得一团糟,这样做不太符合Python的风格,我看不出有什么简单的解决办法。
(编辑:修正了函数名称,不清楚的重构没帮助!抱歉!)
5 个回答
你可以在进行导入的包里使用 __file__
这个变量。只需要用 os.path
里的各种函数,从 __file__
中提取出完整的绝对目录路径,然后把它和你的库文件名连接起来。大概是这样的:
temp = os.path.abspath(__file__)
temp = os.path.realpath(temp)
temp = os.path.dirname(temp)
temp = os.path.join(temp, "_lammps.so")
lib = CDLL(path)
如果你想让它在不同的平台上都能用,可能还需要尝试你核心文件名的不同变种,比如用 .dll
或 .dylib
代替 .so
,还有加不加 lib
前缀,甚至可能要加上版本号。这样做是因为你的构建系统可能会生成这些不同的文件。你可以尝试几种版本,或者直接用 glob.glob
来帮你找到合适的文件。
我觉得很奇怪的是,标准库里居然没有这样的函数。ctypes.util.find_library
这个函数在这方面不够灵活(或者说不够全面),我本以为这种需求是很普遍的。即使只是一个能在 PYTHONPATH
中搜索文件的函数,也会非常有用(虽然写起来并不难)。
不过,似乎如果你把正确的目录添加到 LD_LIBRARY_PATH
中,你 应该能加载它。
在使用strace -eopen命令运行时,你会看到类似这样的内容:
open("tls/x86_64/_lammps.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("tls/_lammps.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("x86_64/_lammps.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("_lammps.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 6
open("/lib/_lammps.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/usr/lib/_lammps.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
这段内容展示了python的ctypes在寻找你的库时,会查看哪些地方。到目前为止,我还没找到可以调整运行环境变量的方法来让它在我的系统上增加搜索位置,也许你需要使用绝对路径。
我在用Linux系统,解决这个问题的方法就是在代码里写上操作系统模块的绝对路径,这样就可以正常工作了。
from ctypes import *
import os
xss = cdll.LoadLibrary(os.path.abspath("libxss.so.1"))
print xss.xss_test_1()
这段代码是用Python 2.7写的。