在Python中设置LD_LIBRARY_PATH

38

有没有一种方法可以在Python运行时指定共享库的路径?

我把fontforge.so放在了fontforge_bin目录中,并尝试了以下操作:

os.environ['LD_LIBRARY_PATH']='fontforge_bin'
sys.path.append('fontforge_bin')
import fontforge

获取并得到

ImportError: fontforge_bin/fontforge.so: cannot open shared object file: No such file or directory

fontforge_bin/fontforge.so 运行 ldd 命令会得到以下结果

linux-vdso.so.1 =>  (0x00007fff2050c000)
libpthread.so.0 => /lib/libpthread.so.0 (0x00007f10ffdef000)
libc.so.6 => /lib/libc.so.6 (0x00007f10ffa6c000)
/lib64/ld-linux-x86-64.so.2 (0x00007f110022d000)

1
如果在运行脚本之前执行 export LD_LIBRARY_PATH=fontforge_bin,那么这将起作用,现在我想从脚本内部设置它。 - Yaroslav Bulatov
使用 sys.path.append 是正确的方法,正如您从错误消息中看到的那样,它确实尝试打开它。我建议您使用目录的完整路径名,而不是相对路径。 - cdarke
3个回答

28

如果您的脚本需要使用环境变量,请在导入模块之前检查环境变量是否存在且正确,如果缺少环境变量,则在os.environ中设置它,然后调用os.execv()来使用相同的命令行参数但更新后的环境变量重新启动Python解释器。

这只建议在任何其他导入(除了os和sys)之前执行,因为可能会出现模块导入副作用,比如打开的文件描述符或套接字,这些可能难以干净地关闭。

此代码设置LD_LIBRARY_PATH和ORACLE_HOME:

#!/usr/bin/python
import os, sys
if 'LD_LIBRARY_PATH' not in os.environ:
    os.environ['LD_LIBRARY_PATH'] = '/usr/lib/oracle/XX.Y/client64/lib'
    os.environ['ORACLE_HOME'] = '/usr/lib/oracle/XX.Y/client64'
    try:
        os.execv(sys.argv[0], sys.argv)
    except Exception, exc:
        print 'Failed re-exec:', exc
        sys.exit(1)
#
# import yourmodule
print 'Success:', os.environ['LD_LIBRARY_PATH']
# your program goes here

最好将该环境变量作为启动环境的一部分进行设置(在父进程或systemd等作业文件中)。


这可能只适用于Linux和其他Unix变体。我不知道它是否适用于Windows或Darwin。 - Will Pierce
1
看起来Apache只接受来自/etc/sysconfig/httpd的LD_LIBRARY_PATH。上述提到的方法在我的情况下不起作用。 - caot
不要在 reticulate 中尝试这个。 - jan-glx
如果 os.environ 中存在 'LD_LIBRARY_PATH' 呢? - Russ Bateman
这可能会导致无限循环! - loretoparisi

18

...你可以通过ctypes从自己选择的某个文件夹加载所有库,从而使它们在不考虑LD_LIBRARY_PATH的情况下对你可用。

from ctypes import *
lib1 = cdll.LoadLibrary('/home/username/lib/some_library.so')

或者在目录中遍历文件...你明白的,一旦加载它,它就为您准备好了[如果依赖项也不在默认路径中,则应该加载它们...]


2
对我没用。当随后导入使用它的模块时,库仍然无法找到。 - Konstantin
2
我确认这个解决方案在一个与问题描述非常相似的场景中起作用。在Linux(Ubuntu 16.04)中,需要导入本地Python模块foo.so,该模块依赖于同一本地路径中的libfoo.so。首先,我尝试将foo.so中的RPATH设置为$ORIGIN/.。只有当我在相同的目录中运行脚本时它才有效。 - Ilia Barahovsky
请记住,不要在32位Python中加载64位dll/so文件,反之亦然。 - pawel lukaszewicz
4
如果你的so文件有进一步的依赖关系,这种方法就不会起作用。 - Michele Belotti
这对于我的lightgbm有效...使用了:cdll.LoadLibrary('/mnt/data-science/miniconda3/lib/libgomp.so.1') - Jon

6

LD_LIBRARY_PATH 设置动态链接器路径;通常情况下无法在运行时更改,因为它通常由动态链接器缓存。

然而 Python 寻找 导入 的位置不在这里,包括模块导入。正确的方法是更改 sys.path

# ls foo/
_csv.so
# python
Python 2.6.6 (r266:84292, Dec 26 2010, 22:31:48)
>>> import sys
>>> sys.path.insert(0, "foo")
>>> import _csv
>>> _csv.__file__
'foo/_csv.so'

(顺便说一下,您可能希望使用ldd命令查看库文件以确定库中是否存在任何奇怪的导入路径。 “ImportError:fontforge_bin / fontforge.so”看起来很奇怪。)

我在我的问题中添加了ldd输出,不确定是否合适。但是,在fontforge_bin中有3个必需的库,我认为链接器找不到。我发现另一种奇怪的链接器错误消息——当我尝试在32位Python下加载64位的“fontforge.so”时,它会在尝试加载“fontforge.so”时出现“文件未找到”的错误。 - Yaroslav Bulatov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接