From c26c00b2ac8cb39601c83ee447de7557ec52876c Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Mon, 6 Apr 2026 01:49:50 +0100 Subject: [PATCH] Allow editable installs to satisfy direct-URL dependencies When a transitive dependency referenced a package by direct URL and the same package was also requested as editable, the resolver saw two distinct candidate types for the same link and raised `ResolutionImpossible`. An editable install subsumes a non-editable direct URL request to the same location, so `_make_base_candidate_from_link` now returns the cached `EditableCandidate` in that case. --- news/10216.bugfix.rst | 2 + .../resolution/resolvelib/factory.py | 2 + tests/functional/test_new_resolver.py | 47 +++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 news/10216.bugfix.rst diff --git a/news/10216.bugfix.rst b/news/10216.bugfix.rst new file mode 100644 index 00000000000..7175b77c3d5 --- /dev/null +++ b/news/10216.bugfix.rst @@ -0,0 +1,2 @@ +Allow installing a package as editable alongside another package that +depends on it, via a direct URL reference to the same location. diff --git a/src/pip/_internal/resolution/resolvelib/factory.py b/src/pip/_internal/resolution/resolvelib/factory.py index ede3e6b2b94..c210f673377 100644 --- a/src/pip/_internal/resolution/resolvelib/factory.py +++ b/src/pip/_internal/resolution/resolvelib/factory.py @@ -219,6 +219,8 @@ def _make_base_candidate_from_link( self._build_failures[link] = e return None + return self._editable_candidate_cache[link] + elif link in self._editable_candidate_cache: return self._editable_candidate_cache[link] else: if link not in self._link_candidate_cache: diff --git a/tests/functional/test_new_resolver.py b/tests/functional/test_new_resolver.py index b5b26128037..ecd1b7f4578 100644 --- a/tests/functional/test_new_resolver.py +++ b/tests/functional/test_new_resolver.py @@ -328,6 +328,53 @@ def test_new_resolver_installs_editable(script: PipTestEnvironment) -> None: script.assert_installed_editable("dep") +def test_new_resolver_editable_satisfies_direct_url_dep( + script: PipTestEnvironment, +) -> None: + """Regression test for https://github.com/pypa/pip/issues/10216. + + Installing ``-e A -e B`` used to fail with ``ResolutionImpossible`` when ``A`` + depends on ``B`` via a direct URL (PEP 440) reference pointing at the same + location as the editable ``B``: + + > Cannot install package-a and package-b because these package versions have + > conflicting dependencies. + + This was because the resolver created both an ``EditableCandidate`` (from the + user-supplied ``-e B``) and a ``LinkCandidate`` (from ``A``'s transitive + direct-URL dependency) for the same link, and treated them as conflicting. + """ + dep_path = create_test_package_with_setup(script, name="dep", version="0.1.0") + dep_url = dep_path.as_uri() + base_path = script.scratch_path / "base" + base_path.mkdir() + base_path.joinpath("setup.py").write_text( + textwrap.dedent( + f""" + from setuptools import setup + setup( + name="base", + version="0.1.0", + install_requires=["dep @ {dep_url}"], + ) + """ + ) + ) + script.pip( + "install", + "--no-build-isolation", + "--no-cache-dir", + "--no-index", + "-e", + base_path, + "-e", + dep_path, + ) + script.assert_installed(base="0.1.0", dep="0.1.0") + script.assert_installed_editable("base") + script.assert_installed_editable("dep") + + @pytest.mark.parametrize( "requires_python, ignore_requires_python, dep_version", [