【问题标题】:Python dependency hell: A compromise between virtualenv and global dependencies?Python 依赖地狱:virtualenv 和全局依赖之间的妥协?
【发布时间】:2023-04-06 10:38:01
【问题描述】:

到目前为止,我已经测试了在 Python 中管理我的项目依赖项的各种方法:

  1. 使用 pip 安装所有全局内容(节省空间,但迟早会给您带来麻烦)
  2. pip & venv 或 virtualenv(管理起来有点麻烦,但在很多情况下都可以)
  3. pipenv 和 pipfile(比 venv/virtualenv 简单一点,但速度慢,而且一些供应商锁定,虚拟 env 隐藏在实际项目文件夹之外的其他位置)
  4. conda 作为包和环境管理器(只要包都在 conda 中可用,那就太好了,混合 pip 和 conda 有点麻烦)
  5. 诗歌 - 我没试过这个
  6. ...

我对所有这些(除了 1.)的问题是我的硬盘空间很快就会填满:我不是开发人员,我在日常工作中使用 Python。因此,我有数百个小项目都在做他们的事情。不幸的是,对于 80% 的项目,我需要“大”包:numpypandasscipymatplotlib - 随便你。一个典型的小项目大约有 1000 到 2000 行代码,但是在 venv/virtualenv/pipenv 中有 800MB 的包依赖。 实际上我有大约 100+ GB 的 HDD 充满了 python 虚拟依赖项。

此外,在每个虚拟环境中安装所有这些都需要时间。我在 Windows 中工作,许多软件包无法从 Windows 中的 pip 轻松安装:ShapelyFionaGDAL - 我需要来自Christoph Gohlke 的预编译轮子。这很容易,但它破坏了大多数工作流程(例如,来自 pipfile 的 pip install -r requirements.txtpipenv install)。我觉得我有 40% 是在安装/更新包依赖项,而我只有 60% 的时间是在编写代码。此外,这些包管理器都没有真正帮助发布和测试代码,所以我需要其他工具,例如setuptoolstoxsemantic-releasetwine...

我和同事谈过,但他们都面临同样的问题,似乎没有人有真正的解决方案。我想知道是否有办法拥有一些包,例如您在大多数项目中使用的那些全局安装的 - 例如,numpypandasscipymatplotlib 将使用 pip 在C:\Python36\Lib\site-packagescondaC:\ProgramData\Miniconda3\Lib\site-packages 中安装 - 这些是开发良好的软件包,通常不会破坏事物。如果,我想尽快在我的项目中解决这个问题。

其他内容将放在本地 virtualenv 文件夹中 - 我很想将我当前的工作流程从 pipenv 移动到 conda

这样的方法有意义吗?至少最近python有很多发展,也许出现了一些我还没有看到的东西。 是否有关于如何在这种混合的全局-本地环境中设置文件的最佳实践指南,例如如何维护setup.pyrequirements.txtpyproject.toml通过Gitlab、Github等共享开发项目?有哪些陷阱/注意事项?

还有来自 Chris Warrick 的 this great blog post 对此进行了非常全面的解释。

[2021 年更新]

由于这篇文章仍然有很多浏览量,这里是一个主观的 2021 年更新:

  • 如果您从事数据科学,(mini)conda 仍然值得一看
  • 否则,Poetrypyproject.toml 似乎是共同商定的分母

[2020 年更新]

