@@ -126,6 +126,15 @@ def force_reinstall(self):
126
126
# type: () -> bool
127
127
return self ._force_reinstall
128
128
129
+ def _fail_if_link_is_unsupported_wheel (self , link : Link ) -> None :
130
+ if not link .is_wheel :
131
+ return
132
+ wheel = Wheel (link .filename )
133
+ if wheel .supported (self ._finder .target_python .get_tags ()):
134
+ return
135
+ msg = f"{ link .filename } is not a supported wheel on this platform."
136
+ raise UnsupportedWheel (msg )
137
+
129
138
def _make_extras_candidate (self , base , extras ):
130
139
# type: (BaseCandidate, FrozenSet[str]) -> ExtrasCandidate
131
140
cache_key = (id (base ), extras )
@@ -278,6 +287,62 @@ def iter_index_candidate_infos():
278
287
incompatible_ids ,
279
288
)
280
289
290
+ def _iter_explicit_candidates_from_base (
291
+ self ,
292
+ identifier : str ,
293
+ requirements : Mapping [str , Iterator [Requirement ]],
294
+ ) -> Iterator [Candidate ]:
295
+ """Produce explicit candidates from the base given an extra-ed package.
296
+
297
+ :param identifier: A dependency identifier in the resolver. If this
298
+ points to an extra-ed package (e.g. ``package[foo]``), explcit
299
+ candidates known for the base package (identified by ``package``)
300
+ are returned. No candidates are generated if this does not point to
301
+ an extra-ed Python package.
302
+ :param requirements: A mapping of requirements known to the resolver,
303
+ as passed in by ``find_matches()``.
304
+ """
305
+ try :
306
+ pkg_req = PackagingRequirement (identifier )
307
+ except InvalidRequirement :
308
+ return
309
+ base_identifier = pkg_req .name
310
+ if base_identifier not in requirements :
311
+ return
312
+ extras = frozenset (pkg_req .extras )
313
+ for req in requirements [base_identifier ]:
314
+ lookup_cand , _ = req .get_candidate_lookup ()
315
+ if lookup_cand is None : # Not explicit.
316
+ continue
317
+ # We've stripped extras from the identifier, and should always
318
+ # get a BaseCandidate here, unless there's a bug elsewhere.
319
+ base_cand = as_base_candidate (lookup_cand )
320
+ assert base_cand is not None
321
+ yield self ._make_extras_candidate (base_cand , extras )
322
+
323
+ def _iter_explicit_candidates_from_constraints (
324
+ self ,
325
+ identifier : str ,
326
+ constraint : Constraint ,
327
+ template : InstallRequirement ,
328
+ ) -> Iterator [Candidate ]:
329
+ """Produce explicit candidates from constraints.
330
+
331
+ This creates "fake" InstallRequirement objects that are basically clones
332
+ of what "should" be the template, but with original_link set to link.
333
+ """
334
+ for link in constraint .links :
335
+ # If we're constrained to install a wheel incompatible with the
336
+ # target architecture, no candidates will ever be valid.
337
+ self ._fail_if_link_is_unsupported_wheel (link )
338
+ yield self ._make_candidate_from_link (
339
+ link ,
340
+ extras = frozenset (),
341
+ template = install_req_from_link_and_ireq (link , template ),
342
+ name = identifier ,
343
+ version = None ,
344
+ )
345
+
281
346
def find_candidates (
282
347
self ,
283
348
identifier : str ,
@@ -291,76 +356,49 @@ def find_candidates(
291
356
# can be made quicker by comparing only the id() values.
292
357
incompat_ids = {id (c ) for c in incompatibilities .get (identifier , ())}
293
358
359
+ # Collect basic lookup information from the requirements.
294
360
explicit_candidates = set () # type: Set[Candidate]
295
361
ireqs = [] # type: List[InstallRequirement]
296
362
for req in requirements [identifier ]:
297
363
cand , ireq = req .get_candidate_lookup ()
298
- if cand is not None and id (cand ) not in incompat_ids :
364
+ if cand is not None and id (cand ):
299
365
explicit_candidates .add (cand )
300
366
if ireq is not None :
301
367
ireqs .append (ireq )
302
368
303
- for link in constraint .links :
304
- if not ireqs :
305
- # If we hit this condition, then we cannot construct a candidate.
306
- # However, if we hit this condition, then none of the requirements
307
- # provided an ireq, so they must have provided an explicit candidate.
308
- # In that case, either the candidate matches, in which case this loop
309
- # doesn't need to do anything, or it doesn't, in which case there's
310
- # nothing this loop can do to recover.
311
- break
312
- if link .is_wheel :
313
- wheel = Wheel (link .filename )
314
- # Check whether the provided wheel is compatible with the target
315
- # platform.
316
- if not wheel .supported (self ._finder .target_python .get_tags ()):
317
- # We are constrained to install a wheel that is incompatible with
318
- # the target architecture, so there are no valid candidates.
319
- # Return early, with no candidates.
320
- return ()
321
- # Create a "fake" InstallRequirement that's basically a clone of
322
- # what "should" be the template, but with original_link set to link.
323
- # Using the given requirement is necessary for preserving hash
324
- # requirements, but without the original_link, direct_url.json
325
- # won't be created.
326
- ireq = install_req_from_link_and_ireq (link , ireqs [0 ])
327
- candidate = self ._make_candidate_from_link (
328
- link ,
329
- extras = frozenset (),
330
- template = ireq ,
331
- name = canonicalize_name (ireq .name ) if ireq .name else None ,
332
- version = None ,
333
- )
334
- if candidate is None :
335
- # _make_candidate_from_link returns None if the wheel fails to build.
336
- # We are constrained to install this wheel, so there are no valid
337
- # candidates.
338
- # Return early, with no candidates.
339
- return ()
340
-
341
- explicit_candidates .add (candidate )
369
+ # If the current identifier contains extras, add explicit candidates
370
+ # from entries from extra-less identifier.
371
+ explicit_candidates = explicit_candidates .union (
372
+ self ._iter_explicit_candidates_from_base (identifier , requirements ),
373
+ )
342
374
343
- # If the current identifier contains extras, also add explicit
344
- # candidates from entries from extra-less identifier.
345
- try :
346
- identifier_req = PackagingRequirement (identifier )
347
- except InvalidRequirement :
348
- base_identifier = None
349
- extras : FrozenSet [str ] = frozenset ()
375
+ # Add explicit candidates from constraints. We only do this if there are
376
+ # kown ireqs, which represent requirements not already explicit. If
377
+ # there are no ireqs, we're constraining already-explicit requirements,
378
+ # which is covered by the else block.
379
+ if ireqs :
380
+ try :
381
+ explicit_candidates .update (
382
+ self ._iter_explicit_candidates_from_constraints (
383
+ identifier ,
384
+ constraint ,
385
+ template = ireqs [0 ],
386
+ ),
387
+ )
388
+ except UnsupportedWheel :
389
+ return ()
350
390
else :
351
- base_identifier = identifier_req .name
352
- extras = frozenset (identifier_req .extras )
353
- if base_identifier and base_identifier in requirements :
354
- for req in requirements [base_identifier ]:
355
- lookup_cand , _ = req .get_candidate_lookup ()
356
- if lookup_cand is None : # Not explicit.
357
- continue
358
- # We've stripped extras from the identifier, and should always
359
- # get a BaseCandidate here, unless there's a bug elsewhere.
360
- base_cand = as_base_candidate (lookup_cand )
361
- assert base_cand is not None
362
- candidate = self ._make_extras_candidate (base_cand , extras )
363
- explicit_candidates .add (candidate )
391
+ explicit_candidates = {
392
+ candidate
393
+ for candidate in explicit_candidates
394
+ if constraint .is_satisfied_by (candidate )
395
+ }
396
+
397
+ explicit_candidates = {
398
+ candidate
399
+ for candidate in explicit_candidates
400
+ if id (candidate ) not in incompat_ids
401
+ }
364
402
365
403
# If none of the requirements want an explicit candidate, we can ask
366
404
# the finder for candidates.
@@ -391,13 +429,7 @@ def make_requirement_from_install_req(self, ireq, requested_extras):
391
429
return None
392
430
if not ireq .link :
393
431
return SpecifierRequirement (ireq )
394
- if ireq .link .is_wheel :
395
- wheel = Wheel (ireq .link .filename )
396
- if not wheel .supported (self ._finder .target_python .get_tags ()):
397
- msg = "{} is not a supported wheel on this platform." .format (
398
- wheel .filename ,
399
- )
400
- raise UnsupportedWheel (msg )
432
+ self ._fail_if_link_is_unsupported_wheel (ireq .link )
401
433
cand = self ._make_candidate_from_link (
402
434
ireq .link ,
403
435
extras = frozenset (ireq .extras ),
0 commit comments