Suppose my package depends on the foo
package, and foo
, in turn, depends on the bar
package, specifically bar>=1.0.0
.
In other words, bar
is a sub-dependency for my package.
Following best practice, my pyproject.toml
(or setup.cfg
) specifies only direct dependencies, no sub-dependencies. For example:
[project]
# ...
dependencies = [
"foo>=2.0",
]
Now suppose bar
version 1.0.1
has been released in order to patch a security vulnerability, but foo
has not picked up on this yet (it still uses bar>=1.0.0
).
Instead of waiting for foo
to catch up, I want to make sure my package enforces the use of bar>=1.0.1
.
The obvious solution would be to add a constraint to my pyproject.toml
as follows:
[project]
# ...
dependencies = [
"foo>=2.0",
# constraint for sub-dependency
"bar>=1.0.1",
]
This works, but now it looks like my project depends directly on bar
, whereas bar
is only a sub-dependency: I don't import anything directly from bar
, and if foo
ever decides not to use bar
anymore, I won't need it either.
Is there a better way to specify a constraint for a sub-dependency using setuptools?
Unless I'm missing something: no. Not using setuptools
.
Your package is going to be built into a binary distribution (wheel, a .whl
file). After that point, setuptools
is no longer in the picture, and the package metadata needs to follow Core metadata specifications, which do not have any facility for specifying constraints.
pip
has a relevant option for this:
-c, --constraint <file> Constrain versions using the given constraints
file. This option can be used multiple times.
You can read more about constrain files in pip documentation.
There's also an argument to be made about what counts as a dependency for a package. If people depend on X
to be secure, and X
depends (transitively) on Y
to be secure, then you could argue in a hand-wavy way that this has to be taken into account inside the dependencies list, even though there's no coupling in the code.
The counterpoint is that this exposes packages that depend on both X
and Y
to a dependency hell. Further, there's no safety hatch in case Y
stops being necessary for your direct dependencies.