PYTHONPATH环境变量前的路径中的Eggs

16 投票
3 回答
12223 浏览
提问于 2025-04-16 17:32

如果我通过 easy_install 安装了一些包,那么这些包的路径会被放在 sys.path 的最前面,优先于 PYTHONPATH 变量中的内容。

举个例子,如果我安装了一个叫 foo 的包,同时在当前目录下也有一个叫 foo 的包,然后我执行以下代码:

PYTHONPATH="." python
>>> import foo

这样的话,程序会使用安装的那个 foo 包,而不是当前目录下的那个。查看 sys.path 的内容可以发现,包的路径确实是放在 PYTHONPATH 之前的。这看起来有点问题。有没有办法改变这种行为呢?

3 个回答

1

我做了一些类似下面的操作,以便在运行顶层的 Python 可执行文件时,将某个路径添加到系统路径的最前面:

import sys
sys.path = ["<your python path>"] + sys.path

通常,对我来说,"<你的 Python 路径>" 是通过使用 __file__ 属性来相对查找一个包含我项目顶层模块的路径。这种做法在生成包时并不推荐使用,不过我似乎并不在意可能带来的后果。也许还有其他替代 __file__ 的方法。

2

可以考虑使用 -S 这个命令行选项来禁止处理 *.pth 文件:

python -c 'import sys; print("\n".join(sys.path))'
python -S -c 'import sys; print("\n".join(sys.path))'

https://docs.python.org/3/library/site.html#site.main

你还可以在使用 site.main() 时加上 -S,这样可以把 *.pth 文件的处理推迟到运行时,比如说为了捕获原始的 sys.path 以便后续添加:

export PYTHONPATH=$(
  PYTHONPATH='' \
  python -c 'import sys; \
    sys.path.extend(sys.argv[1:]); old=list(sys.path); \
    import site; site.main(); \
    [ old.append(p) for p in sys.path if p not in old ]; \
    sys.path=old; \
    print ":".join(sys.path)' \
  $EXTRA_PATH $ANOTHER_PATH)

python -S ... # using explicit PYTHONPATH
  • 从一个空的 PYTHONPATH 开始
  • 用 extend 明确地将新的路径添加到 sys.path
  • 导入 site 模块并调用 site.main()
  • 将新的路径添加到旧路径中,然后把它安装到 sys.path
  • 用 ":" 来打印 PYTHONPATH
  • 对于后续的运行,使用 python -S 是比较好的,这样只会使用 $PYTHONPATH
  • 在设置 PYTHONPATH 时,使用 python -S 可能有用也可能没用,这取决于你是否需要在扩展之前先扩展 sys.path
13

很不幸,这个问题是因为在 setuptools/command/easy_install.py 里有一个硬编码的模板。你可以创建一个修改过的 setuptools 来编辑这个模板,但我没有找到一个干净的方法可以从外部扩展 easy_install。

每次运行 easy_install 时,它都会重新生成一个叫 easy_install.pth 的文件。这里有一个简单的脚本,你可以在 easy_install 之后运行,用来去掉 easy_install.pth 文件的头部和尾部。你可以创建一个包装的 shell 脚本,让它在 easy_install 之后立即运行:

#!/usr/bin/env python
import sys
path = sys.argv[1]
lines = open(path, 'rb').readlines()
if lines and 'import sys' in lines[0]:
    open(path, 'wb').write(''.join(lines[1:-1]) + '\n')

示例:

% easy_install gdata
% PYTHONPATH=xyz python -c 'import sys; print sys.path[:2]'
['', '/Users/pat/virt/lib/python2.6/site-packages/gdata-2.0.14-py2.6.egg']

% ./fix_path ~/virt/lib/python2.6/site-packages/easy_install.pth
% PYTHONPATH=xyz python -c 'import sys; print sys.path[:2]'
['', '/Users/pat/xyz']

为了更清楚,这里是 easy-install.pth 的格式:

import sys; sys.__plen = len(sys.path)
./gdata-2.0.14-py2.6.egg
import sys; new=sys.path[sys.__plen:]; del sys.path[sys.__plen:]; p=getattr(sys,'__egginsert',0); sys.path[p:p]=new; sys.__egginsert = p+len(new)

这两行 import sys 是导致路径开头出现 eggs 的罪魁祸首。我的脚本只是去掉了那些修改 sys.path 的行。

撰写回答