From f8b84c7c677e5fc647cb092097efc1c2d361c762 Mon Sep 17 00:00:00 2001 From: Tzu-ping Chung Date: Sun, 4 Apr 2021 22:01:59 +0800 Subject: [PATCH] Implement extra-ed requirement merging When a requirement is requested multiple times, some via a direct URL ("req @ URL") and some not but with extras ("req[extra] VERSION"), the resolver previous could not correctly find "req[extra]" if "req" is available in an index. This additional logic makes the resolver, when encountering a requirement with identifier "req[extra]", to also look for explicit candidates listed under "req", and add them as found matches for "req[extra]". --- news/8785.bugfix.rst | 4 ++++ .../resolution/resolvelib/factory.py | 22 +++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 news/8785.bugfix.rst diff --git a/news/8785.bugfix.rst b/news/8785.bugfix.rst new file mode 100644 index 00000000000..b84d8d8d58e --- /dev/null +++ b/news/8785.bugfix.rst @@ -0,0 +1,4 @@ +New resolver: When a requirement is requested both via a direct URL +(``req @ URL``) and via version specifier with extras (``req[extra]``), the +resolver will now be able to use the URL to correctly resolve the requirement +with extras. diff --git a/src/pip/_internal/resolution/resolvelib/factory.py b/src/pip/_internal/resolution/resolvelib/factory.py index 6cada5be038..a35f993cd7f 100644 --- a/src/pip/_internal/resolution/resolvelib/factory.py +++ b/src/pip/_internal/resolution/resolvelib/factory.py @@ -16,6 +16,10 @@ cast, ) +from pip._vendor.packaging.requirements import ( + InvalidRequirement, + Requirement as PackagingRequirement, +) from pip._vendor.packaging.specifiers import SpecifierSet from pip._vendor.packaging.utils import NormalizedName, canonicalize_name from pip._vendor.pkg_resources import Distribution @@ -296,6 +300,24 @@ def find_candidates( if ireq is not None: ireqs.append(ireq) + # If the current identifier contains extras, also add explicit + # candidates from entries from extra-less identifier. + try: + identifier_req = PackagingRequirement(identifier) + except InvalidRequirement: + base_identifier = None + extras = frozenset() + else: + base_identifier = identifier_req.name + extras = frozenset(identifier_req.extras) + if base_identifier and base_identifier in requirements: + for req in requirements[base_identifier]: + base_cand, _ = req.get_candidate_lookup() + if base_cand is None: + continue + candidate = self._make_extras_candidate(base_cand, extras) + explicit_candidates.add(candidate) + # If none of the requirements want an explicit candidate, we can ask # the finder for candidates. if not explicit_candidates: