Skip to content

Add support for pip install --only-deps.#13895

Draft
shoeffner wants to merge 3 commits intopypa:mainfrom
shoeffner:only-deps
Draft

Add support for pip install --only-deps.#13895
shoeffner wants to merge 3 commits intopypa:mainfrom
shoeffner:only-deps

Conversation

@shoeffner
Copy link
Copy Markdown

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 #11440.

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)
@shoeffner
Copy link
Copy Markdown
Author

Okay, looks like the legacy resolver requires a similar treatment or has to throw an error. I'll see what I can do :-)

@notatallshaw
Copy link
Copy Markdown
Member

notatallshaw commented Apr 7, 2026

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.

@ichard26

This comment was marked as duplicate.

@shoeffner
Copy link
Copy Markdown
Author

shoeffner commented Apr 8, 2026

The main failure reason is that I missed the fact that the options-object does not always have the proper values (i.e., options.only_dependencies is not available unless specified). To make it work, I first considered getattr(options, 'only_dependencies', False), but after sleeping over it, the cleaner solution seems to be to also add the option to the commands download, lock, and wheel.

While the scope of the issue was to provide --only-deps for install, I believe adding it to the related commands is better than any workaround which would be replaced in the future again.

I will do that today after work.

Copy link
Copy Markdown
Member

@sbidoul sbidoul left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised --only-deps would influence building. In my mind building is orthogonal to that option.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, I'll double-check this, this could be an artifact of my trial-and-error period.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder what should happen if a transitive dependency is also a user requested requirement. Need to think about it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, (note to self), check that InstallRequirement.user_supplied is the same as user_requested here.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "easy way out" would be to do it just as I implemented it here and document this behavior

That sounds reasonable.


# Set up the build isolation, if this requirement should be isolated
if build_isolation:
if build_isolation and not only_dependencies:
Copy link
Copy Markdown
Member

@sbidoul sbidoul Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@shoeffner
Copy link
Copy Markdown
Author

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.
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.

@shoeffner shoeffner marked this pull request as draft April 8, 2026 20:24
default=False,
help=(
"Install only package dependencies, not the package itself. "
"If you specify an optional dependency group such as [doc], "
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"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.

@sbidoul
Copy link
Copy Markdown
Member

sbidoul commented Apr 9, 2026

also add the option to the commands download, lock, and wheel.

Absolutely, that has to be done at the same time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants