pip-tools = pip-compile + pip-sync
一组命令行工具,帮助你保持基于pip
的软件包的新鲜度,即使你已经固定了它们的版本。你确实会固定版本,对吧?(在为生产环境构建Python应用及其依赖项时,你需要确保构建是可预测和确定的。)
安装
与pip
类似,pip-tools
必须安装在你项目的每个虚拟环境中:
$ source /path/to/venv/bin/activate
(venv) $ python -m pip install pip-tools
注意:以下所有示例命令都假设你已激活了项目的虚拟环境。
pip-compile
使用示例
pip-compile
命令允许你从pyproject.toml
、setup.cfg
、setup.py
或requirements.in
中指定的依赖项编译requirements.txt
文件。
可以通过pip-compile
或python -m piptools compile
运行它(如果安装了pipx
并使用了适当的Python版本,也可以使用pipx run --spec pip-tools pip-compile
)。如果你使用多个Python版本,在Windows上还可以运行py -X.Y -m piptools compile
,在其他系统上可以运行pythonX.Y -m piptools compile
。
pip-compile
应该在与你的项目相同的虚拟环境中运行,这样依赖于特定Python版本或其他环境标记的条件依赖项可以相对于你的项目环境进行解析。
注意:如果pip-compile
发现已存在满足依赖关系的requirements.txt
文件,即使有更新可用,也不会进行任何更改。要从头开始编译,首先删除现有的requirements.txt
文件,或参阅更新依赖项了解其他方法。
来自pyproject.toml
的依赖项
pyproject.toml
文件是最新标准,用于配置包和应用程序,建议新项目使用。pip-compile
支持安装你的project.dependencies
以及project.optional-dependencies
。由于这是一个官方标准,你可以使用pip-compile
来固定使用现代标准的打包工具(如Setuptools、Hatch或flit)的项目中的依赖项。
假设你有一个使用Setuptools
打包的'foobar' Python应用程序,你想为生产环境固定它的版本。你可以这样声明项目元数据:
[build-system]
requires = ["setuptools", "setuptools-scm"]
build-backend = "setuptools.build_meta"
[project]
requires-python = ">=3.9"
name = "foobar"
dynamic = ["dependencies", "optional-dependencies"]
[tool.setuptools.dynamic]
dependencies = { file = ["requirements.in"] }
optional-dependencies.test = { file = ["requirements-test.txt"] }
如果你有一个使用Hatch
打包的Django应用程序,你想为生产环境固定它的版本。你还想在一个单独的固定文件中固定你的开发工具。你将django
声明为依赖项,并创建一个包含pytest
的可选依赖项dev
:
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "my-cool-django-app"
version = "42"
dependencies = ["django"]
[project.optional-dependencies]
dev = ["pytest"]
你可以轻松地生成固定文件:
$ pip-compile -o requirements.txt pyproject.toml
#
# This file is autogenerated by pip-compile with Python 3.10
# by the following command:
#
# pip-compile --output-file=requirements.txt pyproject.toml
#
asgiref==3.6.0
# via django
django==4.1.7
# via my-cool-django-app (pyproject.toml)
sqlparse==0.4.3
# via django
$ pip-compile --extra dev -o dev-requirements.txt pyproject.toml
#
# This file is autogenerated by pip-compile with Python 3.10
# by the following command:
#
# pip-compile --extra=dev --output-file=dev-requirements.txt pyproject.toml
#
asgiref==3.6.0
# via django
attrs==22.2.0
# via pytest
django==4.1.7
# via my-cool-django-app (pyproject.toml)
exceptiongroup==1.1.1
# via pytest
iniconfig==2.0.0
# via pytest
packaging==23.0
# via pytest
pluggy==1.0.0
# via pytest
pytest==7.2.2
# via my-cool-django-app (pyproject.toml)
sqlparse==0.4.3
# via django
tomli==2.0.1
# via pytest
这对于固定你的应用程序和保持开源Python包的CI稳定都很有帮助。
来自setup.py
和setup.cfg
的依赖项
pip-compile
也完全支持基于setup.py
和setup.cfg
的使用setuptools
的项目。
只需像往常一样定义你的依赖项和额外依赖项,然后按上述方式运行pip-compile
即可。
来自requirements.in
的依赖项
你也可以使用纯文本文件来定义你的依赖项(例如,如果你不希望你的应用程序成为一个包)。要使用requirements.in
文件声明Django依赖项:
# requirements.in
django
现在,运行pip-compile requirements.in
:
$ pip-compile requirements.in
#
# This file is autogenerated by pip-compile with Python 3.10
# by the following command:
#
# pip-compile requirements.in
#
asgiref==3.6.0
# via django
django==4.1.7
# via -r requirements.in
sqlparse==0.4.3
# via django
它将生成你的requirements.txt
,其中包含所有Django依赖项(以及所有底层依赖项)的固定版本。
(updating-requirements)=
更新依赖项
pip-compile
使用最新版本生成requirements.txt
文件,以满足你在支持的文件中指定的依赖项。
如果pip-compile
发现已存在满足依赖关系的requirements.txt
文件,即使有更新可用,也不会进行任何更改。
要强制 pip-compile
更新现有 requirements.txt
中的所有包,请运行 pip-compile --upgrade
。
要将特定包更新到最新版本或指定版本,请使用 --upgrade-package
或 -P
标志:
# 仅更新 django 包
$ pip-compile --upgrade-package django
# 同时更新 django 和 requests 包
$ pip-compile --upgrade-package django --upgrade-package requests
# 将 django 包更新到最新版本,将 requests 更新到 v2.0.0
$ pip-compile --upgrade-package django --upgrade-package requests==2.0.0
您可以在一个命令中组合使用 --upgrade
和 --upgrade-package
,以对允许的升级施加约束。例如,要升级所有包,同时将 requests 限制在小于 3.0 的最新版本:
$ pip-compile --upgrade --upgrade-package 'requests<3.0'
使用哈希值
如果您想使用自 pip 8.0 版本起提供的哈希检查模式,pip-compile
提供了 --generate-hashes
标志:
$ pip-compile --generate-hashes requirements.in
#
# 此文件由 pip-compile 使用 Python 3.10 自动生成
# 通过以下命令:
#
# pip-compile --generate-hashes requirements.in
#
asgiref==3.6.0 \
--hash=sha256:71e68008da809b957b7ee4b43dbccff33d1b23519fb8344e33f049897077afac \
--hash=sha256:9567dfe7bd8d3c8c892227827c41cce860b368104c3431da67a0c5a65a949506
# via django
django==4.1.7 \
--hash=sha256:44f714b81c5f190d9d2ddad01a532fe502fa01c4cb8faf1d081f4264ed15dcd8 \
--hash=sha256:f2f431e75adc40039ace496ad3b9f17227022e8b11566f4b363da44c7e44761e
# via -r requirements.in
sqlparse==0.4.3 \
--hash=sha256:0323c0ec29cd52bceabc1b4d9d579e311f3e4961b98d174201d5622a23b85e34 \
--hash=sha256:69ca804846bb114d2ec380e4360a8a340db83f0ccf3afceeb1404df028f57268
# via django
输出文件
要将固定的依赖项输出到 requirements.txt
以外的文件名,请使用 --output-file
。这在编译多个文件时可能有用,例如,使用不同的 django 约束来测试库在不同版本下的表现,可以配合 tox 使用:
$ pip-compile --upgrade-package 'django<1.0' --output-file requirements-django0x.txt
$ pip-compile --upgrade-package 'django<2.0' --output-file requirements-django1x.txt
要输出到标准输出,请使用 --output-file=-
:
$ pip-compile --output-file=- > requirements.txt
$ pip-compile - --output-file=- < requirements.in > requirements.txt
向 pip
传递选项
任何有效的 pip
标志或参数都可以通过 pip-compile
的 --pip-args
选项传递,例如:
$ pip-compile requirements.in --pip-args "--retries 10 --timeout 30"
配置
您可以通过在与需求输入文件相同目录下(或当从标准输入管道输入时,在当前工作目录)的配置文件中定义 pip-compile
和 pip-sync
的项目级默认值。默认情况下,pip-compile
和 pip-sync
会首先查找 .pip-tools.toml
文件,然后是 pyproject.toml
。您也可以使用 --config
选项指定其他 TOML 配置文件。
可以同时指定全局和命令特定的配置值。例如,要默认在生成的需求文件输出中包含 pip
哈希值,可以在配置文件中指定:
[tool.pip-tools]
generate-hashes = true
pip-compile
和 pip-sync
中可能多次使用的选项在配置文件中必须定义为列表,即使它们只有一个值。
pip-tools
支持其子命令的所有有效命令行标志的默认值。配置键可以使用下划线而不是破折号,所以上面的配置也可以这样指定:
[tool.pip-tools]
generate_hashes = true
pip-compile
和 pip-sync
特定的配置默认值可以放在单独的部分下。例如,要默认对 pip-compile
执行干运行:
[tool.pip-tools.compile] # "sync" 用于 pip-sync
dry-run = true
这不会影响 pip-sync
命令,后者也有一个 --dry-run
选项。请注意,当同时声明时,局部设置优先于同名的全局设置,因此这也会使 pip-compile
生成哈希值,但会丢弃全局的干运行设置:
[tool.pip-tools]
generate-hashes = true
dry-run = true
[tool.pip-tools.compile]
dry-run = false
您可能会在另一个脚本中包装 pip-compile
命令。为避免使您自定义脚本的用户感到困惑,您可以通过设置 CUSTOM_COMPILE_COMMAND
环境变量来覆盖需求文件顶部生成的更新命令。
$ CUSTOM_COMPILE_COMMAND="./pipcompilewrapper" pip-compile requirements.in
#
# 此文件由 pip-compile 使用 Python 3.10 自动生成
# 通过以下命令:
#
# ./pipcompilewrapper
#
asgiref==3.6.0
# via django
django==4.1.7
# via -r requirements.in
sqlparse==0.4.3
# via django
分层需求的工作流程
如果您有不同的环境需要安装不同但兼容的包,那么您可以创建分层的需求文件,并使用一个层来约束另一个层。
例如,如果您有一个 Django 项目,在生产环境中想要最新的 2.1
版本,而在开发时想使用 Django 调试工具栏,那么您可以为每一层创建两个 *.in
文件:
# requirements.in
django<2.2
在开发需求文件 dev-requirements.in
的顶部,使用 -c requirements.txt
来约束开发需求,使其使用 requirements.txt
中已为生产选择的包。
# dev-requirements.in
-c requirements.txt
django-debug-toolbar<2.2
首先,像往常一样编译 requirements.txt
:
$ pip-compile
#
# 此文件由 pip-compile 使用 Python 3.10 自动生成
# 使用以下命令:
#
# pip-compile
#
django==2.1.15
# 来自 -r requirements.in
pytz==2023.3
# 来自 django
现在编译开发环境的依赖,并使用 requirements.txt
文件作为约束:
$ pip-compile dev-requirements.in
#
# 此文件由 pip-compile 使用 Python 3.10 自动生成
# 使用以下命令:
#
# pip-compile dev-requirements.in
#
django==2.1.15
# 来自
# -c requirements.txt
# django-debug-toolbar
django-debug-toolbar==2.1
# 来自 -r dev-requirements.in
pytz==2023.3
# 来自
# -c requirements.txt
# django
sqlparse==0.4.3
# 来自 django-debug-toolbar
如上所示,尽管 Django 有 2.2
版本可用,但开发环境的依赖仍然只包含 2.1
版本的 Django,因为它们受到了约束。现在两个编译后的依赖文件都可以在开发环境中安全安装。
在生产环境中安装依赖,请使用:
$ pip-sync
在开发环境中安装依赖,可以使用:
$ pip-sync requirements.txt dev-requirements.txt
版本控制集成
你可以将 pip-compile
作为 pre-commit 的钩子使用。
具体说明请参阅 pre-commit 文档。
示例 .pre-commit-config.yaml
:
repos:
- repo: https://github.com/jazzband/pip-tools
rev: 7.4.1
hooks:
- id: pip-compile
你可能想通过配置 args
和/或 files
来自定义 pip-compile
的参数,例如:
repos:
- repo: https://github.com/jazzband/pip-tools
rev: 7.4.1
hooks:
- id: pip-compile
files: ^requirements/production\.(in|txt)$
args: [--index-url=https://example.com, requirements/production.in]
如果你有多个依赖文件,请确保为每个文件创建一个钩子。
repos:
- repo: https://github.com/jazzband/pip-tools
rev: 7.4.1
hooks:
- id: pip-compile
name: pip-compile setup.py
files: ^(setup\.py|requirements\.txt)$
- id: pip-compile
name: pip-compile requirements-dev.in
args: [requirements-dev.in]
files: ^requirements-dev\.(in|txt)$
- id: pip-compile
name: pip-compile requirements-lint.in
args: [requirements-lint.in]
files: ^requirements-lint\.(in|txt)$
- id: pip-compile
name: pip-compile requirements.in
args: [requirements.in]
files: ^requirements\.(in|txt)$
pip-sync
的使用示例
现在你有了 requirements.txt
,可以使用 pip-sync
来更新你的虚拟环境,使其完全匹配文件中的内容。这将安装/升级/卸载所有必要的包,以匹配 requirements.txt
的内容。
使用 pip-sync
或 python -m piptools sync
运行它。如果你使用多个 Python 版本,也可以在 Windows 上运行 py -X.Y -m piptools sync
,在其他系统上运行 pythonX.Y -m piptools sync
。
pip-sync
必须安装在与你的项目相同的虚拟环境中并从中运行,以识别需要安装或升级的包。
注意:pip-sync
只能与由 pip-compile
生成的 requirements.txt
一起使用。
$ pip-sync
正在卸载 flake8-2.4.1:
成功卸载 flake8-2.4.1
正在收集 click==4.1
正在下载 click-4.1-py2.py3-none-any.whl (62kB)
100% |................................| 65kB 1.8MB/s
找到现有安装:click 4.0
正在卸载 click-4.0:
成功卸载 click-4.0
成功安装 click-4.1
要同步多个 *.txt
依赖列表,只需通过命令行参数传递它们,例如:
$ pip-sync dev-requirements.txt requirements.txt
传入空参数会默认使用 requirements.txt
。
任何有效的 pip install
标志或参数都可以通过 pip-sync
的 --pip-args
选项传递,例如:
$ pip-sync requirements.txt --pip-args "--no-cache-dir --no-deps"
注意:pip-sync
不会升级或卸载打包工具,如 setuptools
、pip
或 pip-tools
本身。使用 python -m pip install --upgrade
来升级这些包。
我应该将 requirements.in
和 requirements.txt
提交到版本控制吗?
通常来说,是的。如果你希望从你的源代码控制中获得可重现的环境安装,那么是的,你应该将 requirements.in
和 requirements.txt
都提交到版本控制中。
请注意,如果你在多个 Python 环境中部署(阅读下面的部分),那么你必须为每个 Python 环境提交一个单独的输出文件。
我们建议使用 {env}-requirements.txt
格式
(例如:win32-py3.7-requirements.txt
、macos-py3.10-requirements.txt
等)。
requirements.in
/requirements.txt
和 pip-compile
的跨环境使用
包的依赖可能会根据安装它的 Python 环境而变化。在这里,我们将 Python 环境定义为操作系统、Python 版本(3.7、3.8 等)和 Python 实现(CPython、PyPy 等)的组合。有关确切定义,请参考 PEP 508 环境标记 的可能组合。
由于生成的 requirements.txt
可能因环境而异,用户必须在每个 Python 环境中单独执行 pip-compile
以生成对应环境有效的 requirements.txt
。相同的 requirements.in
可以作为所有环境的源文件,根据需要使用 PEP 508 环境标记,就像常规的跨环境 pip
使用一样。
如果生成的 requirements.txt
对所有 Python 环境保持完全相同,那么它可以安全地在不同的 Python 环境中使用。但是用户应该小心,因为任何包的更新都可能引入依赖于环境的依赖项,从而使任何新生成的 requirements.txt
也变得依赖于环境。作为一般规则,建议用户仍然应该始终在每个目标 Python 环境中执行 pip-compile
以避免问题。
最大化可重现性
pip-tools
是提高构建可重现性的优秀工具。
但有几点需要注意。
pip-compile
将在不同环境中产生不同的结果,如上一节所述。- 必须使用带有
PIP_CONSTRAINT
环境变量的pip
来锁定构建环境中的依赖项,如 #8439 中所述。 - 依赖项来自多个来源。
继续之前的 pyproject.toml
示例,创建单个锁定文件可以这样做:
$ pip-compile --all-build-deps --all-extras --output-file=constraints.txt --strip-extras pyproject.toml
#
# 此文件由 pip-compile 使用 Python 3.9 自动生成
# 通过以下命令:
#
# pip-compile --all-build-deps --all-extras --output-file=constraints.txt --strip-extras pyproject.toml
#
asgiref==3.5.2
# 来自 django
attrs==22.1.0
# 来自 pytest
backports-zoneinfo==0.2.1
# 来自 django
django==4.1
# 来自 my-cool-django-app (pyproject.toml)
editables==0.3
# 来自 hatchling
hatchling==1.11.1
# 来自 my-cool-django-app (pyproject.toml::build-system.requires)
iniconfig==1.1.1
# 来自 pytest
packaging==21.3
# 来自
# hatchling
# pytest
pathspec==0.10.2
# 来自 hatchling
pluggy==1.0.0
# 来自
# hatchling
# pytest
py==1.11.0
# 来自 pytest
pyparsing==3.0.9
# 来自 packaging
pytest==7.1.2
# 来自 my-cool-django-app (pyproject.toml)
sqlparse==0.4.2
# 来自 django
tomli==2.0.1
# 来自
# hatchling
# pytest
一些构建后端可能还会使用 PEP 517 和 PEP 660 中描述的 get_requires_for_build_
钩子动态请求构建依赖项。
这将在输出中以下列后缀之一表示:
(pyproject.toml::build-system.backend::editable)
(pyproject.toml::build-system.backend::sdist)
(pyproject.toml::build-system.backend::wheel)
其他有用的工具
-
pip-compile-multi - 用于多个交叉引用需求文件的 pip-compile 命令包装器。
-
pipdeptree 用于打印已安装包的依赖树。
-
requirements.in
/requirements.txt
语法高亮:- Vim 用户可使用 requirements.txt.vim。
- VS Code 用户可使用 Python 扩展。
- Emacs 用户可使用 pip-requirements.el。
弃用说明
本节列出了当前已弃用的 pip-tools
功能。
- 在下一个主要版本中,
--allow-unsafe
行为将默认启用(https://github.com/jazzband/pip-tools/issues/989)。 使用--no-allow-unsafe
保持旧行为。建议现在传递--allow-unsafe
以适应即将到来的变化。 - 旧版解析器已被弃用,将在未来版本中移除。
新的默认值是
--resolver=backtracking
。 - 在下一个主要版本中,
--strip-extras
行为将默认启用(https://github.com/jazzband/pip-tools/issues/1613)。 使用--no-strip-extras
保持旧行为。
关于解析器的说明
您可以选择默认的回溯解析器或已弃用的旧版解析器。
旧版解析器有时会无法解析依赖项。回溯解析器更加稳健,但通常需要更长的运行时间。
您可以继续使用 --resolver=legacy
选项来使用旧版解析器,但请注意它已被弃用,将在未来版本中移除。