排序 sys.path:首先是 virtualenv,然后是 /usr

Sorting sys.path: first virtualenv, then /usr

为什么 sys.path 在我的 virtualenv 目录之前包含 /usr/...

我用 --system-site-packages

创建了 virtualenv

sys.path 目前看起来是这样的:

/home/my-virtualenv/src/foo
/usr/lib/python2.7/site-packages        <--- /usr paths should be below 
/usr/lib64/python2.7/site-packages
/home/my-virtualenv/lib/python27.zip
/home/my-virtualenv/lib64/python2.7
/home/my-virtualenv/lib64/python2.7/plat-linux2
/home/my-virtualenv/lib64/python2.7/lib-tk
/home/my-virtualenv/lib64/python2.7/lib-old
/home/my-virtualenv/lib64/python2.7/lib-dynload
/usr/lib64/python2.7
/usr/lib/python2.7
/usr/lib64/python2.7/lib-tk
/home/my-virtualenv/lib/python2.7/site-packages

我希望我的 virtualenv (/usr...) 之外的所有路径都在 virtualenv 的路径之下。

否则会发生疯狂的事情:我用 pip 安装了一个包。 Pip 告诉我新版本已安装 (pip freeze | grep -i ...) 但导入确实使用了来自 /usr/lib/python2.7/site-packages

的版本

我无法在我的上下文中使用 --no-site-packages

有没有办法排序sys.path

为什么我使用 system-site-packages

似乎没有直接的方法可以从 virtualenv 中可用的全局站点包中创建单个库。看到这个问题: make some modules from global site-packages available in virtualenv

像 python-gtk 这样的包很难安装在 virtualenv 中。

您可以在导入模块之前简单地添加所需的路径。有点老套,但应该可以解决您的问题

import sys
sys.path.insert(1, '/home/my-virtualenv/lib/python2.7/site-packages')

import lalala

下面的直接解决方案解决了您的问题:

1- 打开 my-virtualenv/lib/python2.7/site.py 文件进行编辑。

2- 在此文件末尾追加以下排序算法行(或解决您问题的任何代码)(无首行缩进):

prefix = __file__.rsplit(os.sep, 3)[0]

@Jan-Philip Gehrcke 建议的代码行下方:

sys.path = sorted(sys.path, key=lambda x: x.startswith(prefix), reverse=True)

3- 运行 您的节目并享受

此解决方案可帮助您对 sys.path 列表进行排序,而无需在程序主体中添加烦人的行。

注意:site模块在初始化时自动导入。可以使用解释器的 -S 选项来抑制自动导入。

如果您使用交互式 shell,您可以设置 PYTHONSTARTUP,或者也可以设置一些 bash 别名,以您喜欢的方式修改您的 PYTHONPATH . 你也可以在你的virtualenvbin目录下修改激活文件activate.cshactivate_this.py;或 site.py 文件作为另一个提到的响应。

pip + virtualenv 是——正如有人指出的那样,这对于系统范围的包来说不是一个非常友好的组合,即没有默认标志说 使用系统 numpypackage x,y 构建环境时。尽管正如评论中所建议的那样,我也建议使用 --no-site-packages 进行构建,然后从 /usr/lib

中专门导入您需要的内容

编辑,讨论后:

"I want all paths outside my virtualenv (/usr...) to be below the paths of the virtualenv. [...] It needs to be sorted."

然后,只需在第一次导入之前对 sys.path 进行排序。给定一个特定的路径 prefix 对应于你的 virtalenv 的位置,这种方法可能就足够了:

sys.path = sorted(sys.path, key=lambda x: x.startswith(prefix), reverse=True)

sorted() 的排序行为是稳定的:具有相同排序键的项目保留原始顺序。这里只使用了两个排序键:TrueFalse。你需要想出一个可靠的方法来设置你的prefix(你可能想硬编码它,或者根据当前工作目录确定它,我相信你找到了一种方法)。

原答案(仍然有效,一般来说):

你不想过多阐述你的需求和应用场景,所以我提供一个更笼统的答案:你可能需要考虑你的方法,并且可能不会期望 virtualenv 完全解决你的问题.

在某些情况下,virtualenv 并不是完美的解决方案。它 一种妥协,这种妥协的一方面在这篇文章中进行了详尽的描述:https://pythonrants.wordpress.com/2013/12/06/why-i-hate-virtualenv-and-pip/

在很多情况下 virtualenv 都有很好的用途,并且做得很好!当然,它对我帮助很大,尤其是在开发方面。在其他场景下,它不是一个完整的解决方案,甚至是一个糟糕的解决方案。

所以,现在我看到了几个不同的选项:

  1. 使用 virtualenv,但控制那些你需要改变的东西。例如,在 进行特定导入之前,从包 中修改 sys.path。虽然有些人可能会考虑这个 "not clean",但它肯定是一种高效可靠地控制目录搜索顺序的非常快捷的方法。 sys.path.insert(1, foo) 实际上被 许多 包和测试环境使用。这一点也不罕见。这种方法可能会在一分钟的工作后为您提供一个可行的解决方案。试一试!
  2. 如果您认为您的情况下的 virtualenv 的行为与记录的不符或明显显示错误行为,请将您的发现报告给项目。他们一定会感谢您的简明反馈。
  3. 如果您认为 virtualenv 没有提供您的应用程序案例中所需的隔离或控制级别,您可能需要查看其他选项,例如 Docker 或 Vagrant,或全功能虚拟机。

我发现解决这个问题的一个简单方法就是将你的 virtualenv site-packages 文件夹添加到 $VIRTUAL_ENV/lib/python2.7/site-packages/_virtualenv_path_extensions.pth 文件或直接输入 shell 激活环境后:

$ add2virtualenv $VIRTUAL_ENV/lib/python2.7/site-packages

这将迫使该文件夹出现在您的系统文件夹之前,正如我注意到它对添加的其他路径所做的那样。

我相信这不是最干净的解决方案,但确实 virtualenv 对路径进行了奇怪的排序。

另一个观察结果是,当我开始 ipython 时,sys.path 以合理的顺序出现,但在 python 解释器内部时却没有,我不确定为什么。