1use std::borrow::Cow;
4use std::cmp::Ordering;
5use std::collections::{BTreeMap, BTreeSet, VecDeque};
6use std::fmt::{Display, Formatter};
7use std::ops::Bound;
8use std::sync::Arc;
9use std::time::Instant;
10use std::{mem, thread};
11
12use futures::{FutureExt, StreamExt};
13use itertools::Itertools;
14use papaya::{HashMap, ResizeMode};
15use pubgrub::{Id, IncompId, Incompatibility, Kind, Ranges, State, Term};
16use rustc_hash::{FxHashMap, FxHashSet};
17use tokio::sync::mpsc::{self, Receiver};
18use tokio::sync::oneshot;
19use tokio_stream::wrappers::ReceiverStream;
20use tracing::{Level, debug, info, instrument, trace, warn};
21
22use uv_configuration::{Constraints, Excludes, Overrides};
23use uv_distribution::{ArchiveMetadata, DistributionDatabase};
24use uv_distribution_types::{
25 BuiltDist, CompatibleDist, DerivationChain, Dist, DistErrorKind, Identifier, IncompatibleDist,
26 IncompatibleSource, IncompatibleWheel, IndexCapabilities, IndexLocations, IndexMetadata,
27 IndexUrl, InstalledDist, Name, PythonRequirementKind, RemoteSource, Requirement,
28 RequiresPython, ResolvedDist, ResolvedDistRef, SourceDist, VersionOrUrlRef, implied_markers,
29};
30use uv_git::GitResolver;
31use uv_normalize::PackageName;
32use uv_pep440::{MIN_VERSION, Version, VersionSpecifiers, release_specifiers_to_ranges};
33use uv_pep508::{
34 MarkerEnvironment, MarkerExpression, MarkerOperator, MarkerTree, MarkerValueString,
35};
36use uv_platform_tags::{IncompatibleTag, Tags};
37use uv_pypi_types::{ConflictItem, ConflictItemRef, ConflictKindRef, Conflicts, VerbatimParsedUrl};
38use uv_resolver_types::PackageNodeKind;
39use uv_static::EnvVars;
40use uv_torch::TorchStrategy;
41use uv_types::{BuildContext, HashStrategy, InstalledPackagesProvider};
42use uv_warnings::warn_user_once;
43
44use crate::candidate_selector::{Candidate, CandidateDist, CandidateSelector};
45use crate::dependency_provider::UvDependencyProvider;
46use crate::error::{NoSolutionError, ResolveError, derivation_tree_packages};
47use crate::fork_indexes::ForkIndexes;
48use crate::fork_urls::ForkUrls;
49use crate::manifest::Manifest;
50use crate::pins::FilePins;
51use crate::preferences::{PreferenceSource, Preferences};
52use crate::pubgrub::{
53 DependencySource, PubGrubDependency, PubGrubPackage, PubGrubPackageInner, PubGrubPriorities,
54 PubGrubPython, Range,
55};
56use crate::python_requirement::PythonRequirement;
57use crate::resolution::ResolverOutput;
58use crate::resolution_mode::ResolutionStrategy;
59pub(crate) use crate::resolver::availability::{
60 ResolverVersion, UnavailableErrorChain, UnavailablePackage, UnavailableReason,
61 UnavailableVersion, UnsatisfiableRequirement,
62};
63use crate::resolver::batch_prefetch::BatchPrefetcher;
64use crate::resolver::derivation::DerivationChainBuilder;
65pub use crate::resolver::environment::ResolverEnvironment;
66use crate::resolver::environment::{
67 ForkingPossibility, fork_version_by_marker, fork_version_by_python_requirement,
68};
69pub(crate) use crate::resolver::fork_map::{ForkMap, ForkSet};
70pub use crate::resolver::index::InMemoryIndex;
71use crate::resolver::indexes::Indexes;
72use crate::resolver::package_source::PackageSource;
73pub use crate::resolver::provider::{
74 DefaultResolverProvider, MetadataResponse, PackageVersionsResult, ResolverProvider,
75 VersionsResponse, WheelMetadataResult,
76};
77pub use crate::resolver::reporter::Reporter;
78pub(crate) use crate::resolver::requests::RegisteredMetadata;
79use crate::resolver::requests::{MetadataRequest, MetadataRequests};
80use crate::resolver::requirements::{RequirementContext, RequirementExpander};
81use crate::resolver::system::SystemDependency;
82pub(crate) use crate::resolver::urls::Urls;
83use crate::universal_marker::UniversalMarker;
84use crate::yanks::AllowedYanks;
85use crate::{DependencyMode, Exclusions, FlatIndex, Options, ResolutionMode, VersionMap, marker};
86pub(crate) use provider::MetadataUnavailable;
87pub(crate) use resolution::{
88 Resolution, ResolutionDependencyEdge, ResolutionNode, ResolutionPackage, ResolvedFork,
89 SelectedDistribution,
90};
91use uv_configuration::ForkStrategy;
92
93mod availability;
94mod batch_prefetch;
95mod derivation;
96mod environment;
97mod fork_map;
98mod index;
99mod indexes;
100mod package_source;
101mod provider;
102mod reporter;
103mod requests;
104mod requirements;
105mod resolution;
106mod system;
107mod urls;
108
109const CONFLICT_THRESHOLD: usize = 5;
111
112pub struct Resolver<Provider: ResolverProvider, InstalledPackages: InstalledPackagesProvider> {
113 state: ResolverState<InstalledPackages>,
114 provider: Provider,
115}
116
117struct ResolverState<InstalledPackages: InstalledPackagesProvider> {
120 project: Option<PackageName>,
121 requirements: Vec<Requirement>,
122 constraints: Constraints,
123 overrides: Overrides,
124 excludes: Excludes,
125 preferences: Preferences,
126 git: GitResolver,
127 capabilities: IndexCapabilities,
128 locations: IndexLocations,
129 exclusions: Exclusions,
130 urls: Urls,
131 indexes: Indexes,
132 dependency_mode: DependencyMode,
133 hasher: HashStrategy,
134 env: ResolverEnvironment,
135 current_environment: MarkerEnvironment,
137 tags: Option<Tags>,
138 python_requirement: PythonRequirement,
139 conflicts: Conflicts,
140 workspace_members: BTreeSet<PackageName>,
141 selector: CandidateSelector,
142 index: InMemoryIndex,
143 installed_packages: InstalledPackages,
144 unavailable_packages: Box<HashMap<PackageName, UnavailablePackage>>,
147 incomplete_packages: Box<HashMap<PackageName, HashMap<Version, MetadataUnavailable>>>,
149 options: Options,
151 reporter: Option<Arc<dyn Reporter>>,
153}
154
155impl<'a, Context: BuildContext, InstalledPackages: InstalledPackagesProvider>
156 Resolver<DefaultResolverProvider<'a, Context>, InstalledPackages>
157{
158 pub fn new(
177 manifest: Manifest,
178 options: Options,
179 python_requirement: &'a PythonRequirement,
180 env: ResolverEnvironment,
181 current_environment: &MarkerEnvironment,
182 conflicts: Conflicts,
183 tags: Option<&'a Tags>,
184 flat_index: &'a FlatIndex,
185 index: &'a InMemoryIndex,
186 hasher: &'a HashStrategy,
187 build_context: &'a Context,
188 installed_packages: InstalledPackages,
189 database: DistributionDatabase<'a, Context>,
190 ) -> Result<Self, ResolveError> {
191 let provider = DefaultResolverProvider::new(
192 database,
193 flat_index,
194 tags,
195 python_requirement.target(),
196 AllowedYanks::from_manifest(&manifest, &env, options.dependency_mode),
197 hasher,
198 options.exclude_newer.clone(),
199 build_context.locations(),
200 build_context.build_options(),
201 build_context.capabilities(),
202 options.minimum_libc_version,
203 );
204
205 Ok(Self::new_custom_io(
206 manifest,
207 options,
208 hasher,
209 env,
210 current_environment,
211 tags.cloned(),
212 python_requirement,
213 conflicts,
214 index,
215 build_context.git(),
216 build_context.capabilities(),
217 build_context.locations(),
218 provider,
219 installed_packages,
220 ))
221 }
222}
223
224impl<Provider: ResolverProvider, InstalledPackages: InstalledPackagesProvider>
225 Resolver<Provider, InstalledPackages>
226{
227 pub fn new_custom_io(
229 manifest: Manifest,
230 options: Options,
231 hasher: &HashStrategy,
232 env: ResolverEnvironment,
233 current_environment: &MarkerEnvironment,
234 tags: Option<Tags>,
235 python_requirement: &PythonRequirement,
236 conflicts: Conflicts,
237 index: &InMemoryIndex,
238 git: &GitResolver,
239 capabilities: &IndexCapabilities,
240 locations: &IndexLocations,
241 provider: Provider,
242 installed_packages: InstalledPackages,
243 ) -> Self {
244 let state = ResolverState {
245 index: index.clone(),
246 git: git.clone(),
247 capabilities: capabilities.clone(),
248 selector: CandidateSelector::for_resolution(&options, &manifest, &env),
249 dependency_mode: options.dependency_mode,
250 urls: Urls::from_manifest(&manifest, &env, git, options.dependency_mode),
251 indexes: Indexes::from_manifest(&manifest, &env, options.dependency_mode),
252 project: manifest.project,
253 workspace_members: manifest.workspace_members,
254 requirements: manifest.requirements,
255 constraints: manifest.constraints,
256 overrides: manifest.overrides,
257 excludes: manifest.excludes,
258 preferences: manifest.preferences,
259 exclusions: manifest.exclusions,
260 hasher: hasher.clone(),
261 locations: locations.clone(),
262 env,
263 current_environment: current_environment.clone(),
264 tags,
265 python_requirement: python_requirement.clone(),
266 conflicts,
267 installed_packages,
268 unavailable_packages: Box::default(),
269 incomplete_packages: Box::default(),
270 options,
271 reporter: None,
272 };
273 Self { state, provider }
274 }
275
276 #[must_use]
278 pub fn with_reporter(self, reporter: Arc<dyn Reporter>) -> Self {
279 Self {
280 state: ResolverState {
281 reporter: Some(reporter.clone()),
282 ..self.state
283 },
284 provider: self
285 .provider
286 .with_reporter(reporter.into_distribution_reporter()),
287 }
288 }
289
290 pub async fn resolve(self) -> Result<ResolverOutput, ResolveError> {
292 let state = Arc::new(self.state);
293 let provider = Arc::new(self.provider);
294
295 let (request_sink, request_stream) = mpsc::channel(300);
299 let requests = MetadataRequests::new(state.index.clone(), request_sink);
300
301 let requests_fut = state.clone().fetch(provider.clone(), request_stream).fuse();
303
304 let solver = state.clone();
306 let (tx, rx) = oneshot::channel();
307 thread::Builder::new()
308 .name("uv-resolver".into())
309 .spawn(move || {
310 let result = solver.solve(&requests);
311
312 let _ = tx.send(result);
314 })
315 .unwrap();
316
317 let resolve_fut = async move { rx.await.map_err(|_| ResolveError::ChannelClosed) };
318
319 let ((), resolution) = tokio::try_join!(requests_fut, resolve_fut)?;
321
322 state.on_complete();
323 resolution
324 }
325}
326
327impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackages> {
328 #[instrument(skip_all)]
329 fn solve(self: Arc<Self>, requests: &MetadataRequests) -> Result<ResolverOutput, ResolveError> {
330 debug!(
331 "Solving with installed Python version: {}",
332 self.python_requirement.exact()
333 );
334 debug!(
335 "Solving with target Python version: {}",
336 self.python_requirement.target()
337 );
338 if !self.options.exclude_newer.is_empty() {
339 debug!("Solving with exclude-newer: {}", self.options.exclude_newer);
340 }
341
342 let mut visited = FxHashSet::default();
343
344 let root = PubGrubPackage::from(PubGrubPackageInner::Root(self.project.clone()));
345 let pubgrub = State::init(root.clone(), MIN_VERSION.clone());
346 let prefetcher = BatchPrefetcher::new(self.capabilities.clone(), requests.clone());
347 let state = ForkState::new(
348 pubgrub,
349 self.env.clone(),
350 self.python_requirement.clone(),
351 prefetcher,
352 );
353 let mut preferences = self.preferences.clone();
354 let mut forked_states = self.env.initial_forked_states(state)?;
355
356 match (self.options.fork_strategy, self.options.resolution_mode) {
362 (ForkStrategy::Fewest, _) | (_, ResolutionMode::Lowest) => {
363 forked_states.sort_by(|a, b| cmp_requires_python(&a.env, &b.env).reverse());
364 }
365 (ForkStrategy::RequiresPython, _) => {
366 forked_states.sort_by(|a, b| cmp_requires_python(&a.env, &b.env));
367 }
368 }
369 let mut resolutions = vec![];
370
371 'FORK: while let Some(mut state) = forked_states.pop() {
372 if let Some(split) = state.env.end_user_fork_display() {
373 let requires_python = state.python_requirement.target();
374 debug!("Solving {split} (requires-python: {requires_python:?})");
375 }
376 let start = Instant::now();
377 loop {
378 let continuation = mem::take(&mut state.continuation);
379 let (highest_priority_pkg, initial_version) = match continuation {
380 ForkContinuation::SelectVersion { package } => (package, None),
381 ForkContinuation::UseVersion { package, version } => (package, Some(version)),
382 ForkContinuation::Propagate => {
383 let result = state.pubgrub.unit_propagation(state.next);
385 match result {
386 Err(err) => {
387 return Err(self.convert_no_solution_err(
389 err,
390 state.fork_urls,
391 state.fork_indexes,
392 &state.known_versions.0,
393 state.env,
394 self.current_environment.clone(),
395 &visited,
396 ));
397 }
398 Ok(conflicts) => {
399 for (affected, incompatibility) in conflicts {
400 state.record_conflict(affected, None, incompatibility);
403 }
404 }
405 }
406
407 if self.dependency_mode.is_transitive() {
409 Self::pre_visit(
410 state.pubgrub.partial_solution.prioritized_packages().map(
411 |(id, range)| (id, &state.pubgrub.package_store[id], range),
412 ),
413 &mut state.pre_visited,
414 &self.urls,
415 &self.indexes,
416 &state.python_requirement,
417 requests,
418 )?;
419 }
420
421 state.reprioritize_conflicts();
422
423 trace!(
424 "Assigned packages: {}",
425 state
426 .pubgrub
427 .partial_solution
428 .extract_solution()
429 .filter(|(p, _)| !state.pubgrub.package_store[*p].is_proxy())
430 .map(|(p, v)| format!("{}=={}", state.pubgrub.package_store[p], v))
431 .join(", ")
432 );
433 let Some((highest_priority_pkg, _)) =
437 state.pubgrub.partial_solution.pick_highest_priority_pkg(
438 |id, _range| state.priorities.get(&state.pubgrub.package_store[id]),
439 )
440 else {
441 if tracing::enabled!(Level::DEBUG) {
443 state.prefetcher.log_tried_versions();
444 }
445 debug!(
446 "{} resolution took {:.3}s",
447 state.env,
448 start.elapsed().as_secs_f32()
449 );
450
451 let resolution = state.into_resolution();
452
453 if matches!(
462 self.options.resolution_mode,
463 ResolutionMode::Lowest | ResolutionMode::Highest
464 ) {
465 let marker = resolution
466 .env
467 .try_universal_markers()
468 .unwrap_or(UniversalMarker::TRUE);
469 for (package, version) in &resolution.nodes {
470 preferences.insert(
471 package.name.clone(),
472 package.index.clone(),
473 marker,
474 version.clone(),
475 PreferenceSource::Resolver,
476 );
477 }
478 }
479
480 resolutions.push(resolution);
481 continue 'FORK;
482 };
483 trace!(
484 "Chose package for decision: {}. remaining choices: {}",
485 state.pubgrub.package_store[highest_priority_pkg],
486 state
487 .pubgrub
488 .partial_solution
489 .undecided_packages()
490 .filter(|(p, _)| !state.pubgrub.package_store[**p].is_proxy())
491 .map(|(p, _)| state.pubgrub.package_store[*p].to_string())
492 .join(", ")
493 );
494
495 (highest_priority_pkg, None)
496 }
497 };
498
499 state.next = highest_priority_pkg;
500
501 let next_id = state.next;
503 let next_package = &state.pubgrub.package_store[state.next];
504
505 let source =
506 PackageSource::from_fork(next_package, &state.fork_urls, &state.fork_indexes);
507
508 self.request_package(next_package, source, requests)?;
520
521 let version = if let Some(version) = initial_version {
522 version
523 } else {
524 let term_intersection = state
525 .pubgrub
526 .partial_solution
527 .term_intersection_for_package(next_id)
528 .expect("a package was chosen but we don't have a term");
529 let range = term_intersection.unwrap_positive();
530
531 let cache_selected_version = match source {
535 PackageSource::Registry(None) => true,
536 PackageSource::Url(_) | PackageSource::Registry(Some(_)) => false,
537 };
538 let decision = if cache_selected_version
539 && let Some((selected_range, version)) =
540 state.selected_versions.get(&next_id)
541 && selected_range == range
542 {
543 Some(ResolverVersion::Unforked(version.clone()))
544 } else {
545 let decision = self.choose_version(
546 next_package,
547 next_id,
548 source,
549 range,
550 &mut state.pins,
551 &preferences,
552 &state.env,
553 &state.python_requirement,
554 &state.pubgrub,
555 &mut visited,
556 requests,
557 )?;
558
559 if cache_selected_version
560 && let Some(ResolverVersion::Unforked(version)) = &decision
561 {
562 state
563 .selected_versions
564 .insert(next_id, (range.clone(), version.clone()));
565 }
566
567 decision
568 };
569
570 let Some(version) = decision else {
572 debug!("No compatible version found for: {next_package}");
573
574 let term_intersection = state
575 .pubgrub
576 .partial_solution
577 .term_intersection_for_package(next_id)
578 .expect("a package was chosen but we don't have a term");
579
580 if let PubGrubPackageInner::Package { name, .. } = &**next_package {
581 if let Some(reason) = self.unavailable_packages.pin().get(name) {
583 state
584 .pubgrub
585 .add_incompatibility(Incompatibility::custom_term(
586 next_id,
587 term_intersection.clone(),
588 UnavailableReason::Package(reason.clone()),
589 ));
590 continue;
591 }
592 }
593
594 state
595 .pubgrub
596 .add_incompatibility(Incompatibility::no_versions(
597 next_id,
598 term_intersection.clone(),
599 ));
600 continue;
601 };
602
603 let version = match version {
604 ResolverVersion::Unforked(version) => version,
605 ResolverVersion::Forked(forks) => {
606 forked_states.extend(self.version_forks_to_fork_states(state, forks));
607 continue 'FORK;
608 }
609 ResolverVersion::Unavailable(version, reason) => {
610 state.add_unavailable_version(
611 version,
612 reason,
613 &self.index,
614 &self.installed_packages,
615 );
616 continue;
617 }
618 };
619
620 if let PackageSource::Registry(index) = source {
622 state.prefetcher.prefetch_batches(
623 next_package,
624 index,
625 &version,
626 term_intersection.unwrap_positive(),
627 state
628 .pubgrub
629 .partial_solution
630 .unchanging_term_for_package(next_id),
631 &state.python_requirement,
632 &self.selector,
633 &state.env,
634 )?;
635 }
636
637 version
638 };
639
640 state.prefetcher.version_tried(next_package, &version);
641
642 self.on_progress(next_package, &version);
643
644 if state
645 .added_dependencies
646 .get(&next_id)
647 .is_some_and(|versions| versions.contains(&version))
648 {
649 state
652 .pubgrub
653 .partial_solution
654 .add_decision(next_id, version);
655 continue;
656 }
657
658 let forked_deps = self.get_dependencies_forking(
660 next_id,
661 next_package,
662 &version,
663 &state.pins,
664 &state.env,
665 &state.python_requirement,
666 &state.pubgrub,
667 )?;
668
669 match forked_deps {
670 ForkedDependencies::Unavailable(reason) => {
671 let versions = state.widen_version_to_gap(
674 &version,
675 &self.index,
676 &self.installed_packages,
677 );
678 state
679 .pubgrub
680 .add_incompatibility(Incompatibility::custom_term(
681 next_id,
682 Term::Positive(versions),
683 UnavailableReason::Version(reason),
684 ));
685 }
686 ForkedDependencies::Unforked(dependencies) => {
687 state
688 .added_dependencies
689 .entry(next_id)
690 .or_default()
691 .insert(version.clone());
692
693 state
695 .visit_package_version_dependencies(
696 next_id,
697 &version,
698 &self.urls,
699 &self.indexes,
700 &dependencies,
701 &self.git,
702 &self.workspace_members,
703 self.selector.resolution_strategy(),
704 )
705 .map_err(|err| {
706 enrich_dependency_error(err, next_id, &version, &state.pubgrub)
707 })?;
708
709 self.visit_dependencies(&dependencies, &state, requests)
711 .map_err(|err| {
712 enrich_dependency_error(err, next_id, &version, &state.pubgrub)
713 })?;
714
715 state.add_package_version_dependencies(
717 next_id,
718 &version,
719 dependencies,
720 &self.index,
721 &self.installed_packages,
722 );
723 }
724 ForkedDependencies::Forked {
725 mut forks,
726 diverging_packages,
727 } => {
728 state
729 .added_dependencies
730 .entry(next_id)
731 .or_default()
732 .insert(version.clone());
733
734 debug!(
735 "Pre-fork {} took {:.3}s",
736 state.env,
737 start.elapsed().as_secs_f32()
738 );
739
740 match (self.options.fork_strategy, self.options.resolution_mode) {
742 (ForkStrategy::Fewest, _) | (_, ResolutionMode::Lowest) => {
743 forks.sort_by(|a, b| {
747 a.cmp_requires_python(b)
748 .reverse()
749 .then_with(|| a.cmp_upper_bounds(b))
750 });
751 }
752 (ForkStrategy::RequiresPython, _) => {
753 forks.sort_by(|a, b| {
757 a.cmp_requires_python(b).then_with(|| a.cmp_upper_bounds(b))
758 });
759 }
760 }
761
762 for new_fork_state in self.forks_to_fork_states(
763 state,
764 &version,
765 forks,
766 requests,
767 &diverging_packages,
768 ) {
769 forked_states.push(new_fork_state?);
770 }
771 continue 'FORK;
772 }
773 ForkedDependencies::RequiresPython(requires_python) => {
774 if matches!(self.options.fork_strategy, ForkStrategy::RequiresPython)
775 && state.env.marker_environment().is_none()
776 {
777 let forks = fork_version_by_python_requirement(
778 &requires_python,
779 &state.python_requirement,
780 &state.env,
781 );
782 if !forks.is_empty() {
783 debug!(
784 "Forking Python requirement `{}` on `{}` for {}=={} ({})",
785 state.python_requirement.target(),
786 &requires_python,
787 next_package,
788 version,
789 forks
790 .iter()
791 .map(ToString::to_string)
792 .collect::<Vec<_>>()
793 .join(", ")
794 );
795
796 let forks = forks
799 .into_iter()
800 .map(|env| VersionFork {
801 env,
802 id: next_id,
803 version: None,
804 })
805 .collect();
806 forked_states
807 .extend(self.version_forks_to_fork_states(state, forks));
808 continue 'FORK;
809 }
810 }
811
812 let versions = state.widen_version_to_gap(
813 &version,
814 &self.index,
815 &self.installed_packages,
816 );
817 state
818 .pubgrub
819 .add_incompatibility(Incompatibility::custom_term(
820 next_id,
821 Term::Positive(versions),
822 UnavailableReason::Version(UnavailableVersion::RequiresPython(
823 requires_python,
824 )),
825 ));
826 }
827 }
828 }
829 }
830 if resolutions.len() > 1 {
831 info!(
832 "Solved your requirements for {} environments",
833 resolutions.len()
834 );
835 }
836 if tracing::enabled!(Level::DEBUG) {
837 for resolution in &resolutions {
838 if let Some(env) = resolution.env.end_user_fork_display() {
839 let packages: FxHashSet<_> = resolution
840 .nodes
841 .keys()
842 .map(|package| &package.name)
843 .collect();
844 debug!(
845 "Distinct solution for {env} with {} package(s)",
846 packages.len()
847 );
848 }
849 }
850 }
851 for resolution in &resolutions {
852 resolution.trace_resolution();
853 }
854 let resolutions = resolutions
855 .into_iter()
856 .map(|resolution| resolution.finalize(&self.index, &self.git))
857 .collect::<Result<Vec<_>, _>>()?;
858 crate::resolution::from_state(
859 resolutions,
860 self.project.as_ref(),
861 &self.workspace_members,
862 self.requirements.clone(),
863 self.constraints.clone(),
864 self.overrides.clone(),
865 &self.preferences,
866 &self.hasher,
867 &self.index,
868 self.python_requirement.target().clone(),
869 &self.conflicts,
870 self.selector.resolution_strategy(),
871 self.options.clone(),
872 )
873 }
874
875 fn forks_to_fork_states<'a, 'index: 'a>(
877 &'a self,
878 current_state: ForkState<'index>,
879 version: &'a Version,
880 forks: Vec<Fork>,
881 requests: &'a MetadataRequests,
882 diverging_packages: &'a BTreeSet<PackageName>,
883 ) -> impl Iterator<Item = Result<ForkState<'index>, ResolveError>> + 'a {
884 debug!(
885 "Splitting resolution on {}=={} over {} into {} resolution{} with separate markers",
886 current_state.pubgrub.package_store[current_state.next],
887 version,
888 diverging_packages
889 .iter()
890 .map(ToString::to_string)
891 .join(", "),
892 forks.len(),
893 if forks.len() == 1 { "" } else { "s" }
894 );
895 assert!(forks.len() >= 2);
896 let package = current_state.next;
902 let mut cur_state = Some(current_state);
903 let forks_len = forks.len();
904 forks
905 .into_iter()
906 .enumerate()
907 .map(move |(i, fork)| {
908 let is_last = i == forks_len - 1;
909 let forked_state = cur_state.take().unwrap();
910 if !is_last {
911 cur_state = Some(forked_state.clone());
912 }
913
914 let env = fork.env.clone();
915 (fork, forked_state.with_env(env))
916 })
917 .map(move |(fork, mut forked_state)| {
918 forked_state
920 .visit_package_version_dependencies(
921 package,
922 version,
923 &self.urls,
924 &self.indexes,
925 &fork.dependencies,
926 &self.git,
927 &self.workspace_members,
928 self.selector.resolution_strategy(),
929 )
930 .map_err(|err| {
931 enrich_dependency_error(err, package, version, &forked_state.pubgrub)
932 })?;
933
934 self.visit_dependencies(&fork.dependencies, &forked_state, requests)
936 .map_err(|err| {
937 enrich_dependency_error(err, package, version, &forked_state.pubgrub)
938 })?;
939
940 forked_state.add_package_version_dependencies(
942 package,
943 version,
944 fork.dependencies,
945 &self.index,
946 &self.installed_packages,
947 );
948
949 Ok(forked_state)
950 })
951 }
952
953 #[expect(clippy::unused_self)]
955 fn version_forks_to_fork_states<'index>(
956 &self,
957 current_state: ForkState<'index>,
958 forks: Vec<VersionFork>,
959 ) -> impl Iterator<Item = ForkState<'index>> {
960 let mut cur_state = Some(current_state);
966 let forks_len = forks.len();
967 forks.into_iter().enumerate().map(move |(i, fork)| {
968 let is_last = i == forks_len - 1;
969 let forked_state = cur_state.take().unwrap();
970 if !is_last {
971 cur_state = Some(forked_state.clone());
972 }
973 let continuation = match fork.version {
974 Some(version) => ForkContinuation::UseVersion {
975 package: fork.id,
976 version,
977 },
978 None => ForkContinuation::SelectVersion { package: fork.id },
979 };
980 forked_state
981 .with_env(fork.env)
982 .with_continuation(continuation)
983 })
984 }
985
986 fn visit_dependencies(
988 &self,
989 dependencies: &[PubGrubDependency],
990 state: &ForkState,
991 requests: &MetadataRequests,
992 ) -> Result<(), ResolveError> {
993 for dependency in dependencies {
994 let PubGrubDependency {
995 package,
996 version: _,
997 parent: _,
998 source: _,
999 } = dependency;
1000 let Some(source) = PackageSource::for_prefetch(
1001 package,
1002 &state.fork_urls,
1003 &state.fork_indexes,
1004 &self.urls,
1005 ) else {
1006 continue;
1007 };
1008 self.request_package(package, source, requests)?;
1009 }
1010 Ok(())
1011 }
1012
1013 fn request_package(
1014 &self,
1015 package: &PubGrubPackage,
1016 source: PackageSource<'_>,
1017 requests: &MetadataRequests,
1018 ) -> Result<(), ResolveError> {
1019 let Some(name) = package.name_no_root() else {
1021 return Ok(());
1022 };
1023
1024 match source {
1025 PackageSource::Url(url) => {
1026 if !self.hasher.allows_url(&url.verbatim) {
1028 return Err(ResolveError::UnhashedPackage(name.clone()));
1029 }
1030
1031 let dist = Dist::from_url(name.clone(), url.clone())?;
1033 requests.enqueue_metadata(MetadataRequest::Dist(dist))?;
1034 }
1035 PackageSource::Registry(index) => {
1036 requests.enqueue_package(name, index)?;
1037 }
1038 }
1039 Ok(())
1040 }
1041
1042 fn pre_visit<'data>(
1045 packages: impl Iterator<
1046 Item = (
1047 Id<PubGrubPackage>,
1048 &'data PubGrubPackage,
1049 &'data Range<Version>,
1050 ),
1051 >,
1052 pre_visited: &mut FxHashMap<Id<PubGrubPackage>, Range<Version>>,
1053 urls: &Urls,
1054 indexes: &Indexes,
1055 python_requirement: &PythonRequirement,
1056 requests: &MetadataRequests,
1057 ) -> Result<(), ResolveError> {
1058 for (id, package, range) in packages {
1061 let PubGrubPackageInner::Package {
1062 name,
1063 kind: PackageNodeKind::Base,
1064 marker: MarkerTree::TRUE,
1065 } = &**package
1066 else {
1067 continue;
1068 };
1069 if urls.any_url(name) {
1072 continue;
1073 }
1074 if indexes.contains_key(name) {
1076 continue;
1077 }
1078 if pre_visited.get(&id) == Some(range) {
1081 continue;
1082 }
1083 pre_visited.insert(id, range.clone());
1084 requests.prefetch(name, range, python_requirement)?;
1085 }
1086 Ok(())
1087 }
1088
1089 #[cfg_attr(feature = "tracing-durations-export", instrument(skip_all, fields(%package)))]
1097 fn choose_version<'index>(
1098 &self,
1099 package: &PubGrubPackage,
1100 id: Id<PubGrubPackage>,
1101 source: PackageSource<'_>,
1102 range: &Range<Version>,
1103 pins: &mut FilePins<'index>,
1104 preferences: &Preferences,
1105 env: &ResolverEnvironment,
1106 python_requirement: &PythonRequirement,
1107 pubgrub: &State<UvDependencyProvider>,
1108 visited: &mut FxHashSet<PackageName>,
1109 requests: &'index MetadataRequests,
1110 ) -> Result<Option<ResolverVersion>, ResolveError> {
1111 match &**package {
1112 PubGrubPackageInner::Root(_) => {
1113 Ok(Some(ResolverVersion::Unforked(MIN_VERSION.clone())))
1114 }
1115
1116 PubGrubPackageInner::Python(_) => {
1117 Ok(None)
1120 }
1121
1122 PubGrubPackageInner::System(_) => {
1123 let Some(version) = range.as_singleton() else {
1126 return Ok(None);
1127 };
1128 Ok(Some(ResolverVersion::Unforked(version.clone())))
1129 }
1130
1131 PubGrubPackageInner::Marker { name, .. }
1132 | PubGrubPackageInner::Extra { name, .. }
1133 | PubGrubPackageInner::Group { name, .. }
1134 | PubGrubPackageInner::Package { name, .. } => match source {
1135 PackageSource::Url(url) => self.choose_version_url(
1136 id,
1137 name,
1138 range,
1139 url,
1140 env,
1141 python_requirement,
1142 pubgrub,
1143 pins,
1144 requests,
1145 ),
1146 PackageSource::Registry(index) => self.choose_version_registry(
1147 package,
1148 id,
1149 name,
1150 index,
1151 range,
1152 preferences,
1153 env,
1154 python_requirement,
1155 pubgrub,
1156 pins,
1157 visited,
1158 requests,
1159 ),
1160 },
1161 }
1162 }
1163
1164 fn choose_version_url<'index>(
1167 &self,
1168 id: Id<PubGrubPackage>,
1169 name: &PackageName,
1170 range: &Range<Version>,
1171 url: &VerbatimParsedUrl,
1172 env: &ResolverEnvironment,
1173 python_requirement: &PythonRequirement,
1174 pubgrub: &State<UvDependencyProvider>,
1175 pins: &mut FilePins<'index>,
1176 requests: &'index MetadataRequests,
1177 ) -> Result<Option<ResolverVersion>, ResolveError> {
1178 debug!(
1179 "Searching for a compatible version of {name} @ {} ({range})",
1180 url.verbatim
1181 );
1182
1183 let dist = Dist::from_url(name.clone(), url.clone())?;
1184 let registered = requests.metadata(&dist)?;
1185 let response = registered.wait();
1186
1187 let metadata = match &*response {
1189 MetadataResponse::Found(archive) => &archive.metadata,
1190 MetadataResponse::Unavailable(reason) => {
1191 self.unavailable_packages
1192 .pin()
1193 .insert(name.clone(), reason.into());
1194 return Ok(None);
1195 }
1196 MetadataResponse::Error(dist, err) => {
1199 return Err(ResolveError::Dist(
1200 DistErrorKind::from_requested_dist(dist, &**err),
1201 dist.clone(),
1202 DerivationChain::default(),
1203 err.clone(),
1204 ));
1205 }
1206 };
1207
1208 let version = &metadata.version;
1209
1210 if !range.contains(version) {
1212 return Ok(None);
1213 }
1214
1215 if let Dist::Built(dist) = &dist {
1218 let filename = match &dist {
1219 BuiltDist::Registry(dist) => &dist.best_wheel().filename,
1220 BuiltDist::DirectUrl(dist) => &dist.filename,
1221 BuiltDist::GitPath(dist) => &dist.filename,
1222 BuiltDist::Path(dist) => &dist.filename,
1223 };
1224
1225 if env.marker_environment().is_none() && !self.options.artifact_environments.is_empty()
1227 {
1228 let wheel_marker = implied_markers(filename, self.options.minimum_libc_version);
1229 for environment_marker in self.options.artifact_environments.iter().copied() {
1232 if env.included_by_marker(environment_marker)
1234 && env.included_by_marker(
1235 find_environments(id, pubgrub).and(environment_marker),
1236 )
1237 {
1238 if !env.included_by_marker(wheel_marker.and(environment_marker)) {
1240 return Ok(Some(ResolverVersion::Unavailable(
1241 version.clone(),
1242 UnavailableVersion::IncompatibleDist(IncompatibleDist::Wheel(
1243 IncompatibleWheel::MissingPlatform(environment_marker),
1244 )),
1245 )));
1246 }
1247 }
1248 }
1249 }
1250
1251 if !python_requirement.target().matches_wheel_tag(filename) {
1253 return Ok(Some(ResolverVersion::Unavailable(
1254 filename.version.clone(),
1255 UnavailableVersion::IncompatibleDist(IncompatibleDist::Wheel(
1256 IncompatibleWheel::Tag(IncompatibleTag::AbiPythonVersion),
1257 )),
1258 )));
1259 }
1260 }
1261
1262 if let Some(requires_python) = metadata.requires_python.as_ref() {
1264 if !python_requirement.target().is_contained_by(requires_python) {
1265 let kind = if python_requirement.installed() == python_requirement.target() {
1266 PythonRequirementKind::Installed
1267 } else {
1268 PythonRequirementKind::Target
1269 };
1270 return Ok(Some(ResolverVersion::Unavailable(
1271 version.clone(),
1272 UnavailableVersion::IncompatibleDist(IncompatibleDist::Source(
1273 IncompatibleSource::RequiresPython(requires_python.clone(), kind),
1274 )),
1275 )));
1276 }
1277 }
1278
1279 pins.insert_url(name, version, registered);
1280 Ok(Some(ResolverVersion::Unforked(version.clone())))
1281 }
1282
1283 fn choose_version_registry<'index>(
1286 &self,
1287 package: &PubGrubPackage,
1288 id: Id<PubGrubPackage>,
1289 name: &PackageName,
1290 index: Option<&IndexMetadata>,
1291 range: &Range<Version>,
1292 preferences: &Preferences,
1293 env: &ResolverEnvironment,
1294 python_requirement: &PythonRequirement,
1295 pubgrub: &State<UvDependencyProvider>,
1296 pins: &mut FilePins<'index>,
1297 visited: &mut FxHashSet<PackageName>,
1298 requests: &'index MetadataRequests,
1299 ) -> Result<Option<ResolverVersion>, ResolveError> {
1300 let versions_response = requests.request_package(name, index)?.wait();
1302 let index = index.map(IndexMetadata::url);
1303 visited.insert(name.clone());
1304
1305 let version_maps = match *versions_response {
1306 VersionsResponse::Found(ref version_maps) => version_maps.as_slice(),
1307 VersionsResponse::NoIndex => {
1308 self.unavailable_packages
1309 .pin()
1310 .insert(name.clone(), UnavailablePackage::NoIndex);
1311 &[]
1312 }
1313 VersionsResponse::Offline => {
1314 self.unavailable_packages
1315 .pin()
1316 .insert(name.clone(), UnavailablePackage::Offline);
1317 &[]
1318 }
1319 VersionsResponse::NotFound => {
1320 self.unavailable_packages
1321 .pin()
1322 .insert(name.clone(), UnavailablePackage::NotFound);
1323 &[]
1324 }
1325 };
1326
1327 debug!("Searching for a compatible version of {package} ({range})");
1328
1329 let Some(candidate) = self.selector.select(
1331 name,
1332 range,
1333 version_maps,
1334 preferences,
1335 &self.installed_packages,
1336 &self.exclusions,
1337 index,
1338 env,
1339 self.tags.as_ref(),
1340 ) else {
1341 return Ok(None);
1343 };
1344
1345 let dist = match candidate.dist() {
1346 CandidateDist::Compatible(dist) => dist,
1347 CandidateDist::Incompatible {
1348 incompatible_dist: incompatibility,
1349 prioritized_dist: _,
1350 } => {
1351 return Ok(Some(ResolverVersion::Unavailable(
1353 candidate.version().clone(),
1354 UnavailableVersion::IncompatibleDist(incompatibility.clone()),
1357 )));
1358 }
1359 };
1360
1361 if let Some((requires_python, incompatibility)) =
1363 Self::check_requires_python(dist, python_requirement)
1364 {
1365 if matches!(self.options.fork_strategy, ForkStrategy::RequiresPython) {
1366 if env.marker_environment().is_none() {
1367 let forks = fork_version_by_python_requirement(
1368 requires_python,
1369 python_requirement,
1370 env,
1371 );
1372 if !forks.is_empty() {
1373 debug!(
1374 "Forking Python requirement `{}` on `{}` for {}=={} ({})",
1375 python_requirement.target(),
1376 requires_python,
1377 name,
1378 candidate.version(),
1379 forks
1380 .iter()
1381 .map(ToString::to_string)
1382 .collect::<Vec<_>>()
1383 .join(", ")
1384 );
1385 let forks = forks
1386 .into_iter()
1387 .map(|env| VersionFork {
1388 env,
1389 id,
1390 version: None,
1391 })
1392 .collect();
1393 return Ok(Some(ResolverVersion::Forked(forks)));
1394 }
1395 }
1396 }
1397
1398 return Ok(Some(ResolverVersion::Unavailable(
1399 candidate.version().clone(),
1400 UnavailableVersion::IncompatibleDist(incompatibility),
1401 )));
1402 }
1403
1404 if env.marker_environment().is_none()
1408 && let Some(upper_bound) = python_requirement.target().range().upper().specifier()
1409 && !dist
1410 .matches_python_requirement(&RequiresPython::from_specifiers(upper_bound.into()))
1411 {
1412 return Ok(Some(ResolverVersion::Unavailable(
1413 candidate.version().clone(),
1414 UnavailableVersion::IncompatibleDist(IncompatibleDist::Wheel(
1415 IncompatibleWheel::Tag(IncompatibleTag::AbiPythonVersion),
1416 )),
1417 )));
1418 }
1419
1420 if let Some(forked) = self.fork_version_registry(
1422 &candidate,
1423 dist,
1424 version_maps,
1425 package,
1426 id,
1427 name,
1428 index,
1429 range,
1430 preferences,
1431 env,
1432 pubgrub,
1433 pins,
1434 requests,
1435 )? {
1436 return Ok(Some(forked));
1437 }
1438
1439 let filename = match dist.for_installation() {
1440 ResolvedDistRef::InstallableRegistrySourceDist { sdist, .. } => sdist
1441 .filename()
1442 .unwrap_or(Cow::Borrowed("unknown filename")),
1443 ResolvedDistRef::InstallableRegistryBuiltDist { wheel, .. } => wheel
1444 .filename()
1445 .unwrap_or(Cow::Borrowed("unknown filename")),
1446 ResolvedDistRef::Installed { .. } => Cow::Borrowed("installed"),
1447 };
1448
1449 debug!(
1450 "Selecting: {}=={} [{}] ({})",
1451 name,
1452 candidate.version(),
1453 candidate.choice_kind(),
1454 filename,
1455 );
1456 self.visit_candidate(&candidate, dist, package, name, pins, requests)?;
1457
1458 let version = candidate.version().clone();
1459 Ok(Some(ResolverVersion::Unforked(version)))
1460 }
1461
1462 fn fork_version_registry<'index>(
1475 &self,
1476 candidate: &Candidate,
1477 dist: &CompatibleDist,
1478 version_maps: &[VersionMap],
1479 package: &PubGrubPackage,
1480 id: Id<PubGrubPackage>,
1481 name: &PackageName,
1482 index: Option<&IndexUrl>,
1483 range: &Range<Version>,
1484 preferences: &Preferences,
1485 env: &ResolverEnvironment,
1486 pubgrub: &State<UvDependencyProvider>,
1487 pins: &mut FilePins<'index>,
1488 requests: &'index MetadataRequests,
1489 ) -> Result<Option<ResolverVersion>, ResolveError> {
1490 let Some(fork_markers) = env.fork_markers() else {
1492 return Ok(None);
1493 };
1494
1495 let artifact_markers = dist.implied_markers();
1496 if artifact_markers.is_true() {
1497 return Ok(None);
1498 }
1499
1500 for marker in self.options.artifact_environments.iter().copied() {
1503 if env.included_by_marker(marker) {
1505 if !env.included_by_marker(artifact_markers.and(marker))
1507 && env.included_by_marker(find_environments(id, pubgrub).and(marker))
1508 {
1509 let coverage = dist.implied_markers().and(fork_markers);
1514 let split = marker.restrict(marker.or(coverage));
1515 let Some((left, right)) = fork_version_by_marker(env, split) else {
1516 return Ok(Some(ResolverVersion::Unavailable(
1517 candidate.version().clone(),
1518 UnavailableVersion::IncompatibleDist(IncompatibleDist::Wheel(
1519 IncompatibleWheel::MissingPlatform(marker),
1520 )),
1521 )));
1522 };
1523
1524 debug!(
1525 "Forking on required platform `{}` for {}=={} ({})",
1526 marker.try_to_string().unwrap_or_else(|| "true".to_string()),
1527 name,
1528 candidate.version(),
1529 [&left, &right]
1530 .iter()
1531 .map(ToString::to_string)
1532 .collect::<Vec<_>>()
1533 .join(", ")
1534 );
1535 let forks = vec![
1536 VersionFork {
1537 env: left,
1538 id,
1539 version: None,
1540 },
1541 VersionFork {
1542 env: right,
1543 id,
1544 version: None,
1545 },
1546 ];
1547 return Ok(Some(ResolverVersion::Forked(forks)));
1548 }
1549 }
1550 }
1551
1552 if !candidate.version().is_local() {
1554 return Ok(None);
1555 }
1556
1557 debug!(
1558 "Looking at local version: {}=={}",
1559 name,
1560 candidate.version()
1561 );
1562
1563 let range = range.clone().intersection(&Range::singleton(
1565 candidate.version().clone().without_local(),
1566 ));
1567
1568 let Some(base_candidate) = self.selector.select(
1569 name,
1570 &range,
1571 version_maps,
1572 preferences,
1573 &self.installed_packages,
1574 &self.exclusions,
1575 index,
1576 env,
1577 self.tags.as_ref(),
1578 ) else {
1579 return Ok(None);
1580 };
1581 let CandidateDist::Compatible(base_dist) = base_candidate.dist() else {
1582 return Ok(None);
1583 };
1584
1585 let mut remainder = {
1587 let mut remainder = base_dist.implied_markers();
1588 remainder = remainder.and(artifact_markers.negate());
1589 remainder
1590 };
1591 if remainder.is_false() {
1592 return Ok(None);
1593 }
1594
1595 if !env.included_by_marker(remainder) {
1599 return Ok(None);
1600 }
1601
1602 if !env.included_by_marker(artifact_markers) {
1605 let filename = match dist.for_installation() {
1606 ResolvedDistRef::InstallableRegistrySourceDist { sdist, .. } => sdist
1607 .filename()
1608 .unwrap_or(Cow::Borrowed("unknown filename")),
1609 ResolvedDistRef::InstallableRegistryBuiltDist { wheel, .. } => wheel
1610 .filename()
1611 .unwrap_or(Cow::Borrowed("unknown filename")),
1612 ResolvedDistRef::Installed { .. } => Cow::Borrowed("installed"),
1613 };
1614
1615 debug!(
1616 "Preferring non-local candidate: {}=={} [{}] ({})",
1617 name,
1618 base_candidate.version(),
1619 base_candidate.choice_kind(),
1620 filename,
1621 );
1622 self.visit_candidate(&base_candidate, base_dist, package, name, pins, requests)?;
1623
1624 return Ok(Some(ResolverVersion::Unforked(
1625 base_candidate.version().clone(),
1626 )));
1627 }
1628
1629 for value in [
1638 arcstr::literal!("darwin"),
1639 arcstr::literal!("linux"),
1640 arcstr::literal!("win32"),
1641 ] {
1642 let sys_platform = MarkerTree::expression(MarkerExpression::String {
1643 key: MarkerValueString::SysPlatform,
1644 operator: MarkerOperator::Equal,
1645 value,
1646 });
1647 if artifact_markers.is_disjoint(sys_platform) && !remainder.is_disjoint(sys_platform) {
1648 remainder = remainder.or(sys_platform);
1649 }
1650 }
1651
1652 let Some((base_env, local_env)) = fork_version_by_marker(env, remainder) else {
1654 return Ok(None);
1655 };
1656
1657 debug!(
1658 "Forking platform for {}=={} ({})",
1659 name,
1660 candidate.version(),
1661 [&base_env, &local_env]
1662 .iter()
1663 .map(ToString::to_string)
1664 .collect::<Vec<_>>()
1665 .join(", ")
1666 );
1667 self.visit_candidate(candidate, dist, package, name, pins, requests)?;
1668 self.visit_candidate(&base_candidate, base_dist, package, name, pins, requests)?;
1669
1670 let forks = vec![
1671 VersionFork {
1672 env: base_env.clone(),
1673 id,
1674 version: Some(base_candidate.version().clone()),
1675 },
1676 VersionFork {
1677 env: local_env.clone(),
1678 id,
1679 version: Some(candidate.version().clone()),
1680 },
1681 ];
1682 Ok(Some(ResolverVersion::Forked(forks)))
1683 }
1684
1685 fn visit_candidate<'index>(
1687 &self,
1688 candidate: &Candidate,
1689 dist: &CompatibleDist,
1690 package: &PubGrubPackage,
1691 name: &PackageName,
1692 pins: &mut FilePins<'index>,
1693 requests: &'index MetadataRequests,
1694 ) -> Result<(), ResolveError> {
1695 let request = if matches!(&**package, PubGrubPackageInner::Package { .. })
1696 && self.dependency_mode.is_transitive()
1697 {
1698 Some(|| {
1699 requests.request_metadata(
1700 MetadataRequest::Resolved(dist.for_resolution()),
1701 |request| {
1702 if name != request.name() {
1703 return Err(ResolveError::MismatchedPackageName {
1704 request: "distribution",
1705 expected: name.clone(),
1706 actual: request.name().clone(),
1707 });
1708 }
1709 if !self
1711 .hasher
1712 .allows_package(candidate.name(), candidate.version())
1713 {
1714 return Err(ResolveError::UnhashedPackage(candidate.name().clone()));
1715 }
1716 Ok(())
1717 },
1718 )
1719 })
1720 } else {
1721 None
1722 };
1723 pins.insert(candidate, dist, request)
1724 }
1725
1726 fn check_requires_python<'dist>(
1729 dist: &'dist CompatibleDist,
1730 python_requirement: &PythonRequirement,
1731 ) -> Option<(&'dist VersionSpecifiers, IncompatibleDist)> {
1732 let requires_python = dist.requires_python()?;
1733 if python_requirement.target().is_contained_by(requires_python) {
1734 None
1735 } else {
1736 let incompatibility = if matches!(dist, CompatibleDist::CompatibleWheel { .. }) {
1737 IncompatibleDist::Wheel(IncompatibleWheel::RequiresPython(
1738 requires_python.clone(),
1739 if python_requirement.installed() == python_requirement.target() {
1740 PythonRequirementKind::Installed
1741 } else {
1742 PythonRequirementKind::Target
1743 },
1744 ))
1745 } else {
1746 IncompatibleDist::Source(IncompatibleSource::RequiresPython(
1747 requires_python.clone(),
1748 if python_requirement.installed() == python_requirement.target() {
1749 PythonRequirementKind::Installed
1750 } else {
1751 PythonRequirementKind::Target
1752 },
1753 ))
1754 };
1755 Some((requires_python, incompatibility))
1756 }
1757 }
1758
1759 fn get_dependencies_forking(
1761 &self,
1762 id: Id<PubGrubPackage>,
1763 package: &PubGrubPackage,
1764 version: &Version,
1765 pins: &FilePins,
1766 env: &ResolverEnvironment,
1767 python_requirement: &PythonRequirement,
1768 pubgrub: &State<UvDependencyProvider>,
1769 ) -> Result<ForkedDependencies, ResolveError> {
1770 let dependencies =
1771 self.get_dependencies(id, package, version, pins, env, python_requirement, pubgrub)?;
1772 if env.marker_environment().is_some() {
1773 Ok(ForkedDependencies::from_dependencies_platform_specific(
1774 dependencies,
1775 ))
1776 } else {
1777 Ok(ForkedDependencies::from_dependencies_universal(
1778 dependencies,
1779 env,
1780 python_requirement,
1781 &self.conflicts,
1782 ))
1783 }
1784 }
1785
1786 #[instrument(skip_all, fields(%package, %version))]
1788 fn get_dependencies(
1789 &self,
1790 id: Id<PubGrubPackage>,
1791 package: &PubGrubPackage,
1792 version: &Version,
1793 pins: &FilePins,
1794 env: &ResolverEnvironment,
1795 python_requirement: &PythonRequirement,
1796 pubgrub: &State<UvDependencyProvider>,
1797 ) -> Result<Dependencies, ResolveError> {
1798 let expander = RequirementExpander::new(
1799 &self.constraints,
1800 &self.overrides,
1801 &self.excludes,
1802 env,
1803 python_requirement,
1804 );
1805 let dependencies = match &**package {
1806 PubGrubPackageInner::Root(_) => {
1807 let requirements = expander.expand(&self.requirements, RequirementContext::Root);
1808
1809 PubGrubDependency::from_requirements(
1810 &self.conflicts,
1811 requirements,
1812 None,
1813 Some(package),
1814 )
1815 }
1816
1817 PubGrubPackageInner::Package {
1818 name,
1819 kind,
1820 marker: _,
1821 } => {
1822 if self.dependency_mode.is_direct() {
1824 return Ok(Dependencies::Unforkable(Vec::default()));
1825 }
1826
1827 let registered = pins
1828 .metadata(name, version)
1829 .ok_or_else(|| ResolveError::UnregisteredTask(format!("{name}=={version}")))?;
1830
1831 if self.dependency_mode.is_transitive()
1833 && self.unavailable_packages.pin().contains_key(name)
1834 && self.installed_packages.get_packages(name).is_empty()
1835 {
1836 debug_assert!(
1837 false,
1838 "Dependencies were requested for a package that is not available"
1839 );
1840 return Err(ResolveError::PackageUnavailable(name.clone()));
1841 }
1842
1843 let response = registered.wait();
1845
1846 let metadata = match &*response {
1847 MetadataResponse::Found(archive) => &archive.metadata,
1848 MetadataResponse::Unavailable(reason) => {
1849 let unavailable_version = UnavailableVersion::from(reason);
1850 let message = unavailable_version.singular_message();
1851 if let Some(err) = reason.source() {
1852 warn!("{name} {message}: {err}");
1854 } else {
1855 warn!("{name} {message}");
1856 }
1857 let incomplete_packages = self.incomplete_packages.pin();
1858 let versions = incomplete_packages.get_or_insert(
1859 name.clone(),
1860 HashMap::builder().resize_mode(ResizeMode::Blocking).build(),
1861 );
1862 versions.pin().insert(version.clone(), reason.clone());
1863 return Ok(Dependencies::Unavailable(unavailable_version));
1864 }
1865 MetadataResponse::Error(dist, err) => {
1866 let chain = DerivationChainBuilder::from_state(id, version, pubgrub)
1867 .unwrap_or_default();
1868 return Err(ResolveError::Dist(
1869 DistErrorKind::from_requested_dist(dist, &**err),
1870 dist.clone(),
1871 chain,
1872 err.clone(),
1873 ));
1874 }
1875 };
1876
1877 if let Some(requires_python) = &metadata.requires_python {
1880 if !python_requirement.target().is_contained_by(requires_python) {
1881 return Ok(Dependencies::RequiresPython(requires_python.clone()));
1882 }
1883 }
1884
1885 let system_dependencies = self
1887 .options
1888 .torch_backend
1889 .as_ref()
1890 .filter(|torch_backend| matches!(torch_backend, TorchStrategy::Cuda { .. }))
1891 .filter(|torch_backend| torch_backend.has_system_dependency(name))
1892 .and_then(|_| pins.get(name, version).and_then(ResolvedDist::index))
1893 .map(IndexUrl::url)
1894 .and_then(SystemDependency::from_index)
1895 .into_iter()
1896 .inspect(|system_dependency| {
1897 debug!(
1898 "Adding system dependency `{}` for `{package}@{version}`",
1899 system_dependency
1900 );
1901 })
1902 .map(PubGrubDependency::from);
1903
1904 let (requirements, context) = match kind {
1905 PackageNodeKind::Group(group) => (
1906 metadata
1907 .dependency_groups
1908 .get(group)
1909 .map_or(&[][..], AsRef::as_ref),
1910 RequirementContext::Group { name, version },
1911 ),
1912 PackageNodeKind::Extra(extra) => (
1913 metadata.requires_dist.as_ref(),
1914 RequirementContext::Extra {
1915 name,
1916 version,
1917 extra,
1918 },
1919 ),
1920 PackageNodeKind::Base => (
1921 metadata.requires_dist.as_ref(),
1922 RequirementContext::Package { name, version },
1923 ),
1924 };
1925 let requirements = expander.expand(requirements, context);
1926
1927 PubGrubDependency::from_requirements(
1928 &self.conflicts,
1929 requirements,
1930 kind.group(),
1931 Some(package),
1932 )
1933 .map(|mut dependencies| {
1934 dependencies.extend(system_dependencies);
1935 dependencies
1936 })
1937 }
1938
1939 PubGrubPackageInner::Python(_) => return Ok(Dependencies::Unforkable(Vec::default())),
1940
1941 PubGrubPackageInner::System(_) => return Ok(Dependencies::Unforkable(Vec::default())),
1942
1943 PubGrubPackageInner::Marker { name, marker } => {
1945 return Ok(Dependencies::Unforkable(
1946 [MarkerTree::TRUE, *marker]
1947 .into_iter()
1948 .map(move |marker| PubGrubDependency {
1949 package: PubGrubPackage::from(PubGrubPackageInner::Package {
1950 name: name.clone(),
1951 kind: PackageNodeKind::Base,
1952 marker,
1953 }),
1954 version: Range::singleton(version.clone()),
1955 parent: None,
1956 source: DependencySource::Unspecified,
1957 })
1958 .collect(),
1959 ));
1960 }
1961
1962 PubGrubPackageInner::Extra {
1964 name,
1965 extra,
1966 marker,
1967 } => {
1968 return Ok(Dependencies::Unforkable(
1969 [MarkerTree::TRUE, *marker]
1970 .into_iter()
1971 .dedup()
1972 .flat_map(move |marker| {
1973 [None, Some(extra)]
1974 .into_iter()
1975 .map(move |extra| PubGrubDependency {
1976 package: PubGrubPackage::from(PubGrubPackageInner::Package {
1977 name: name.clone(),
1978 kind: extra
1979 .cloned()
1980 .map_or(PackageNodeKind::Base, PackageNodeKind::Extra),
1981 marker,
1982 }),
1983 version: Range::singleton(version.clone()),
1984 parent: None,
1985 source: DependencySource::Unspecified,
1986 })
1987 })
1988 .collect(),
1989 ));
1990 }
1991
1992 PubGrubPackageInner::Group {
1994 name,
1995 group,
1996 marker,
1997 } => {
1998 return Ok(Dependencies::Unforkable(
1999 [MarkerTree::TRUE, *marker]
2000 .into_iter()
2001 .dedup()
2002 .map(|marker| PubGrubDependency {
2003 package: PubGrubPackage::from(PubGrubPackageInner::Package {
2004 name: name.clone(),
2005 kind: PackageNodeKind::Group(group.clone()),
2006 marker,
2007 }),
2008 version: Range::singleton(version.clone()),
2009 parent: None,
2010 source: DependencySource::Unspecified,
2011 })
2012 .collect(),
2013 ));
2014 }
2015 };
2016 Ok(match dependencies {
2017 Ok(dependencies) => Dependencies::Available(dependencies),
2018 Err(requirement) => {
2019 Dependencies::Unavailable(UnavailableVersion::UnsatisfiableDependency(requirement))
2020 }
2021 })
2022 }
2023
2024 async fn fetch<Provider: ResolverProvider>(
2026 self: Arc<Self>,
2027 provider: Arc<Provider>,
2028 request_stream: Receiver<Request>,
2029 ) -> Result<(), ResolveError> {
2030 let mut response_stream = ReceiverStream::new(request_stream)
2031 .map(|request| self.process_request(request, &*provider).boxed_local())
2032 .buffer_unordered(usize::MAX);
2036
2037 while let Some(response) = response_stream.next().await {
2038 match response? {
2039 Some(Response::Package(name, index, version_map)) => {
2040 trace!("Received package metadata for: {name}");
2041 if let Some(index) = index {
2042 self.index
2043 .explicit()
2044 .done((name, index), Arc::new(version_map));
2045 } else {
2046 self.index.implicit().done(name, Arc::new(version_map));
2047 }
2048 }
2049 Some(Response::Installed { dist, metadata }) => {
2050 trace!("Received installed distribution metadata for: {dist}");
2051 self.index
2052 .distributions()
2053 .done(dist.distribution_id(), Arc::new(metadata));
2054 }
2055 Some(Response::Dist { dist, metadata }) => {
2056 let dist_kind = match dist {
2057 Dist::Built(_) => "built",
2058 Dist::Source(_) => "source",
2059 };
2060 trace!("Received {dist_kind} distribution metadata for: {dist}");
2061 if let MetadataResponse::Unavailable(reason) = &metadata {
2062 let message = UnavailableVersion::from(reason).singular_message();
2063 if let Some(err) = reason.source() {
2064 warn!("{dist} {message}: {err}");
2066 } else {
2067 warn!("{dist} {message}");
2068 }
2069 }
2070 self.index
2071 .distributions()
2072 .done(dist.distribution_id(), Arc::new(metadata));
2073 }
2074 None => {}
2075 }
2076 }
2077
2078 Ok::<(), ResolveError>(())
2079 }
2080
2081 #[instrument(skip_all, fields(%request))]
2082 async fn process_request<Provider: ResolverProvider>(
2083 &self,
2084 request: Request,
2085 provider: &Provider,
2086 ) -> Result<Option<Response>, ResolveError> {
2087 match request {
2088 Request::Package(package_name, index) => {
2090 let package_versions = provider
2091 .get_package_versions(&package_name, index.as_ref())
2092 .boxed_local()
2093 .await
2094 .map_err(ResolveError::Client)?;
2095
2096 Ok(Some(Response::Package(
2097 package_name,
2098 index.map(IndexMetadata::into_url),
2099 package_versions,
2100 )))
2101 }
2102
2103 Request::Dist(dist) => {
2105 if let Some(version) = dist.version() {
2106 if let Some(index) = dist.index() {
2107 let versions_response = self.index.implicit().get(dist.name());
2109 if let Some(VersionsResponse::Found(version_maps)) =
2110 versions_response.as_deref()
2111 {
2112 for version_map in version_maps {
2113 if version_map.index() == Some(index) {
2114 let Some(metadata) = version_map.get_metadata(version) else {
2115 continue;
2116 };
2117 debug!("Found registry-provided metadata for: {dist}");
2118 return Ok(Some(Response::Dist {
2119 dist,
2120 metadata: MetadataResponse::Found(
2121 ArchiveMetadata::from_metadata23(metadata),
2122 ),
2123 }));
2124 }
2125 }
2126 }
2127
2128 let versions_response = self
2130 .index
2131 .explicit()
2132 .get(&(dist.name().clone(), index.clone()));
2133 if let Some(VersionsResponse::Found(version_maps)) =
2134 versions_response.as_deref()
2135 {
2136 for version_map in version_maps {
2137 let Some(metadata) = version_map.get_metadata(version) else {
2138 continue;
2139 };
2140 debug!("Found registry-provided metadata for: {dist}");
2141 return Ok(Some(Response::Dist {
2142 dist,
2143 metadata: MetadataResponse::Found(
2144 ArchiveMetadata::from_metadata23(metadata),
2145 ),
2146 }));
2147 }
2148 }
2149 }
2150 }
2151
2152 let metadata = provider
2153 .get_or_build_wheel_metadata(&dist)
2154 .boxed_local()
2155 .await?;
2156
2157 if let MetadataResponse::Found(metadata) = &metadata {
2158 if &metadata.metadata.name != dist.name() {
2159 return Err(ResolveError::MismatchedPackageName {
2160 request: "distribution metadata",
2161 expected: dist.name().clone(),
2162 actual: metadata.metadata.name.clone(),
2163 });
2164 }
2165 }
2166
2167 Ok(Some(Response::Dist { dist, metadata }))
2168 }
2169
2170 Request::Installed(dist) => {
2171 let metadata = provider.get_installed_metadata(&dist).boxed_local().await?;
2172
2173 if let MetadataResponse::Found(metadata) = &metadata {
2174 if &metadata.metadata.name != dist.name() {
2175 return Err(ResolveError::MismatchedPackageName {
2176 request: "installed metadata",
2177 expected: dist.name().clone(),
2178 actual: metadata.metadata.name.clone(),
2179 });
2180 }
2181 }
2182
2183 Ok(Some(Response::Installed { dist, metadata }))
2184 }
2185
2186 Request::Prefetch(package_name, range, python_requirement) => {
2188 let versions_response = self
2190 .index
2191 .implicit()
2192 .get_registered(package_name.clone())
2193 .ok_or_else(|| ResolveError::UnregisteredTask(package_name.to_string()))?
2194 .wait()
2195 .await;
2196
2197 let version_map = match *versions_response {
2198 VersionsResponse::Found(ref version_map) => version_map,
2199 VersionsResponse::NoIndex => {
2201 self.unavailable_packages
2202 .pin()
2203 .insert(package_name.clone(), UnavailablePackage::NoIndex);
2204
2205 return Ok(None);
2206 }
2207 VersionsResponse::Offline => {
2208 self.unavailable_packages
2209 .pin()
2210 .insert(package_name.clone(), UnavailablePackage::Offline);
2211
2212 return Ok(None);
2213 }
2214 VersionsResponse::NotFound => {
2215 self.unavailable_packages
2216 .pin()
2217 .insert(package_name.clone(), UnavailablePackage::NotFound);
2218
2219 return Ok(None);
2220 }
2221 };
2222
2223 let env = ResolverEnvironment::universal(vec![]);
2225
2226 let Some(candidate) = self.selector.select(
2229 &package_name,
2230 &range,
2231 version_map,
2232 &self.preferences,
2233 &self.installed_packages,
2234 &self.exclusions,
2235 None,
2236 &env,
2237 self.tags.as_ref(),
2238 ) else {
2239 return Ok(None);
2240 };
2241
2242 let Some(dist) = candidate.compatible() else {
2244 return Ok(None);
2245 };
2246
2247 for version_map in version_map {
2249 if let Some(metadata) = version_map.get_metadata(candidate.version()) {
2250 let dist = dist.for_resolution();
2251 if version_map.index() == dist.index() {
2252 debug!("Found registry-provided metadata for: {dist}");
2253
2254 let metadata =
2255 MetadataResponse::Found(ArchiveMetadata::from_metadata23(metadata));
2256
2257 let dist = dist.to_owned();
2258 if &package_name != dist.name() {
2259 return Err(ResolveError::MismatchedPackageName {
2260 request: "distribution",
2261 expected: package_name,
2262 actual: dist.name().clone(),
2263 });
2264 }
2265
2266 let response = match dist {
2267 ResolvedDist::Installable { dist, .. } => Response::Dist {
2268 dist: (*dist).clone(),
2269 metadata,
2270 },
2271 ResolvedDist::Installed { dist } => Response::Installed {
2272 dist: (*dist).clone(),
2273 metadata,
2274 },
2275 };
2276
2277 return Ok(Some(response));
2278 }
2279 }
2280 }
2281
2282 if dist.wheel().is_none() {
2286 if !self.selector.use_highest_version(&package_name, &env) {
2287 if let Some((lower, _)) = range.iter().next() {
2288 if lower == Bound::Unbounded {
2289 debug!(
2290 "Skipping prefetch for unbounded minimum-version range: {package_name} ({range})"
2291 );
2292 return Ok(None);
2293 }
2294 }
2295 }
2296 }
2297
2298 if let Some(requires_python) = dist.requires_python() {
2300 if !python_requirement.target().is_contained_by(requires_python) {
2301 return Ok(None);
2302 }
2303 }
2304
2305 if !self
2307 .hasher
2308 .allows_package(candidate.name(), candidate.version())
2309 {
2310 return Ok(None);
2311 }
2312
2313 let dist = dist.for_resolution();
2315 if self.index.distributions().register(dist.distribution_id()) {
2316 let dist = dist.to_owned();
2317 if &package_name != dist.name() {
2318 return Err(ResolveError::MismatchedPackageName {
2319 request: "distribution",
2320 expected: package_name,
2321 actual: dist.name().clone(),
2322 });
2323 }
2324
2325 let response = match dist {
2326 ResolvedDist::Installable { dist, .. } => {
2327 let metadata = provider
2328 .get_or_build_wheel_metadata(&dist)
2329 .boxed_local()
2330 .await?;
2331
2332 Response::Dist {
2333 dist: (*dist).clone(),
2334 metadata,
2335 }
2336 }
2337 ResolvedDist::Installed { dist } => {
2338 let metadata =
2339 provider.get_installed_metadata(&dist).boxed_local().await?;
2340
2341 Response::Installed {
2342 dist: (*dist).clone(),
2343 metadata,
2344 }
2345 }
2346 };
2347
2348 Ok(Some(response))
2349 } else {
2350 Ok(None)
2351 }
2352 }
2353 }
2354 }
2355
2356 fn convert_no_solution_err(
2357 &self,
2358 mut err: pubgrub::NoSolutionError<UvDependencyProvider>,
2359 fork_urls: ForkUrls,
2360 fork_indexes: ForkIndexes,
2361 known_versions: &FxHashMap<PackageName, Arc<[Version]>>,
2362 env: ResolverEnvironment,
2363 current_environment: MarkerEnvironment,
2364 visited: &FxHashSet<PackageName>,
2365 ) -> ResolveError {
2366 err = NoSolutionError::collapse_local_version_segments(NoSolutionError::collapse_proxies(
2367 err,
2368 ));
2369 err = NoSolutionError::narrow_widened_sets(err, known_versions);
2370
2371 let mut unavailable_packages = FxHashMap::default();
2372 for package in derivation_tree_packages(&err) {
2373 if let PubGrubPackageInner::Package { name, .. } = &**package {
2374 if let Some(reason) = self.unavailable_packages.pin().get(name) {
2375 unavailable_packages.insert(name.clone(), reason.clone());
2376 }
2377 }
2378 }
2379
2380 let mut incomplete_packages = FxHashMap::default();
2381 let incomplete_packages_cache = self.incomplete_packages.pin();
2382 for package in derivation_tree_packages(&err) {
2383 if let PubGrubPackageInner::Package { name, .. } = &**package
2384 && let Some(versions) = incomplete_packages_cache.get(name)
2385 {
2386 for (version, reason) in &versions.pin() {
2387 incomplete_packages
2388 .entry(name.clone())
2389 .or_insert_with(BTreeMap::default)
2390 .insert(version.clone(), reason.clone());
2391 }
2392 }
2393 }
2394
2395 let mut available_indexes = FxHashMap::default();
2396 let mut included_versions = FxHashMap::default();
2397 let mut available_versions = FxHashMap::default();
2398
2399 let available_version_cutoff: Option<jiff::Timestamp> =
2400 std::env::var(EnvVars::UV_INTERNAL__TEST_AVAILABLE_VERSION_CUTOFF)
2401 .ok()
2402 .and_then(|s| s.parse().ok());
2403
2404 for package in derivation_tree_packages(&err) {
2405 let Some(name) = package.name() else { continue };
2406 if !visited.contains(name) {
2407 continue;
2412 }
2413 let versions_response = if let Some(index) = fork_indexes.get(name) {
2414 self.index
2415 .explicit()
2416 .get(&(name.clone(), index.url().clone()))
2417 } else {
2418 self.index.implicit().get(name)
2419 };
2420 if let Some(response) = versions_response {
2421 if let VersionsResponse::Found(ref version_maps) = *response {
2422 for version_map in version_maps {
2424 let package_included_versions = included_versions
2425 .entry(name.clone())
2426 .or_insert_with(BTreeSet::new);
2427 let package_available_versions = available_versions
2428 .entry(name.clone())
2429 .or_insert_with(BTreeSet::new);
2430
2431 for (version, dists) in version_map.iter(&Ranges::full()) {
2432 let excluded_from_included = || {
2437 let Some(included_version_cutoff) =
2438 version_map.included_version_cutoff()
2439 else {
2440 return false;
2441 };
2442 let Some(prioritized_dist) = dists.prioritized_dist() else {
2443 return true;
2444 };
2445 prioritized_dist.files().all(|file| {
2446 file.upload_time_utc_ms.is_none_or(|upload_time| {
2447 upload_time >= included_version_cutoff.as_millisecond()
2448 })
2449 })
2450 };
2451
2452 if !excluded_from_included() {
2453 package_included_versions.insert(version.clone());
2454 }
2455
2456 let excluded_from_available = || {
2462 let Some(ref exclude_newer) = available_version_cutoff else {
2463 return false;
2464 };
2465 let Some(prioritized_dist) = dists.prioritized_dist() else {
2466 return false;
2467 };
2468 prioritized_dist.files().all(|file| {
2469 file.upload_time_utc_ms.is_some_and(|upload_time| {
2470 upload_time >= exclude_newer.as_millisecond()
2471 })
2472 })
2473 };
2474
2475 if !excluded_from_available() {
2476 package_available_versions.insert(version.clone());
2477 }
2478 }
2479 }
2480
2481 available_indexes
2483 .entry(name.clone())
2484 .or_insert(BTreeSet::new())
2485 .extend(
2486 version_maps
2487 .iter()
2488 .filter_map(|version_map| version_map.index().cloned()),
2489 );
2490 }
2491 }
2492 }
2493
2494 ResolveError::NoSolution(Box::new(NoSolutionError::new(
2495 err,
2496 self.index.clone(),
2497 included_versions,
2498 available_versions,
2499 available_indexes,
2500 self.selector.clone(),
2501 self.python_requirement.clone(),
2502 self.locations.clone(),
2503 self.capabilities.clone(),
2504 unavailable_packages,
2505 incomplete_packages,
2506 fork_urls,
2507 fork_indexes,
2508 env,
2509 current_environment,
2510 self.tags.clone(),
2511 self.workspace_members.clone(),
2512 self.options.clone(),
2513 )))
2514 }
2515
2516 fn on_progress(&self, package: &PubGrubPackage, version: &Version) {
2517 if let Some(reporter) = self.reporter.as_ref() {
2518 match &**package {
2519 PubGrubPackageInner::Root(_) => {}
2520 PubGrubPackageInner::Python(_) => {}
2521 PubGrubPackageInner::System(_) => {}
2522 PubGrubPackageInner::Marker { .. } => {}
2523 PubGrubPackageInner::Extra { .. } => {}
2524 PubGrubPackageInner::Group { .. } => {}
2525 PubGrubPackageInner::Package { name, .. } => {
2526 reporter.on_progress(name, &VersionOrUrlRef::Version(version));
2527 }
2528 }
2529 }
2530 }
2531
2532 fn on_complete(&self) {
2533 if let Some(reporter) = self.reporter.as_ref() {
2534 reporter.on_complete();
2535 }
2536 }
2537}
2538
2539#[derive(Clone, Default)]
2542struct KnownVersions(FxHashMap<PackageName, Arc<[Version]>>);
2543
2544impl KnownVersions {
2545 fn get_or_update<'a, InstalledPackages: InstalledPackagesProvider>(
2558 &'a mut self,
2559 index: &InMemoryIndex,
2560 installed_packages: &InstalledPackages,
2561 source: PackageSource<'_>,
2562 package: &PubGrubPackage,
2563 ) -> Option<&'a [Version]> {
2564 let name = package.name_no_root()?;
2565 let PackageSource::Registry(index_metadata) = source else {
2567 return None;
2568 };
2569 if !self.0.contains_key(name) {
2570 let response = if let Some(index_metadata) = index_metadata {
2571 index
2572 .explicit()
2573 .get(&(name.clone(), index_metadata.url().clone()))?
2574 } else {
2575 index.implicit().get(name)?
2576 };
2577 let VersionsResponse::Found(ref version_maps) = *response else {
2578 return None;
2579 };
2580 let mut versions: Vec<Version> = version_maps
2581 .iter()
2582 .flat_map(|version_map| version_map.included_versions().cloned())
2583 .chain(
2584 installed_packages
2585 .get_packages(name)
2586 .iter()
2587 .map(|dist| dist.version().clone()),
2588 )
2589 .collect();
2590 versions.sort_unstable();
2591 versions.dedup();
2592 self.0.insert(name.clone(), versions.into());
2593 }
2594 Some(&self.0[name][..])
2595 }
2596}
2597
2598#[derive(Clone, Default)]
2600enum ForkContinuation {
2601 #[default]
2603 Propagate,
2604 SelectVersion { package: Id<PubGrubPackage> },
2606 UseVersion {
2608 package: Id<PubGrubPackage>,
2609 version: Version,
2610 },
2611}
2612
2613#[derive(Clone)]
2615pub(crate) struct ForkState<'index> {
2616 pubgrub: State<UvDependencyProvider>,
2624 continuation: ForkContinuation,
2626 next: Id<PubGrubPackage>,
2628 pins: FilePins<'index>,
2637 fork_urls: ForkUrls,
2643 fork_indexes: ForkIndexes,
2648 priorities: PubGrubPriorities,
2654 added_dependencies: FxHashMap<Id<PubGrubPackage>, FxHashSet<Version>>,
2657 pre_visited: FxHashMap<Id<PubGrubPackage>, Range<Version>>,
2659 selected_versions: FxHashMap<Id<PubGrubPackage>, (Range<Version>, Version)>,
2661 known_versions: KnownVersions,
2665 env: ResolverEnvironment,
2680 python_requirement: PythonRequirement,
2693 conflict_tracker: ConflictTracker,
2694 prefetcher: BatchPrefetcher,
2698}
2699
2700impl<'index> ForkState<'index> {
2701 fn new(
2702 pubgrub: State<UvDependencyProvider>,
2703 env: ResolverEnvironment,
2704 python_requirement: PythonRequirement,
2705 prefetcher: BatchPrefetcher,
2706 ) -> Self {
2707 Self {
2708 continuation: ForkContinuation::Propagate,
2709 next: pubgrub.root_package,
2710 pubgrub,
2711 pins: FilePins::default(),
2712 fork_urls: ForkUrls::default(),
2713 fork_indexes: ForkIndexes::default(),
2714 priorities: PubGrubPriorities::default(),
2715 added_dependencies: FxHashMap::default(),
2716 pre_visited: FxHashMap::default(),
2717 selected_versions: FxHashMap::default(),
2718 known_versions: KnownVersions::default(),
2719 env,
2720 python_requirement,
2721 conflict_tracker: ConflictTracker::default(),
2722 prefetcher,
2723 }
2724 }
2725
2726 fn visit_package_version_dependencies(
2729 &mut self,
2730 for_package: Id<PubGrubPackage>,
2731 for_version: &Version,
2732 urls: &Urls,
2733 indexes: &Indexes,
2734 dependencies: &[PubGrubDependency],
2735 git: &GitResolver,
2736 workspace_members: &BTreeSet<PackageName>,
2737 resolution_strategy: &ResolutionStrategy,
2738 ) -> Result<(), ResolveError> {
2739 for dependency in dependencies {
2740 let PubGrubDependency {
2741 package,
2742 version,
2743 parent: _,
2744 source,
2745 } = dependency;
2746
2747 let mut has_url = false;
2748 if let Some(name) = package.name() {
2749 for url in urls.get_url(&self.env, name, source.verbatim_url(), git)? {
2754 self.fork_urls.insert(name, url, &self.env)?;
2755 has_url = true;
2756 }
2757
2758 if let Some(index) = source.explicit_index() {
2759 self.fork_indexes.insert(name, index, &self.env)?;
2760 }
2761
2762 for index in indexes.get(name, &self.env) {
2764 self.fork_indexes.insert(name, index, &self.env)?;
2765 }
2766 }
2767
2768 if let Some(name) = self.pubgrub.package_store[for_package]
2769 .name_no_root()
2770 .filter(|name| !workspace_members.contains(name))
2771 {
2772 debug!(
2773 "Adding transitive dependency for {name}=={for_version}: {package}{version}"
2774 );
2775 } else {
2776 debug!("Adding direct dependency: {package}{version}");
2778
2779 let missing_lower_bound = version
2781 .bounding_range()
2782 .is_none_or(|(lowest, _highest)| lowest == Bound::Unbounded);
2783 let strategy_lowest = matches!(
2784 resolution_strategy,
2785 ResolutionStrategy::Lowest | ResolutionStrategy::LowestDirect(..)
2786 );
2787
2788 if !has_url && missing_lower_bound && strategy_lowest {
2789 let name = package.name_no_root().unwrap();
2790 let bound_on_other_package = dependencies.iter().any(|other| {
2797 Some(name) == other.package.name()
2798 && !other
2799 .version
2800 .bounding_range()
2801 .is_none_or(|(lowest, _highest)| lowest == Bound::Unbounded)
2802 });
2803
2804 if !bound_on_other_package {
2805 warn_user_once!(
2806 "The direct dependency `{name}` is unpinned. \
2807 Consider setting a lower bound when using `--resolution lowest` \
2808 or `--resolution lowest-direct` to avoid using outdated versions.",
2809 );
2810 }
2811 }
2812 }
2813
2814 self.priorities.insert(package, version, &self.fork_urls);
2816 if let Some(base_package) = package.base_package() {
2819 self.priorities
2820 .insert(&base_package, version, &self.fork_urls);
2821 }
2822 }
2823
2824 Ok(())
2825 }
2826
2827 fn add_package_version_dependencies<InstalledPackages: InstalledPackagesProvider>(
2833 &mut self,
2834 for_package: Id<PubGrubPackage>,
2835 for_version: &Version,
2836 dependencies: Vec<PubGrubDependency>,
2837 index: &InMemoryIndex,
2838 installed_packages: &InstalledPackages,
2839 ) {
2840 for dependency in &dependencies {
2841 let PubGrubDependency {
2842 package,
2843 version,
2844 parent: _,
2845 source: _,
2846 } = dependency;
2847
2848 let Some(base_package) = package.base_package() else {
2849 continue;
2850 };
2851
2852 let proxy_package = self.pubgrub.package_store.alloc(package.clone());
2853 let base_package_id = self.pubgrub.package_store.alloc(base_package.clone());
2854 self.pubgrub.add_proxy_package_incompatibility(
2855 proxy_package,
2856 base_package_id,
2857 version.clone(),
2858 );
2859 }
2860
2861 let versions = self.widen_version_to_gap(for_version, index, installed_packages);
2864 let conflict = self.pubgrub.add_package_version_dependencies(
2865 self.next,
2866 for_version.clone(),
2867 versions,
2868 dependencies.into_iter().map(|dependency| {
2869 let PubGrubDependency {
2870 package,
2871 version,
2872 parent: _,
2873 source: _,
2874 } = dependency;
2875 (package, version)
2876 }),
2877 );
2878
2879 if let Some(incompatibility) = conflict {
2882 self.record_conflict(for_package, Some(for_version), incompatibility);
2883 }
2884 }
2885
2886 fn widen_version_to_gap<InstalledPackages: InstalledPackagesProvider>(
2888 &mut self,
2889 version: &Version,
2890 index: &InMemoryIndex,
2891 installed_packages: &InstalledPackages,
2892 ) -> Range<Version> {
2893 widen_to_gap(
2894 version,
2895 self.known_versions.get_or_update(
2896 index,
2897 installed_packages,
2898 PackageSource::from_fork(
2899 &self.pubgrub.package_store[self.next],
2900 &self.fork_urls,
2901 &self.fork_indexes,
2902 ),
2903 &self.pubgrub.package_store[self.next],
2904 ),
2905 )
2906 }
2907
2908 fn record_conflict(
2909 &mut self,
2910 affected: Id<PubGrubPackage>,
2911 version: Option<&Version>,
2912 incompatibility: IncompId<PubGrubPackage, Range<Version>, UnavailableReason>,
2913 ) {
2914 let mut culprit_is_real = false;
2915 for (incompatible, _term) in self.pubgrub.incompatibility_store[incompatibility].iter() {
2916 if incompatible == affected {
2917 continue;
2918 }
2919 if self.pubgrub.package_store[affected].name()
2920 == self.pubgrub.package_store[incompatible].name()
2921 {
2922 continue;
2925 }
2926 culprit_is_real = true;
2927 let culprit_count = self
2928 .conflict_tracker
2929 .culprit
2930 .entry(incompatible)
2931 .or_default();
2932 *culprit_count += 1;
2933 if *culprit_count == CONFLICT_THRESHOLD {
2934 self.conflict_tracker.deprioritize.push(incompatible);
2935 }
2936 }
2937 if culprit_is_real {
2940 if tracing::enabled!(Level::DEBUG) {
2941 let incompatibility = self.pubgrub.incompatibility_store[incompatibility]
2942 .iter()
2943 .map(|(package, _term)| &self.pubgrub.package_store[package])
2944 .join(", ");
2945 if let Some(version) = version {
2946 debug!(
2947 "Recording dependency conflict of {}=={} from incompatibility of ({})",
2948 self.pubgrub.package_store[affected], version, incompatibility
2949 );
2950 } else {
2951 debug!(
2952 "Recording unit propagation conflict of {} from incompatibility of ({})",
2953 self.pubgrub.package_store[affected], incompatibility
2954 );
2955 }
2956 }
2957
2958 let affected_count = self.conflict_tracker.affected.entry(self.next).or_default();
2959 *affected_count += 1;
2960 if *affected_count == CONFLICT_THRESHOLD {
2961 self.conflict_tracker.prioritize.push(self.next);
2962 }
2963 }
2964 }
2965
2966 fn reprioritize_conflicts(&mut self) {
2970 for package in self.conflict_tracker.prioritize.drain(..) {
2971 let changed = self
2972 .priorities
2973 .mark_conflict_early(&self.pubgrub.package_store[package]);
2974 if changed {
2975 debug!(
2976 "Package {} has too many conflicts (affected), prioritizing",
2977 &self.pubgrub.package_store[package]
2978 );
2979 } else {
2980 debug!(
2981 "Package {} has too many conflicts (affected), already {:?}",
2982 self.pubgrub.package_store[package],
2983 self.priorities.get(&self.pubgrub.package_store[package])
2984 );
2985 }
2986 }
2987
2988 for package in self.conflict_tracker.deprioritize.drain(..) {
2989 let changed = self
2990 .priorities
2991 .mark_conflict_late(&self.pubgrub.package_store[package]);
2992 if changed {
2993 debug!(
2994 "Package {} has too many conflicts (culprit), deprioritizing and backtracking",
2995 self.pubgrub.package_store[package],
2996 );
2997 let backtrack_level = self.pubgrub.backtrack_package(package);
2998 if let Some(backtrack_level) = backtrack_level {
2999 debug!("Backtracked {backtrack_level} decisions");
3000 } else {
3001 debug!(
3002 "Package {} is not decided, cannot backtrack",
3003 self.pubgrub.package_store[package]
3004 );
3005 }
3006 } else {
3007 debug!(
3008 "Package {} has too many conflicts (culprit), already {:?}",
3009 self.pubgrub.package_store[package],
3010 self.priorities.get(&self.pubgrub.package_store[package])
3011 );
3012 }
3013 }
3014 }
3015
3016 fn add_unavailable_version<InstalledPackages: InstalledPackagesProvider>(
3024 &mut self,
3025 version: Version,
3026 reason: UnavailableVersion,
3027 index: &InMemoryIndex,
3028 installed_packages: &InstalledPackages,
3029 ) {
3030 let versions = self.widen_version_to_gap(&version, index, installed_packages);
3031
3032 if let UnavailableVersion::IncompatibleDist(
3036 IncompatibleDist::Source(IncompatibleSource::RequiresPython(requires_python, kind))
3037 | IncompatibleDist::Wheel(IncompatibleWheel::RequiresPython(requires_python, kind)),
3038 ) = reason
3039 {
3040 let package = &self.next;
3041 let python = self.pubgrub.package_store.alloc(PubGrubPackage::from(
3042 PubGrubPackageInner::Python(match kind {
3043 PythonRequirementKind::Installed => PubGrubPython::Installed,
3044 PythonRequirementKind::Target => PubGrubPython::Target,
3045 }),
3046 ));
3047 self.pubgrub
3048 .add_incompatibility(Incompatibility::from_dependency(
3049 *package,
3050 versions,
3051 (
3052 python,
3053 Range::from_versions(release_specifiers_to_ranges(requires_python)),
3054 ),
3055 ));
3056 self.pubgrub
3057 .partial_solution
3058 .add_decision(self.next, version);
3059 return;
3060 }
3061 self.pubgrub
3062 .add_incompatibility(Incompatibility::custom_term(
3063 self.next,
3064 Term::Positive(versions),
3065 UnavailableReason::Version(reason),
3066 ));
3067 }
3068
3069 fn with_continuation(mut self, continuation: ForkContinuation) -> Self {
3070 self.continuation = continuation;
3071 self
3072 }
3073
3074 fn with_env(mut self, env: ResolverEnvironment) -> Self {
3076 self.selected_versions.clear();
3077 self.env = env;
3078 if let Some(req) = self.env.narrow_python_requirement(&self.python_requirement) {
3080 debug!("Narrowed `requires-python` bound to: {}", req.target());
3081 self.python_requirement = req;
3082 }
3083 self
3084 }
3085
3086 fn source(
3090 &self,
3091 name: &PackageName,
3092 version: &Version,
3093 ) -> (Option<&VerbatimParsedUrl>, Option<&IndexUrl>) {
3094 let url = self.fork_urls.get(name);
3095 let index = url
3096 .is_none()
3097 .then(|| {
3098 self.pins
3099 .get(name, version)
3100 .expect("Every package should be pinned")
3101 .index()
3102 })
3103 .flatten();
3104 (url, index)
3105 }
3106
3107 fn into_resolution(self) -> Resolution<'index> {
3108 let solution: FxHashMap<_, _> = self.pubgrub.partial_solution.extract_solution().collect();
3109 let edge_count: usize = solution
3110 .keys()
3111 .map(|package| self.pubgrub.incompatibilities[package].len())
3112 .sum();
3113 let mut edges: Vec<ResolutionDependencyEdge> = Vec::with_capacity(edge_count);
3114 for (package, self_version) in &solution {
3115 for id in &self.pubgrub.incompatibilities[package] {
3116 let incompatibility = &self.pubgrub.incompatibility_store[*id];
3117 let pubgrub::Kind::FromDependencyOf(self_package, dependency_package) =
3118 &incompatibility.kind
3119 else {
3120 continue;
3121 };
3122 let (self_package, dependency_package) = (*self_package, *dependency_package);
3123 let Some((self_range, dependency_range)) =
3124 incompatibility.dependency_version_sets()
3125 else {
3126 continue;
3127 };
3128 let dependency_range =
3129 dependency_range.map_or_else(|| Cow::Owned(Range::empty()), Cow::Borrowed);
3130 if *package != self_package {
3131 continue;
3132 }
3133 if !self_range.contains(self_version) {
3134 continue;
3135 }
3136 let Some(dependency_version) = solution.get(&dependency_package) else {
3137 continue;
3138 };
3139 if !dependency_range.contains(dependency_version) {
3140 continue;
3141 }
3142
3143 let self_package = &self.pubgrub.package_store[self_package];
3144 let dependency_package = &self.pubgrub.package_store[dependency_package];
3145
3146 let (self_name, self_kind) = match &**self_package {
3147 PubGrubPackageInner::Package {
3148 name: self_name,
3149 kind: self_kind,
3150 marker: _,
3151 } => (Some(self_name), self_kind),
3152
3153 PubGrubPackageInner::Root(_) => (None, &PackageNodeKind::Base),
3154
3155 _ => continue,
3156 };
3157
3158 let (name, kind, marker) = match &**dependency_package {
3159 PubGrubPackageInner::Package { name, kind, marker } => {
3160 debug_assert!(kind.is_base(), "Packages should depend on a variant proxy");
3161
3162 if self_kind.group().is_none() && self_name == Some(name) {
3165 continue;
3166 }
3167 (name, kind.clone(), *marker)
3168 }
3169 PubGrubPackageInner::Marker { name, marker } => {
3170 if self_kind.group().is_none() && self_name == Some(name) {
3171 continue;
3172 }
3173 (name, PackageNodeKind::Base, *marker)
3174 }
3175 PubGrubPackageInner::Extra {
3176 name,
3177 extra,
3178 marker,
3179 } => {
3180 if self_kind.group().is_none() {
3181 debug_assert!(self_name != Some(name), "Extras should be flattened");
3182 }
3183 (name, PackageNodeKind::Extra(extra.clone()), *marker)
3184 }
3185 PubGrubPackageInner::Group {
3186 name,
3187 group,
3188 marker,
3189 } => {
3190 debug_assert!(self_name != Some(name), "Groups should be flattened");
3191 (name, PackageNodeKind::Group(group.clone()), *marker)
3192 }
3193 PubGrubPackageInner::Root(_)
3194 | PubGrubPackageInner::Python(_)
3195 | PubGrubPackageInner::System(_) => continue,
3196 };
3197 let from = self_name.map(|name| {
3198 let (url, index) = self.source(name, self_version);
3199 ResolutionNode {
3200 package: ResolutionPackage {
3201 name: name.clone(),
3202 kind: self_kind.clone(),
3203 url: url.cloned(),
3204 index: index.cloned(),
3205 },
3206 version: self_version.clone(),
3207 }
3208 });
3209
3210 let (url, index) = self.source(name, dependency_version);
3211 let to = ResolutionNode {
3212 package: ResolutionPackage {
3213 name: name.clone(),
3214 kind,
3215 url: url.cloned(),
3216 index: index.cloned(),
3217 },
3218 version: dependency_version.clone(),
3219 };
3220 let edge = ResolutionDependencyEdge { from, to, marker };
3221
3222 if let PubGrubPackageInner::Extra { .. } = &**dependency_package {
3225 let mut base_edge = edge.clone();
3226 base_edge.to.package.kind = PackageNodeKind::Base;
3227 edges.push(edge);
3228 edges.push(base_edge);
3229 } else {
3230 edges.push(edge);
3231 }
3232 }
3233 }
3234
3235 let nodes = solution
3236 .into_iter()
3237 .filter_map(|(package, version)| {
3238 if let PubGrubPackageInner::Package {
3239 name,
3240 kind,
3241 marker: MarkerTree::TRUE,
3242 } = &*self.pubgrub.package_store[package]
3243 {
3244 let (url, index) = self.source(name, &version);
3245 Some((
3246 ResolutionPackage {
3247 name: name.clone(),
3248 kind: kind.clone(),
3249 url: url.cloned(),
3250 index: index.cloned(),
3251 },
3252 version,
3253 ))
3254 } else {
3255 None
3256 }
3257 })
3258 .collect();
3259
3260 Resolution {
3261 nodes,
3262 edges,
3263 pins: self.pins,
3264 env: self.env,
3265 }
3266 }
3267}
3268
3269fn widen_to_gap(version: &Version, known_versions: Option<&[Version]>) -> Range<Version> {
3278 let versions = Range::singleton(version.clone());
3279 match known_versions {
3280 Some(known_versions) if !known_versions.is_empty() => {
3281 versions.widen_versions(known_versions)
3282 }
3283 _ => versions,
3284 }
3285}
3286
3287#[derive(Debug)]
3289#[expect(clippy::large_enum_variant)]
3290pub(crate) enum Request {
3291 Package(PackageName, Option<IndexMetadata>),
3293 Dist(Dist),
3295 Installed(InstalledDist),
3297 Prefetch(PackageName, Range<Version>, PythonRequirement),
3299}
3300
3301impl<'a> From<ResolvedDistRef<'a>> for Request {
3302 fn from(dist: ResolvedDistRef<'a>) -> Self {
3303 match dist {
3309 ResolvedDistRef::InstallableRegistrySourceDist { sdist, prioritized } => {
3310 let source = prioritized.source_dist().expect("a source distribution");
3313 assert_eq!(
3314 (&sdist.name, &sdist.version),
3315 (&source.name, &source.version),
3316 "expected chosen sdist to match prioritized sdist"
3317 );
3318 Self::Dist(Dist::Source(SourceDist::Registry(source)))
3319 }
3320 ResolvedDistRef::InstallableRegistryBuiltDist {
3321 wheel, prioritized, ..
3322 } => {
3323 assert_eq!(
3324 Some(&wheel.filename),
3325 prioritized.best_wheel().map(|(wheel, _)| &wheel.filename),
3326 "expected chosen wheel to match best wheel"
3327 );
3328 let built = prioritized.built_dist().expect("at least one wheel");
3331 Self::Dist(Dist::Built(BuiltDist::Registry(built)))
3332 }
3333 ResolvedDistRef::Installed { dist } => Self::Installed(dist.clone()),
3334 }
3335 }
3336}
3337
3338impl Display for Request {
3339 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
3340 match self {
3341 Self::Package(package_name, _) => {
3342 write!(f, "Versions {package_name}")
3343 }
3344 Self::Dist(dist) => {
3345 write!(f, "Metadata {dist}")
3346 }
3347 Self::Installed(dist) => {
3348 write!(f, "Installed metadata {dist}")
3349 }
3350 Self::Prefetch(package_name, range, _) => {
3351 write!(f, "Prefetch {package_name} {range}")
3352 }
3353 }
3354 }
3355}
3356
3357#[derive(Debug)]
3358#[expect(clippy::large_enum_variant)]
3359enum Response {
3360 Package(PackageName, Option<IndexUrl>, VersionsResponse),
3362 Dist {
3364 dist: Dist,
3365 metadata: MetadataResponse,
3366 },
3367 Installed {
3369 dist: InstalledDist,
3370 metadata: MetadataResponse,
3371 },
3372}
3373
3374enum Dependencies {
3380 Unavailable(UnavailableVersion),
3382 Available(Vec<PubGrubDependency>),
3388 RequiresPython(VersionSpecifiers),
3390 Unforkable(Vec<PubGrubDependency>),
3396}
3397
3398#[derive(Debug)]
3405enum ForkedDependencies {
3406 Unavailable(UnavailableVersion),
3408 Unforked(Vec<PubGrubDependency>),
3412 Forked {
3418 forks: Vec<Fork>,
3419 diverging_packages: BTreeSet<PackageName>,
3421 },
3422 RequiresPython(VersionSpecifiers),
3424}
3425
3426impl ForkedDependencies {
3427 fn from_dependencies_universal(
3434 dependencies: Dependencies,
3435 env: &ResolverEnvironment,
3436 python_requirement: &PythonRequirement,
3437 conflicts: &Conflicts,
3438 ) -> Self {
3439 let deps = match dependencies {
3440 Dependencies::Available(deps) => deps,
3441 Dependencies::Unforkable(deps) => return Self::Unforked(deps),
3442 Dependencies::RequiresPython(requires_python) => {
3443 return Self::RequiresPython(requires_python);
3444 }
3445 Dependencies::Unavailable(err) => return Self::Unavailable(err),
3446 };
3447 let mut name_to_deps: BTreeMap<PackageName, Vec<PubGrubDependency>> = BTreeMap::new();
3448 for dep in deps {
3449 let name = dep
3450 .package
3451 .name()
3452 .expect("dependency always has a name")
3453 .clone();
3454 name_to_deps.entry(name).or_default().push(dep);
3455 }
3456 let (mut forks, diverging_packages) =
3457 Self::fork(name_to_deps, env, python_requirement, conflicts);
3458 if forks.is_empty() {
3459 Self::Unforked(vec![])
3460 } else if forks.len() == 1 {
3461 Self::Unforked(forks.pop().unwrap().dependencies)
3462 } else {
3463 Self::Forked {
3464 forks,
3465 diverging_packages,
3466 }
3467 }
3468 }
3469
3470 fn from_dependencies_platform_specific(dependencies: Dependencies) -> Self {
3473 match dependencies {
3474 Dependencies::Available(deps) | Dependencies::Unforkable(deps) => Self::Unforked(deps),
3475 Dependencies::RequiresPython(requires_python) => Self::RequiresPython(requires_python),
3476 Dependencies::Unavailable(err) => Self::Unavailable(err),
3477 }
3478 }
3479
3480 fn fork(
3488 name_to_deps: BTreeMap<PackageName, Vec<PubGrubDependency>>,
3489 env: &ResolverEnvironment,
3490 python_requirement: &PythonRequirement,
3491 conflicts: &Conflicts,
3492 ) -> (Vec<Fork>, BTreeSet<PackageName>) {
3493 let python_marker = python_requirement.to_marker_tree();
3494
3495 let mut forks = vec![Fork::new(env.clone())];
3496 let mut diverging_packages = BTreeSet::new();
3497 for (name, mut deps) in name_to_deps {
3498 assert!(!deps.is_empty(), "every name has at least one dependency");
3499 if let [dep] = deps.as_slice() {
3510 if marker::requires_python(dep.package.marker())
3518 .is_none_or(|bound| !python_requirement.raises(&bound))
3519 {
3520 let dep = deps.pop().unwrap();
3521 let marker = dep.package.marker();
3522 for fork in &mut forks {
3523 if fork.env.included_by_marker(marker) {
3524 fork.add_dependency(dep.clone());
3525 }
3526 }
3527 continue;
3528 }
3529 } else {
3530 if let Some(dep) = deps.first() {
3532 let marker = dep.package.marker();
3533 if deps.iter().all(|dep| marker == dep.package.marker()) {
3534 if marker::requires_python(marker)
3538 .is_none_or(|bound| !python_requirement.raises(&bound))
3539 {
3540 for dep in deps {
3541 for fork in &mut forks {
3542 if fork.env.included_by_marker(marker) {
3543 fork.add_dependency(dep.clone());
3544 }
3545 }
3546 }
3547 continue;
3548 }
3549 }
3550 }
3551 }
3552 for dep in deps {
3553 let mut forker = match ForkingPossibility::new(env, &dep) {
3554 ForkingPossibility::Possible(forker) => forker,
3555 ForkingPossibility::DependencyAlwaysExcluded => {
3556 continue;
3559 }
3560 ForkingPossibility::NoForkingPossible => {
3561 for fork in &mut forks {
3564 fork.add_dependency(dep.clone());
3565 }
3566 continue;
3567 }
3568 };
3569 diverging_packages.insert(name.clone());
3571
3572 let mut new = vec![];
3573 for fork in std::mem::take(&mut forks) {
3574 let Some((remaining_forker, envs)) = forker.fork(&fork.env) else {
3575 new.push(fork);
3576 continue;
3577 };
3578 forker = remaining_forker;
3579
3580 for fork_env in envs {
3581 let mut new_fork = fork.clone();
3582 new_fork.set_env(fork_env);
3583 if forker.included(&new_fork.env) {
3588 new_fork.add_dependency(dep.clone());
3589 }
3590 if new_fork.env.included_by_marker(python_marker) {
3593 new.push(new_fork);
3594 }
3595 }
3596 }
3597 forks = new;
3598 }
3599 }
3600 for set in conflicts.iter() {
3615 let mut new = vec![];
3616 for fork in std::mem::take(&mut forks) {
3617 let mut has_conflicting_dependency = false;
3625 for item in set.iter() {
3626 if fork.contains_conflicting_item(item.as_ref()) {
3627 has_conflicting_dependency = true;
3628 diverging_packages.insert(item.package().clone());
3629 break;
3630 }
3631 }
3632 if !has_conflicting_dependency {
3633 new.push(fork);
3634 continue;
3635 }
3636
3637 let non_excluded: Vec<_> = set
3643 .iter()
3644 .filter(|item| fork.env.included_by_group(item.as_ref()))
3645 .collect();
3646 if non_excluded.len() < 2 {
3647 let dominated = non_excluded.iter().all(|item| {
3652 !conflicts.iter().any(|other_set| {
3653 !std::ptr::eq(set, other_set)
3654 && other_set.contains(item.package(), item.kind().as_ref())
3655 && other_set
3656 .iter()
3657 .filter(|other_item| {
3658 other_item.package() != item.package()
3659 || other_item.kind() != item.kind()
3660 })
3661 .any(|other_item| {
3662 fork.env.included_by_group(other_item.as_ref())
3663 })
3664 })
3665 });
3666 if dominated {
3667 let rules: Vec<_> = set
3672 .iter()
3673 .filter(|item| !fork.env.included_by_group(item.as_ref()))
3674 .cloned()
3675 .map(Err)
3676 .collect();
3677 if let Some(filtered) = fork.filter(rules) {
3678 new.push(filtered);
3679 }
3680 continue;
3681 }
3682 }
3683
3684 if let Some(fork_none) = fork.clone().filter(set.iter().cloned().map(Err)) {
3686 new.push(fork_none);
3687 }
3688
3689 for (i, _) in set.iter().enumerate() {
3697 let fork_allows_group = fork.clone().filter(
3698 set.iter()
3699 .cloned()
3700 .enumerate()
3701 .map(|(j, group)| if i == j { Ok(group) } else { Err(group) }),
3702 );
3703 if let Some(fork_allows_group) = fork_allows_group {
3704 new.push(fork_allows_group);
3705 }
3706 }
3707 }
3708 forks = new;
3709 }
3710 (forks, diverging_packages)
3711 }
3712}
3713
3714#[derive(Clone, Debug)]
3724struct Fork {
3725 dependencies: Vec<PubGrubDependency>,
3734 conflicts: crate::FxHashbrownSet<ConflictItem>,
3740 env: ResolverEnvironment,
3752}
3753
3754impl Fork {
3755 fn new(env: ResolverEnvironment) -> Self {
3758 Self {
3759 dependencies: vec![],
3760 conflicts: crate::FxHashbrownSet::default(),
3761 env,
3762 }
3763 }
3764
3765 fn add_dependency(&mut self, dep: PubGrubDependency) {
3767 if let Some(conflicting_item) = dep.conflicting_item() {
3768 self.conflicts.insert(conflicting_item.to_owned());
3769 }
3770 self.dependencies.push(dep);
3771 }
3772
3773 fn set_env(&mut self, env: ResolverEnvironment) {
3778 self.env = env;
3779 self.dependencies.retain(|dep| {
3780 let marker = dep.package.marker();
3781 if self.env.included_by_marker(marker) {
3782 return true;
3783 }
3784 if let Some(conflicting_item) = dep.conflicting_item() {
3785 self.conflicts.remove(&conflicting_item);
3786 }
3787 false
3788 });
3789 }
3790
3791 fn contains_conflicting_item(&self, item: ConflictItemRef<'_>) -> bool {
3794 self.conflicts.contains(&item)
3795 }
3796
3797 fn filter(
3804 mut self,
3805 rules: impl IntoIterator<Item = Result<ConflictItem, ConflictItem>>,
3806 ) -> Option<Self> {
3807 self.env = self.env.filter_by_group(rules)?;
3808 self.dependencies.retain(|dep| {
3809 let Some(conflicting_item) = dep.conflicting_item() else {
3810 return true;
3811 };
3812 if self.env.included_by_group(conflicting_item) {
3813 return true;
3814 }
3815 match conflicting_item.kind() {
3816 ConflictKindRef::Project => {
3819 if dep.parent.is_some() {
3820 return true;
3821 }
3822 }
3823 ConflictKindRef::Group(_) => {}
3824 ConflictKindRef::Extra(_) => {}
3825 }
3826 self.conflicts.remove(&conflicting_item);
3827 false
3828 });
3829 Some(self)
3830 }
3831
3832 fn cmp_requires_python(&self, other: &Self) -> Ordering {
3834 cmp_requires_python(&self.env, &other.env)
3835 }
3836
3837 fn cmp_upper_bounds(&self, other: &Self) -> Ordering {
3839 let self_upper_bounds = self
3844 .dependencies
3845 .iter()
3846 .filter(|dep| {
3847 dep.version
3848 .bounding_range()
3849 .is_some_and(|(_, upper)| !matches!(upper, Bound::Unbounded))
3850 })
3851 .count();
3852 let other_upper_bounds = other
3853 .dependencies
3854 .iter()
3855 .filter(|dep| {
3856 dep.version
3857 .bounding_range()
3858 .is_some_and(|(_, upper)| !matches!(upper, Bound::Unbounded))
3859 })
3860 .count();
3861
3862 self_upper_bounds.cmp(&other_upper_bounds)
3863 }
3864}
3865
3866fn cmp_requires_python(
3868 self_env: &ResolverEnvironment,
3869 other_env: &ResolverEnvironment,
3870) -> Ordering {
3871 let self_bound = self_env.requires_python().unwrap_or_default();
3881 let other_bound = other_env.requires_python().unwrap_or_default();
3882 self_bound.lower().cmp(other_bound.lower())
3883}
3884
3885impl Eq for Fork {}
3886
3887impl PartialEq for Fork {
3888 fn eq(&self, other: &Self) -> bool {
3889 self.dependencies == other.dependencies && self.env == other.env
3890 }
3891}
3892
3893#[derive(Debug, Clone)]
3894pub(crate) struct VersionFork {
3895 env: ResolverEnvironment,
3897 id: Id<PubGrubPackage>,
3899 version: Option<Version>,
3901}
3902
3903fn enrich_dependency_error(
3905 error: ResolveError,
3906 id: Id<PubGrubPackage>,
3907 version: &Version,
3908 pubgrub: &State<UvDependencyProvider>,
3909) -> ResolveError {
3910 let Some(name) = pubgrub.package_store[id].name_no_root() else {
3911 return error;
3912 };
3913 let chain = DerivationChainBuilder::from_state(id, version, pubgrub).unwrap_or_default();
3914 ResolveError::Dependencies(Box::new(error), name.clone(), version.clone(), chain)
3915}
3916
3917fn find_environments(id: Id<PubGrubPackage>, state: &State<UvDependencyProvider>) -> MarkerTree {
3919 let package = &state.package_store[id];
3920 if package.is_root() {
3921 return MarkerTree::TRUE;
3922 }
3923
3924 let mut ancestors = FxHashSet::default();
3927 let mut stack = vec![id];
3928 let mut root = None;
3929 ancestors.insert(id);
3930
3931 while let Some(current) = stack.pop() {
3932 let Some(incompatibilities) = state.incompatibilities.get(¤t) else {
3933 continue;
3934 };
3935
3936 for index in incompatibilities {
3937 let incompat = &state.incompatibility_store[*index];
3938 if let Kind::FromDependencyOf(parent, child) = &incompat.kind {
3939 if current != *child {
3940 continue;
3941 }
3942 if ancestors.insert(*parent) {
3943 if state.package_store[*parent].is_root() {
3944 root = Some(*parent);
3945 }
3946 stack.push(*parent);
3947 }
3948 }
3949 }
3950 }
3951
3952 let Some(root) = root else {
3953 return MarkerTree::FALSE;
3954 };
3955
3956 let mut environments = FxHashMap::default();
3959 let mut queue = VecDeque::from([root]);
3960 environments.insert(root, MarkerTree::TRUE);
3961
3962 while let Some(current) = queue.pop_front() {
3963 let Some(current_environment) = environments.get(¤t).copied() else {
3964 continue;
3965 };
3966 let Some(incompatibilities) = state.incompatibilities.get(¤t) else {
3967 continue;
3968 };
3969
3970 for index in incompatibilities {
3971 let incompat = &state.incompatibility_store[*index];
3972 let Kind::FromDependencyOf(parent, child) = &incompat.kind else {
3973 continue;
3974 };
3975 if current != *parent || !ancestors.contains(child) {
3976 continue;
3977 }
3978
3979 let mut next_environment = state.package_store[*child].marker();
3980 next_environment = next_environment.and(current_environment);
3981
3982 let entry = environments.entry(*child).or_insert(MarkerTree::FALSE);
3983 let mut combined = *entry;
3984 combined = combined.or(next_environment);
3985 if combined != *entry {
3986 *entry = combined;
3987 queue.push_back(*child);
3988 }
3989 }
3990 }
3991
3992 environments.remove(&id).unwrap_or(MarkerTree::FALSE)
3993}
3994
3995#[derive(Debug, Default, Clone)]
3996struct ConflictTracker {
3997 affected: FxHashMap<Id<PubGrubPackage>, usize>,
3999 prioritize: Vec<Id<PubGrubPackage>>,
4003 culprit: FxHashMap<Id<PubGrubPackage>, usize>,
4005 deprioritize: Vec<Id<PubGrubPackage>>,
4009}
4010
4011#[cfg(test)]
4012mod tests {
4013 use super::*;
4014
4015 fn versions(versions: &[&str]) -> Vec<Version> {
4016 versions
4017 .iter()
4018 .map(|version| version.parse().expect("valid version"))
4019 .collect()
4020 }
4021
4022 #[test]
4023 fn widens_a_version_to_its_gap() {
4024 let known_versions = versions(&["1.0", "2.0", "3.0"]);
4025 let version: Version = "2.0".parse().expect("valid version");
4026
4027 assert_eq!(
4029 widen_to_gap(&version, Some(&known_versions)).to_string(),
4030 ">1.0, <3.0"
4031 );
4032
4033 let version: Version = "1.0".parse().expect("valid version");
4035 assert_eq!(
4036 widen_to_gap(&version, Some(&known_versions)).to_string(),
4037 "<2.0"
4038 );
4039 let version: Version = "3.0".parse().expect("valid version");
4040 assert_eq!(
4041 widen_to_gap(&version, Some(&known_versions)).to_string(),
4042 ">2.0"
4043 );
4044 }
4045
4046 #[test]
4047 fn widens_a_version_without_known_versions_to_itself() {
4048 let version: Version = "2.0".parse().expect("valid version");
4049
4050 assert_eq!(
4052 widen_to_gap(&version, None),
4053 Range::singleton(version.clone())
4054 );
4055
4056 assert_eq!(widen_to_gap(&version, Some(&[])), Range::singleton(version));
4058 }
4059}