Add support for pip install --only-deps.#13895
Conversation
This commit follows the agreed interface in [1]:
pip install --only-deps . # project.dependencies
pip install --only-deps .[doc] # project.dependencies and project.optional-dependencies
If --only-deps or its longform --only-dependencies is provided, neither the
explicitly requested packages nor the build dependencies are installed, instead
only the "runtime" dependencies are used.
--only-deps and --no-deps are mutually exclusive and pip will refuse to install
anything if both are specified.
Because --only-deps does not install build dependencies, no implicit or dynamic
dependencies can be managed this way, that means a build tool cannot
automatically install more dependencies by executing additional code.
First part of pypa#11440.
[1]: pypa#11440 (comment)
|
Okay, looks like the legacy resolver requires a similar treatment or has to throw an error. I'll see what I can do :-) |
|
FYI, it's completely fine to ban the user from providing only deps and use legacy resolver, if that makes things simpler. Recently there is movement on dropping the legacy resolver. |
This comment was marked as duplicate.
This comment was marked as duplicate.
|
The main failure reason is that I missed the fact that the options-object does not always have the proper values (i.e., While the scope of the issue was to provide I will do that today after work. |
sbidoul
left a comment
There was a problem hiding this comment.
Thanks for working on this!
A couple of comments/questions.
| self.req.editable_sanity_check() | ||
| # Check if the current environment provides build dependencies | ||
| if check_build_deps: | ||
| if check_build_deps and not only_dependencies: |
There was a problem hiding this comment.
I'm surprised --only-deps would influence building. In my mind building is orthogonal to that option.
There was a problem hiding this comment.
Hm, I'll double-check this, this could be an artifact of my trial-and-error period.
There was a problem hiding this comment.
This particular check is an optimization: if we do not need to build (because we only install the dependencies), we do not need to check if the build requirements are provided by the current environment.
I believe in one of my earlier drafts the check failed due to some changes somewhere else which I reverted -- I imagine that the check_requirements originally included the build tools causing the check to fail...
I'll remove the second check as it doesn't have any relevance here and introduce a test to check that the build dependencies are installed.
What is more relevant is the check a few lines above:
if build_isolation and not only_dependencies:Here we prevent the build environment from being prepared if we only want to install the dependencies.
|
|
||
| if self.only_dependencies: | ||
| for requested in collected.user_requested: | ||
| req_set.requirements.pop(requested, None) |
There was a problem hiding this comment.
I wonder what should happen if a transitive dependency is also a user requested requirement. Need to think about it.
There was a problem hiding this comment.
Also, (note to self), check that InstallRequirement.user_supplied is the same as user_requested here.
There was a problem hiding this comment.
what should happen if a transitive dependency is also a user requested requirement
This is a very good questions, indeed! I discussed this briefly in the issue by mentioning that this would not install the relevant requirement (i.e., using --only-deps with a requirements.txt generated by pip freeze would behave in an unexpected way).
The "easy way out" would be to do it just as I implemented it here and document this behavior (plus, maybe issue a warning if user_requested contains more than one requirement and --only-deps is specified) -- I believe the main use-case for "--only-deps" would be installing from a pyproject.toml where you would usually only have one requested requirement (the project being developed on).
An alternative would be to issue a warning only if the requested is also a transitive dependency – which would be a more difficult bookkeeping task. Plus, we'd still have to decide whether to install the dependency or not.
There was a problem hiding this comment.
The "easy way out" would be to do it just as I implemented it here and document this behavior
That sounds reasonable.
…o resolver can be fix-up'd later
…quirement can be fixup'd later
|
|
||
| # Set up the build isolation, if this requirement should be isolated | ||
| if build_isolation: | ||
| if build_isolation and not only_dependencies: |
There was a problem hiding this comment.
This should not change either. If a build is necessary to compute the metadata, a proper isolated environment will be necessary.
If users do not want installation of build dependencies, they can use --no-build-isolation but that is orthogonal to the --only-deps option.
There was a problem hiding this comment.
I introduced this for two reasons:
- the (default) build-system does not work with the test setuptools version (0.9.8)
- "--only-deps" as thought of in the original issue implied that there'd also be an "--only-build-deps", implying that --only-deps would not install build dependencies -- and this seemed to be the best place to disable that (see also my comment above)
Regarding the first point: I will try to change this to a different build-system in the tests so we can remove the check here. (Tests should not require code changes in that sense, plus I already have a similar test setup for the --only-build-deps branch)
Regarding the second point: This would answer the question for an "--only-build-deps" flag, which will become redundant and not needed. That kind of resolves the conceptual problems I had with it anyways, which is a good thing.
There was a problem hiding this comment.
I don't quite understand the link being made between the two but admittedly the original issue had become very confusing. But --only-deps and --only-build-deps are really two completely independent features, and can be addressed separately.
I think this PR is on the right track. I have not tested it yet but if you remove the part that modifies the build process and only keep the filtering out of user-requested packages at the end of the resolution it should be correct.
|
I cannot reproduce the CI failures locally: FAILED tests/unit/metadata/test_metadata.py::test_trailing_slash_directory_metadata[/path/to/foo.egg-info] - importlib.metadata.MetadataNotFound: No package metadata was found. But they seem to be failing on the main branch as well. |
| default=False, | ||
| help=( | ||
| "Install only package dependencies, not the package itself. " | ||
| "If you specify an optional dependency group such as [doc], " |
There was a problem hiding this comment.
"optional dependency group" could be confusing as it combines "optional dependencies" (extras) and "dependency groups" which are very different things. Maybe it's better to just say this option causes pip to install dependencies of the provided requirements and not the requirements themselves?
We can refine the help text later, when all open questions are resolved, though.
This makes me think it is worth testing how --only-deps works in combination with --group, if that makes sense at all.
Absolutely, that has to be done at the same time. |
This commit follows the agreed interface in 1:
If --only-deps or its longform --only-dependencies is provided, neither the explicitly requested packages nor the build dependencies are installed, instead only the "runtime" dependencies are used.
--only-deps and --no-deps are mutually exclusive and pip will refuse to install anything if both are specified.
Because --only-deps does not install build dependencies, no implicit or dynamic dependencies can be managed this way, that means a build tool cannot automatically install more dependencies by executing additional code.
First part of #11440.