Skip to content

Commit 5f8f40e

Browse files
committed
refinements
1 parent 937d8f0 commit 5f8f40e

File tree

4 files changed

+46
-28
lines changed

4 files changed

+46
-28
lines changed

src/pip/_internal/req/constructors.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,8 @@ def install_req_without(
520520
req.specifier = SpecifierSet(prereleases=req.specifier.prereleases)
521521
return InstallRequirement(
522522
req=req,
523-
comes_from=ireq.comes_from,
523+
# TODO: document this!!!!
524+
comes_from=ireq,
524525
editable=ireq.editable,
525526
link=ireq.link,
526527
markers=ireq.markers,

src/pip/_internal/resolution/resolvelib/candidates.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,6 @@ def _prepare(self) -> BaseDistribution:
237237
self._check_metadata_consistency(dist)
238238
return dist
239239

240-
# TODO: add Explicit dependency on self to extra reqs can benefit from it?
241240
def iter_dependencies(self, with_requires: bool) -> Iterable[Optional[Requirement]]:
242241
requires = self.dist.iter_dependencies() if with_requires else ()
243242
for r in requires:
@@ -428,9 +427,19 @@ def __init__(
428427
self,
429428
base: BaseCandidate,
430429
extras: FrozenSet[str],
430+
ireq: Optional[InstallRequirement] = None,
431431
) -> None:
432+
"""
433+
:param ireq: the InstallRequirement that led to this candidate, if it
434+
differs from the base's InstallRequirement. This will often be the
435+
case in the sense that this candidate's requirement has the extras
436+
while the base's does not. Unlike the InstallRequirement backed
437+
candidates, this requirement is used solely for reporting purposes,
438+
it does not do any leg work.
439+
"""
432440
self.base = base
433441
self.extras = extras
442+
self._ireq = ireq
434443

435444
def __str__(self) -> str:
436445
name, rest = str(self.base).split(" ", 1)
@@ -504,7 +513,9 @@ def iter_dependencies(self, with_requires: bool) -> Iterable[Optional[Requiremen
504513

505514
for r in self.base.dist.iter_dependencies(valid_extras):
506515
yield from factory.make_requirements_from_spec(
507-
str(r), self.base._ireq, valid_extras
516+
str(r),
517+
self._ireq if self._ireq is not None else self.base._ireq,
518+
valid_extras,
508519
)
509520

510521
def get_install_requirement(self) -> Optional[InstallRequirement]:

src/pip/_internal/resolution/resolvelib/factory.py

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -138,13 +138,16 @@ def _fail_if_link_is_unsupported_wheel(self, link: Link) -> None:
138138
raise UnsupportedWheel(msg)
139139

140140
def _make_extras_candidate(
141-
self, base: BaseCandidate, extras: FrozenSet[str]
141+
self,
142+
base: BaseCandidate,
143+
extras: FrozenSet[str],
144+
ireq: Optional[InstallRequirement] = None,
142145
) -> ExtrasCandidate:
143146
cache_key = (id(base), extras)
144147
try:
145148
candidate = self._extras_candidate_cache[cache_key]
146149
except KeyError:
147-
candidate = ExtrasCandidate(base, extras)
150+
candidate = ExtrasCandidate(base, extras, ireq=ireq)
148151
self._extras_candidate_cache[cache_key] = candidate
149152
return candidate
150153

@@ -161,7 +164,7 @@ def _make_candidate_from_dist(
161164
self._installed_candidate_cache[dist.canonical_name] = base
162165
if not extras:
163166
return base
164-
return self._make_extras_candidate(base, extras)
167+
return self._make_extras_candidate(base, extras, ireq=template)
165168

166169
def _make_candidate_from_link(
167170
self,
@@ -223,7 +226,7 @@ def _make_candidate_from_link(
223226

224227
if not extras:
225228
return base
226-
return self._make_extras_candidate(base, extras)
229+
return self._make_extras_candidate(base, extras, ireq=template)
227230

228231
def _iter_found_candidates(
229232
self,
@@ -389,16 +392,17 @@ def find_candidates(
389392
# candidates from entries from extra-less identifier.
390393
with contextlib.suppress(InvalidRequirement):
391394
parsed_requirement = get_requirement(identifier)
392-
explicit_candidates.update(
393-
self._iter_explicit_candidates_from_base(
394-
requirements.get(parsed_requirement.name, ()),
395-
frozenset(parsed_requirement.extras),
396-
),
397-
)
398-
for req in requirements.get(parsed_requirement.name, []):
399-
_, ireq = req.get_candidate_lookup()
400-
if ireq is not None:
401-
ireqs.append(ireq)
395+
if parsed_requirement.name != identifier:
396+
explicit_candidates.update(
397+
self._iter_explicit_candidates_from_base(
398+
requirements.get(parsed_requirement.name, ()),
399+
frozenset(parsed_requirement.extras),
400+
),
401+
)
402+
for req in requirements.get(parsed_requirement.name, []):
403+
_, ireq = req.get_candidate_lookup()
404+
if ireq is not None:
405+
ireqs.append(ireq)
402406

403407
# Add explicit candidates from constraints. We only do this if there are
404408
# known ireqs, which represent requirements not already explicit. If
@@ -444,7 +448,6 @@ def find_candidates(
444448
def _make_requirements_from_install_req(
445449
self, ireq: InstallRequirement, requested_extras: Iterable[str]
446450
) -> list[Requirement]:
447-
# TODO: docstring
448451
"""
449452
Returns requirement objects associated with the given InstallRequirement. In
450453
most cases this will be a single object but the following special cases exist:
@@ -454,7 +457,6 @@ def _make_requirements_from_install_req(
454457
extra. This allows centralized constraint handling for the base,
455458
resulting in fewer candidate rejections.
456459
"""
457-
# TODO: implement -> split in base req with constraint and extra req without
458460
if not ireq.match_markers(requested_extras):
459461
logger.info(
460462
"Ignoring %s: markers '%s' don't match your environment",
@@ -466,6 +468,10 @@ def _make_requirements_from_install_req(
466468
if ireq.extras and ireq.req.specifier:
467469
return [
468470
SpecifierRequirement(ireq, drop_extras=True),
471+
# TODO: put this all the way at the back to have even fewer candidates?
472+
# TODO: probably best to keep specifier as it makes the report
473+
# slightly more readable -> should also update SpecReq constructor
474+
# and req.constructors.install_req_without
469475
SpecifierRequirement(ireq, drop_specifier=True),
470476
]
471477
else:

src/pip/_internal/resolution/resolvelib/requirements.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ def is_satisfied_by(self, candidate: Candidate) -> bool:
4040
return candidate == self.candidate
4141

4242

43-
# TODO: add some comments
4443
class SpecifierRequirement(Requirement):
4544
# TODO: document additional options
4645
def __init__(
@@ -52,15 +51,17 @@ def __init__(
5251
) -> None:
5352
assert ireq.link is None, "This is a link, not a specifier"
5453
self._drop_extras: bool = drop_extras
55-
self._original_extras = frozenset(ireq.extras)
56-
# TODO: name
57-
self._original_req = ireq.req
58-
self._ireq = install_req_without(
59-
ireq, without_extras=self._drop_extras, without_specifier=drop_specifier
54+
self._extras = frozenset(ireq.extras if not drop_extras else ())
55+
self._ireq = (
56+
ireq
57+
if not drop_extras and not drop_specifier
58+
else install_req_without(
59+
ireq, without_extras=self._drop_extras, without_specifier=drop_specifier
60+
)
6061
)
6162

6263
def __str__(self) -> str:
63-
return str(self._original_req)
64+
return str(self._ireq)
6465

6566
def __repr__(self) -> str:
6667
return "{class_name}({requirement!r})".format(
@@ -73,12 +74,11 @@ def project_name(self) -> NormalizedName:
7374
assert self._ireq.req, "Specifier-backed ireq is always PEP 508"
7475
return canonicalize_name(self._ireq.req.name)
7576

76-
# TODO: make sure this can still be identified for error reporting purposes
7777
@property
7878
def name(self) -> str:
7979
return format_name(
8080
self.project_name,
81-
self._original_extras if not self._drop_extras else frozenset(),
81+
self._extras,
8282
)
8383

8484
def format_for_error(self) -> str:

0 commit comments

Comments
 (0)