半年后,我可以说与 Conda (Miniconda) 合作解决了我的大部分问题:

  • 它可以在每个系统、WSL、Windows、本机 Linux 等上运行。conda env create -f myenv.yml 在每个平台上都是相同的
  • 大多数包已经在 conda-forge 上可用,很容易让 conda-forge 接受自己的包
  • 对于那些不在 conda 上的包,我可以在 conda 环境中安装pip 并使用 pip 从 pypi 添加包。提示:conda update --all -n myenv -c conda-forge 只会更新来自 conda 的软件包,而不是使用 pip 安装的软件包。 Pip 安装的依赖项必须使用pip install pack_name --upgrade 手动更新。请注意,在 conda 中使用 pip 安装软件包是一种紧急解决方案,通常应为 avoided
  • 我可以创建 strict 或 open environment.yml,指定 conda 通道优先级、来自 conda 的包和来自 pip 的包
  • 我可以在单个语句中从这些 yml 创建 conda 环境,例如在 Gitlab 持续集成中设置开发环境,使用 Miniconda3 Docker - 这使得测试运行非常简单直接
  • ymls 中的包版本可以定义为严格或开放,视情况而定。例如。您可以将环境修复为 Python 3.6,但让它检索此版本范围内的任何安全更新(例如 3.6.9)
  • 我发现conda几乎可以解决Windows中所有c编译依赖的问题; Windows 中的 conda env 确实允许将 freezing python 代码转换为可执行文件(经过测试!),该可执行文件可以分发给由于某种原因无法使用包管理器的 Windows 最终用户。
  • 关于“大依赖”的问题:我最终创建了许多特定(即小)和一些非特定(即大)conda 环境:例如,我有一个相当大的jupyter_env,其中 jupyter 实验室和大多数安装了我的科学软件包(numpy、geos、pandas scipy 等) - 每当我需要访问这些工具时,我都会激活它,我可以在一个地方保持这些工具的最新状态。对于特定包的开发,我有仅用于包依赖项的额外环境(例如packe1_env)。我总共有大约 10 个环境,这是可以管理的。一些通用工具安装在基本 conda 环境中,例如pylint。警告:要使 pylint/pycodestyle/autopep8 等在 VS Code 中工作(例如),它必须安装到包含 python-code-dependencies 的同一环境中 - 否则,您将收到未解决的导入警告
  • 我用Chocolatey windows 包管理器安装了 miniconda。我用conda update -n base conda 保持最新状态,我的环境每周用conda update --all -n myenv -c conda-forge 更新一次,就像一个魅力!
  • 新更新:有一个--stack 标志可用(截至2019-02-07)允许堆叠conda 环境,例如conda activate my_big_env 然后conda activate --stack dev_tools_env 允许在许多环境中提供一些通用包。但是,请谨慎使用 - 我发现代码 linters,例如 pylint,必须与被 lint 代码的依赖项位于同一环境中
  • 新更新 2:我从 Windows Subsystem for Linux (WSL) 开始使用 conda,这再次显着改善了我的工作流程:软件包安装速度更快,我可以在直接连接到 WSL 的 Windows 中使用 VS Code Insiders并且在 Linux 环境中使用 python 包的错误要少得多。
  • 另一个更新附带说明,Miniconda Docker 允许将本地 conda env 工作流完美地转换为容器化基础架构(CI 和 CD),现在对此进行了一段时间的测试并且非常满意 - Dockerfile 更干净与 Python Docker 相比,因为 conda 比 pip 管理更多的依赖项工作。我现在越来越多地使用它,例如,在使用从容器内启动的 jupyter lab 时。
  • 是的,我偶然发现了 conda 环境中某些包之间的兼容性问题,但很少见。有两种方法:如果它是一个必须稳定工作的重要环境,请启用conda config --env --set channel_priority strict - 这只会安装兼容的版本。如果包组合很少且很少见,这可能会导致无法解决的依赖冲突(即无法创建 env)。在这种情况下,我通常为实验性开发创建更小的环境,使用更少的包并将channel_priority 设置为flexible(默认值)。有时,存在更容易解决的包子集,例如geoviews-core(而不是geoviews)或matplotlib-base(而不是matplotlib)。对于 strict 无法解决的那些实验性环境,尝试较低的 python 版本也是一种好方法,例如conda create -n jupyter_exp_env python=3.6 -c conda-forge。不得已的 hack 是使用 pip 安装软件包,这可以避免 conda 的软件包解析器(但可能会导致环境不稳定和其他问题,您已被警告!)。确保首先在您的环境中明确安装pip

一个整体的缺点是 conda 在使用大型 conda-forge 通道时会变得有点慢。他们是working on it,但同时 conda-forge 索引的增长速度非常快。

