Python 依赖管理的迷宫

2022年09月14日11:15:02 科技 1187

在这篇文章中,我想对 Python 中的依赖管理有所了解。Python 依赖管理是一个完全不同的世界。

Python 依赖管理的迷宫 - 天天要闻

20 多年来,我一直在为 JVM 开发代码,首先是 Java,然后是 Kotlin。但是,JVM 并不是灵丹妙药,例如,在脚本中:

  1. 虚拟机需要额外的内存
  2. 在许多情况下,脚本运行的时间不够长,无法在性能方面获得任何好处。字节码被解释并且永远不会编译为本机代码。

由于这些原因,我现在用 Python 编写脚本。其中之一从不同来源收集社交媒体指标并将其存储在 BigQuery 中以供分析。

我不是 Python 开发人员,但我正在学习 - 很难。在这篇文章中,我想对 Python 中的依赖管理有所了解。

Python 中足够的依赖管理

在 JVM 上,依赖管理似乎是一个已解决的问题。首先,您选择您的构建工具,最好是 Maven 或另一种我不应该命名的工具。然后,您声明您的直接依赖关系,并且该工具管理间接依赖关系。这并不意味着没有陷阱,但您可以或多或少地快速解决它们。

Python 依赖管理是一个完全不同的世界。首先,在 Python 中,运行时及其依赖项是系统范围的。一个系统只有一个运行时,并且依赖项在该系统上的所有项目之间共享。因为不可行,所以开始一个新项目的第一件事就是创建一个虚拟环境。

这个问题的解决方案是创建一个虚拟环境,一个自包含的目录树,其中包含特定版本的 Python 的 Python 安装,以及一些额外的包。

然后不同的应用程序可以使用不同的虚拟环境。为了解决前面的冲突需求示例,应用程序 A 可以拥有自己的安装了 1.0 版的虚拟环境,而应用程序 B 拥有另一个安装了 2.0 版的虚拟环境。如果应用程序 B 需要将库升级到版本 3.0,这不会影响应用程序 A 的环境。

--虚拟环境和包

一旦完成,事情就会认真开始。

pipPython 提供了一个开箱即用的依赖管理工具:

您可以使用名为 pip 的程序安装、升级和删除软件包。

--使用 pip 管理包

工作流程如下:

  1. 在虚拟环境中安装所需的依赖项:pip install flask
  2. 在安装了所有必需的依赖项后,将它们保存在requirements.txt按约定命名的文件中:pip freeze > requirements.txt

    该文件应与常规代码一起保存在一个人的VCS中。

  3. pip其他项目开发人员可以通过指向安装相同的依赖项requirements.txt:pip install -r requirements.txt

以下是requirements.txt上述命令的结果:

click==8.1.3
Flask==2.2.2
itsdangerous==2.1.2
Jinja2==3.1.2
MarkupSafe==2.1.1
Werkzeug==2.2.2

依赖和传递依赖

在描述这个问题之前,我们需要解释一下什么是传递依赖。传递依赖项是项目不直接需要的依赖项,而是项目的依赖项之一或依赖项的依赖项一直需要的依赖项。在上面的示例中,我添加了flask依赖项,但pip总共安装了 6 个依赖项。

我们可以安装deptree依赖来检查依赖树。

pip install deptree

输出如下:

Flask==2.2.2 # flask
Werkzeug==2.2.2 # Werkzeug>=2.2.2
MarkupSafe==2.1.1 # MarkupSafe>=2.1.1
Jinja2==3.1.2 # Jinja2>=3.0
MarkupSafe==2.1.1 # MarkupSafe>=2.0
itsdangerous==2.1.2 # itsdangerous>=2.0
click==8.1.3 # click>=8.0

# deptree and pip trees

它的内容如下:Flaskrequires Werkzeug,这反过来又需要MarkupSafe。Werkzeug并MarkupSafe有资格作为我的项目的传递依赖项。

版本部分也很有趣。第一部分提到安装的版本,而注释部分是指兼容的版本范围。例如Jinja需要版本3.0或以上,安装的版本为3.1.2.

安装的版本是安装时找到的最新兼容pip版本。pip并deptree了解setup.py沿每个库分发的文件的兼容性:

安装脚本是使用 Distutils 构建、分发和安装模块的所有活动的中心。设置脚本的主要目的是向 Distutils 描述您的模块分发,以便对您的模块进行操作的各种命令执行正确的操作。

--编写安装脚本

flask在这里:

from setuptools import setup

setup(
name="Flask",
install_requires=[
"Werkzeug >= 2.2.2",
"Jinja2 >= 3.0",
"itsdangerous >= 2.0",
"click >= 8.0",
"importlib-metadata >= 3.6.0; python_version < '3.10'",
],
extras_require={
"async": ["asgiref >= 3.2"],
"dotenv": ["python-dotenv"],
},
)

点和传递依赖

出现问题是因为我希望我的依赖项是最新的。为此,我已将 Dependabot 配置为监视requirements.txt. 当这样的事件发生时,它会在我的 repo 中打开一个PR 。大多数时候,PR 就像一个魅力,但在少数情况下,当我在合并后运行脚本时会发生错误。它如下所示:

