3 个版本
0.0.3 | 2019年9月9日 |
---|---|
0.0.2 | 2019年9月4日 |
0.0.1 | 2019年8月11日 |
#374 in 构建实用工具
210KB
4K SLoC
Py 包
此工具管理 Python 安装和依赖。它实现了 PEP 582 -- Python 本地包目录 和 PEP 518 -- 指定 Python 项目的最小构建系统要求
它将后者隔离在项目目录中,并在使用此目录的环境中使用 Python。根据 PEP 582,依赖项存储在项目目录 → __pypackages__
→ 3.7
(等等) → lib
。
目标:使使用和发布 Python 项目尽可能简单。无需理解 Python 环境,即可安全地使用依赖项。
仅支持 Python ≥ 3.4。您无需安装 Python 即可使用它;如果尚未安装,它将安装指定的 Python 版本。
安装
有两种安装方式
-
从 发行版页面 下载二进制文件。提供了 Debian/Ubuntu 和 Windows 的安装程序。在 Debian 或 Ubuntu 上,下载并运行 此 deb。在 Windows 上,下载并运行 此安装程序。或者,下载适当的二进制文件(例如
pypackage.exe
或pypackage
)并将其放置在系统路径可访问的位置。例如,Linux 中的/usr/bin
,或在 Windows 中的~\AppData\Local\Programs\Python\Python37\bin
。 -
如果您已安装 Rust,最便捷的方式是运行
cargo install pypackage
。
快速入门
- (可选) 在现有项目文件夹中运行
pypackage init
,或者运行pypackage new projname
来创建新的项目文件夹。init
从requirements.txt
或Pipfile
导入数据;new
创建带有基本功能的文件夹 - 运行
pypackage install
来设置 Python,并使用pyproject.toml
同步依赖项,或向其添加依赖项 - 运行
pypackage python
来运行 Python
为什么还要添加另一个 Python 管理器?
Pipenv
和 Poetry
都解决了这个问题的一部分。这个工具不同的原因如下:
-
由于使用依赖项的缓存数据库,其依赖项解析和锁定速度更快,而不是下载和检查每个包,或者依赖于pypi 仓库上可用的不完整数据。
-
由于不使用 Python 来安装或运行,它保持了安装和环境无关性。这使设置和使用尽可能简单和无需决策变得非常重要。在 Linux 上尤为重要,因为可能安装了多个 Python 版本,具有不同的版本和访问级别。这避免了复杂性,特别是对于新用户。Python 基础 CLI 工具由于
PATH
没有按预期配置,所以从pip
安装后可能无法正确运行。 -
它管理 Python 安装 - 允许您选择要使用的 Python 版本(≥ 3.4)。如果已安装,则使用该版本。如果没有,它将下载二进制文件,存储在
~/python-installs
中,并使用该版本。它允许用户在pyproject.toml
中选择要使用的 Python 版本,然后自动使用该版本,按需安装。 -
它将依赖项保留在项目目录中的
__pypackages__
中,并且不修改外部文件(除了新的 Python 安装)。这是一个微妙之处,但强化了没有隐藏状态需要关心的想法。 -
它将始终使用指定的 Python 版本。这是
Poetry
的一个显著问题;它可能会选择错误的安装(例如 Python2 而不是 Python3),并且没有明显的方法来更改它。 -
可以安装依赖项的多个版本,从而解决冲突的子依赖项。例如:您的包需要
Dep A >= 1.0
和Dep B
。Dep B
需要Dep A == 0.9
。在许多情况下,Poetry
和Pipenv
将无法解决依赖项,但我们通过这种方式做到了。自己用来自pypi的几个随机依赖项试一试;你可能会在使用Poetry
或Pipenv
时遇到这个问题。限制:这不会适用于某些编译依赖项,尝试使用这种方式打包将触发错误。
虚拟环境很容易。这有什么用?
有些人喜欢虚拟环境的工作流程 - 它只需要 Python 包含的工具,并且使用很少的控制台命令来创建、激活和设置环境。然而,根据工作流程,这些命令可能很长,取决于虚拟环境和项目的路径,并且每次使用它时都需要修改终端的状态,这可能会觉得不方便或不优雅。
如果你对现有的流程感到满意,可能没有必要更改,但我认为我们可以做得更好。这尤其适用于那些没有完全理解venvs,或者不了解使用系统Python的潜在危险的新Python用户。
Pipenv
通过自动化环境使用,并允许可重复的依赖关系解析来改进工作流程。 Poetry
通过改进 Pipenv
的API、速度和依赖关系解析,以及通过使用项目配置来改进打包和分发过程。这两个工具都对它们运行的Python环境很敏感。这个工具试图在上文提到的区域内改进这两个工具。它的目标尽可能直观。
Conda
优雅地解决了这些问题,但维护一个与 PyPi
分离的二进制库。如果你需要的所有软件包都可在 Conda
中找到,这可能是最优解决方案。如果不是,则需要回退到 Pip
,这意味着使用两个不同的包管理器。
在构建和部署软件包时,通常使用一组退化文件:setup.py
、setup.cfg
、requirements.txt
和 MANIFEST.in
。我们使用 pyproject.toml
作为构建和发布所需的项目信息的唯一来源。
一个详尽的特征表
(请在此处提交任何不准确、不完整或误导性的内容)
这些工具具有不同的范围和目的
名称 | Pip + venv | Pipenv | Poetry | pyenv | pythonloc | Conda | this |
---|---|---|---|---|---|---|---|
管理依赖 | ✓ | ✓ | ✓ | ✓ | ✓ | ||
管理Python安装 | ✓ | ✓ | |||||
Py环境无关 | ✓ | ✓ | ✓ | ||||
包含在Python中 | ✓ | ||||||
与项目一起存储软件包 | ✓ | ✓ | |||||
锁定依赖 | ✓ | ✓ | ✓ | ✓ | |||
需要更改会话状态 | ✓ | ✓ | |||||
慢 | ✓ | ||||||
干净的构建/发布流程 | ✓ | ✓ | |||||
有错误 | ✓ | ||||||
支持旧Python版本 | 使用 virtualenv |
✓ | ✓ | ✓ | ✓ | ✓ |
使用
- 在你的项目目录中创建一个
pyproject.toml
文件。请注意,运行init
、new
或install
会自动创建此文件。有关详细信息,请参阅 PEP 518。
示例内容
[tool.pypackage]
py_version = "3.7"
name = "runcible"
version = "0.1.0"
author = "John Hackworth"
[tool.pypackage.dependencies]
numpy = "^1.16.4"
diffeqpy = "1.1.0"
用于元数据的 [tool.pypackage]
部分是可选的,除非需要构建和分发软件包。包含所有依赖项的 [tool.pypyackage.dependencies]
部分类似于 requirements.txt
。
您可以指定 extra
依赖项,这些依赖项仅在通过传递显式标志给 pypackage install
或在另一个项目中包含具有适当标志的项目时安装。例如,需要此软件包的包可以启用 pip install -e
等。
[tool.pypackage.extras]
test = ["pytest", "nose"]
secure = ["crypto"]
如果您想安装带有额外依赖项的依赖项,请使用如下语法
[tool.pypackage.dependencies]
ipython = { version = "^7.7.0", extras = ["qtconsole"] }
有关如何在此 Cargo.toml
-启发式的 semvar 格式中指定依赖项的详细信息,请参阅 本指南。
我们还尝试从pyproject.toml
文件的tool.poetry
部分解析元数据和依赖项,因此如果您使用该工具,则无需修改格式。
您可以做什么
管理依赖项
pypackage install
- 安装pyproject.toml
中所有包,并删除未(递归地)指定的包pypackage install toolz
- 如果在install
之后指定了一个或多个包,这些包将被添加到pyproject.toml
并安装。pypackage install numpy==1.16.4 matplotlib>=3.1.0
- 具有多个依赖项和指定版本的示例pypackage uninstall toolz
- 删除一个或多个依赖项
在环境中运行REPL和Python文件
pypackage python
- 运行Python REPLpypackage python main.py
- 运行Python文件pypackage ipython
、pypackage black
等 - 运行类似于ipython
的CLI脚本。这可能是由依赖项安装的,或者指定在[tool.pypackage]
、scripts
下。pypackage run ipython
- 不同的语法
构建和发布
pypackage package
- 打包用于分发(内部使用setuptools,并根据适用情况构建源和wheel。)pypackage package --features "test all"
- 打包用于分发,启用了在pyproject.toml
中定义的功能pypackage publish
- 上传到PyPi(在pyproject.toml
中指定仓库。内部使用Twine
。)
杂项
pypackage list
- 显示所有已安装的包和控制台脚本pypackage new projname
- 创建一个包含项目基本元素的目录:readme、pyproject.toml、.gitignore和代码目录pypackage init
- 在现有项目目录中创建一个pyproject.toml
文件。根据需要从requirements.text
和Pipfile
中获取信息。pypackage reset
- 删除环境,并卸载所有包pypackage clear
- 清除全局下载包缓存,在~/python-installs/dependency-cache
pypackage -V
- 获取此工具的当前版本pypackage help
获取帮助,包括可用命令列表
安装和锁定的工作原理
运行pypackage install
将项目已安装的依赖项与pyproject.toml
中指定的依赖项同步。它生成pypackage.lock
,在后续运行中,只要继续满足pyproject.toml
中指定的约束,每个包的依赖项将保持固定版本。通过CLI添加包名,例如pypackage install matplotlib
,只是在该操作之前添加该要求。pypackage.lock
不打算直接编辑。
在 pyproject.toml
中列出的每个依赖项都会在 pypackage.lock
中检查是否有兼容的匹配项。如果锁定文件中存在满足约束的项,则同步的版本将与锁定文件中列出的版本匹配。如果不满足,则将新的条目添加到锁定文件中,包含 pyproject.toml
中允许的最高版本。完成后,将按照更新后的锁定文件中的列表顺序安装和删除包。
此工具从 pypi
下载和提取 wheels,如果没有可用,则从源代码构建 wheels。它使用 SHA256
验证下载文件的完整性,并使用 pypi
上列出的信息,将确切版本存储在锁定文件中。
当从 pyproject.toml
中移除依赖项时,如果它及其子依赖项不是其他包所需的,则从 __pypackages__
文件夹中移除。
依赖项是如何解决的
使用来自 PyPi 仓库(可用版本和哈希信息)和 pydeps
数据库的信息来确定依赖项的兼容版本。我们使用专门为此项目构建的 pydeps
,因为 pypi
上存储的依赖项信息不一致。使用此缓存数据库构建依赖项图。我们尝试使用每个包的最新兼容版本。
如果所有包都只指定一次,或者多次指定相同的最新兼容版本,则完成解决,准备安装和同步。
如果一个包以不同最新兼容版本重复包含,但其中之一与所有要求兼容,则安装该版本。如果不兼容,则搜索所有版本以找到兼容版本。
如果仍然无法找到满足所有要求的包版本,则按需安装多个版本,将它们存储在不同的目录中,并根据需要修改其父目录的导入。
注意,在上述未列出的情况下,可能可以解决依赖项,而不是安装多个版本。例如,我们可以尝试不同的顶级包组合,检查解决方案,然后根据需要向下调整子包。我们不这样做,因为它很慢,没有成功的保证,并且涉及安装包的旧版本。
尚未实现
- 从除
pypi
之外的其他来源安装(例如仓库) - 如果依赖项使用编译代码,则可能无法安装多个版本
- 锁定文件缺少一些信息,如哈希值
- 通过 CLI 以指定版本约束或额外内容的方式添加依赖项
- 开发人员需求
- 打包和发布使用编译扩展的项目
- 如果尚未安装,则下载并安装您指定的 Python 版本
构建并将您的项目上传到 PyPi
为了构建和发布您的项目,需要在 pyproject.toml
中提供额外的信息,该信息模仿 setup.py
中的内容。例如
[tool.pypackage]
name = "everythingkiller"
py_version = "3.6"
version = "0.1.0"
author = "Fraa Erasmas"
author_email = "[email protected]"
description = "Small, but packs a punch!"
homepage = "https://everything.math"
repository = "https://github.com/raz/everythingkiller"
license = "MIT"
classifiers = [
"Topic :: System :: Hardware",
"Topic :: Scientific/Engineering :: Human Machine Interfaces",
]
scripts = { activate = "jeejah:activate" }
[tool.pypackage.dependencies]
numpy = "^1.16.4"
manim = "0.1.8"
ipython = {version = "^7.7.0", extras=["qtconsole"]}
从源代码构建此软件
如果您想从源代码构建,请 下载并安装 Rust,克隆仓库,然后在仓库目录中运行 cargo build --release
。
例如在 Linux 上
curl https://sh.rustup.rs -sSf | sh
git clone https://github.com/david-oconnor/pypackage.git
cd pypackage
cargo build --release
更新
如果通过 Cargo
安装,请运行 cargo install pypackage --force
。
贡献
如果您注意到意外的行为或缺少的功能,请提交一个问题,或提交一个PR。如果您看到意外的行为,可能是存在一个bug!请提交一个问题,列出未正确安装的依赖项。
依赖项缓存仓库
- GitHub 示例API调用:
https://pydeps.herokuapp.com/requests
,https://pydeps.herokuapp.com/requests/2.21.0
。这会拉取requests
包的所有顶级依赖项,以及版本2.21.0
的依赖项。对于第一次运行此命令的包/版本组合,可能较慢。随后的调用,无论由谁进行,都应该很快。这是因为需要下载并在服务器上安装每个包以正确确定依赖项,因为pypi仓库
的信息不可靠。
注意事项
- 确保
pypackage
二进制文件可在您的路径中访问。如果通过deb
、msi
或Cargo
安装,这应该会自动设置。 - 确保
__pypackages__
和.venv
在您的.gitignore
文件中。
参考
依赖项
~30–43MB
~848K SLoC