【问题讨论】:

  • 如果互联网使用不是问题,您可以考虑直接在 google colab notebooks 上工作,否则您已经谈到的方法将是最好的
  • 对 Python 问题始终使用通用 Python 标签
  • “混合 pip 和 conda 有点老套”,您可以使用 conda 虚拟环境并使用 pip 安装所有内容? (虽然我 - 也许是无知 - 不知道为什么混合会导致问题)
  • 是的,但是我又面临一个问题,我需要在 conda 虚拟环境中安装所有东西,这占用了太多空间。另外,我不知道如何在这种混合的 pip/conda env 下更新 requirements.txt 或让 cx_freeze 正确
  • 我同意让一切正常工作所需的工作量...对于 pipenv,您将环境变量 PIPENV_VENV_IN_PROJECT 设置为 true,然后 pipenv 创建项目目录中的虚拟环境(默认为.venv

标签: python python-3.x dependency-management


【解决方案1】:

我想知道是否有办法拥有一些包,例如您在大多数项目中使用的那些,全局安装的......其他东西会放在本地 virtualenv-folders 中

是的,virtualenv 支持这个。全局安装全局需要的包,然后在创建 virtualenv 时提供 --system-site-packages 选项,以便生成的 virtualenv 仍然能够使用全局安装的包。使用 tox 时,您可以在创建的 virtualenvs 中设置此选项,方法是在适当的 [testenv] 部分中包含 sitepackages=true

【讨论】:

  • 哇,谢谢!这似乎正是我所需要的。你知道这是否也适用于 conda 虚拟环境?我还需要测试这是否适用于 cx_freeze 为 Windows 用户编译 *.exe 中的所有内容。
  • @Alex:我没有使用 conda(或 cx_freeze)的经验,所以无法回答。
  • 谢谢,我会在这里测试和报告!
  • 我们可以用 pipenv 做到这一点吗?
【解决方案2】:

问题

您列出了许多没有一种方法可能完全解决的问题:

  • 空格

'我需要“大”包:numpy、pandas、scipy、matplotlib...实际上我有大约 100+ GB 的 HDD 充满了 python 虚拟依赖项'

  • 时间

...在每个虚拟环境中安装所有这些都需要时间

  • 出版

...这些包管理器都没有真正帮助发布和测试代码...

  • 工作流程

我很想将我当前的工作流程从 pipenv 转移到 conda。

谢天谢地,您所描述的并不是困扰包管理器的经典​​依赖问题——循环依赖、固定依赖、版本控制等。


详情

我已经在 Windows 上使用 conda 多年了,受到类似限制,并取得了相当大的成功。 Conda 最初旨在简化与 scipy 相关的软件包的安装。它仍然存在。

如果您使用“scipy 堆栈”(scipy、numpy、pandas ......),conda 是您最可靠的选择。

康达可以

  • 安装 scipy 包
  • 安装 C 扩展和非 Python 包(需要运行 numpy 和其他包)
  • 集成 conda 包、conda 通道(您应该查看此内容)和 pip 以访问包
  • 与虚拟环境的依赖关系分离

康达不能

  • 帮助发布代码

可重现的环境

如果需要,以下步骤应该有助于重现 virtualenvs:

  • 不要使用 pip 安装 scipy 包。我会依靠 conda 来完成繁重的工作。它更快,更稳定。您可以在 conda 环境中 pip 安装不太常见的软件包。
  • 有时,pip 包可能会与环境中的 conda 包发生冲突(请参阅解决此问题的 release notes)。

避免点子问题

我想知道是否有办法拥有一些包,例如您在大多数项目中使用的那些,全局安装的......其他东西会放在本地 virtualenv-folders 中

非 conda 工具

  • pipx 是一个类似 pip 的工具,可以创建全局虚拟环境。
  • virtualenv 传统上为每个项目创建虚拟环境,但幸运的是 @jwodder 的回答解释了如何使用全局包。
  • virtualenv-wrapper 促进全局虚拟环境。

康达

但是,如果您想继续使用 conda,您可以尝试以下方法:

A.将工作环境与基本环境分开,例如workenv。将其视为您的 goto、“全球”环境来完成您的大部分日常工作。

> conda create -n workenv python=3.7 numpy pandas matplotblib scipy
> activate workenv
(workenv)>

B.在工作环境的clone 中测试不常见的 pip 包(或重量级 conda 包)的安装

> conda create --name testenv --clone workenv
> activate testenv
(testenv)> pip install pint

或者,使用requirements.txt 文件创建包含最少软件包的新环境

C.将依赖项备份到类似于 requirements.txt 的文件中,每个 virtualenv 称为 environment.yml。 (可选)制作一个脚本以在每个环境中运行此命令。请参阅docs 共享/创建环境文件。从这个文件中创建未来的环境:

> conda create --name testenv --file environment.yml
> activate testenv
(testenv)> conda list

出版

打包问题是一个持续的、独立的问题,随着PEP 518pyproject.toml 文件的出现而受到关注(参见作者B. Cannon 的相关blog post)。诸如flitpoetry 之类的打包工具采用了这种现代约定来进行分发并将它们发布到服务器或打包索引(PyPI)。 pyproject.toml 概念试图摆脱对 setuptools 具有特定依赖性的传统 setup.py 文件。

依赖关系

pipenvpoetry 等工具具有独特的现代方法,可通过“锁定”文件解决依赖性问题。此文件允许您跟踪和重现依赖关系图的状态,这是迄今为止 Python 打包世界中的新事物(请参阅 Pipfile 与 setup.py here 的更多信息)。此外,有人声称您仍然可以将这些工具与 conda 结合使用,尽管我尚未测试这些声明的范围。锁定文件尚未标准化,但根据核心开发人员 B. Canon 在 Python 打包的未来interview 中表示,(~33m) “我想让我们到达那里。 " (见更新)。

总结

如果您正在使用 scipy 堆栈中的任何包,请使用 conda(推荐):

  • 为了节省空间、时间和工作流问题,请使用 conda 或 miniconda。
  • 要解决部署应用程序或在依赖项上使用“锁定”文件的问题,请结合 conda 考虑以下内容:
    • pipenv:用于部署和制作Pipfile.lock
    • poetry:用于部署和制作poetry.lock
  • 要在 PyPI 上发布库,请考虑:
    • pipenv:通过pipenv install -e. 开发并使用twine 手动发布
    • flit:自动打包并*发布
    • poetry:自动打包并发布

另见

  • conda docs 关于管理环境文件。
  • 播客采访 B. Cannon 讨论一般包装问题,pyproject.tomllock filestools
  • K. Reitz 的播客采访讨论了包装 toolspipenvpip,37m)和开发环境。

