@@ -173,6 +173,31 @@ def _add_to_criteria(self, criteria, requirement, parent):
173
173
raise RequirementsConflicted (criterion )
174
174
criteria [identifier ] = criterion
175
175
176
+ def _remove_information_from_criteria (self , criteria , parents ):
177
+ """Remove information from parents of criteria.
178
+
179
+ Concretely, removes all values from each criterion's ``information``
180
+ field that have one of ``parents`` as provider of the requirement.
181
+
182
+ :param criteria: The criteria to update.
183
+ :param parents: Identifiers for which to remove information from all criteria.
184
+ """
185
+ if not parents :
186
+ return
187
+ for key , criterion in criteria .items ():
188
+ criteria [key ] = Criterion (
189
+ criterion .candidates ,
190
+ [
191
+ information
192
+ for information in criterion .information
193
+ if (
194
+ information [1 ] is None
195
+ or self ._p .identify (information [1 ]) not in parents
196
+ )
197
+ ],
198
+ criterion .incompatibilities ,
199
+ )
200
+
176
201
def _get_preference (self , name ):
177
202
return self ._p .get_preference (
178
203
identifier = name ,
@@ -212,6 +237,7 @@ def _attempt_to_pin_criterion(self, name):
212
237
try :
213
238
criteria = self ._get_updated_criteria (candidate )
214
239
except RequirementsConflicted as e :
240
+ self ._r .rejecting_candidate (e .criterion , candidate )
215
241
causes .append (e .criterion )
216
242
continue
217
243
@@ -281,8 +307,6 @@ def _backtrack(self):
281
307
# Also mark the newly known incompatibility.
282
308
incompatibilities_from_broken .append ((name , [candidate ]))
283
309
284
- self ._r .backtracking (candidate = candidate )
285
-
286
310
# Create a new state from the last known-to-work one, and apply
287
311
# the previously gathered incompatibility information.
288
312
def _patch_criteria ():
@@ -368,6 +392,11 @@ def resolve(self, requirements, max_rounds):
368
392
self ._r .ending (state = self .state )
369
393
return self .state
370
394
395
+ # keep track of satisfied names to calculate diff after pinning
396
+ satisfied_names = set (self .state .criteria .keys ()) - set (
397
+ unsatisfied_names
398
+ )
399
+
371
400
# Choose the most preferred unpinned criterion to try.
372
401
name = min (unsatisfied_names , key = self ._get_preference )
373
402
failure_causes = self ._attempt_to_pin_criterion (name )
@@ -384,6 +413,17 @@ def resolve(self, requirements, max_rounds):
384
413
if not success :
385
414
raise ResolutionImpossible (self .state .backtrack_causes )
386
415
else :
416
+ # discard as information sources any invalidated names
417
+ # (unsatisfied names that were previously satisfied)
418
+ newly_unsatisfied_names = {
419
+ key
420
+ for key , criterion in self .state .criteria .items ()
421
+ if key in satisfied_names
422
+ and not self ._is_current_pin_satisfying (key , criterion )
423
+ }
424
+ self ._remove_information_from_criteria (
425
+ self .state .criteria , newly_unsatisfied_names
426
+ )
387
427
# Pinning was successful. Push a new state to do another pin.
388
428
self ._push_new_state ()
389
429
0 commit comments