I would like some way to declare aliases or groups of optional dependencies. For example:
[project]
...
dynamic = ["optional-dependencies"]
[tool.optdeps.optional-dependencies]
test = ["pytest"]
doc = ["sphinx"]
style = ["black"]
[tool.optdeps.dependency-aliases]
tests = ["test"] # Help out misspellers
dev = ["test", "doc", "style"]
all = ["*"]
This would resolve into:
[project.optional-dependencies]
test = ["pytest"]
doc = ["sphinx"]
style = ["black"]
tests = ["pytest"]
dev = ["pytest", "sphinx", "black"]
all = ["pytest", "sphinx", "black"]
It’s a simple thing to do, and it would seem cleanest to write a single tool to do it rather than propose a patch to every backend or propose an addition to PEP-621.
From my understanding, there’s no way to add something to [build-system]
that would tip off setuptools or flit or whatever that it should let another tool do some munging on pyproject.toml
and call out to my imaginary tool optdeps
to get back a filled-in optional-dependencies
. So I think I would need to instead write a wrapper that would call these backends and either pass them a modified pyproject.toml
or modify the metadata of the built sdist or wheel.
Is there any guidance on writing such wrappers, and would it be general to any backend, or would I need to dig into each tool to figure out how to patch it?
There is a more general problem here, where any project metadata field could potentially be dynamic and a small tool could be devised to resolve it for other backends. For example, setuptools_scm
could be allowed to set version
for any backend.
Background on aliases with setuptools
In the old setup.py
days, I saw some projects provide an all
extra that would be built from all of the other extras:
extras_require = {
"test": ["x", "y", "z"],
"doc": ["a", "b", "c"],
...
}
extras_require["all"] = list(set(req for extra in extras_require.values() for req in extra))
You might also have a dev
extra that combined a common set, such as tests, docs, and auto-formatters.
extras_require["dev"] = list(set(
req
for extra in ("doc", "test", "style")
for req in extras_require[extra]```
))
Using setup.cfg
allowed you to do this in a declarative way, thanks to ConfigParser
:
[options.extras_require]
test =
x
y
z
doc =
a
b
c
all =
%(test)s
%(doc)s
I can keep doing this in setuptools, at the cost of keeping around both pyproject.toml
and setup.cfg
(or setup.py
), but there doesn’t seem to be any way to do the equivalent in pyproject.toml
. So if I want to try out or migrate to a new build backend, I’m back to a place where convenience extras need to be checked for consistency rather than just built from their components.