更新

【讨论】:

  • 这就是我要找的东西,非常感谢!我最初的问题确实很不具体,但是您设法解决了我的关键问题。 workenv 的提示,pyproject.toml 的解释和 pipenv/poetry 依赖图的背景对我来说是新的,我会尝试一下!
  • 没问题。我鼓励看看 conda 频道。社区已经创建了许多软件包,并且可以与 conda 中的默认软件包一起使用。此外,所提到的采访很好地了解了最新技术。祝你好运。
  • 我已经用一份简短的报告更新了我上面的问题。真的很高兴我迈出了 conda 的一步,谢谢!
【解决方案3】:

我的最新进展:

结果证明 Conda 包管理器对我来说比 pipenv 更好,原因如下:

  • 默认情况下,全局依赖项可从 conda 虚拟环境中获得
  • 安装/更新依赖比 pipenv 快
  • 结合 pip 和 conda 确实没有那么问题,对于任何有 conda 包可用的东西,使用 conda 安装,如果没有,只需使用 pip 安装
  • 通过使用environment.yml,可以在几秒钟内在 linux 和 windows 上重新创建环境和依赖项 - environment.yml 允许分别指定 pip 和 conda 依赖项(例如,这解决了 Fiona、Shapely 的上述问题, GDal 等在 Windows 中,通过使用 conda 版本)
  • conda 解决了跨平台(例如 linux、mac、win)维护包/依赖项的大部分困难
  • 通过conda run 将 conda(例如 miniconda)并排安装到独立的 python 安装和使用 conda 没有问题
  • 如果缺少environments.yml,可以从requirements.txt (conda create -n new environment --file requirements.txt) 创建一个环境

不幸的是,创建environment.yml 的过程似乎在任何地方都没有得到一致的描述。过了一会儿,我意识到自动创建的文件 (conda env export environment.yml) should be manually edited 包含尽可能少的依赖项列表(并让 conda 在安装时解决其余的问题)。否则,environment.yml 将不兼容跨系统。

无论如何,这个工作流程解决了我上面描述的大部分问题,我很高兴我不再需要使用 pipenv 或 virtualenv。

还有一些缺点,

  1. 需要在多个文件中维护依赖关系:

    • setup.py
    • environment.yml
  2. 无法在其环境中直接执行程序(例如使用快捷方式),例如这对pipenv run 没有问题,但是:
    • conda run 不会自动 source activate env
    • 这是一个open issue,可能会在某个时候得到解决
  3. cx_freeze 不会正确包含来自 conda env 外部的全局依赖项
  4. 如果您需要需要编译的依赖项(例如 C-Extensions 等),conda 将很困难,请参见下文或here

【讨论】:

  • 我会敦促谨慎使用 pip 和 anaconda。仅仅因为一个包现在不能通过 conda 获得,并不意味着它永远不可用。如果 conda 尝试安装 pip 已经安装的软件包,您将遇到问题。如果有问题的包被间接重新安装为另一个包的依赖项,这可能会特别令人困惑。
  • 如果一个包在 conda 中不可用,但需要作为依赖项,你有什么建议?
  • 首先,在github.com/ContinuumIO/anaconda-issues/issues 上提出一个问题,如果这对你来说还不够快,那就不要使用 anaconda。如果你因为非 python 依赖而不得不使用 anaconda,那么你别无选择,只能使用 pip。但是请注意,将来您的安装可能会中断(可能是静默),并且您将获得未定义的行为,或者您可能必须从头开始重新安装 anaconda 和您的软件包。
  • 一个可能出错的例子:你需要libA v2.0,但只有 v1.0 可以通过 conda 获得,所以你使用 pip 安装。然后通过 conda 安装 libB v3.0。 libB 依赖于 libA v1.0,因此 conda 在 v2.0 上安装它。您根据libA v2.0 中的功能编写代码,但实际上它将使用libA v1.0。您的程序崩溃的最佳情况。最坏的情况:它继续,但结果错误。所以你需要在 pip 和 conda 中手动检查你的包依赖关系,以确保它们没有冲突。
  • 是的,这是有道理的,我想我只是在使用 numpy 和 mkl 时遇到了这个问题。无论如何,我有很多 non-python dependencies(数据科学),所以几乎没有办法绕过 anaconda。我会调查问题报告,谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-21
  • 1970-01-01
  • 1970-01-01
  • 2021-06-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多