3 个版本

0.0.3 2019年9月9日
0.0.2 2019年9月4日
0.0.1 2019年8月11日

#374 in 构建实用工具

MIT 许可证

210KB
4K SLoC

docs.rs Build Status

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.exepypackage)并将其放置在系统路径可访问的位置。例如,Linux 中的 /usr/bin,或在 Windows 中的 ~\AppData\Local\Programs\Python\Python37\bin

  • 如果您已安装 Rust,最便捷的方式是运行 cargo install pypackage

快速入门

  • (可选) 在现有项目文件夹中运行 pypackage init,或者运行 pypackage new projname 来创建新的项目文件夹。initrequirements.txtPipfile 导入数据;new 创建带有基本功能的文件夹
  • 运行 pypackage install 来设置 Python,并使用 pyproject.toml 同步依赖项,或向其添加依赖项
  • 运行 pypackage python 来运行 Python

为什么还要添加另一个 Python 管理器?

PipenvPoetry 都解决了这个问题的一部分。这个工具不同的原因如下:

  • 由于使用依赖项的缓存数据库,其依赖项解析和锁定速度更快,而不是下载和检查每个包,或者依赖于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.0Dep BDep B 需要 Dep A == 0.9。在许多情况下,PoetryPipenv 将无法解决依赖项,但我们通过这种方式做到了。自己用来自pypi的几个随机依赖项试一试;你可能会在使用 PoetryPipenv 时遇到这个问题。限制:这不会适用于某些编译依赖项,尝试使用这种方式打包将触发错误。

虚拟环境很容易。这有什么用?

希望我们不会用一个问题替换另一个问题

有些人喜欢虚拟环境的工作流程 - 它只需要 Python 包含的工具,并且使用很少的控制台命令来创建、激活和设置环境。然而,根据工作流程,这些命令可能很长,取决于虚拟环境和项目的路径,并且每次使用它时都需要修改终端的状态,这可能会觉得不方便或不优雅。

如果你对现有的流程感到满意,可能没有必要更改,但我认为我们可以做得更好。这尤其适用于那些没有完全理解venvs,或者不了解使用系统Python的潜在危险的新Python用户。

Pipenv 通过自动化环境使用,并允许可重复的依赖关系解析来改进工作流程。 Poetry 通过改进 Pipenv 的API、速度和依赖关系解析,以及通过使用项目配置来改进打包和分发过程。这两个工具都对它们运行的Python环境很敏感。这个工具试图在上文提到的区域内改进这两个工具。它的目标尽可能直观。

Conda 优雅地解决了这些问题,但维护一个与 PyPi 分离的二进制库。如果你需要的所有软件包都可在 Conda 中找到,这可能是最优解决方案。如果不是,则需要回退到 Pip,这意味着使用两个不同的包管理器。

在构建和部署软件包时,通常使用一组退化文件:setup.pysetup.cfgrequirements.txtMANIFEST.in。我们使用 pyproject.toml 作为构建和发布所需的项目信息的唯一来源。

一个详尽的特征表

(请在此处提交任何不准确、不完整或误导性的内容)

这些工具具有不同的范围和目的

名称 Pip + venv Pipenv Poetry pyenv pythonloc Conda this
管理依赖
管理Python安装
Py环境无关
包含在Python中
与项目一起存储软件包
锁定依赖
需要更改会话状态
干净的构建/发布流程
有错误
支持旧Python版本 使用 virtualenv

使用

  • 在你的项目目录中创建一个 pyproject.toml 文件。请注意,运行 initnewinstall 会自动创建此文件。有关详细信息,请参阅 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 REPL
  • pypackage python main.py - 运行Python文件
  • pypackage ipythonpypackage 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.textPipfile中获取信息。
  • 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/requestshttps://pydeps.herokuapp.com/requests/2.21.0。这会拉取 requests 包的所有顶级依赖项,以及版本 2.21.0 的依赖项。对于第一次运行此命令的包/版本组合,可能较慢。随后的调用,无论由谁进行,都应该很快。这是因为需要下载并在服务器上安装每个包以正确确定依赖项,因为 pypi仓库 的信息不可靠。

注意事项

  • 确保 pypackage 二进制文件可在您的路径中访问。如果通过 debmsiCargo 安装,这应该会自动设置。
  • 确保 __pypackages__.venv 在您的 .gitignore 文件中。

参考

依赖项

~30–43MB
~848K SLoC