Python中的递归可选依赖

我喜欢将元数据打包到可执行文件中的(慢慢消失的)原因之一setup.py是能够具有可选依赖项,这些依赖项是其他依赖项的组合。从pip 21.2 开始,无需运行代码就可以做到这一点。

包的可选依赖项(也称为extras)是一组命名的依赖项,通过将它们的名称放在包名称后面的方括号内来安装。例如pip install httpx[http2],将安装httpx以及支持 HTTP/2 所需的可选依赖项。您可以一次指定多个额外的:pip install httpx[http2,cli]将安装 HTTP/2 和httpx的 CLI 界面所需的一切。


我喜欢在开发中使用可选依赖项的组合,通过dev额外包含运行测试、构建文档和在交互式开发中有用但不应该出现在 CI 中的工具所需的一切。在运行测试时我不需要Sphinx而在构建文档时我不需要pytest。但是当我在这个项目上工作时,我需要一个更好的调试器或MyPy。

在一个setup.py文件中,它看起来像这样:

extras = {
    "tests": ["pytest"],
    "docs": ["sphinx"],
}
​
extras["dev"] = extras["tests"] + extras["docs"] + ["pdbpp"]
​
setup(
    name="my-pkg",
    # ...
    extras_require=extras,
)

pip install -e .[dev]现在将安装pytest、sphinx和pdbpp. 这样,我的开发环境就准备好了。这对于公共项目特别有用,因为为您的贡献者设置本地开发环境的工作减少到了一行。


这很方便,它让我在很长一段时间内无法使用pyproject.toml(PEP 621 )进行静态配置。其中,这意味着我的可选依赖项中的重复.

然而,我也一直对静态元数据和Hatch或Flit等现代打包工具很感兴趣。

Cog:静态模板

当我和 Python 社区一起发现Cog时,我第一次将脚趾伸入静态水域。Cog允许通过将模板逻辑隐藏在注释后面来在静态文件中应用内联模板。

所以我声明了我的开发特定依赖项(在这种情况下只是pdbpp),然后是一个Cog模板块,它读取和解析pyproject.toml(本身!)并添加来自testsand的依赖项docs:

[project.optional-dependencies]
tests = ["pytest"]
docs = ["sphinx"]
​
dev = [
    "pdbpp",
    # [[[cog
    # import pathlib, tomli
    # cfg = tomli.loads(pathlib.Path("pyproject.toml").read_text())
    # opt = cfg["project"]["optional-dependencies"]
    # for dep in opt["tests"] + opt["docs"]:
    #     print(f'"{dep}",')
    # ]]]
    "pytest",
    "sphinx",
    # [[[end]]]
]

如您所见,pytest和Sphinx是dev列表的一部分,每当我更改tests或docs运行Cog# ]]]时,列表和之间的列表# [[[end]]]都会更新。


为了运行Cog并确保文件没有过期,我使用了两个同样在 CI 中运行的tox目标:cogCheck

[tox]
envlist = cogCheck,cog  # ...
​
[testenv:cogCheck]
description = "Ensure pyproject.toml is up to date"
skip_install = true
deps = {[testenv:cog]deps}
commands = python -m cogapp --check -P pyproject.toml
​
[testenv:cog]
description = "Update pyproject.toml's metadata"
skip_install = true
deps =
    cogapp>=3.3.0
    tomli
commands = python -m cogapp -rP pyproject.toml

这很酷,而且我还使用Cog将我的 README 导入并修改为 PyPI 的长描述,所以它会保留下来。但是,它也有点笨拙,尤其是对于没有长描述的公司内部项目。

终于把我们带到了今天的话题!

开源阅读3.0免费小说神器,支持第三方书源的替换,无广告告别VIP!

pip 21.2:递归依赖

Python 的打包进度可能很慢,但很稳定。从pip 21.2 开始,您可以在可选依赖项中引用您自己的项目:

[project]
name = "my-pkg"
​
[project.optional-dependencies]
tests = ["pytest"]
docs = ["sphinx"]
dev = [
    "my-pkg[tests,docs]",  # <-- !!!
    "pdbpp",
]

由于dev最终用户不使用,因此需要一个前沿(也就是只有一年的)pip版本并不是什么大问题。

这是一个在野外看起来如何的示例:structlog的pyproject.toml.


可能会为您节省一些时间的额外提示:额外的名称像包名称一样标准化。额外foo_bar的必须称为foo-bar. 理想情况下完全停止在额外名称中使用下划线。

去哪儿dev?

对于那些对版本固定非常直言不讳的人来说,我使用可选依赖项而不是 pin 文件可能会令人惊讶。

我没有固定我的开源包的开发依赖项,因为没有足够的活动来证明依赖项更新的不断提交流失是合理的。就目前而言,更实际的是在发生中断的 CI 时修复它(这种情况很少见),而不是成为大多数提交是依赖项更新的项目之一。

这是我的权衡计算——你的可能完全不同。如果您的 CI 由于依赖项更新而定期中断,您应该查看 pin 文件和服务,例如Dependabot。


当然,递归可选依赖关系不仅仅对我用于说明的这个示例有用。

你可能感兴趣的:(python,开发语言,numpy)