纯文本1错误:libfoo 1.0.0 要求 libbar<2.5,>=2.0,但您将拥有不兼容的 libbar 2.5。

问题是 Dependabot 为列出的每个库打开一个 PR。但是可以发布一个新的库版本,这超出了兼容性的范围。

想象一下下面的情况。我的项目需要libfoo依赖。反过来,libfoo需要libbar依赖。在安装时,pip使用最新版本libfoo和最新兼容版本的libbar. 结果requirements.txt是:

纯文本1libfoo==1.0.02库==2.0

一切都按预期工作。过了一会儿,Dependabot 运行并发现libbar已经发布了一个新版本,例如2.5. 忠实地,它打开了一个 PR 来合并以下更改:

纯文本1libfoo==1.0.02库==2.5

是否出现上述问题仅取决于如何libfoo 1.0.0在setup.py. 如果2.5在兼容范围内,则有效;如果没有,它不会。

pip-compile拯救

问题pip在于它列出了传递依赖和直接依赖。Dependabot 然后获取所有依赖项的最新版本,但不验证传递依赖项版本更新是否在该范围内。它可能会检查,但requirements.txt文件格式不是结构化的:它不区分直接依赖和传递依赖。显而易见的解决方案是仅列出直接依赖项。

好消息是pip只允许列出直接依赖项;它自动安装传递依赖。坏消息是我们现在有两个requirements.txt选项无法区分它们:一些仅列出直接依赖关系,而另一些则列出所有依赖关系。

它需要一个替代方案。pip-tools有一个:

  1. 一个在一个文件中列出了它们的直接依赖关系,该requirements.in文件的格式与requirements.txt
  2. 该pip-compile工具requirements.txt从requirements.in.

例如,给定我们的 Flask 示例:

#
# This file is autogenerated by pip-compile with python 3.10
# To update, run:
#
# pip-compile requirements.in
#
click==8.1.3
# via flask
flask==2.2.2
# via -r requirements.in
itsdangerous==2.1.2
# via flask
jinja2==3.1.2
# via flask
markupsafe==2.1.1
# via
# jinja2
# werkzeug
werkzeug==2.2.2
# via flask

pip install -r requirements.txt

它具有以下好处和后果:

  • 生成的requirements.txt包含注释以了解依赖关系树
  • 由于pip-compile生成文件,您不应将其保存在 VCS 中
  • 该项目与依赖于的遗留工具兼容requirements.txt
  • 最后但同样重要的是,它改变了安装工作流程。不是安装包然后保存它们,而是首先列出包然后安装它们。

此外,Dependabot 可以管理pip-compile.

结论

这篇文章描述了默认 Python 的依赖管理系统以及它如何破坏自动版本升级。我们继续描述pip-compile解决问题的替代方案。

请注意,Python 存在依赖管理规范,PEP 621 – 在 pyproject.toml 中存储项目元数据。它类似于 Maven 的 POM,但格式不同。这在我的脚本上下文中是多余的,因为我不需要分发项目。但是你应该这样做,知道它pip-compile是兼容的。


科技分类资讯推荐

长安与东风重组新进展:朱华荣称不会改变长安既定战略 - 天天要闻

长安与东风重组新进展:朱华荣称不会改变长安既定战略

2月9日,长安汽车和东风集团股份(00489.HK)同步发布了控股股东“正在与其他国资央企集团筹划重组事项”的信息。长安汽车的控股股东是兵装集团,而东风集团股份的控股股东是东风公司。随即,长安汽车和东风集团这两家汽车央企将合并重组,成为业内关注的焦点。
公安部出手了!年龄限制放宽10年、送考下乡,2025年考驾照不难了 - 天天要闻

公安部出手了!年龄限制放宽10年、送考下乡,2025年考驾照不难了

电动车加强管理以后,要求机动车类型的车辆需要持证上路,但是老年人考驾照却受阻,一方面有年龄的限制,另一方面偏远山区考驾照不方便,所以在2025年公安部出手了,年龄限制放宽10年,同时推出送考下乡服务,还进一步的降低考驾照的费用,2025年起考摩托车驾照不难了。
从“星灵安全守护体系”到昊铂HL,看懂广汽科技日 - 天天要闻

从“星灵安全守护体系”到昊铂HL,看懂广汽科技日

发布会以技术切入,并全程围绕安全展开。广汽集团董事长、总经理冯兴亚率先登场,宣布2025年四季度将正式上市支持L3级智能驾驶的车型,他同时强调面向自动驾驶时代对智能驾驶技术、整车安全架构以及突发风险处理能力的要求更高。如何才能满足更高的要求?冯兴亚提到了“广汽
关税大棒下,最受伤的车企出现了 - 天天要闻

关税大棒下,最受伤的车企出现了

特朗普的关税大棒刚挥出,尚未吓退“外敌”,却先刺痛了自己。近日,拥有玛莎拉蒂、Jeep等14个品牌的全球第四大车企斯泰兰蒂斯突然宣布裁撤900名美国工人,关闭加拿大和墨西哥两家工厂,北美生产线陷入瘫痪。几乎同一时间,捷豹路虎宣布暂停对美出口一个月,奥迪更是直接