Eh. I disagree here?
If pip install somepackage (or uv, or whatever) isn’t the “normal” way to install a package and you’re expected to “just go look at the documentation and do what it tells you to do”, then you can easily say things like:
- Listing the required dependencies for a project is superfluous and causes complications, after all you can just document
pip install somepackage andanotherpackage andathirdpackage.
- Build backends and the PEP 517 interface are superfluous and cause complications, after all you can just document the correct correct commands to run to install and invoke the correct tool to produce a wheel file that you pass into pip.
- Wheel files, and pip itself, are superfluous even, after all you could just document the command that tells the package to install itself.
What you’re describing as a hypothetical “this is what the normal way to install a thing” should be, is what the normal way to install something was 20 years ago. The last 20 years of packaging evolution within not just Python, but the entire industry, has basically been people going “you know, maybe it isn’t actually a good thing if every project needs to document exactly how to install itself, especially when for most of them we can provide structured metadata that provides a consistent experience rather than each project being a special snowflake”.
Obviously there are some projects that are just too complicated or complex to fit within those parameters, and sometimes some dimensions introduce far more complexity than they’re worth, and those projects have to be outliers. I don’t think that means we should have some sort of blanket idea that every project should have to document “how do I install this project” and think that’s going to be reasonable (or that we’re even going to be successful in convincing users when the whole rest of the industry has been moving the other way).
FWIW I’d contest that it’s incorrect to say become, given that a number of widely used projects are putting “nice to have but ultimately not really required” dependencies as required dependencies.
Pretending that dependency trees are pristine and perfect today is ignoring the ground truth of the ecosystem, they’re already murky, and in some cases that murkiness is a direct result of not having a feature like this. For those projects, the concerns around repackagers are improved by this metadata, not hurt.
Yes, there is a trade off, and my stance has been that we should be enabling the project authors, who have far more context available to them on what makes sense for their users, to make the right choice for their users.
As I see it, the trade offs are basically:
If we keep the status quo, the set of dependencies installed by default will continue to be limited to the set of dependencies the package author declares are “required”. Packages with optional, but highly recommended or “default backend” dependencies will have to make a choice between:
- Specifying those highly recommended dependencies as optional dependencies, and requiring users to seek them out and ask for them to be installed to get the “recommended” out of the box experience.
- Specifying those highly recommended dependencies as required dependencies, and forcing people who don’t want those dependencies to manually determine which are truly required and which are actually optional and fork the package (or “break” their install to remove them even though the metadata says they’re required).
- Implementing the “default extras” feature themselves, by making a
{}-core package which has the true required dependencies specified, and a {} package which depends on that and the recommended/default dependencies.
We know that all 3 of those cases are happening today, so none of them are hypothetical.
The first case is the best case for the repackagers and for the understand-ability of dependency trees. Unfortunately it’s also the worst experience for users out of the box, so projects that want to focus on their out of the box experience are incentivized to pick one of the other options.
The second option is the worst case for repackagers and for the understand-ability of dependency trees, but it provides that “out of the box” experience that those projects want. Unfortunately it has the lowest friction for package authors, so unless they feel strongly about keeping their “required” dependencies list to be what is actually required, this is going to be an attractive option.
The third option is a middle ground between them, it’s easy for repackagers to determine what is truly required ({}-core vs {}), but dependent projects have to make their own decisions about whether they depend on {}-core and keep their dependency tree minimal (and if they do, whether they surface their own core vs default package) or whether they depend on {} and pull in the recommended dependencies. Unfortunately this option has the highest friction for package authors, they’re suddenly forced to rename their “real” package from foo to foo-core and publish two packages instead of one (which then adds it’s own question about whether foo should depend on foo-core with a == or a >=,< or no constraint at all? should the foo-core and foo readme be the same? one empty? 2 maintained?).
As far as I can reason, default extras as specified by PEP 771 are basically equivalent in spirit to the third option above, except it’s foo and foo[] instead of foo and foo-core. Where it differs from that, I see primarily positives:
- It does not introduce much, if any, additional friction for package authors, so they’re far more likely to actually use it instead of the second option above where they over-specify their dependencies.
- It still requires dependent projects to make their own decisions about whether they depend on
foo (with default extras) or foo[] (without default extras), and similarly I expect the “I just didn’t think about it” option to be the “with default optional dependencies” choice, but it doesn’t make that any worse, it just changes whether the minimal dependency is spelled foo-core or foo[].
- It does not introduce the weirdness around how
foo should depend on foo-core, surfacing readmes and documentation, etc.
- It wastes less shared resources on PyPI and on the users machines as there’s less of these empty “meta packages” laying around (though these meta packages are likely to be pretty small).
As far as I can tell from the various discussions, the only real negatives PEP 771 has over the “core package” pattern, is:
- It has less friction, therefore package authors are more likely to use it, so the negatives of the “core package” pattern that still remain are more likely to be encountered.
- Stuff that’s inherent to new features; it’ll require ecosystem effort to make sure it’s well supported (for instance, making sure that what extras were requested being surfaced in tool output) and teaching people about it, and adding another concept to know.
Of those, I think the first of those is the only real fundamental question about the conceptual trade offs to PEP 771 (e.g. not questions around syntax, or specific details, but the idea in general), and when we compare that to the status quo:
Do we think it’s better to enable the projects that are currently choosing the worst case option for the concerns folks like you have raised to switch to a middle ground option that still gives them most of what they want without much additional friction, at the risk that maybe some projects who are currently choosing the first option are being held back from choosing the third option only due to the increased friction?
For me personally, I think the cohort of projects that are currently choosing option 1 and are being held back from option 3 only by the amount of friction is entails is pretty small, and I believe the cohort of projects who are choosing option 2, but would choose PEP 771 if it were available is much larger .
I think the group of people who are willing to blindly add things to a list of default extras without understanding the ramifications but who also care enough about keeping their “required dependencies” list honest and/or minimal to not just blindly add stuff to that list is a pretty small group.
My rationale for that is that if you don’t care about or understand the ramifications of “these extras will be installed by default”, which is one piece of dependency metadata, then it’s hard to imagine that you’re going to treat another piece of dependency metadata much differently.
As far as I can tell though, those are the actual trade offs, and like it’s silly for proponents of PEP 771 to pretend that there is absolutely no downsides to PEP 771 , it’s also silly for the detractors to pretend that all packages are currently choosing option 1 and thus our dependency trees are clean and pure or that option 3 doesn’t have almost all of the same downsides they’re worried about for PEP 771 and then some.