【问题标题】:Sorting sys.path: first virtualenv, then /usr对 sys.path 排序:首先是 virtualenv,然后是 /usr
【发布时间】:2023-04-05 16:20:01
【问题描述】:

为什么 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 中安装。

【问题讨论】:

  • “我不能在我的上下文中使用--no-site-packages”:请详细说明。
  • @Kevin 我可以解释一下,但是会发生什么变化?我不能在我的上下文中使用 --no-site-packages - 就是这样。问题是关于排序 sys.path :-)
  • 坦率地说,--system-site-packages 坏了。将所有东西都安装到 virtualenv 中几乎总是更好。您的情况(显然)不同这一事实使我很难在没有更多信息的情况下为您提供有用的答案。
  • 这个限制(没有--no-site-packages)是因为一些共享的全局包吗?如果是这样,为什么不在 virtualenv 中复制它们?
  • 我更新了问题并添加了“为什么我使用 system-site-packages”部分

标签: python path pip virtualenv


【解决方案1】:

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

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

import lalala

【讨论】:

  • 在某些情况下,这最好的方法!可能项目使用它来确保自动测试实际测试开发代码,而不是安装在系统或其他任何地方的某些包。示例:github.com/sphinx-doc/sphinx/blob/master/tests/run.py#L21
  • 对不起,这不是我回答的解决方案。从上面引用:“我希望我的 virtualenv (/usr...) 之外的所有路径都低于 virtualenv 的路径。”
【解决方案2】:

以下直接解决方案可以解决您的问题:

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 选项抑制自动导入。

【讨论】:

  • 你能详细说明这个方法是做什么的吗?为什么要执行这种相当不可读的排序方法?与仅确保要导入的 目录位于 sys.path 中的第一个位置相比,您的方法有什么优势?
  • 此方法将sys.path中所有不以环境目录路径开头的项目移动到结束。 一个问题通常有多个解决方案(编程的基本法则)。
  • pre = __file__.rsplit(os.sep, 3)[0] 肯定不会可靠地工作,无论你真正想用这个实现什么。我猜您想以某种方式动态确定已执行 python 脚本文件的部分路径。如果是这样,请在此处阅读:stackoverflow.com/questions/2632199/…
  • 当前问题不是关于这个问题,我不坚持使用这个算法@guettli 可以使用任何解决这个问题的算法,这个引入的算法只是一个建议;-) 感谢你的技巧。
  • 我了解您基本上是想根据前缀对sys.path 中的项目进行排序。您的代码确实不太可读,可能是错误的并且难以调试。它“不是pythonic”。最好使用sorted() 并利用此方法稳定的事实(为具有相同排序键的项目保留原始顺序)。归结为s = sorted(sys.path, key=lambda x: x.startswith(prefix), reverse=True)。比你的代码简单得多,对吧?
【解决方案3】:

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

pip + virtualenv 是 - 正如已经指出的那样,对于系统范围的包来说不是一个非常友好的组合,即没有默认标志用于说 use system numpypackage x, y 在构建环境时。尽管正如 cmets 中所建议的那样,我也建议使用 --no-site-packages 构建,然后从 /usr/lib 专门导入您需要的内容

【讨论】:

    【解决方案4】:

    编辑,讨论后:

    “我希望我的 virtualenv (/usr...) 之外的所有路径都低于 virtualenv 的路径。 [...] 需要对其进行排序。”

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

    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执行某个导入之前。虽然有些人可能认为这“不干净”,但它肯定是一种高效可靠地控制目录搜索顺序的快速方法。 sys.path.insert(1, foo) 实际上被 许多 包和测试环境使用。这一点也不罕见。这种方法可能会在您工作一分钟后为您提供有效的解决方案。试试看!
    2. 如果您认为您的情况下的 virtualenv 的行为与记录不符或明显显示错误行为,请向项目报告您的发现。他们一定会感谢您的简明反馈。
    3. 如果您认为 virtualenv 无法提供应用程序所需的隔离或控制级别,您可能需要考虑其他选项,例如 Docker 或 Vagrant,或功能齐全的虚拟机。

    【讨论】:

    • 感谢您的回复。我对 virtualenv 很满意。由于我不想在这里解释的原因,我们需要包含全局站点包。在我们使用默认(不是站点包)之前。如果 sys.path 以某种方式被订购,一切都会再次好起来的。我认为 site.py 解决方案是一个很好的解决方案。
    • 好的,只需修改sys.path进行导入。这真的很简单!你不需要为此使用site.py。示例:sys.path.insert(1, "/opt/packages"); import superduper。 -- 另外,您可能想对那些提供了一些见解的答案投赞成票!
    • 我认为“在导入之前只修改 sys.path”对我来说不是解决方案。如果有二十个文件我使用import superduper,我需要使用sys.path.insert() 二十次。不,我不喜欢这个。我赞成我目前最喜欢的答案。
    • “在进行导入之前修改 sys.path”实际上 您赞成的答案的解决方案。想想命中:你不需要在多个地方修改sys.path。这是一个共享的全球!在 first 导入之前修改它,例如在__init__.py。或者你第一次导入的地方! site.py 也可以,我只想指出一般不需要。此外,通常您不需要复杂的排序算法,您只需要首先从要导入的位置有 that 目录,其余项目的顺序不需要问题。
    • 既然如此,那么我真的可以推荐一个基于sorted() 的解决方案,就像sorted(sys.path, key=lambda x: x.startswith(prefix), reverse=True)。唯一棘手的事情是可靠地找到您的prefix。这完全取决于您的项目结构和运行时要求。
    【解决方案5】:

    我发现解决此问题的一种简单方法是将您的 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 解释器内部时,不知道为什么。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-05-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多