在Python中导入前设置LD_LIBRARY_PATH
Python使用一个叫做PYTHONPATH
的环境变量来决定它应该在哪些文件夹里寻找模块。你可以通过修改sys.path
来进行一些尝试,这对于纯Python模块来说效果很好。
但是,如果一个模块使用了共享对象文件或静态库,它会在LD_LIBRARY_PATH
中寻找这些文件(在Linux系统上)。不过,这个路径不太好更改,而且根据不同的平台也会有所不同。
解决这个问题的一个快速方法是设置环境变量,或者像这样运行脚本:LD_LIBRARY_PATH=. ./script.py
,但这样的话,每次打开新的终端时你都得重新设置一次。
另外,在我的情况下,.so
文件总是和.py
文件在同一个目录里,但它们也可能会被移动到其他绝对路径,所以我希望每次运行脚本时都能自动设置这些路径。
我该如何在运行时独立于平台地编辑Python解释器查找库的路径呢?
编辑:
我已经尝试过os.environ['LD_LIBRARY_PATH'] = os.getcwd()
,但没有成功。
5 个回答
从coreutils 8.30版本开始,你可以使用 env -S
来把脚本的开头那一行(也叫shebang行)分成不同的参数:
#!/usr/bin/env -S LD_LIBRARY_PATH=/path/to/lib python options
为了兼容老旧的系统,你可以利用一个特点:在shell中,所有命令都可以用引号括起来,而在python中,这些命令就只是字符串而已:
#!/bin/sh
"export" "LD_LIBRARY_PATH=/path/to/lib:$LD_LIBRARY_PATH"
"exec" "python3" "$0" "$@"
# Further python program
import somemodule
这个解决方案在环境重新初始化的时候效果很好。
import os
os.environ['LD_LIBRARY_PATH'] = os.getcwd() # or whatever path you want
这段代码需要在合适的地方使用……
os.execv(sys.argv[0], sys.argv)
在Python中,当你获取环境变量的值,比如用 os.environ['LD_LIBRARY_PATH']
或 os.environ['PATH']
时,实际上是从它的父进程(通常是bash)那里复制这些值到一个字典里。也就是说,bash进程的环境变量会传递给运行中的Python实例。
你可以通过在bash中输入 env
命令来查看这些环境变量。
另外,你也可以通过访问 /proc/<pid>/environ
来查看这些环境数据。如果你在修改任何环境变量后引入一个无限循环(比如 while 1: pass
),你就能看到这些数据。
如果你在Python脚本中修改了环境变量,然后去 /proc/<pid>/environ
查看这个变量的值,你会发现实际的变量数据并没有被修改,尽管Python脚本显示的字典中的值是更新过的。
实际上,当你在Python脚本中修改一个环境变量,比如 os.environ['LD_LIBRARY_PATH']='/<new_location>'
,只是更新了本地字典中的值,而这个字典并没有映射到进程的环境变量部分。因此,这个修改不会反映到当前进程的环境中,因为只有本地字典被修改了。
所以,如果我们想让新的环境变量生效,就需要使用 execv
来覆盖进程的内存映像,更新环境变量的数据。
例子:
new_lib = '/<new_location>'
if not new_lib in os.environ['LD_LIBRARY_PATH']:
os.environ['LD_LIBRARY_PATH'] += ':'+new_lib
try:
os.execv(sys.argv[0], sys.argv)
except Exception as e:
sys.exit('EXCEPTION: Failed to Execute under modified environment, '+e)
import xyz
#do something else
限制:理想情况下,Python不应该允许修改 os.environ
变量。但由于没有常量字典数据类型,它允许对数据变量进行修改。实际上,修改这些值没有什么用,因为它不会对运行中的进程的真实环境产生任何影响,除非使用 execv
。
我解决这个问题的方法是在Python脚本的第一行加上这个内容(而不是通常的shebang):
exec env LD_LIBRARY_PATH=/some/path/to/lib /path/to/specific/python -x "$0" "$@"
下面是这个方法的工作原理:
- 如果没有shebang,当前的命令行会把这个文件当成一个shell脚本来处理,
- “exec”确保这一行也是这个文件中由shell执行的最后一条命令,
- 这里的“env”用来设置一些环境变量,比如LD_LIBRARY_PATH,
- 可以指定Python解释器的确切路径,或者“env”可以在PATH中找到它,
- “-x”是Python的一个选项,它会让Python解释器忽略第一行,
- “$0”代表脚本的名字,而“$@”会被位置参数替代。
更新:请查看下面的编辑内容。
我会使用:
import os
os.environ['LD_LIBRARY_PATH'] = os.getcwd() # or whatever path you want
这段代码只会在当前程序运行期间设置LD_LIBRARY_PATH
这个环境变量。
编辑:看起来这个变量需要在启动Python之前设置:在运行时更改LD_LIBRARY_PATH以适应ctypes
所以我建议你可以写一个包装的.sh
脚本(如果你坚持的话,也可以用.py
脚本)。另外,正如@chepner提到的,你可能还想考虑把你的.so
文件安装到一个标准的位置(在虚拟环境内)。