Skip to main content

uv_resolver/resolver/
mod.rs

1//! Given a set of requirements, find a set of compatible packages.
2
3use 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::{iter, slice, thread};
11
12use either::Either;
13use futures::{FutureExt, StreamExt};
14use itertools::Itertools;
15use papaya::{HashMap, ResizeMode};
16use pubgrub::{Id, IncompId, Incompatibility, Kind, Ranges, State};
17use rustc_hash::{FxHashMap, FxHashSet};
18use tokio::sync::mpsc::{self, Receiver, Sender};
19use tokio::sync::oneshot;
20use tokio_stream::wrappers::ReceiverStream;
21use tracing::{Level, debug, info, instrument, trace, warn};
22
23use uv_configuration::{Constraints, Excludes, Overrides};
24use uv_distribution::{ArchiveMetadata, DistributionDatabase};
25use uv_distribution_types::{
26    BuiltDist, CompatibleDist, DerivationChain, Dist, DistErrorKind, Identifier, IncompatibleDist,
27    IncompatibleSource, IncompatibleWheel, IndexCapabilities, IndexLocations, IndexMetadata,
28    IndexUrl, InstalledDist, Name, PythonRequirementKind, RemoteSource, Requirement, ResolvedDist,
29    ResolvedDistRef, SourceDist, VersionOrUrlRef, implied_markers,
30};
31use uv_git::GitResolver;
32use uv_normalize::{ExtraName, GroupName, PackageName};
33use uv_pep440::{MIN_VERSION, Version, VersionSpecifiers, release_specifiers_to_ranges};
34use uv_pep508::{
35    MarkerEnvironment, MarkerExpression, MarkerOperator, MarkerTree, MarkerValueString,
36};
37use uv_platform_tags::{IncompatibleTag, Tags};
38use uv_pypi_types::{ConflictItem, ConflictItemRef, ConflictKindRef, Conflicts, VerbatimParsedUrl};
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_strategy::ForkStrategy;
49use crate::fork_urls::ForkUrls;
50use crate::manifest::Manifest;
51use crate::pins::FilePins;
52use crate::preferences::{PreferenceSource, Preferences};
53use crate::pubgrub::{
54    DependencySource, PubGrubDependency, PubGrubPackage, PubGrubPackageInner, PubGrubPriorities,
55    PubGrubPython, Range,
56};
57use crate::python_requirement::PythonRequirement;
58use crate::resolution::ResolverOutput;
59use crate::resolution_mode::ResolutionStrategy;
60pub(crate) use crate::resolver::availability::{
61    ResolverVersion, UnavailableErrorChain, UnavailablePackage, UnavailableReason,
62    UnavailableVersion, UnsatisfiableRequirement,
63};
64use crate::resolver::batch_prefetch::BatchPrefetcher;
65use crate::resolver::derivation::DerivationChainBuilder;
66pub use crate::resolver::environment::ResolverEnvironment;
67use crate::resolver::environment::{
68    ForkingPossibility, fork_version_by_marker, fork_version_by_python_requirement,
69};
70pub(crate) use crate::resolver::fork_map::{ForkMap, ForkSet};
71pub use crate::resolver::index::InMemoryIndex;
72use crate::resolver::indexes::Indexes;
73pub use crate::resolver::provider::{
74    DefaultResolverProvider, MetadataResponse, PackageVersionsResult, ResolverProvider,
75    VersionsResponse, WheelMetadataResult,
76};
77pub use crate::resolver::reporter::Reporter;
78use crate::resolver::system::SystemDependency;
79pub(crate) use crate::resolver::urls::Urls;
80use crate::universal_marker::UniversalMarker;
81use crate::yanks::AllowedYanks;
82use crate::{DependencyMode, Exclusions, FlatIndex, Options, ResolutionMode, VersionMap, marker};
83pub(crate) use provider::MetadataUnavailable;
84pub(crate) use resolution::{Resolution, ResolutionDependencyEdge, ResolutionPackage};
85
86mod availability;
87mod batch_prefetch;
88mod derivation;
89mod environment;
90mod fork_map;
91mod index;
92mod indexes;
93mod provider;
94mod reporter;
95mod resolution;
96mod system;
97mod urls;
98
99/// The number of conflicts a package may accumulate before we re-prioritize and backtrack.
100const CONFLICT_THRESHOLD: usize = 5;
101
102pub struct Resolver<Provider: ResolverProvider, InstalledPackages: InstalledPackagesProvider> {
103    state: ResolverState<InstalledPackages>,
104    provider: Provider,
105}
106
107/// State that is shared between the prefetcher and the PubGrub solver during
108/// resolution, across all forks.
109struct ResolverState<InstalledPackages: InstalledPackagesProvider> {
110    project: Option<PackageName>,
111    requirements: Vec<Requirement>,
112    constraints: Constraints,
113    overrides: Overrides,
114    excludes: Excludes,
115    preferences: Preferences,
116    git: GitResolver,
117    capabilities: IndexCapabilities,
118    locations: IndexLocations,
119    exclusions: Exclusions,
120    urls: Urls,
121    indexes: Indexes,
122    dependency_mode: DependencyMode,
123    hasher: HashStrategy,
124    env: ResolverEnvironment,
125    // The environment of the current Python interpreter.
126    current_environment: MarkerEnvironment,
127    tags: Option<Tags>,
128    python_requirement: PythonRequirement,
129    conflicts: Conflicts,
130    workspace_members: BTreeSet<PackageName>,
131    selector: CandidateSelector,
132    index: InMemoryIndex,
133    installed_packages: InstalledPackages,
134    // Papaya's maps are large on Windows, so box them to keep resolver futures small.
135    /// Incompatibilities for packages that are entirely unavailable.
136    unavailable_packages: Box<HashMap<PackageName, UnavailablePackage>>,
137    /// Incompatibilities for packages that are unavailable at specific versions.
138    incomplete_packages: Box<HashMap<PackageName, HashMap<Version, MetadataUnavailable>>>,
139    /// The options that were used to configure this resolver.
140    options: Options,
141    /// The reporter to use for this resolver.
142    reporter: Option<Arc<dyn Reporter>>,
143}
144
145impl<'a, Context: BuildContext, InstalledPackages: InstalledPackagesProvider>
146    Resolver<DefaultResolverProvider<'a, Context>, InstalledPackages>
147{
148    /// Initialize a new resolver using the default backend doing real requests.
149    ///
150    /// Reads the flat index entries.
151    ///
152    /// # Marker environment
153    ///
154    /// The marker environment is optional.
155    ///
156    /// When a marker environment is not provided, the resolver is said to be
157    /// in "universal" mode. When in universal mode, the resolution produced
158    /// may contain multiple versions of the same package. And thus, in order
159    /// to use the resulting resolution, there must be a "universal"-aware
160    /// reader of the resolution that knows to exclude distributions that can't
161    /// be used in the current environment.
162    ///
163    /// When a marker environment is provided, the resolver is in
164    /// "non-universal" mode, which corresponds to standard `pip` behavior that
165    /// works only for a specific marker environment.
166    pub fn new(
167        manifest: Manifest,
168        options: Options,
169        python_requirement: &'a PythonRequirement,
170        env: ResolverEnvironment,
171        current_environment: &MarkerEnvironment,
172        conflicts: Conflicts,
173        tags: Option<&'a Tags>,
174        flat_index: &'a FlatIndex,
175        index: &'a InMemoryIndex,
176        hasher: &'a HashStrategy,
177        build_context: &'a Context,
178        installed_packages: InstalledPackages,
179        database: DistributionDatabase<'a, Context>,
180    ) -> Result<Self, ResolveError> {
181        let provider = DefaultResolverProvider::new(
182            database,
183            flat_index,
184            tags,
185            python_requirement.target(),
186            AllowedYanks::from_manifest(&manifest, &env, options.dependency_mode),
187            hasher,
188            options.exclude_newer.clone(),
189            build_context.locations(),
190            build_context.build_options(),
191            build_context.capabilities(),
192        );
193
194        Ok(Self::new_custom_io(
195            manifest,
196            options,
197            hasher,
198            env,
199            current_environment,
200            tags.cloned(),
201            python_requirement,
202            conflicts,
203            index,
204            build_context.git(),
205            build_context.capabilities(),
206            build_context.locations(),
207            provider,
208            installed_packages,
209        ))
210    }
211}
212
213impl<Provider: ResolverProvider, InstalledPackages: InstalledPackagesProvider>
214    Resolver<Provider, InstalledPackages>
215{
216    /// Initialize a new resolver using a user provided backend.
217    pub fn new_custom_io(
218        manifest: Manifest,
219        options: Options,
220        hasher: &HashStrategy,
221        env: ResolverEnvironment,
222        current_environment: &MarkerEnvironment,
223        tags: Option<Tags>,
224        python_requirement: &PythonRequirement,
225        conflicts: Conflicts,
226        index: &InMemoryIndex,
227        git: &GitResolver,
228        capabilities: &IndexCapabilities,
229        locations: &IndexLocations,
230        provider: Provider,
231        installed_packages: InstalledPackages,
232    ) -> Self {
233        let state = ResolverState {
234            index: index.clone(),
235            git: git.clone(),
236            capabilities: capabilities.clone(),
237            selector: CandidateSelector::for_resolution(&options, &manifest, &env),
238            dependency_mode: options.dependency_mode,
239            urls: Urls::from_manifest(&manifest, &env, git, options.dependency_mode),
240            indexes: Indexes::from_manifest(&manifest, &env, options.dependency_mode),
241            project: manifest.project,
242            workspace_members: manifest.workspace_members,
243            requirements: manifest.requirements,
244            constraints: manifest.constraints,
245            overrides: manifest.overrides,
246            excludes: manifest.excludes,
247            preferences: manifest.preferences,
248            exclusions: manifest.exclusions,
249            hasher: hasher.clone(),
250            locations: locations.clone(),
251            env,
252            current_environment: current_environment.clone(),
253            tags,
254            python_requirement: python_requirement.clone(),
255            conflicts,
256            installed_packages,
257            unavailable_packages: Box::default(),
258            incomplete_packages: Box::default(),
259            options,
260            reporter: None,
261        };
262        Self { state, provider }
263    }
264
265    /// Set the [`Reporter`] to use for this installer.
266    #[must_use]
267    pub fn with_reporter(self, reporter: Arc<dyn Reporter>) -> Self {
268        Self {
269            state: ResolverState {
270                reporter: Some(reporter.clone()),
271                ..self.state
272            },
273            provider: self
274                .provider
275                .with_reporter(reporter.into_distribution_reporter()),
276        }
277    }
278
279    /// Resolve a set of requirements into a set of pinned versions.
280    pub async fn resolve(self) -> Result<ResolverOutput, ResolveError> {
281        let state = Arc::new(self.state);
282        let provider = Arc::new(self.provider);
283
284        // A channel to fetch package metadata (e.g., given `flask`, fetch all versions) and version
285        // metadata (e.g., given `flask==1.0.0`, fetch the metadata for that version).
286        // Channel size is set large to accommodate batch prefetching.
287        let (request_sink, request_stream) = mpsc::channel(300);
288
289        // Run the fetcher.
290        let requests_fut = state.clone().fetch(provider.clone(), request_stream).fuse();
291
292        // Spawn the PubGrub solver on a dedicated thread.
293        let solver = state.clone();
294        let (tx, rx) = oneshot::channel();
295        thread::Builder::new()
296            .name("uv-resolver".into())
297            .spawn(move || {
298                let result = solver.solve(&request_sink);
299
300                // This may fail if the main thread returned early due to an error.
301                let _ = tx.send(result);
302            })
303            .unwrap();
304
305        let resolve_fut = async move { rx.await.map_err(|_| ResolveError::ChannelClosed) };
306
307        // Wait for both to complete.
308        let ((), resolution) = tokio::try_join!(requests_fut, resolve_fut)?;
309
310        state.on_complete();
311        resolution
312    }
313}
314
315impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackages> {
316    #[instrument(skip_all)]
317    fn solve(
318        self: Arc<Self>,
319        request_sink: &Sender<Request>,
320    ) -> Result<ResolverOutput, ResolveError> {
321        debug!(
322            "Solving with installed Python version: {}",
323            self.python_requirement.exact()
324        );
325        debug!(
326            "Solving with target Python version: {}",
327            self.python_requirement.target()
328        );
329        if !self.options.exclude_newer.is_empty() {
330            debug!("Solving with exclude-newer: {}", self.options.exclude_newer);
331        }
332
333        let mut visited = FxHashSet::default();
334
335        let root = PubGrubPackage::from(PubGrubPackageInner::Root(self.project.clone()));
336        let pubgrub = State::init(root.clone(), MIN_VERSION.clone());
337        let prefetcher = BatchPrefetcher::new(
338            self.capabilities.clone(),
339            self.index.clone(),
340            request_sink.clone(),
341        );
342        let state = ForkState::new(
343            pubgrub,
344            self.env.clone(),
345            self.python_requirement.clone(),
346            prefetcher,
347        );
348        let mut preferences = self.preferences.clone();
349        let mut forked_states = self.env.initial_forked_states(state)?;
350        let mut resolutions = vec![];
351
352        'FORK: while let Some(mut state) = forked_states.pop() {
353            if let Some(split) = state.env.end_user_fork_display() {
354                let requires_python = state.python_requirement.target();
355                debug!("Solving {split} (requires-python: {requires_python:?})");
356            }
357            let start = Instant::now();
358            loop {
359                let highest_priority_pkg =
360                    if let Some(initial) = state.initial_id.take() {
361                        // If we just forked based on `requires-python`, we can skip unit
362                        // propagation, since we already propagated the package that initiated
363                        // the fork.
364                        initial
365                    } else {
366                        // Run unit propagation.
367                        let result = state.pubgrub.unit_propagation(state.next);
368                        match result {
369                            Err(err) => {
370                                // If unit propagation failed, there is no solution.
371                                return Err(self.convert_no_solution_err(
372                                    err,
373                                    state.fork_urls,
374                                    state.fork_indexes,
375                                    &state.known_versions,
376                                    state.env,
377                                    self.current_environment.clone(),
378                                    &visited,
379                                ));
380                            }
381                            Ok(conflicts) => {
382                                for (affected, incompatibility) in conflicts {
383                                    // Conflict tracking: If there was a conflict, track affected and
384                                    // culprit for all root cause incompatibilities
385                                    state.record_conflict(affected, None, incompatibility);
386                                }
387                            }
388                        }
389
390                        // Pre-visit all candidate packages, to allow metadata to be fetched in parallel.
391                        if self.dependency_mode.is_transitive() {
392                            Self::pre_visit(
393                                state.pubgrub.partial_solution.prioritized_packages().map(
394                                    |(id, range)| (id, &state.pubgrub.package_store[id], range),
395                                ),
396                                &mut state.pre_visited,
397                                &self.urls,
398                                &self.indexes,
399                                &state.python_requirement,
400                                request_sink,
401                            )?;
402                        }
403
404                        state.reprioritize_conflicts();
405
406                        trace!(
407                            "Assigned packages: {}",
408                            state
409                                .pubgrub
410                                .partial_solution
411                                .extract_solution()
412                                .filter(|(p, _)| !state.pubgrub.package_store[*p].is_proxy())
413                                .map(|(p, v)| format!("{}=={}", state.pubgrub.package_store[p], v))
414                                .join(", ")
415                        );
416                        // Choose a package.
417                        // We aren't allowed to use the term intersection as it would extend the
418                        // mutable borrow of `state`.
419                        let Some((highest_priority_pkg, _)) =
420                            state.pubgrub.partial_solution.pick_highest_priority_pkg(
421                                |id, _range| state.priorities.get(&state.pubgrub.package_store[id]),
422                            )
423                        else {
424                            // All packages have been assigned, the fork has been successfully resolved
425                            if tracing::enabled!(Level::DEBUG) {
426                                state.prefetcher.log_tried_versions();
427                            }
428                            debug!(
429                                "{} resolution took {:.3}s",
430                                state.env,
431                                start.elapsed().as_secs_f32()
432                            );
433
434                            let resolution = state.into_resolution();
435
436                            // Walk over the selected versions, and mark them as preferences. We have to
437                            // add forks back as to not override the preferences from the lockfile for
438                            // the next fork
439                            //
440                            // If we're using a resolution mode that varies based on whether a dependency is
441                            // direct or transitive, skip preferences, as we risk adding a preference from
442                            // one fork (in which it's a transitive dependency) to another fork (in which
443                            // it's direct).
444                            if matches!(
445                                self.options.resolution_mode,
446                                ResolutionMode::Lowest | ResolutionMode::Highest
447                            ) {
448                                let marker = resolution
449                                    .env
450                                    .try_universal_markers()
451                                    .unwrap_or(UniversalMarker::TRUE);
452                                for (package, version) in &resolution.nodes {
453                                    preferences.insert(
454                                        package.name.clone(),
455                                        package.index.clone(),
456                                        marker,
457                                        version.clone(),
458                                        PreferenceSource::Resolver,
459                                    );
460                                }
461                            }
462
463                            resolutions.push(resolution);
464                            continue 'FORK;
465                        };
466                        trace!(
467                            "Chose package for decision: {}. remaining choices: {}",
468                            state.pubgrub.package_store[highest_priority_pkg],
469                            state
470                                .pubgrub
471                                .partial_solution
472                                .undecided_packages()
473                                .filter(|(p, _)| !state.pubgrub.package_store[**p].is_proxy())
474                                .map(|(p, _)| state.pubgrub.package_store[*p].to_string())
475                                .join(", ")
476                        );
477
478                        highest_priority_pkg
479                    };
480
481                state.next = highest_priority_pkg;
482
483                // TODO(charlie): Remove as many usages of `next_package` as we can.
484                let next_id = state.next;
485                let next_package = &state.pubgrub.package_store[state.next];
486
487                let url = next_package
488                    .name()
489                    .and_then(|name| state.fork_urls.get(name));
490                let index = next_package
491                    .name()
492                    .and_then(|name| state.fork_indexes.get(name));
493
494                // Consider:
495                // ```toml
496                // dependencies = [
497                //   "iniconfig == 1.1.1 ; python_version < '3.12'",
498                //   "iniconfig @ https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl ; python_version >= '3.12'",
499                // ]
500                // ```
501                // In the `python_version < '3.12'` case, we haven't pre-visited `iniconfig` yet,
502                // since we weren't sure whether it might also be a URL requirement when
503                // transforming the requirements. For that case, we do another request here
504                // (idempotent due to caching).
505                self.request_package(next_package, url, index, request_sink)?;
506
507                let version = if let Some(version) = state.initial_version.take() {
508                    // If we just forked based on platform support, we can skip version selection,
509                    // since the fork operation itself already selected the appropriate version for
510                    // the platform.
511                    version
512                } else {
513                    let term_intersection = state
514                        .pubgrub
515                        .partial_solution
516                        .term_intersection_for_package(next_id)
517                        .expect("a package was chosen but we don't have a term");
518                    let range = term_intersection.unwrap_positive();
519
520                    // Within a fixed resolver environment, an implicit registry candidate is
521                    // stable for a given range and pre-release policy. Avoid repeating candidate
522                    // selection when PubGrub revisits an identical decision after backtracking.
523                    let cache_selected_version = url.is_none() && index.is_none();
524                    let decision = if cache_selected_version
525                        && let Some((selected_range, version)) =
526                            state.selected_versions.get(&next_id)
527                        && selected_range == range
528                    {
529                        Some(ResolverVersion::Unforked(version.clone()))
530                    } else {
531                        let decision = self.choose_version(
532                            next_package,
533                            next_id,
534                            index.map(IndexMetadata::url),
535                            range,
536                            &mut state.pins,
537                            &preferences,
538                            &state.fork_urls,
539                            &state.env,
540                            &state.python_requirement,
541                            &state.pubgrub,
542                            &mut visited,
543                            request_sink,
544                        )?;
545
546                        if cache_selected_version
547                            && let Some(ResolverVersion::Unforked(version)) = &decision
548                        {
549                            state
550                                .selected_versions
551                                .insert(next_id, (range.clone(), version.clone()));
552                        }
553
554                        decision
555                    };
556
557                    // Pick the next compatible version.
558                    let Some(version) = decision else {
559                        debug!("No compatible version found for: {next_package}");
560
561                        let term_intersection = state
562                            .pubgrub
563                            .partial_solution
564                            .term_intersection_for_package(next_id)
565                            .expect("a package was chosen but we don't have a term");
566
567                        if let PubGrubPackageInner::Package { name, .. } = &**next_package {
568                            // Check if the decision was due to the package being unavailable
569                            if let Some(reason) = self.unavailable_packages.pin().get(name) {
570                                state
571                                    .pubgrub
572                                    .add_incompatibility(Incompatibility::custom_term(
573                                        next_id,
574                                        term_intersection.clone(),
575                                        UnavailableReason::Package(reason.clone()),
576                                    ));
577                                continue;
578                            }
579                        }
580
581                        state
582                            .pubgrub
583                            .add_incompatibility(Incompatibility::no_versions(
584                                next_id,
585                                term_intersection.clone(),
586                            ));
587                        continue;
588                    };
589
590                    let version = match version {
591                        ResolverVersion::Unforked(version) => version,
592                        ResolverVersion::Forked(forks) => {
593                            forked_states.extend(self.version_forks_to_fork_states(state, forks));
594                            continue 'FORK;
595                        }
596                        ResolverVersion::Unavailable(version, reason) => {
597                            state.add_unavailable_version(version, reason);
598                            continue;
599                        }
600                    };
601
602                    // Only consider registry packages for prefetch.
603                    if url.is_none() {
604                        state.prefetcher.prefetch_batches(
605                            next_package,
606                            index,
607                            &version,
608                            term_intersection.unwrap_positive(),
609                            state
610                                .pubgrub
611                                .partial_solution
612                                .unchanging_term_for_package(next_id),
613                            &state.python_requirement,
614                            &self.selector,
615                            &state.env,
616                        )?;
617                    }
618
619                    version
620                };
621
622                state.prefetcher.version_tried(next_package, &version);
623
624                self.on_progress(next_package, &version);
625
626                if state
627                    .added_dependencies
628                    .get(&next_id)
629                    .is_some_and(|versions| versions.contains(&version))
630                {
631                    // `dep_incompats` are already in `incompatibilities` so we know there are not satisfied
632                    // terms and can add the decision directly.
633                    state
634                        .pubgrub
635                        .partial_solution
636                        .add_decision(next_id, version);
637                    continue;
638                }
639
640                // Retrieve that package dependencies.
641                let forked_deps = self.get_dependencies_forking(
642                    next_id,
643                    next_package,
644                    &version,
645                    &state.pins,
646                    &state.fork_urls,
647                    &state.env,
648                    &state.python_requirement,
649                    &state.pubgrub,
650                )?;
651
652                match forked_deps {
653                    ForkedDependencies::Unavailable(reason) => {
654                        // Then here, if we get a reason that we consider unrecoverable, we should
655                        // show the derivation chain.
656                        state
657                            .pubgrub
658                            .add_incompatibility(Incompatibility::custom_version(
659                                next_id,
660                                version.clone(),
661                                UnavailableReason::Version(reason),
662                            ));
663                    }
664                    ForkedDependencies::Unforked(dependencies) => {
665                        state
666                            .added_dependencies
667                            .entry(next_id)
668                            .or_default()
669                            .insert(version.clone());
670
671                        // Enrich the state with any URLs, etc.
672                        state
673                            .visit_package_version_dependencies(
674                                next_id,
675                                &version,
676                                &self.urls,
677                                &self.indexes,
678                                &dependencies,
679                                &self.git,
680                                &self.workspace_members,
681                                self.selector.resolution_strategy(),
682                            )
683                            .map_err(|err| {
684                                enrich_dependency_error(err, next_id, &version, &state.pubgrub)
685                            })?;
686
687                        // Emit a request to fetch the metadata for each registry package.
688                        self.visit_dependencies(&dependencies, &state, request_sink)
689                            .map_err(|err| {
690                                enrich_dependency_error(err, next_id, &version, &state.pubgrub)
691                            })?;
692
693                        // Add the dependencies to the state.
694                        state.add_package_version_dependencies(
695                            next_id,
696                            &version,
697                            dependencies,
698                            &self.index,
699                            &self.installed_packages,
700                        );
701                    }
702                    ForkedDependencies::Forked {
703                        mut forks,
704                        diverging_packages,
705                    } => {
706                        state
707                            .added_dependencies
708                            .entry(next_id)
709                            .or_default()
710                            .insert(version.clone());
711
712                        debug!(
713                            "Pre-fork {} took {:.3}s",
714                            state.env,
715                            start.elapsed().as_secs_f32()
716                        );
717
718                        // Prioritize the forks.
719                        match (self.options.fork_strategy, self.options.resolution_mode) {
720                            (ForkStrategy::Fewest, _) | (_, ResolutionMode::Lowest) => {
721                                // Prefer solving forks with lower Python bounds, since they're more
722                                // likely to produce solutions that work for forks with higher
723                                // Python bounds (whereas the inverse is not true).
724                                forks.sort_by(|a, b| {
725                                    a.cmp_requires_python(b)
726                                        .reverse()
727                                        .then_with(|| a.cmp_upper_bounds(b))
728                                });
729                            }
730                            (ForkStrategy::RequiresPython, _) => {
731                                // Otherwise, prefer solving forks with higher Python bounds, since
732                                // we want to prioritize choosing the latest-compatible package
733                                // version for each Python version.
734                                forks.sort_by(|a, b| {
735                                    a.cmp_requires_python(b).then_with(|| a.cmp_upper_bounds(b))
736                                });
737                            }
738                        }
739
740                        for new_fork_state in self.forks_to_fork_states(
741                            state,
742                            &version,
743                            forks,
744                            request_sink,
745                            &diverging_packages,
746                        ) {
747                            forked_states.push(new_fork_state?);
748                        }
749                        continue 'FORK;
750                    }
751                    ForkedDependencies::RequiresPython(requires_python) => {
752                        if matches!(self.options.fork_strategy, ForkStrategy::RequiresPython)
753                            && state.env.marker_environment().is_none()
754                        {
755                            let forks = fork_version_by_python_requirement(
756                                &requires_python,
757                                &state.python_requirement,
758                                &state.env,
759                            );
760                            if !forks.is_empty() {
761                                debug!(
762                                    "Forking Python requirement `{}` on `{}` for {}=={} ({})",
763                                    state.python_requirement.target(),
764                                    &requires_python,
765                                    next_package,
766                                    version,
767                                    forks
768                                        .iter()
769                                        .map(ToString::to_string)
770                                        .collect::<Vec<_>>()
771                                        .join(", ")
772                                );
773
774                                // Revisit the version in each fork so its dependencies are added
775                                // under the narrowed Python requirement.
776                                let forks = forks
777                                    .into_iter()
778                                    .map(|env| VersionFork {
779                                        env,
780                                        id: next_id,
781                                        version: None,
782                                    })
783                                    .collect();
784                                forked_states
785                                    .extend(self.version_forks_to_fork_states(state, forks));
786                                continue 'FORK;
787                            }
788                        }
789
790                        state
791                            .pubgrub
792                            .add_incompatibility(Incompatibility::custom_version(
793                                next_id,
794                                version.clone(),
795                                UnavailableReason::Version(UnavailableVersion::RequiresPython(
796                                    requires_python,
797                                )),
798                            ));
799                    }
800                }
801            }
802        }
803        if resolutions.len() > 1 {
804            info!(
805                "Solved your requirements for {} environments",
806                resolutions.len()
807            );
808        }
809        if tracing::enabled!(Level::DEBUG) {
810            for resolution in &resolutions {
811                if let Some(env) = resolution.env.end_user_fork_display() {
812                    let packages: FxHashSet<_> = resolution
813                        .nodes
814                        .keys()
815                        .map(|package| &package.name)
816                        .collect();
817                    debug!(
818                        "Distinct solution for {env} with {} package(s)",
819                        packages.len()
820                    );
821                }
822            }
823        }
824        for resolution in &resolutions {
825            resolution.trace_resolution();
826        }
827        ResolverOutput::from_state(
828            &resolutions,
829            self.requirements.clone(),
830            self.constraints.clone(),
831            self.overrides.clone(),
832            &self.preferences,
833            &self.index,
834            &self.git,
835            self.python_requirement.target().clone(),
836            &self.conflicts,
837            self.selector.resolution_strategy(),
838            self.options.clone(),
839        )
840    }
841
842    /// Convert the dependency [`Fork`]s into [`ForkState`]s.
843    fn forks_to_fork_states<'a>(
844        &'a self,
845        current_state: ForkState,
846        version: &'a Version,
847        forks: Vec<Fork>,
848        request_sink: &'a Sender<Request>,
849        diverging_packages: &'a BTreeSet<PackageName>,
850    ) -> impl Iterator<Item = Result<ForkState, ResolveError>> + 'a {
851        debug!(
852            "Splitting resolution on {}=={} over {} into {} resolution{} with separate markers",
853            current_state.pubgrub.package_store[current_state.next],
854            version,
855            diverging_packages
856                .iter()
857                .map(ToString::to_string)
858                .join(", "),
859            forks.len(),
860            if forks.len() == 1 { "" } else { "s" }
861        );
862        assert!(forks.len() >= 2);
863        // This is a somewhat tortured technique to ensure
864        // that our resolver state is only cloned as much
865        // as it needs to be. We basically move the state
866        // into `forked_states`, and then only clone it if
867        // there is at least one more fork to visit.
868        let package = current_state.next;
869        let mut cur_state = Some(current_state);
870        let forks_len = forks.len();
871        forks
872            .into_iter()
873            .enumerate()
874            .map(move |(i, fork)| {
875                let is_last = i == forks_len - 1;
876                let forked_state = cur_state.take().unwrap();
877                if !is_last {
878                    cur_state = Some(forked_state.clone());
879                }
880
881                let env = fork.env.clone();
882                (fork, forked_state.with_env(env))
883            })
884            .map(move |(fork, mut forked_state)| {
885                // Enrich the state with any URLs, etc.
886                forked_state
887                    .visit_package_version_dependencies(
888                        package,
889                        version,
890                        &self.urls,
891                        &self.indexes,
892                        &fork.dependencies,
893                        &self.git,
894                        &self.workspace_members,
895                        self.selector.resolution_strategy(),
896                    )
897                    .map_err(|err| {
898                        enrich_dependency_error(err, package, version, &forked_state.pubgrub)
899                    })?;
900
901                // Emit a request to fetch the metadata for each registry package.
902                self.visit_dependencies(&fork.dependencies, &forked_state, request_sink)
903                    .map_err(|err| {
904                        enrich_dependency_error(err, package, version, &forked_state.pubgrub)
905                    })?;
906
907                // Add the dependencies to the state.
908                forked_state.add_package_version_dependencies(
909                    package,
910                    version,
911                    fork.dependencies,
912                    &self.index,
913                    &self.installed_packages,
914                );
915
916                Ok(forked_state)
917            })
918    }
919
920    /// Convert the dependency [`Fork`]s into [`ForkState`]s.
921    #[expect(clippy::unused_self)]
922    fn version_forks_to_fork_states(
923        &self,
924        current_state: ForkState,
925        forks: Vec<VersionFork>,
926    ) -> impl Iterator<Item = ForkState> + '_ {
927        // This is a somewhat tortured technique to ensure
928        // that our resolver state is only cloned as much
929        // as it needs to be. We basically move the state
930        // into `forked_states`, and then only clone it if
931        // there is at least one more fork to visit.
932        let mut cur_state = Some(current_state);
933        let forks_len = forks.len();
934        forks.into_iter().enumerate().map(move |(i, fork)| {
935            let is_last = i == forks_len - 1;
936            let mut forked_state = cur_state.take().unwrap();
937            if !is_last {
938                cur_state = Some(forked_state.clone());
939            }
940            forked_state.initial_id = Some(fork.id);
941            forked_state.initial_version = fork.version;
942            forked_state.with_env(fork.env)
943        })
944    }
945
946    /// Visit a set of [`PubGrubDependency`] entities prior to selection.
947    fn visit_dependencies(
948        &self,
949        dependencies: &[PubGrubDependency],
950        state: &ForkState,
951        request_sink: &Sender<Request>,
952    ) -> Result<(), ResolveError> {
953        for dependency in dependencies {
954            let PubGrubDependency {
955                package,
956                version: _,
957                parent: _,
958                source: _,
959            } = dependency;
960            let url = package.name().and_then(|name| state.fork_urls.get(name));
961            let index = package.name().and_then(|name| state.fork_indexes.get(name));
962            self.visit_package(package, url, index, request_sink)?;
963        }
964        Ok(())
965    }
966
967    /// Visit a [`PubGrubPackage`] prior to selection. This should be called on a [`PubGrubPackage`]
968    /// before it is selected, to allow metadata to be fetched in parallel.
969    fn visit_package(
970        &self,
971        package: &PubGrubPackage,
972        url: Option<&VerbatimParsedUrl>,
973        index: Option<&IndexMetadata>,
974        request_sink: &Sender<Request>,
975    ) -> Result<(), ResolveError> {
976        // Ignore unresolved URL packages, i.e., packages that use a direct URL in some forks.
977        if url.is_none() && package.name().is_none_or(|name| self.urls.any_url(name)) {
978            return Ok(());
979        }
980
981        self.request_package(package, url, index, request_sink)
982    }
983
984    fn request_package(
985        &self,
986        package: &PubGrubPackage,
987        url: Option<&VerbatimParsedUrl>,
988        index: Option<&IndexMetadata>,
989        request_sink: &Sender<Request>,
990    ) -> Result<(), ResolveError> {
991        // Only request real packages.
992        let Some(name) = package.name_no_root() else {
993            return Ok(());
994        };
995
996        if let Some(url) = url {
997            // Verify that the package is allowed under the hash-checking policy.
998            if !self.hasher.allows_url(&url.verbatim) {
999                return Err(ResolveError::UnhashedPackage(name.clone()));
1000            }
1001
1002            // Emit a request to fetch the metadata for this distribution.
1003            let dist = Dist::from_url(name.clone(), url.clone())?;
1004            if self.index.distributions().register(dist.distribution_id()) {
1005                request_sink.blocking_send(Request::Dist(dist))?;
1006            }
1007        } else if let Some(index) = index {
1008            // Emit a request to fetch the metadata for this package on the index.
1009            if self
1010                .index
1011                .explicit()
1012                .register((name.clone(), index.url().clone()))
1013            {
1014                request_sink.blocking_send(Request::Package(name.clone(), Some(index.clone())))?;
1015            }
1016        } else {
1017            // Emit a request to fetch the metadata for this package.
1018            if self.index.implicit().register(name.clone()) {
1019                request_sink.blocking_send(Request::Package(name.clone(), None))?;
1020            }
1021        }
1022        Ok(())
1023    }
1024
1025    /// Visit the set of [`PubGrubPackage`] candidates prior to selection. This allows us to fetch
1026    /// metadata for all packages in parallel.
1027    fn pre_visit<'data>(
1028        packages: impl Iterator<
1029            Item = (
1030                Id<PubGrubPackage>,
1031                &'data PubGrubPackage,
1032                &'data Range<Version>,
1033            ),
1034        >,
1035        pre_visited: &mut FxHashMap<Id<PubGrubPackage>, Range<Version>>,
1036        urls: &Urls,
1037        indexes: &Indexes,
1038        python_requirement: &PythonRequirement,
1039        request_sink: &Sender<Request>,
1040    ) -> Result<(), ResolveError> {
1041        // Iterate over the potential packages, and fetch file metadata for any of them. These
1042        // represent our current best guesses for the versions that we _might_ select.
1043        for (id, package, range) in packages {
1044            let PubGrubPackageInner::Package {
1045                name,
1046                extra: None,
1047                group: None,
1048                marker: MarkerTree::TRUE,
1049            } = &**package
1050            else {
1051                continue;
1052            };
1053            // Avoid pre-visiting packages that have any URLs in any fork. At this point we can't
1054            // tell whether they are registry distributions or which url they use.
1055            if urls.any_url(name) {
1056                continue;
1057            }
1058            // Avoid visiting packages that may use an explicit index.
1059            if indexes.contains_key(name) {
1060                continue;
1061            }
1062            // Unit propagation often leaves a package's range unchanged. Although prefetching the
1063            // same package and range is idempotent, selecting its candidate is not free.
1064            if pre_visited.get(&id) == Some(range) {
1065                continue;
1066            }
1067            pre_visited.insert(id, range.clone());
1068            request_sink.blocking_send(Request::Prefetch(
1069                name.clone(),
1070                range.clone(),
1071                python_requirement.clone(),
1072            ))?;
1073        }
1074        Ok(())
1075    }
1076
1077    /// Returns the sorted, deduplicated candidate universe used to widen dependency ranges.
1078    ///
1079    /// Every selectable version must be present: omitting one could extend a dependency
1080    /// incompatibility across it, while including an unselectable version only prevents a
1081    /// possible simplification. The result is therefore conservative, including yanked and
1082    /// otherwise unavailable versions from every index plus installed versions missing from the
1083    /// indexes. Versions past the exclude-newer cutoff are omitted because resolution treats them
1084    /// as nonexistent.
1085    ///
1086    /// Non-blocking: Returns `None` if the version map hasn't been fetched yet, or if the
1087    /// package is not a registry package.
1088    fn known_versions<'a>(
1089        index: &InMemoryIndex,
1090        installed_packages: &InstalledPackages,
1091        fork_urls: &ForkUrls,
1092        fork_indexes: &ForkIndexes,
1093        known_versions: &'a mut FxHashMap<PackageName, Arc<[Version]>>,
1094        package: &PubGrubPackage,
1095    ) -> Option<&'a [Version]> {
1096        let name = package.name_no_root()?;
1097        // Versions of packages from a URL or the workspace are not registry versions.
1098        if fork_urls.get(name).is_some() {
1099            return None;
1100        }
1101        if !known_versions.contains_key(name) {
1102            let response = if let Some(index_metadata) = fork_indexes.get(name) {
1103                index
1104                    .explicit()
1105                    .get(&(name.clone(), index_metadata.url().clone()))?
1106            } else {
1107                index.implicit().get(name)?
1108            };
1109            let VersionsResponse::Found(ref version_maps) = *response else {
1110                return None;
1111            };
1112            let mut versions: Vec<Version> = version_maps
1113                .iter()
1114                .flat_map(|version_map| version_map.included_versions().cloned())
1115                .chain(
1116                    installed_packages
1117                        .get_packages(name)
1118                        .iter()
1119                        .map(|dist| dist.version().clone()),
1120                )
1121                .collect();
1122            versions.sort_unstable();
1123            versions.dedup();
1124            known_versions.insert(name.clone(), versions.into());
1125        }
1126        Some(&known_versions[name][..])
1127    }
1128
1129    /// Given a candidate package, choose the next version in range to try.
1130    ///
1131    /// Returns `None` when there are no versions in the given range, rejecting the current partial
1132    /// solution.
1133    // TODO(konsti): re-enable tracing. This trace is crucial to understanding the
1134    // tracing-durations-export diagrams, but it took ~5% resolver thread runtime for apache-airflow
1135    // when I last measured.
1136    #[cfg_attr(feature = "tracing-durations-export", instrument(skip_all, fields(%package)))]
1137    fn choose_version(
1138        &self,
1139        package: &PubGrubPackage,
1140        id: Id<PubGrubPackage>,
1141        index: Option<&IndexUrl>,
1142        range: &Range<Version>,
1143        pins: &mut FilePins,
1144        preferences: &Preferences,
1145        fork_urls: &ForkUrls,
1146        env: &ResolverEnvironment,
1147        python_requirement: &PythonRequirement,
1148        pubgrub: &State<UvDependencyProvider>,
1149        visited: &mut FxHashSet<PackageName>,
1150        request_sink: &Sender<Request>,
1151    ) -> Result<Option<ResolverVersion>, ResolveError> {
1152        match &**package {
1153            PubGrubPackageInner::Root(_) => {
1154                Ok(Some(ResolverVersion::Unforked(MIN_VERSION.clone())))
1155            }
1156
1157            PubGrubPackageInner::Python(_) => {
1158                // Dependencies on Python are only added when a package is incompatible; as such,
1159                // we don't need to do anything here.
1160                Ok(None)
1161            }
1162
1163            PubGrubPackageInner::System(_) => {
1164                // We don't care what the actual version is here, just that it's consistent across
1165                // the dependency graph.
1166                let Some(version) = range.as_singleton() else {
1167                    return Ok(None);
1168                };
1169                Ok(Some(ResolverVersion::Unforked(version.clone())))
1170            }
1171
1172            PubGrubPackageInner::Marker { name, .. }
1173            | PubGrubPackageInner::Extra { name, .. }
1174            | PubGrubPackageInner::Group { name, .. }
1175            | PubGrubPackageInner::Package { name, .. } => {
1176                if let Some(url) = package.name().and_then(|name| fork_urls.get(name)) {
1177                    self.choose_version_url(id, name, range, url, env, python_requirement, pubgrub)
1178                } else {
1179                    self.choose_version_registry(
1180                        package,
1181                        id,
1182                        name,
1183                        index,
1184                        range,
1185                        preferences,
1186                        env,
1187                        python_requirement,
1188                        pubgrub,
1189                        pins,
1190                        visited,
1191                        request_sink,
1192                    )
1193                }
1194            }
1195        }
1196    }
1197
1198    /// Select a version for a URL requirement. Since there is only one version per URL, we return
1199    /// that version if it is in range and `None` otherwise.
1200    fn choose_version_url(
1201        &self,
1202        id: Id<PubGrubPackage>,
1203        name: &PackageName,
1204        range: &Range<Version>,
1205        url: &VerbatimParsedUrl,
1206        env: &ResolverEnvironment,
1207        python_requirement: &PythonRequirement,
1208        pubgrub: &State<UvDependencyProvider>,
1209    ) -> Result<Option<ResolverVersion>, ResolveError> {
1210        debug!(
1211            "Searching for a compatible version of {name} @ {} ({range})",
1212            url.verbatim
1213        );
1214
1215        let dist = Dist::from_url(name.clone(), url.clone())?;
1216        let distribution_id = dist.distribution_id();
1217        let response = self
1218            .index
1219            .distributions()
1220            .wait_blocking(&distribution_id)
1221            .map_err(|_| ResolveError::UnregisteredTask(dist.to_string()))?;
1222
1223        // If we failed to fetch the metadata for a URL, we can't proceed.
1224        let metadata = match &*response {
1225            MetadataResponse::Found(archive) => &archive.metadata,
1226            MetadataResponse::Unavailable(reason) => {
1227                self.unavailable_packages
1228                    .pin()
1229                    .insert(name.clone(), reason.into());
1230                return Ok(None);
1231            }
1232            // TODO(charlie): Add derivation chain for URL dependencies. In practice, this isn't
1233            // critical since we fetch URL dependencies _prior_ to invoking the resolver.
1234            MetadataResponse::Error(dist, err) => {
1235                return Err(ResolveError::Dist(
1236                    DistErrorKind::from_requested_dist(dist, &**err),
1237                    dist.clone(),
1238                    DerivationChain::default(),
1239                    err.clone(),
1240                ));
1241            }
1242        };
1243
1244        let version = &metadata.version;
1245
1246        // The version is incompatible with the requirement.
1247        if !range.contains(version) {
1248            return Ok(None);
1249        }
1250
1251        // If the URL points to a pre-built wheel, and the wheel's supported Python versions don't
1252        // match our `Requires-Python`, mark it as incompatible.
1253        if let Dist::Built(dist) = &dist {
1254            let filename = match &dist {
1255                BuiltDist::Registry(dist) => &dist.best_wheel().filename,
1256                BuiltDist::DirectUrl(dist) => &dist.filename,
1257                BuiltDist::GitPath(dist) => &dist.filename,
1258                BuiltDist::Path(dist) => &dist.filename,
1259            };
1260
1261            // If the wheel does _not_ cover an environment that requires artifact coverage, it's
1262            // incompatible.
1263            if env.marker_environment().is_none() && !self.options.artifact_environments.is_empty()
1264            {
1265                let wheel_marker = implied_markers(filename);
1266                // If the caller marked an environment as requiring artifact coverage, ensure it
1267                // has coverage.
1268                for environment_marker in self.options.artifact_environments.iter().copied() {
1269                    // If the platform is part of the current environment...
1270                    if env.included_by_marker(environment_marker)
1271                        && !find_environments(id, pubgrub).is_disjoint(environment_marker)
1272                    {
1273                        // ...but the wheel doesn't support it, it's incompatible.
1274                        if wheel_marker.is_disjoint(environment_marker) {
1275                            return Ok(Some(ResolverVersion::Unavailable(
1276                                version.clone(),
1277                                UnavailableVersion::IncompatibleDist(IncompatibleDist::Wheel(
1278                                    IncompatibleWheel::MissingPlatform(environment_marker),
1279                                )),
1280                            )));
1281                        }
1282                    }
1283                }
1284            }
1285
1286            // If the wheel's Python tag doesn't match the target Python, it's incompatible.
1287            if !python_requirement.target().matches_wheel_tag(filename) {
1288                return Ok(Some(ResolverVersion::Unavailable(
1289                    filename.version.clone(),
1290                    UnavailableVersion::IncompatibleDist(IncompatibleDist::Wheel(
1291                        IncompatibleWheel::Tag(IncompatibleTag::AbiPythonVersion),
1292                    )),
1293                )));
1294            }
1295        }
1296
1297        // The version is incompatible due to its `Requires-Python` requirement.
1298        if let Some(requires_python) = metadata.requires_python.as_ref() {
1299            if !python_requirement.target().is_contained_by(requires_python) {
1300                let kind = if python_requirement.installed() == python_requirement.target() {
1301                    PythonRequirementKind::Installed
1302                } else {
1303                    PythonRequirementKind::Target
1304                };
1305                return Ok(Some(ResolverVersion::Unavailable(
1306                    version.clone(),
1307                    UnavailableVersion::IncompatibleDist(IncompatibleDist::Source(
1308                        IncompatibleSource::RequiresPython(requires_python.clone(), kind),
1309                    )),
1310                )));
1311            }
1312        }
1313
1314        Ok(Some(ResolverVersion::Unforked(version.clone())))
1315    }
1316
1317    /// Given a candidate registry requirement, choose the next version in range to try, or `None`
1318    /// if there is no version in this range.
1319    fn choose_version_registry(
1320        &self,
1321        package: &PubGrubPackage,
1322        id: Id<PubGrubPackage>,
1323        name: &PackageName,
1324        index: Option<&IndexUrl>,
1325        range: &Range<Version>,
1326        preferences: &Preferences,
1327        env: &ResolverEnvironment,
1328        python_requirement: &PythonRequirement,
1329        pubgrub: &State<UvDependencyProvider>,
1330        pins: &mut FilePins,
1331        visited: &mut FxHashSet<PackageName>,
1332        request_sink: &Sender<Request>,
1333    ) -> Result<Option<ResolverVersion>, ResolveError> {
1334        // Wait for the metadata to be available.
1335        let versions_response = if let Some(index) = index {
1336            self.index
1337                .explicit()
1338                .wait_blocking(&(name.clone(), index.clone()))
1339                .map_err(|_| ResolveError::UnregisteredTask(name.to_string()))?
1340        } else {
1341            self.index
1342                .implicit()
1343                .wait_blocking(name)
1344                .map_err(|_| ResolveError::UnregisteredTask(name.to_string()))?
1345        };
1346        visited.insert(name.clone());
1347
1348        let version_maps = match *versions_response {
1349            VersionsResponse::Found(ref version_maps) => version_maps.as_slice(),
1350            VersionsResponse::NoIndex => {
1351                self.unavailable_packages
1352                    .pin()
1353                    .insert(name.clone(), UnavailablePackage::NoIndex);
1354                &[]
1355            }
1356            VersionsResponse::Offline => {
1357                self.unavailable_packages
1358                    .pin()
1359                    .insert(name.clone(), UnavailablePackage::Offline);
1360                &[]
1361            }
1362            VersionsResponse::NotFound => {
1363                self.unavailable_packages
1364                    .pin()
1365                    .insert(name.clone(), UnavailablePackage::NotFound);
1366                &[]
1367            }
1368        };
1369
1370        debug!("Searching for a compatible version of {package} ({range})");
1371
1372        // Find a version.
1373        let Some(candidate) = self.selector.select(
1374            name,
1375            range,
1376            version_maps,
1377            preferences,
1378            &self.installed_packages,
1379            &self.exclusions,
1380            index,
1381            env,
1382            self.tags.as_ref(),
1383        ) else {
1384            // Short circuit: we couldn't find _any_ versions for a package.
1385            return Ok(None);
1386        };
1387
1388        let dist = match candidate.dist() {
1389            CandidateDist::Compatible(dist) => dist,
1390            CandidateDist::Incompatible {
1391                incompatible_dist: incompatibility,
1392                prioritized_dist: _,
1393            } => {
1394                // If the version is incompatible because no distributions are compatible, exit early.
1395                return Ok(Some(ResolverVersion::Unavailable(
1396                    candidate.version().clone(),
1397                    // TODO(charlie): We can avoid this clone; the candidate is dropped here and
1398                    // owns the incompatibility.
1399                    UnavailableVersion::IncompatibleDist(incompatibility.clone()),
1400                )));
1401            }
1402        };
1403
1404        // Check whether the version is incompatible due to its Python requirement.
1405        if let Some((requires_python, incompatibility)) =
1406            Self::check_requires_python(dist, python_requirement)
1407        {
1408            if matches!(self.options.fork_strategy, ForkStrategy::RequiresPython) {
1409                if env.marker_environment().is_none() {
1410                    let forks = fork_version_by_python_requirement(
1411                        requires_python,
1412                        python_requirement,
1413                        env,
1414                    );
1415                    if !forks.is_empty() {
1416                        debug!(
1417                            "Forking Python requirement `{}` on `{}` for {}=={} ({})",
1418                            python_requirement.target(),
1419                            requires_python,
1420                            name,
1421                            candidate.version(),
1422                            forks
1423                                .iter()
1424                                .map(ToString::to_string)
1425                                .collect::<Vec<_>>()
1426                                .join(", ")
1427                        );
1428                        let forks = forks
1429                            .into_iter()
1430                            .map(|env| VersionFork {
1431                                env,
1432                                id,
1433                                version: None,
1434                            })
1435                            .collect();
1436                        return Ok(Some(ResolverVersion::Forked(forks)));
1437                    }
1438                }
1439            }
1440
1441            return Ok(Some(ResolverVersion::Unavailable(
1442                candidate.version().clone(),
1443                UnavailableVersion::IncompatibleDist(incompatibility),
1444            )));
1445        }
1446
1447        // Check whether this version covers all supported platforms; and, if not, generate a fork.
1448        if let Some(forked) = self.fork_version_registry(
1449            &candidate,
1450            dist,
1451            version_maps,
1452            package,
1453            id,
1454            name,
1455            index,
1456            range,
1457            preferences,
1458            env,
1459            pubgrub,
1460            pins,
1461            request_sink,
1462        )? {
1463            return Ok(Some(forked));
1464        }
1465
1466        let filename = match dist.for_installation() {
1467            ResolvedDistRef::InstallableRegistrySourceDist { sdist, .. } => sdist
1468                .filename()
1469                .unwrap_or(Cow::Borrowed("unknown filename")),
1470            ResolvedDistRef::InstallableRegistryBuiltDist { wheel, .. } => wheel
1471                .filename()
1472                .unwrap_or(Cow::Borrowed("unknown filename")),
1473            ResolvedDistRef::Installed { .. } => Cow::Borrowed("installed"),
1474        };
1475
1476        debug!(
1477            "Selecting: {}=={} [{}] ({})",
1478            name,
1479            candidate.version(),
1480            candidate.choice_kind(),
1481            filename,
1482        );
1483        self.visit_candidate(&candidate, dist, package, name, pins, request_sink)?;
1484
1485        let version = candidate.version().clone();
1486        Ok(Some(ResolverVersion::Unforked(version)))
1487    }
1488
1489    /// Determine whether a candidate covers all supported platforms; and, if not, generate a fork.
1490    ///
1491    /// This only ever applies to versions that lack source distributions And, for now, we only
1492    /// apply it in two cases:
1493    ///
1494    /// 1. Local versions, where the non-local version has greater platform coverage. The intent is
1495    ///    such that, if we're resolving PyTorch, and we choose `torch==2.5.2+cpu`, we want to
1496    ///    fork so that we can select `torch==2.5.2` on macOS (since the `+cpu` variant doesn't
1497    ///    include any macOS wheels).
1498    /// 2. Platforms that the user explicitly marks as "required" (opt-in). For example, the user
1499    ///    might require that the generated resolution always includes wheels for x86 macOS, and
1500    ///    fails entirely if the platform is unsupported.
1501    fn fork_version_registry(
1502        &self,
1503        candidate: &Candidate,
1504        dist: &CompatibleDist,
1505        version_maps: &[VersionMap],
1506        package: &PubGrubPackage,
1507        id: Id<PubGrubPackage>,
1508        name: &PackageName,
1509        index: Option<&IndexUrl>,
1510        range: &Range<Version>,
1511        preferences: &Preferences,
1512        env: &ResolverEnvironment,
1513        pubgrub: &State<UvDependencyProvider>,
1514        pins: &mut FilePins,
1515        request_sink: &Sender<Request>,
1516    ) -> Result<Option<ResolverVersion>, ResolveError> {
1517        // This only applies to universal resolutions.
1518        if env.marker_environment().is_some() {
1519            return Ok(None);
1520        }
1521
1522        // If the package is already compatible with all environments (as is the case for
1523        // packages that include a source distribution), we don't need to fork.
1524        if dist.implied_markers().is_true() {
1525            return Ok(None);
1526        }
1527
1528        // If the caller marked an environment as requiring artifact coverage, ensure it has
1529        // coverage.
1530        for marker in self.options.artifact_environments.iter().copied() {
1531            // If the platform is part of the current environment...
1532            if env.included_by_marker(marker) {
1533                // But isn't supported by the distribution...
1534                if dist.implied_markers().is_disjoint(marker)
1535                    && !find_environments(id, pubgrub).is_disjoint(marker)
1536                {
1537                    // Then we need to fork.
1538                    let Some((left, right)) = fork_version_by_marker(env, marker) else {
1539                        return Ok(Some(ResolverVersion::Unavailable(
1540                            candidate.version().clone(),
1541                            UnavailableVersion::IncompatibleDist(IncompatibleDist::Wheel(
1542                                IncompatibleWheel::MissingPlatform(marker),
1543                            )),
1544                        )));
1545                    };
1546
1547                    debug!(
1548                        "Forking on required platform `{}` for {}=={} ({})",
1549                        marker.try_to_string().unwrap_or_else(|| "true".to_string()),
1550                        name,
1551                        candidate.version(),
1552                        [&left, &right]
1553                            .iter()
1554                            .map(ToString::to_string)
1555                            .collect::<Vec<_>>()
1556                            .join(", ")
1557                    );
1558                    let forks = vec![
1559                        VersionFork {
1560                            env: left,
1561                            id,
1562                            version: None,
1563                        },
1564                        VersionFork {
1565                            env: right,
1566                            id,
1567                            version: None,
1568                        },
1569                    ];
1570                    return Ok(Some(ResolverVersion::Forked(forks)));
1571                }
1572            }
1573        }
1574
1575        // For now, we only apply this to local versions.
1576        if !candidate.version().is_local() {
1577            return Ok(None);
1578        }
1579
1580        debug!(
1581            "Looking at local version: {}=={}",
1582            name,
1583            candidate.version()
1584        );
1585
1586        // If there's a non-local version...
1587        let range = range.clone().intersection(&Range::singleton(
1588            candidate.version().clone().without_local(),
1589        ));
1590
1591        let Some(base_candidate) = self.selector.select(
1592            name,
1593            &range,
1594            version_maps,
1595            preferences,
1596            &self.installed_packages,
1597            &self.exclusions,
1598            index,
1599            env,
1600            self.tags.as_ref(),
1601        ) else {
1602            return Ok(None);
1603        };
1604        let CandidateDist::Compatible(base_dist) = base_candidate.dist() else {
1605            return Ok(None);
1606        };
1607
1608        // ...and the non-local version has greater platform support...
1609        let mut remainder = {
1610            let mut remainder = base_dist.implied_markers();
1611            remainder = remainder.and(dist.implied_markers().negate());
1612            remainder
1613        };
1614        if remainder.is_false() {
1615            return Ok(None);
1616        }
1617
1618        // If the remainder isn't relevant to the current environment, there's no need to fork.
1619        // For example, if we're solving for `sys_platform == 'darwin'` but the remainder is
1620        // `sys_platform == 'linux'`, we don't need to fork.
1621        if !env.included_by_marker(remainder) {
1622            return Ok(None);
1623        }
1624
1625        // Similarly, if the local distribution is incompatible with the current environment, then
1626        // use the base distribution instead (but don't fork).
1627        if !env.included_by_marker(dist.implied_markers()) {
1628            let filename = match dist.for_installation() {
1629                ResolvedDistRef::InstallableRegistrySourceDist { sdist, .. } => sdist
1630                    .filename()
1631                    .unwrap_or(Cow::Borrowed("unknown filename")),
1632                ResolvedDistRef::InstallableRegistryBuiltDist { wheel, .. } => wheel
1633                    .filename()
1634                    .unwrap_or(Cow::Borrowed("unknown filename")),
1635                ResolvedDistRef::Installed { .. } => Cow::Borrowed("installed"),
1636            };
1637
1638            debug!(
1639                "Preferring non-local candidate: {}=={} [{}] ({})",
1640                name,
1641                base_candidate.version(),
1642                base_candidate.choice_kind(),
1643                filename,
1644            );
1645            self.visit_candidate(
1646                &base_candidate,
1647                base_dist,
1648                package,
1649                name,
1650                pins,
1651                request_sink,
1652            )?;
1653
1654            return Ok(Some(ResolverVersion::Unforked(
1655                base_candidate.version().clone(),
1656            )));
1657        }
1658
1659        // If the implied markers includes _some_ macOS environments, but the remainder doesn't,
1660        // then we can extend the implied markers to include _all_ macOS environments. Same goes for
1661        // Linux and Windows.
1662        //
1663        // The idea here is that the base version could support (e.g.) ARM macOS, but not Intel
1664        // macOS. But if _neither_ version supports Intel macOS, we'd rather use `sys_platform == 'darwin'`
1665        // instead of `sys_platform == 'darwin' and platform_machine == 'arm64'`, since it's much
1666        // simpler, and _neither_ version will succeed with Intel macOS anyway.
1667        for value in [
1668            arcstr::literal!("darwin"),
1669            arcstr::literal!("linux"),
1670            arcstr::literal!("win32"),
1671        ] {
1672            let sys_platform = MarkerTree::expression(MarkerExpression::String {
1673                key: MarkerValueString::SysPlatform,
1674                operator: MarkerOperator::Equal,
1675                value,
1676            });
1677            if dist.implied_markers().is_disjoint(sys_platform)
1678                && !remainder.is_disjoint(sys_platform)
1679            {
1680                remainder = remainder.or(sys_platform);
1681            }
1682        }
1683
1684        // Otherwise, we need to fork.
1685        let Some((base_env, local_env)) = fork_version_by_marker(env, remainder) else {
1686            return Ok(None);
1687        };
1688
1689        debug!(
1690            "Forking platform for {}=={} ({})",
1691            name,
1692            candidate.version(),
1693            [&base_env, &local_env]
1694                .iter()
1695                .map(ToString::to_string)
1696                .collect::<Vec<_>>()
1697                .join(", ")
1698        );
1699        self.visit_candidate(candidate, dist, package, name, pins, request_sink)?;
1700        self.visit_candidate(
1701            &base_candidate,
1702            base_dist,
1703            package,
1704            name,
1705            pins,
1706            request_sink,
1707        )?;
1708
1709        let forks = vec![
1710            VersionFork {
1711                env: base_env.clone(),
1712                id,
1713                version: Some(base_candidate.version().clone()),
1714            },
1715            VersionFork {
1716                env: local_env.clone(),
1717                id,
1718                version: Some(candidate.version().clone()),
1719            },
1720        ];
1721        Ok(Some(ResolverVersion::Forked(forks)))
1722    }
1723
1724    /// Visit a selected candidate.
1725    fn visit_candidate(
1726        &self,
1727        candidate: &Candidate,
1728        dist: &CompatibleDist,
1729        package: &PubGrubPackage,
1730        name: &PackageName,
1731        pins: &mut FilePins,
1732        request_sink: &Sender<Request>,
1733    ) -> Result<(), ResolveError> {
1734        // We want to return a package pinned to a specific version; but we _also_ want to
1735        // store the exact file that we selected to satisfy that version.
1736        pins.insert(candidate, dist);
1737
1738        // Emit a request to fetch the metadata for this version.
1739        if matches!(&**package, PubGrubPackageInner::Package { .. }) {
1740            if self.dependency_mode.is_transitive() {
1741                let dist = dist.for_resolution();
1742                if self.index.distributions().register(dist.distribution_id()) {
1743                    if name != dist.name() {
1744                        return Err(ResolveError::MismatchedPackageName {
1745                            request: "distribution",
1746                            expected: name.clone(),
1747                            actual: dist.name().clone(),
1748                        });
1749                    }
1750                    // Verify that the package is allowed under the hash-checking policy.
1751                    if !self
1752                        .hasher
1753                        .allows_package(candidate.name(), candidate.version())
1754                    {
1755                        return Err(ResolveError::UnhashedPackage(candidate.name().clone()));
1756                    }
1757
1758                    let request = Request::from(dist);
1759                    request_sink.blocking_send(request)?;
1760                }
1761            }
1762        }
1763
1764        Ok(())
1765    }
1766
1767    /// Check if the distribution is incompatible with the Python requirement, and if so, return
1768    /// the incompatibility.
1769    fn check_requires_python<'dist>(
1770        dist: &'dist CompatibleDist,
1771        python_requirement: &PythonRequirement,
1772    ) -> Option<(&'dist VersionSpecifiers, IncompatibleDist)> {
1773        let requires_python = dist.requires_python()?;
1774        if python_requirement.target().is_contained_by(requires_python) {
1775            None
1776        } else {
1777            let incompatibility = if matches!(dist, CompatibleDist::CompatibleWheel { .. }) {
1778                IncompatibleDist::Wheel(IncompatibleWheel::RequiresPython(
1779                    requires_python.clone(),
1780                    if python_requirement.installed() == python_requirement.target() {
1781                        PythonRequirementKind::Installed
1782                    } else {
1783                        PythonRequirementKind::Target
1784                    },
1785                ))
1786            } else {
1787                IncompatibleDist::Source(IncompatibleSource::RequiresPython(
1788                    requires_python.clone(),
1789                    if python_requirement.installed() == python_requirement.target() {
1790                        PythonRequirementKind::Installed
1791                    } else {
1792                        PythonRequirementKind::Target
1793                    },
1794                ))
1795            };
1796            Some((requires_python, incompatibility))
1797        }
1798    }
1799
1800    /// Given a candidate package and version, return its dependencies.
1801    fn get_dependencies_forking(
1802        &self,
1803        id: Id<PubGrubPackage>,
1804        package: &PubGrubPackage,
1805        version: &Version,
1806        pins: &FilePins,
1807        fork_urls: &ForkUrls,
1808        env: &ResolverEnvironment,
1809        python_requirement: &PythonRequirement,
1810        pubgrub: &State<UvDependencyProvider>,
1811    ) -> Result<ForkedDependencies, ResolveError> {
1812        let dependencies = self.get_dependencies(
1813            id,
1814            package,
1815            version,
1816            pins,
1817            fork_urls,
1818            env,
1819            python_requirement,
1820            pubgrub,
1821        )?;
1822        if env.marker_environment().is_some() {
1823            Ok(ForkedDependencies::from_dependencies_platform_specific(
1824                dependencies,
1825            ))
1826        } else {
1827            Ok(ForkedDependencies::from_dependencies_universal(
1828                dependencies,
1829                env,
1830                python_requirement,
1831                &self.conflicts,
1832            ))
1833        }
1834    }
1835
1836    /// Given a candidate package and version, return its dependencies.
1837    #[instrument(skip_all, fields(%package, %version))]
1838    fn get_dependencies(
1839        &self,
1840        id: Id<PubGrubPackage>,
1841        package: &PubGrubPackage,
1842        version: &Version,
1843        pins: &FilePins,
1844        fork_urls: &ForkUrls,
1845        env: &ResolverEnvironment,
1846        python_requirement: &PythonRequirement,
1847        pubgrub: &State<UvDependencyProvider>,
1848    ) -> Result<Dependencies, ResolveError> {
1849        let dependencies = match &**package {
1850            PubGrubPackageInner::Root(_) => {
1851                let no_dev_deps = BTreeMap::default();
1852                let requirements = self.flatten_requirements(
1853                    &self.requirements,
1854                    &no_dev_deps,
1855                    None,
1856                    None,
1857                    None,
1858                    None,
1859                    env,
1860                    python_requirement,
1861                );
1862
1863                PubGrubDependency::from_requirements(
1864                    &self.conflicts,
1865                    requirements,
1866                    None,
1867                    Some(package),
1868                )
1869            }
1870
1871            PubGrubPackageInner::Package {
1872                name,
1873                extra,
1874                group,
1875                marker: _,
1876            } => {
1877                // If we're excluding transitive dependencies, short-circuit.
1878                if self.dependency_mode.is_direct() {
1879                    return Ok(Dependencies::Unforkable(Vec::default()));
1880                }
1881
1882                // Look up the distribution ID from the pins (common case) or fork URLs.
1883                let owned_id;
1884                let distribution_id = if let Some((_, metadata_id)) =
1885                    pins.dist_and_id(name, version)
1886                {
1887                    metadata_id
1888                } else if let Some(url) = fork_urls.get(name) {
1889                    let dist = Dist::from_url(name.clone(), url.clone())?;
1890                    owned_id = dist.distribution_id();
1891                    &owned_id
1892                } else {
1893                    debug_assert!(
1894                        false,
1895                        "Dependencies were requested for a package without a pinned distribution"
1896                    );
1897                    return Err(ResolveError::UnregisteredTask(format!("{name}=={version}")));
1898                };
1899
1900                // If the package does not exist in the registry or locally, we cannot fetch its dependencies
1901                if self.dependency_mode.is_transitive()
1902                    && self.unavailable_packages.pin().contains_key(name)
1903                    && self.installed_packages.get_packages(name).is_empty()
1904                {
1905                    debug_assert!(
1906                        false,
1907                        "Dependencies were requested for a package that is not available"
1908                    );
1909                    return Err(ResolveError::PackageUnavailable(name.clone()));
1910                }
1911
1912                // Wait for the metadata to be available.
1913                let response = self
1914                    .index
1915                    .distributions()
1916                    .wait_blocking(distribution_id)
1917                    .map_err(|_| ResolveError::UnregisteredTask(format!("{name}=={version}")))?;
1918
1919                let metadata = match &*response {
1920                    MetadataResponse::Found(archive) => &archive.metadata,
1921                    MetadataResponse::Unavailable(reason) => {
1922                        let unavailable_version = UnavailableVersion::from(reason);
1923                        let message = unavailable_version.singular_message();
1924                        if let Some(err) = reason.source() {
1925                            // Show the detailed error for metadata parse errors.
1926                            warn!("{name} {message}: {err}");
1927                        } else {
1928                            warn!("{name} {message}");
1929                        }
1930                        let incomplete_packages = self.incomplete_packages.pin();
1931                        let versions = incomplete_packages.get_or_insert(
1932                            name.clone(),
1933                            HashMap::builder().resize_mode(ResizeMode::Blocking).build(),
1934                        );
1935                        versions.pin().insert(version.clone(), reason.clone());
1936                        return Ok(Dependencies::Unavailable(unavailable_version));
1937                    }
1938                    MetadataResponse::Error(dist, err) => {
1939                        let chain = DerivationChainBuilder::from_state(id, version, pubgrub)
1940                            .unwrap_or_default();
1941                        return Err(ResolveError::Dist(
1942                            DistErrorKind::from_requested_dist(dist, &**err),
1943                            dist.clone(),
1944                            chain,
1945                            err.clone(),
1946                        ));
1947                    }
1948                };
1949
1950                // If there was no requires-python on the index page, we may have an incompatible
1951                // distribution or need to fork.
1952                if let Some(requires_python) = &metadata.requires_python {
1953                    if !python_requirement.target().is_contained_by(requires_python) {
1954                        return Ok(Dependencies::RequiresPython(requires_python.clone()));
1955                    }
1956                }
1957
1958                // Identify any system dependencies based on the index URL.
1959                let system_dependencies = self
1960                    .options
1961                    .torch_backend
1962                    .as_ref()
1963                    .filter(|torch_backend| matches!(torch_backend, TorchStrategy::Cuda { .. }))
1964                    .filter(|torch_backend| torch_backend.has_system_dependency(name))
1965                    .and_then(|_| pins.get(name, version).and_then(ResolvedDist::index))
1966                    .map(IndexUrl::url)
1967                    .and_then(SystemDependency::from_index)
1968                    .into_iter()
1969                    .inspect(|system_dependency| {
1970                        debug!(
1971                            "Adding system dependency `{}` for `{package}@{version}`",
1972                            system_dependency
1973                        );
1974                    })
1975                    .map(PubGrubDependency::from);
1976
1977                let requirements = self.flatten_requirements(
1978                    &metadata.requires_dist,
1979                    &metadata.dependency_groups,
1980                    extra.as_ref(),
1981                    group.as_ref(),
1982                    Some(name),
1983                    Some(version),
1984                    env,
1985                    python_requirement,
1986                );
1987
1988                PubGrubDependency::from_requirements(
1989                    &self.conflicts,
1990                    requirements,
1991                    group.as_ref(),
1992                    Some(package),
1993                )
1994                .map(|mut dependencies| {
1995                    dependencies.extend(system_dependencies);
1996                    dependencies
1997                })
1998            }
1999
2000            PubGrubPackageInner::Python(_) => return Ok(Dependencies::Unforkable(Vec::default())),
2001
2002            PubGrubPackageInner::System(_) => return Ok(Dependencies::Unforkable(Vec::default())),
2003
2004            // Add a dependency on both the marker and base package.
2005            PubGrubPackageInner::Marker { name, marker } => {
2006                return Ok(Dependencies::Unforkable(
2007                    [MarkerTree::TRUE, *marker]
2008                        .into_iter()
2009                        .map(move |marker| PubGrubDependency {
2010                            package: PubGrubPackage::from(PubGrubPackageInner::Package {
2011                                name: name.clone(),
2012                                extra: None,
2013                                group: None,
2014                                marker,
2015                            }),
2016                            version: Range::singleton(version.clone()),
2017                            parent: None,
2018                            source: DependencySource::Unspecified,
2019                        })
2020                        .collect(),
2021                ));
2022            }
2023
2024            // Add a dependency on both the extra and base package, with and without the marker.
2025            PubGrubPackageInner::Extra {
2026                name,
2027                extra,
2028                marker,
2029            } => {
2030                return Ok(Dependencies::Unforkable(
2031                    [MarkerTree::TRUE, *marker]
2032                        .into_iter()
2033                        .dedup()
2034                        .flat_map(move |marker| {
2035                            [None, Some(extra)]
2036                                .into_iter()
2037                                .map(move |extra| PubGrubDependency {
2038                                    package: PubGrubPackage::from(PubGrubPackageInner::Package {
2039                                        name: name.clone(),
2040                                        extra: extra.cloned(),
2041                                        group: None,
2042                                        marker,
2043                                    }),
2044                                    version: Range::singleton(version.clone()),
2045                                    parent: None,
2046                                    source: DependencySource::Unspecified,
2047                                })
2048                        })
2049                        .collect(),
2050                ));
2051            }
2052
2053            // Add a dependency on the dependency group, with and without the marker.
2054            PubGrubPackageInner::Group {
2055                name,
2056                group,
2057                marker,
2058            } => {
2059                return Ok(Dependencies::Unforkable(
2060                    [MarkerTree::TRUE, *marker]
2061                        .into_iter()
2062                        .dedup()
2063                        .map(|marker| PubGrubDependency {
2064                            package: PubGrubPackage::from(PubGrubPackageInner::Package {
2065                                name: name.clone(),
2066                                extra: None,
2067                                group: Some(group.clone()),
2068                                marker,
2069                            }),
2070                            version: Range::singleton(version.clone()),
2071                            parent: None,
2072                            source: DependencySource::Unspecified,
2073                        })
2074                        .collect(),
2075                ));
2076            }
2077        };
2078        Ok(match dependencies {
2079            Ok(dependencies) => Dependencies::Available(dependencies),
2080            Err(requirement) => {
2081                Dependencies::Unavailable(UnavailableVersion::UnsatisfiableDependency(requirement))
2082            }
2083        })
2084    }
2085
2086    /// The regular and dev dependencies filtered by Python version and the markers of this fork,
2087    /// plus the extras dependencies of the current package (e.g., `black` depending on
2088    /// `black[colorama]`).
2089    fn flatten_requirements<'a>(
2090        &'a self,
2091        dependencies: &'a [Requirement],
2092        dev_dependencies: &'a BTreeMap<GroupName, Box<[Requirement]>>,
2093        extra: Option<&'a ExtraName>,
2094        dev: Option<&'a GroupName>,
2095        name: Option<&'a PackageName>,
2096        version: Option<&'a Version>,
2097        env: &'a ResolverEnvironment,
2098        python_requirement: &'a PythonRequirement,
2099    ) -> impl Iterator<Item = Cow<'a, Requirement>> {
2100        let python_marker = python_requirement.to_marker_tree();
2101
2102        if let Some(dev) = dev {
2103            // Dependency groups can include the project itself, so no need to flatten recursive
2104            // dependencies.
2105            Either::Left(Either::Left(self.requirements_for_extra(
2106                dev_dependencies.get(dev).into_iter().flatten(),
2107                extra,
2108                None,
2109                name.zip(version),
2110                env,
2111                python_marker,
2112                python_requirement,
2113            )))
2114        } else if !dependencies
2115            .iter()
2116            .any(|req| name == Some(&req.name) && !req.extras.is_empty())
2117        {
2118            // If the project doesn't define any recursive dependencies, take the fast path.
2119            Either::Left(Either::Right(self.requirements_for_extra(
2120                dependencies.iter(),
2121                extra,
2122                name.zip(version),
2123                name.zip(version),
2124                env,
2125                python_marker,
2126                python_requirement,
2127            )))
2128        } else {
2129            let mut requirements = self
2130                .requirements_for_extra(
2131                    dependencies.iter(),
2132                    extra,
2133                    name.zip(version),
2134                    name.zip(version),
2135                    env,
2136                    python_marker,
2137                    python_requirement,
2138                )
2139                .collect::<Vec<_>>();
2140
2141            // Transitively process all extras that are recursively included, starting with the current
2142            // extra.
2143            let mut seen = FxHashSet::<(ExtraName, MarkerTree)>::default();
2144            let mut queue: VecDeque<_> = requirements
2145                .iter()
2146                .filter(|req| name == Some(&req.name))
2147                .flat_map(|req| req.extras.iter().cloned().map(|extra| (extra, req.marker)))
2148                .collect();
2149            while let Some((extra, marker)) = queue.pop_front() {
2150                if !seen.insert((extra.clone(), marker)) {
2151                    continue;
2152                }
2153                for requirement in self.requirements_for_extra(
2154                    dependencies,
2155                    Some(&extra),
2156                    name.zip(version),
2157                    name.zip(version),
2158                    env,
2159                    python_marker,
2160                    python_requirement,
2161                ) {
2162                    let requirement = match requirement {
2163                        Cow::Owned(mut requirement) => {
2164                            requirement.marker = requirement.marker.and(marker);
2165                            requirement
2166                        }
2167                        Cow::Borrowed(requirement) => {
2168                            let mut marker = marker;
2169                            marker = marker.and(requirement.marker);
2170                            Requirement {
2171                                name: requirement.name.clone(),
2172                                extras: requirement.extras.clone(),
2173                                groups: requirement.groups.clone(),
2174                                source: requirement.source.clone(),
2175                                origin: requirement.origin.clone(),
2176                                marker: marker.simplify_extras(slice::from_ref(&extra)),
2177                            }
2178                        }
2179                    };
2180                    if name == Some(&requirement.name) {
2181                        // Add each transitively included extra.
2182                        queue.extend(
2183                            requirement
2184                                .extras
2185                                .iter()
2186                                .cloned()
2187                                .map(|extra| (extra, requirement.marker)),
2188                        );
2189                    } else {
2190                        // Add the requirements for that extra.
2191                        requirements.push(Cow::Owned(requirement));
2192                    }
2193                }
2194            }
2195
2196            // Retain any self-constraints for that extra, e.g., if `project[foo]` includes
2197            // `project[bar]>1.0`, as a dependency, we need to propagate `project>1.0`, in addition to
2198            // transitively expanding `project[bar]`.
2199            let mut self_constraints = vec![];
2200            for req in &requirements {
2201                if name == Some(&req.name) && !req.source.is_empty() {
2202                    self_constraints.push(Requirement {
2203                        name: req.name.clone(),
2204                        extras: Box::new([]),
2205                        groups: req.groups.clone(),
2206                        source: req.source.clone(),
2207                        origin: req.origin.clone(),
2208                        marker: req.marker,
2209                    });
2210                }
2211            }
2212
2213            // Drop all the self-requirements now that we flattened them out.
2214            requirements.retain(|req| name != Some(&req.name) || req.extras.is_empty());
2215            requirements.extend(self_constraints.into_iter().map(Cow::Owned));
2216
2217            Either::Right(requirements.into_iter())
2218        }
2219    }
2220
2221    /// The set of the regular and dev dependencies, filtered by Python version,
2222    /// the markers of this fork and the requested extra.
2223    fn requirements_for_extra<'data, 'parameters>(
2224        &'data self,
2225        dependencies: impl IntoIterator<Item = &'data Requirement> + 'parameters,
2226        extra: Option<&'parameters ExtraName>,
2227        override_package: Option<(&'parameters PackageName, &'parameters Version)>,
2228        exclusion_package: Option<(&'parameters PackageName, &'parameters Version)>,
2229        env: &'parameters ResolverEnvironment,
2230        python_marker: MarkerTree,
2231        python_requirement: &'parameters PythonRequirement,
2232    ) -> impl Iterator<Item = Cow<'data, Requirement>> + 'parameters
2233    where
2234        'data: 'parameters,
2235    {
2236        self.overrides
2237            .apply_for_package(override_package, dependencies)
2238            .filter(move |requirement| {
2239                !self
2240                    .excludes
2241                    .contains_for_package(exclusion_package, &requirement.name)
2242            })
2243            .map(move |mut requirement| {
2244                // Split the marker into production and optional components. If we have e.g.
2245                // `foo; sys_platform == 'win32' or extra == 'feature'`
2246                // we split it into
2247                // `foo; sys_platform == 'win32'` (production) when `extra` is `None`,
2248                // `foo; extra == 'feature'` (optional) when `extra` is `Some("feature")`.
2249                // The requirements are then separately tracked in production and optional
2250                // dependencies respectively.
2251
2252                let marker = match extra {
2253                    Some(extra) => requirement
2254                        .marker
2255                        .simplify_extras(slice::from_ref(extra))
2256                        .simplify_not_extras_with(|candidate| candidate != extra)
2257                        .and(
2258                            requirement
2259                                .marker
2260                                .simplify_not_extras_with(|_| true)
2261                                .negate(),
2262                        ),
2263                    None => requirement.marker.simplify_not_extras_with(|_| true),
2264                };
2265
2266                if requirement.marker != marker {
2267                    requirement.to_mut().marker = marker;
2268                }
2269
2270                requirement
2271            })
2272            .filter(move |requirement| {
2273                Self::is_requirement_applicable(
2274                    requirement,
2275                    extra,
2276                    env,
2277                    python_marker,
2278                    python_requirement,
2279                )
2280            })
2281            .flat_map(move |requirement| {
2282                iter::once(requirement.clone()).chain(self.constraints_for_requirement(
2283                    requirement,
2284                    extra,
2285                    env,
2286                    python_marker,
2287                    python_requirement,
2288                ))
2289            })
2290    }
2291
2292    /// Whether a requirement is applicable for the Python version, the markers of this fork and the
2293    /// requested extra.
2294    fn is_requirement_applicable(
2295        requirement: &Requirement,
2296        extra: Option<&ExtraName>,
2297        env: &ResolverEnvironment,
2298        python_marker: MarkerTree,
2299        python_requirement: &PythonRequirement,
2300    ) -> bool {
2301        // If the requirement isn't relevant for the current platform, skip it.
2302        match extra {
2303            Some(source_extra) => {
2304                if !requirement.evaluate_markers(env.marker_environment(), &[]) {
2305                    return false;
2306                }
2307
2308                if !env.included_by_group(ConflictItemRef::from((&requirement.name, source_extra)))
2309                {
2310                    return false;
2311                }
2312            }
2313            None => {
2314                if !requirement.evaluate_markers(env.marker_environment(), &[]) {
2315                    return false;
2316                }
2317            }
2318        }
2319
2320        // If the requirement would not be selected with any Python version
2321        // supported by the root, skip it.
2322        if python_marker.is_disjoint(requirement.marker) {
2323            trace!(
2324                "Skipping {requirement} because of Requires-Python: {requires_python}",
2325                requires_python = python_requirement.target(),
2326            );
2327            return false;
2328        }
2329
2330        // If we're in a fork in universal mode, ignore any dependency that isn't part of
2331        // this fork (but will be part of another fork).
2332        if !env.included_by_marker(requirement.marker) {
2333            trace!("Skipping {requirement} because of {env}");
2334            return false;
2335        }
2336
2337        true
2338    }
2339
2340    /// The constraints applicable to the requirement, filtered by Python version, the markers of
2341    /// this fork and the requested extra.
2342    fn constraints_for_requirement<'data, 'parameters>(
2343        &'data self,
2344        requirement: Cow<'data, Requirement>,
2345        extra: Option<&'parameters ExtraName>,
2346        env: &'parameters ResolverEnvironment,
2347        python_marker: MarkerTree,
2348        python_requirement: &'parameters PythonRequirement,
2349    ) -> impl Iterator<Item = Cow<'data, Requirement>> + 'parameters
2350    where
2351        'data: 'parameters,
2352    {
2353        self.constraints
2354            .get(&requirement.name)
2355            .into_iter()
2356            .flatten()
2357            .filter_map(move |constraint| {
2358                // If the requirement would not be selected with any Python version
2359                // supported by the root, skip it.
2360                let constraint = if constraint.marker.is_true() {
2361                    // Additionally, if the requirement is `requests ; sys_platform == 'darwin'`
2362                    // and the constraint is `requests ; python_version == '3.6'`, the
2363                    // constraint should only apply when _both_ markers are true.
2364                    if requirement.marker.is_true() {
2365                        Cow::Borrowed(constraint)
2366                    } else {
2367                        let mut marker = constraint.marker;
2368                        marker = marker.and(requirement.marker);
2369
2370                        if marker.is_false() {
2371                            trace!(
2372                                "Skipping {constraint} because of disjoint markers: `{}` vs. `{}`",
2373                                constraint.marker.try_to_string().unwrap(),
2374                                requirement.marker.try_to_string().unwrap(),
2375                            );
2376                            return None;
2377                        }
2378
2379                        Cow::Owned(Requirement {
2380                            name: constraint.name.clone(),
2381                            extras: constraint.extras.clone(),
2382                            groups: constraint.groups.clone(),
2383                            source: constraint.source.clone(),
2384                            origin: constraint.origin.clone(),
2385                            marker,
2386                        })
2387                    }
2388                } else {
2389                    let requires_python = python_requirement.target();
2390
2391                    let mut marker = constraint.marker;
2392                    marker = marker.and(requirement.marker);
2393
2394                    if marker.is_false() {
2395                        trace!(
2396                            "Skipping {constraint} because of disjoint markers: `{}` vs. `{}`",
2397                            constraint.marker.try_to_string().unwrap(),
2398                            requirement.marker.try_to_string().unwrap(),
2399                        );
2400                        return None;
2401                    }
2402
2403                    // Additionally, if the requirement is `requests ; sys_platform == 'darwin'`
2404                    // and the constraint is `requests ; python_version == '3.6'`, the
2405                    // constraint should only apply when _both_ markers are true.
2406                    if python_marker.is_disjoint(marker) {
2407                        trace!(
2408                            "Skipping constraint {requirement} because of Requires-Python: {requires_python}"
2409                        );
2410                        return None;
2411                    }
2412
2413                    if marker == constraint.marker {
2414                        Cow::Borrowed(constraint)
2415                    } else {
2416                        Cow::Owned(Requirement {
2417                            name: constraint.name.clone(),
2418                            extras: constraint.extras.clone(),
2419                            groups: constraint.groups.clone(),
2420                            source: constraint.source.clone(),
2421                            origin: constraint.origin.clone(),
2422                            marker,
2423                        })
2424                    }
2425                };
2426
2427                // If we're in a fork in universal mode, ignore any dependency that isn't part of
2428                // this fork (but will be part of another fork).
2429                if !env.included_by_marker(constraint.marker) {
2430                    trace!("Skipping {constraint} because of {env}");
2431                    return None;
2432                }
2433
2434                // If the constraint isn't relevant for the current platform, skip it.
2435                match extra {
2436                    Some(source_extra) => {
2437                        if !constraint
2438                            .evaluate_markers(env.marker_environment(), slice::from_ref(source_extra))
2439                        {
2440                            return None;
2441                        }
2442                        if !env.included_by_group(ConflictItemRef::from((&requirement.name, source_extra)))
2443                        {
2444                            return None;
2445                        }
2446                    }
2447                    None => {
2448                        if !constraint.evaluate_markers(env.marker_environment(), &[]) {
2449                            return None;
2450                        }
2451                    }
2452                }
2453
2454                Some(constraint)
2455            })
2456    }
2457
2458    /// Fetch the metadata for a stream of packages and versions.
2459    async fn fetch<Provider: ResolverProvider>(
2460        self: Arc<Self>,
2461        provider: Arc<Provider>,
2462        request_stream: Receiver<Request>,
2463    ) -> Result<(), ResolveError> {
2464        let mut response_stream = ReceiverStream::new(request_stream)
2465            .map(|request| self.process_request(request, &*provider).boxed_local())
2466            // Allow as many futures as possible to start in the background.
2467            // Backpressure is provided by at a more granular level by `DistributionDatabase`
2468            // and `SourceDispatch`, as well as the bounded request channel.
2469            .buffer_unordered(usize::MAX);
2470
2471        while let Some(response) = response_stream.next().await {
2472            match response? {
2473                Some(Response::Package(name, index, version_map)) => {
2474                    trace!("Received package metadata for: {name}");
2475                    if let Some(index) = index {
2476                        self.index
2477                            .explicit()
2478                            .done((name, index), Arc::new(version_map));
2479                    } else {
2480                        self.index.implicit().done(name, Arc::new(version_map));
2481                    }
2482                }
2483                Some(Response::Installed { dist, metadata }) => {
2484                    trace!("Received installed distribution metadata for: {dist}");
2485                    self.index
2486                        .distributions()
2487                        .done(dist.distribution_id(), Arc::new(metadata));
2488                }
2489                Some(Response::Dist { dist, metadata }) => {
2490                    let dist_kind = match dist {
2491                        Dist::Built(_) => "built",
2492                        Dist::Source(_) => "source",
2493                    };
2494                    trace!("Received {dist_kind} distribution metadata for: {dist}");
2495                    if let MetadataResponse::Unavailable(reason) = &metadata {
2496                        let message = UnavailableVersion::from(reason).singular_message();
2497                        if let Some(err) = reason.source() {
2498                            // Show the detailed error for metadata parse errors.
2499                            warn!("{dist} {message}: {err}");
2500                        } else {
2501                            warn!("{dist} {message}");
2502                        }
2503                    }
2504                    self.index
2505                        .distributions()
2506                        .done(dist.distribution_id(), Arc::new(metadata));
2507                }
2508                None => {}
2509            }
2510        }
2511
2512        Ok::<(), ResolveError>(())
2513    }
2514
2515    #[instrument(skip_all, fields(%request))]
2516    async fn process_request<Provider: ResolverProvider>(
2517        &self,
2518        request: Request,
2519        provider: &Provider,
2520    ) -> Result<Option<Response>, ResolveError> {
2521        match request {
2522            // Fetch package metadata from the registry.
2523            Request::Package(package_name, index) => {
2524                let package_versions = provider
2525                    .get_package_versions(&package_name, index.as_ref())
2526                    .boxed_local()
2527                    .await
2528                    .map_err(ResolveError::Client)?;
2529
2530                Ok(Some(Response::Package(
2531                    package_name,
2532                    index.map(IndexMetadata::into_url),
2533                    package_versions,
2534                )))
2535            }
2536
2537            // Fetch distribution metadata from the distribution database.
2538            Request::Dist(dist) => {
2539                if let Some(version) = dist.version() {
2540                    if let Some(index) = dist.index() {
2541                        // Check the implicit indexes for pre-provided metadata.
2542                        let versions_response = self.index.implicit().get(dist.name());
2543                        if let Some(VersionsResponse::Found(version_maps)) =
2544                            versions_response.as_deref()
2545                        {
2546                            for version_map in version_maps {
2547                                if version_map.index() == Some(index) {
2548                                    let Some(metadata) = version_map.get_metadata(version) else {
2549                                        continue;
2550                                    };
2551                                    debug!("Found registry-provided metadata for: {dist}");
2552                                    return Ok(Some(Response::Dist {
2553                                        dist,
2554                                        metadata: MetadataResponse::Found(
2555                                            ArchiveMetadata::from_metadata23(metadata),
2556                                        ),
2557                                    }));
2558                                }
2559                            }
2560                        }
2561
2562                        // Check the explicit indexes for pre-provided metadata.
2563                        let versions_response = self
2564                            .index
2565                            .explicit()
2566                            .get(&(dist.name().clone(), index.clone()));
2567                        if let Some(VersionsResponse::Found(version_maps)) =
2568                            versions_response.as_deref()
2569                        {
2570                            for version_map in version_maps {
2571                                let Some(metadata) = version_map.get_metadata(version) else {
2572                                    continue;
2573                                };
2574                                debug!("Found registry-provided metadata for: {dist}");
2575                                return Ok(Some(Response::Dist {
2576                                    dist,
2577                                    metadata: MetadataResponse::Found(
2578                                        ArchiveMetadata::from_metadata23(metadata),
2579                                    ),
2580                                }));
2581                            }
2582                        }
2583                    }
2584                }
2585
2586                let metadata = provider
2587                    .get_or_build_wheel_metadata(&dist)
2588                    .boxed_local()
2589                    .await?;
2590
2591                if let MetadataResponse::Found(metadata) = &metadata {
2592                    if &metadata.metadata.name != dist.name() {
2593                        return Err(ResolveError::MismatchedPackageName {
2594                            request: "distribution metadata",
2595                            expected: dist.name().clone(),
2596                            actual: metadata.metadata.name.clone(),
2597                        });
2598                    }
2599                }
2600
2601                Ok(Some(Response::Dist { dist, metadata }))
2602            }
2603
2604            Request::Installed(dist) => {
2605                let metadata = provider.get_installed_metadata(&dist).boxed_local().await?;
2606
2607                if let MetadataResponse::Found(metadata) = &metadata {
2608                    if &metadata.metadata.name != dist.name() {
2609                        return Err(ResolveError::MismatchedPackageName {
2610                            request: "installed metadata",
2611                            expected: dist.name().clone(),
2612                            actual: metadata.metadata.name.clone(),
2613                        });
2614                    }
2615                }
2616
2617                Ok(Some(Response::Installed { dist, metadata }))
2618            }
2619
2620            // Pre-fetch the package and distribution metadata.
2621            Request::Prefetch(package_name, range, python_requirement) => {
2622                // Wait for the package metadata to become available.
2623                let versions_response = self
2624                    .index
2625                    .implicit()
2626                    .wait(&package_name)
2627                    .await
2628                    .map_err(|_| ResolveError::UnregisteredTask(package_name.to_string()))?;
2629
2630                let version_map = match *versions_response {
2631                    VersionsResponse::Found(ref version_map) => version_map,
2632                    // Short-circuit if we did not find any versions for the package
2633                    VersionsResponse::NoIndex => {
2634                        self.unavailable_packages
2635                            .pin()
2636                            .insert(package_name.clone(), UnavailablePackage::NoIndex);
2637
2638                        return Ok(None);
2639                    }
2640                    VersionsResponse::Offline => {
2641                        self.unavailable_packages
2642                            .pin()
2643                            .insert(package_name.clone(), UnavailablePackage::Offline);
2644
2645                        return Ok(None);
2646                    }
2647                    VersionsResponse::NotFound => {
2648                        self.unavailable_packages
2649                            .pin()
2650                            .insert(package_name.clone(), UnavailablePackage::NotFound);
2651
2652                        return Ok(None);
2653                    }
2654                };
2655
2656                // We don't have access to the fork state when prefetching.
2657                let env = ResolverEnvironment::universal(vec![]);
2658
2659                // Try to find a compatible version. If there aren't any compatible versions,
2660                // short-circuit.
2661                let Some(candidate) = self.selector.select(
2662                    &package_name,
2663                    &range,
2664                    version_map,
2665                    &self.preferences,
2666                    &self.installed_packages,
2667                    &self.exclusions,
2668                    None,
2669                    &env,
2670                    self.tags.as_ref(),
2671                ) else {
2672                    return Ok(None);
2673                };
2674
2675                // If there is not a compatible distribution, short-circuit.
2676                let Some(dist) = candidate.compatible() else {
2677                    return Ok(None);
2678                };
2679
2680                // If the registry provided metadata for this distribution, use it.
2681                for version_map in version_map {
2682                    if let Some(metadata) = version_map.get_metadata(candidate.version()) {
2683                        let dist = dist.for_resolution();
2684                        if version_map.index() == dist.index() {
2685                            debug!("Found registry-provided metadata for: {dist}");
2686
2687                            let metadata =
2688                                MetadataResponse::Found(ArchiveMetadata::from_metadata23(metadata));
2689
2690                            let dist = dist.to_owned();
2691                            if &package_name != dist.name() {
2692                                return Err(ResolveError::MismatchedPackageName {
2693                                    request: "distribution",
2694                                    expected: package_name,
2695                                    actual: dist.name().clone(),
2696                                });
2697                            }
2698
2699                            let response = match dist {
2700                                ResolvedDist::Installable { dist, .. } => Response::Dist {
2701                                    dist: (*dist).clone(),
2702                                    metadata,
2703                                },
2704                                ResolvedDist::Installed { dist } => Response::Installed {
2705                                    dist: (*dist).clone(),
2706                                    metadata,
2707                                },
2708                            };
2709
2710                            return Ok(Some(response));
2711                        }
2712                    }
2713                }
2714
2715                // Avoid prefetching source distributions with unbounded lower-bound ranges. This
2716                // often leads to failed attempts to build legacy versions of packages that are
2717                // incompatible with modern build tools.
2718                if dist.wheel().is_none() {
2719                    if !self.selector.use_highest_version(&package_name, &env) {
2720                        if let Some((lower, _)) = range.iter().next() {
2721                            if lower == Bound::Unbounded {
2722                                debug!(
2723                                    "Skipping prefetch for unbounded minimum-version range: {package_name} ({range})"
2724                                );
2725                                return Ok(None);
2726                            }
2727                        }
2728                    }
2729                }
2730
2731                // Validate the Python requirement.
2732                if let Some(requires_python) = dist.requires_python() {
2733                    if !python_requirement.target().is_contained_by(requires_python) {
2734                        return Ok(None);
2735                    }
2736                }
2737
2738                // Verify that the package is allowed under the hash-checking policy.
2739                if !self
2740                    .hasher
2741                    .allows_package(candidate.name(), candidate.version())
2742                {
2743                    return Ok(None);
2744                }
2745
2746                // Emit a request to fetch the metadata for this version.
2747                let dist = dist.for_resolution();
2748                if self.index.distributions().register(dist.distribution_id()) {
2749                    let dist = dist.to_owned();
2750                    if &package_name != dist.name() {
2751                        return Err(ResolveError::MismatchedPackageName {
2752                            request: "distribution",
2753                            expected: package_name,
2754                            actual: dist.name().clone(),
2755                        });
2756                    }
2757
2758                    let response = match dist {
2759                        ResolvedDist::Installable { dist, .. } => {
2760                            let metadata = provider
2761                                .get_or_build_wheel_metadata(&dist)
2762                                .boxed_local()
2763                                .await?;
2764
2765                            Response::Dist {
2766                                dist: (*dist).clone(),
2767                                metadata,
2768                            }
2769                        }
2770                        ResolvedDist::Installed { dist } => {
2771                            let metadata =
2772                                provider.get_installed_metadata(&dist).boxed_local().await?;
2773
2774                            Response::Installed {
2775                                dist: (*dist).clone(),
2776                                metadata,
2777                            }
2778                        }
2779                    };
2780
2781                    Ok(Some(response))
2782                } else {
2783                    Ok(None)
2784                }
2785            }
2786        }
2787    }
2788
2789    fn convert_no_solution_err(
2790        &self,
2791        mut err: pubgrub::NoSolutionError<UvDependencyProvider>,
2792        fork_urls: ForkUrls,
2793        fork_indexes: ForkIndexes,
2794        known_versions: &FxHashMap<PackageName, Arc<[Version]>>,
2795        env: ResolverEnvironment,
2796        current_environment: MarkerEnvironment,
2797        visited: &FxHashSet<PackageName>,
2798    ) -> ResolveError {
2799        err = NoSolutionError::collapse_local_version_segments(NoSolutionError::collapse_proxies(
2800            err,
2801        ));
2802        err = NoSolutionError::narrow_widened_sets(err, known_versions);
2803
2804        let mut unavailable_packages = FxHashMap::default();
2805        for package in derivation_tree_packages(&err) {
2806            if let PubGrubPackageInner::Package { name, .. } = &**package {
2807                if let Some(reason) = self.unavailable_packages.pin().get(name) {
2808                    unavailable_packages.insert(name.clone(), reason.clone());
2809                }
2810            }
2811        }
2812
2813        let mut incomplete_packages = FxHashMap::default();
2814        let incomplete_packages_cache = self.incomplete_packages.pin();
2815        for package in derivation_tree_packages(&err) {
2816            if let PubGrubPackageInner::Package { name, .. } = &**package
2817                && let Some(versions) = incomplete_packages_cache.get(name)
2818            {
2819                for (version, reason) in &versions.pin() {
2820                    incomplete_packages
2821                        .entry(name.clone())
2822                        .or_insert_with(BTreeMap::default)
2823                        .insert(version.clone(), reason.clone());
2824                }
2825            }
2826        }
2827
2828        let mut available_indexes = FxHashMap::default();
2829        let mut included_versions = FxHashMap::default();
2830        let mut available_versions = FxHashMap::default();
2831
2832        let available_version_cutoff: Option<jiff::Timestamp> =
2833            std::env::var(EnvVars::UV_TEST_AVAILABLE_VERSION_CUTOFF)
2834                .ok()
2835                .and_then(|s| s.parse().ok());
2836
2837        for package in derivation_tree_packages(&err) {
2838            let Some(name) = package.name() else { continue };
2839            if !visited.contains(name) {
2840                // Avoid including version data for packages that exist in the derivation
2841                // tree, but were never visited during resolution. We _may_ have metadata for
2842                // these packages, but it's non-deterministic, and omitting them ensures that
2843                // we represent the state of the resolver at the time of failure.
2844                continue;
2845            }
2846            let versions_response = if let Some(index) = fork_indexes.get(name) {
2847                self.index
2848                    .explicit()
2849                    .get(&(name.clone(), index.url().clone()))
2850            } else {
2851                self.index.implicit().get(name)
2852            };
2853            if let Some(response) = versions_response {
2854                if let VersionsResponse::Found(ref version_maps) = *response {
2855                    // Track included and available versions, across all indexes.
2856                    for version_map in version_maps {
2857                        let package_included_versions = included_versions
2858                            .entry(name.clone())
2859                            .or_insert_with(BTreeSet::new);
2860                        let package_available_versions = available_versions
2861                            .entry(name.clone())
2862                            .or_insert_with(BTreeSet::new);
2863
2864                        for (version, dists) in version_map.iter(&Ranges::full()) {
2865                            // Included versions are those that survive the effective
2866                            // `exclude-newer` filter used during resolution. Files with
2867                            // missing upload times are treated as excluded (matching
2868                            // the resolution behavior in `version_map.rs`).
2869                            let excluded_from_included = || {
2870                                let Some(included_version_cutoff) =
2871                                    version_map.included_version_cutoff()
2872                                else {
2873                                    return false;
2874                                };
2875                                let Some(prioritized_dist) = dists.prioritized_dist() else {
2876                                    return true;
2877                                };
2878                                prioritized_dist.files().all(|file| {
2879                                    file.upload_time_utc_ms.is_none_or(|upload_time| {
2880                                        upload_time >= included_version_cutoff.as_millisecond()
2881                                    })
2882                                })
2883                            };
2884
2885                            if !excluded_from_included() {
2886                                package_included_versions.insert(version.clone());
2887                            }
2888
2889                            // Available versions are used in resolver error reporting,
2890                            // and can be bounded by a test-only cutoff for deterministic
2891                            // snapshots. Files with missing upload times are *not*
2892                            // excluded, since we only filter versions we can confirm
2893                            // were published after the cutoff.
2894                            let excluded_from_available = || {
2895                                let Some(ref exclude_newer) = available_version_cutoff else {
2896                                    return false;
2897                                };
2898                                let Some(prioritized_dist) = dists.prioritized_dist() else {
2899                                    return false;
2900                                };
2901                                prioritized_dist.files().all(|file| {
2902                                    file.upload_time_utc_ms.is_some_and(|upload_time| {
2903                                        upload_time >= exclude_newer.as_millisecond()
2904                                    })
2905                                })
2906                            };
2907
2908                            if !excluded_from_available() {
2909                                package_available_versions.insert(version.clone());
2910                            }
2911                        }
2912                    }
2913
2914                    // Track the indexes in which the package is available.
2915                    available_indexes
2916                        .entry(name.clone())
2917                        .or_insert(BTreeSet::new())
2918                        .extend(
2919                            version_maps
2920                                .iter()
2921                                .filter_map(|version_map| version_map.index().cloned()),
2922                        );
2923                }
2924            }
2925        }
2926
2927        ResolveError::NoSolution(Box::new(NoSolutionError::new(
2928            err,
2929            self.index.clone(),
2930            included_versions,
2931            available_versions,
2932            available_indexes,
2933            self.selector.clone(),
2934            self.python_requirement.clone(),
2935            self.locations.clone(),
2936            self.capabilities.clone(),
2937            unavailable_packages,
2938            incomplete_packages,
2939            fork_urls,
2940            fork_indexes,
2941            env,
2942            current_environment,
2943            self.tags.clone(),
2944            self.workspace_members.clone(),
2945            self.options.clone(),
2946        )))
2947    }
2948
2949    fn on_progress(&self, package: &PubGrubPackage, version: &Version) {
2950        if let Some(reporter) = self.reporter.as_ref() {
2951            match &**package {
2952                PubGrubPackageInner::Root(_) => {}
2953                PubGrubPackageInner::Python(_) => {}
2954                PubGrubPackageInner::System(_) => {}
2955                PubGrubPackageInner::Marker { .. } => {}
2956                PubGrubPackageInner::Extra { .. } => {}
2957                PubGrubPackageInner::Group { .. } => {}
2958                PubGrubPackageInner::Package { name, .. } => {
2959                    reporter.on_progress(name, &VersionOrUrlRef::Version(version));
2960                }
2961            }
2962        }
2963    }
2964
2965    fn on_complete(&self) {
2966        if let Some(reporter) = self.reporter.as_ref() {
2967            reporter.on_complete();
2968        }
2969    }
2970}
2971
2972/// State that is used during unit propagation in the resolver, one instance per fork.
2973#[derive(Clone)]
2974pub(crate) struct ForkState {
2975    /// The internal state used by the resolver.
2976    ///
2977    /// Note that not all parts of this state are strictly internal. For
2978    /// example, the edges in the dependency graph generated as part of the
2979    /// output of resolution are derived from the "incompatibilities" tracked
2980    /// in this state. We also ultimately retrieve the final set of version
2981    /// assignments (to packages) from this state's "partial solution."
2982    pubgrub: State<UvDependencyProvider>,
2983    /// The initial package to select. If set, the first iteration over this state will avoid
2984    /// asking PubGrub for the highest-priority package, and will instead use the provided package.
2985    initial_id: Option<Id<PubGrubPackage>>,
2986    /// The initial version to select. If set, the first iteration over this state will avoid
2987    /// asking PubGrub for the highest-priority version, and will instead use the provided version.
2988    initial_version: Option<Version>,
2989    /// The next package on which to run unit propagation.
2990    next: Id<PubGrubPackage>,
2991    /// The set of pinned versions we accrue throughout resolution.
2992    ///
2993    /// The key of this map is a package name, and each package name maps to
2994    /// a set of versions for that package. Each version in turn is mapped
2995    /// to the concrete distribution selected for installation, along with the
2996    /// concrete distribution whose metadata was used during resolution.
2997    /// After resolution is finished, this map is consulted to recover both the
2998    /// locked artifact and the metadata backing the resolved dependency edges.
2999    pins: FilePins,
3000    /// Ensure we don't have duplicate URLs in any branch.
3001    ///
3002    /// Unlike [`Urls`], we add only the URLs we have seen in this branch, and there can be only
3003    /// one URL per package. By prioritizing direct URL dependencies over registry dependencies,
3004    /// this map is populated for all direct URL packages before we look at any registry packages.
3005    fork_urls: ForkUrls,
3006    /// Ensure we don't have duplicate indexes in any branch.
3007    ///
3008    /// Unlike [`Indexes`], we add only the indexes we have seen in this branch, and there can be
3009    /// only one index per package.
3010    fork_indexes: ForkIndexes,
3011    /// When dependencies for a package are retrieved, this map of priorities
3012    /// is updated based on how each dependency was specified. Certain types
3013    /// of dependencies have more "priority" than others (like direct URL
3014    /// dependencies). These priorities help determine which package to
3015    /// consider next during resolution.
3016    priorities: PubGrubPriorities,
3017    /// This keeps track of the set of versions for each package that we've
3018    /// already visited during resolution. This avoids doing redundant work.
3019    added_dependencies: FxHashMap<Id<PubGrubPackage>, FxHashSet<Version>>,
3020    /// The last range scheduled for prefetch for each undecided package.
3021    pre_visited: FxHashMap<Id<PubGrubPackage>, Range<Version>>,
3022    /// The last version selected for each package and range in a specific environment.
3023    selected_versions: FxHashMap<Id<PubGrubPackage>, (Range<Version>, Version)>,
3024    /// All known versions for each package, from the version maps and the installed packages,
3025    /// used to keep the version sets in the partial solution minimal.
3026    ///
3027    /// Per fork, since the index for a package can differ between forks.
3028    known_versions: FxHashMap<PackageName, Arc<[Version]>>,
3029    /// The marker expression that created this state.
3030    ///
3031    /// The root state always corresponds to a marker expression that is always
3032    /// `true` for every `MarkerEnvironment`.
3033    ///
3034    /// In non-universal mode, forking never occurs and so this marker
3035    /// expression is always `true`.
3036    ///
3037    /// Whenever dependencies are fetched, all requirement specifications
3038    /// are checked for disjointness with the marker expression of the fork
3039    /// in which those dependencies were fetched. If a requirement has a
3040    /// completely disjoint marker expression (i.e., it can never be true given
3041    /// that the marker expression that provoked the fork is true), then that
3042    /// dependency is completely ignored.
3043    env: ResolverEnvironment,
3044    /// The Python requirement for this fork. Defaults to the Python requirement for
3045    /// the resolution, but may be narrowed if a `python_version` marker is present
3046    /// in a given fork.
3047    ///
3048    /// For example, in:
3049    /// ```text
3050    /// numpy >=1.26 ; python_version >= "3.9"
3051    /// numpy <1.26 ; python_version < "3.9"
3052    /// ```
3053    ///
3054    /// The top fork has a narrower Python compatibility range, and thus can find a
3055    /// solution that omits Python 3.8 support.
3056    python_requirement: PythonRequirement,
3057    conflict_tracker: ConflictTracker,
3058    /// Prefetch package versions for packages with many rejected versions.
3059    ///
3060    /// Tracked on the fork state to avoid counting each identical version between forks as new try.
3061    prefetcher: BatchPrefetcher,
3062}
3063
3064impl ForkState {
3065    fn new(
3066        pubgrub: State<UvDependencyProvider>,
3067        env: ResolverEnvironment,
3068        python_requirement: PythonRequirement,
3069        prefetcher: BatchPrefetcher,
3070    ) -> Self {
3071        Self {
3072            initial_id: None,
3073            initial_version: None,
3074            next: pubgrub.root_package,
3075            pubgrub,
3076            pins: FilePins::default(),
3077            fork_urls: ForkUrls::default(),
3078            fork_indexes: ForkIndexes::default(),
3079            priorities: PubGrubPriorities::default(),
3080            added_dependencies: FxHashMap::default(),
3081            pre_visited: FxHashMap::default(),
3082            selected_versions: FxHashMap::default(),
3083            known_versions: FxHashMap::default(),
3084            env,
3085            python_requirement,
3086            conflict_tracker: ConflictTracker::default(),
3087            prefetcher,
3088        }
3089    }
3090
3091    /// Visit the dependencies for the selected version of the current package, incorporating any
3092    /// relevant URLs and pinned indexes into the [`ForkState`].
3093    fn visit_package_version_dependencies(
3094        &mut self,
3095        for_package: Id<PubGrubPackage>,
3096        for_version: &Version,
3097        urls: &Urls,
3098        indexes: &Indexes,
3099        dependencies: &[PubGrubDependency],
3100        git: &GitResolver,
3101        workspace_members: &BTreeSet<PackageName>,
3102        resolution_strategy: &ResolutionStrategy,
3103    ) -> Result<(), ResolveError> {
3104        for dependency in dependencies {
3105            let PubGrubDependency {
3106                package,
3107                version,
3108                parent: _,
3109                source,
3110            } = dependency;
3111
3112            let mut has_url = false;
3113            if let Some(name) = package.name() {
3114                // From the [`Requirement`] to [`PubGrubDependency`] conversion, we get a URL if the
3115                // requirement was a URL requirement. `Urls` applies canonicalization to this and
3116                // override URLs to both URL and registry requirements, which we then check for
3117                // conflicts using [`ForkUrl`].
3118                for url in urls.get_url(&self.env, name, source.verbatim_url(), git)? {
3119                    self.fork_urls.insert(name, url, &self.env)?;
3120                    has_url = true;
3121                }
3122
3123                if let Some(index) = source.explicit_index() {
3124                    self.fork_indexes.insert(name, index, &self.env)?;
3125                }
3126
3127                // If the package is pinned to an exact index, add it to the fork.
3128                for index in indexes.get(name, &self.env) {
3129                    self.fork_indexes.insert(name, index, &self.env)?;
3130                }
3131            }
3132
3133            if let Some(name) = self.pubgrub.package_store[for_package]
3134                .name_no_root()
3135                .filter(|name| !workspace_members.contains(name))
3136            {
3137                debug!(
3138                    "Adding transitive dependency for {name}=={for_version}: {package}{version}"
3139                );
3140            } else {
3141                // A dependency from the root package or `requirements.txt`.
3142                debug!("Adding direct dependency: {package}{version}");
3143
3144                // Warn the user if a direct dependency lacks a lower bound in `--lowest` resolution.
3145                let missing_lower_bound = version
3146                    .bounding_range()
3147                    .is_none_or(|(lowest, _highest)| lowest == Bound::Unbounded);
3148                let strategy_lowest = matches!(
3149                    resolution_strategy,
3150                    ResolutionStrategy::Lowest | ResolutionStrategy::LowestDirect(..)
3151                );
3152
3153                if !has_url && missing_lower_bound && strategy_lowest {
3154                    let name = package.name_no_root().unwrap();
3155                    // Handle cases where a package is listed both without and with a lower bound.
3156                    // Example:
3157                    // ```
3158                    // "coverage[toml] ; python_version < '3.11'",
3159                    // "coverage >= 7.10.0",
3160                    // ```
3161                    let bound_on_other_package = dependencies.iter().any(|other| {
3162                        Some(name) == other.package.name()
3163                            && !other
3164                                .version
3165                                .bounding_range()
3166                                .is_none_or(|(lowest, _highest)| lowest == Bound::Unbounded)
3167                    });
3168
3169                    if !bound_on_other_package {
3170                        warn_user_once!(
3171                            "The direct dependency `{name}` is unpinned. \
3172                            Consider setting a lower bound when using `--resolution lowest` \
3173                            or `--resolution lowest-direct` to avoid using outdated versions.",
3174                        );
3175                    }
3176                }
3177            }
3178
3179            // Update the package priorities.
3180            self.priorities.insert(package, version, &self.fork_urls);
3181            // As we're adding an incompatibility from the proxy package to the base package,
3182            // we need to register the base package.
3183            if let Some(base_package) = package.base_package() {
3184                self.priorities
3185                    .insert(&base_package, version, &self.fork_urls);
3186            }
3187        }
3188
3189        Ok(())
3190    }
3191
3192    /// Adds the dependencies for the selected version of the current package.
3193    ///
3194    /// For registry packages, the depending version is widened across gaps containing no other
3195    /// known version before its incompatibilities are added. Packages without a complete registry
3196    /// version map retain the selected version's singleton range.
3197    fn add_package_version_dependencies<InstalledPackages: InstalledPackagesProvider>(
3198        &mut self,
3199        for_package: Id<PubGrubPackage>,
3200        for_version: &Version,
3201        dependencies: Vec<PubGrubDependency>,
3202        index: &InMemoryIndex,
3203        installed_packages: &InstalledPackages,
3204    ) {
3205        for dependency in &dependencies {
3206            let PubGrubDependency {
3207                package,
3208                version,
3209                parent: _,
3210                source: _,
3211            } = dependency;
3212
3213            let Some(base_package) = package.base_package() else {
3214                continue;
3215            };
3216
3217            let proxy_package = self.pubgrub.package_store.alloc(package.clone());
3218            let base_package_id = self.pubgrub.package_store.alloc(base_package.clone());
3219            self.pubgrub.add_proxy_package_incompatibility(
3220                proxy_package,
3221                base_package_id,
3222                version.clone(),
3223            );
3224        }
3225
3226        // Widen across gaps so rejected adjacent versions merge into contiguous ranges rather
3227        // than leaving one hole per version.
3228        let versions = Range::singleton(for_version.clone());
3229        let versions = if let Some(known_versions) =
3230            ResolverState::<InstalledPackages>::known_versions(
3231                index,
3232                installed_packages,
3233                &self.fork_urls,
3234                &self.fork_indexes,
3235                &mut self.known_versions,
3236                &self.pubgrub.package_store[self.next],
3237            )
3238            .filter(|versions| !versions.is_empty())
3239        {
3240            versions.widen_versions(known_versions)
3241        } else {
3242            // A decided version is always selectable and thus in the list, but an empty list
3243            // would unsoundly widen to the full range.
3244            versions
3245        };
3246        let conflict = self.pubgrub.add_package_version_dependencies(
3247            self.next,
3248            for_version.clone(),
3249            versions,
3250            dependencies.into_iter().map(|dependency| {
3251                let PubGrubDependency {
3252                    package,
3253                    version,
3254                    parent: _,
3255                    source: _,
3256                } = dependency;
3257                (package, version)
3258            }),
3259        );
3260
3261        // Conflict tracking: If the version was rejected due to its dependencies, record culprit
3262        // and affected.
3263        if let Some(incompatibility) = conflict {
3264            self.record_conflict(for_package, Some(for_version), incompatibility);
3265        }
3266    }
3267
3268    fn record_conflict(
3269        &mut self,
3270        affected: Id<PubGrubPackage>,
3271        version: Option<&Version>,
3272        incompatibility: IncompId<PubGrubPackage, Range<Version>, UnavailableReason>,
3273    ) {
3274        let mut culprit_is_real = false;
3275        for (incompatible, _term) in self.pubgrub.incompatibility_store[incompatibility].iter() {
3276            if incompatible == affected {
3277                continue;
3278            }
3279            if self.pubgrub.package_store[affected].name()
3280                == self.pubgrub.package_store[incompatible].name()
3281            {
3282                // Don't track conflicts between a marker package and the main package, when the
3283                // marker is "copying" the obligations from the main package through conflicts.
3284                continue;
3285            }
3286            culprit_is_real = true;
3287            let culprit_count = self
3288                .conflict_tracker
3289                .culprit
3290                .entry(incompatible)
3291                .or_default();
3292            *culprit_count += 1;
3293            if *culprit_count == CONFLICT_THRESHOLD {
3294                self.conflict_tracker.deprioritize.push(incompatible);
3295            }
3296        }
3297        // Don't track conflicts between a marker package and the main package, when the
3298        // marker is "copying" the obligations from the main package through conflicts.
3299        if culprit_is_real {
3300            if tracing::enabled!(Level::DEBUG) {
3301                let incompatibility = self.pubgrub.incompatibility_store[incompatibility]
3302                    .iter()
3303                    .map(|(package, _term)| &self.pubgrub.package_store[package])
3304                    .join(", ");
3305                if let Some(version) = version {
3306                    debug!(
3307                        "Recording dependency conflict of {}=={} from incompatibility of ({})",
3308                        self.pubgrub.package_store[affected], version, incompatibility
3309                    );
3310                } else {
3311                    debug!(
3312                        "Recording unit propagation conflict of {} from incompatibility of ({})",
3313                        self.pubgrub.package_store[affected], incompatibility
3314                    );
3315                }
3316            }
3317
3318            let affected_count = self.conflict_tracker.affected.entry(self.next).or_default();
3319            *affected_count += 1;
3320            if *affected_count == CONFLICT_THRESHOLD {
3321                self.conflict_tracker.prioritize.push(self.next);
3322            }
3323        }
3324    }
3325
3326    /// Change the priority of often conflicting packages and backtrack.
3327    ///
3328    /// To be called after unit propagation.
3329    fn reprioritize_conflicts(&mut self) {
3330        for package in self.conflict_tracker.prioritize.drain(..) {
3331            let changed = self
3332                .priorities
3333                .mark_conflict_early(&self.pubgrub.package_store[package]);
3334            if changed {
3335                debug!(
3336                    "Package {} has too many conflicts (affected), prioritizing",
3337                    &self.pubgrub.package_store[package]
3338                );
3339            } else {
3340                debug!(
3341                    "Package {} has too many conflicts (affected), already {:?}",
3342                    self.pubgrub.package_store[package],
3343                    self.priorities.get(&self.pubgrub.package_store[package])
3344                );
3345            }
3346        }
3347
3348        for package in self.conflict_tracker.deprioritize.drain(..) {
3349            let changed = self
3350                .priorities
3351                .mark_conflict_late(&self.pubgrub.package_store[package]);
3352            if changed {
3353                debug!(
3354                    "Package {} has too many conflicts (culprit), deprioritizing and backtracking",
3355                    self.pubgrub.package_store[package],
3356                );
3357                let backtrack_level = self.pubgrub.backtrack_package(package);
3358                if let Some(backtrack_level) = backtrack_level {
3359                    debug!("Backtracked {backtrack_level} decisions");
3360                } else {
3361                    debug!(
3362                        "Package {} is not decided, cannot backtrack",
3363                        self.pubgrub.package_store[package]
3364                    );
3365                }
3366            } else {
3367                debug!(
3368                    "Package {} has too many conflicts (culprit), already {:?}",
3369                    self.pubgrub.package_store[package],
3370                    self.priorities.get(&self.pubgrub.package_store[package])
3371                );
3372            }
3373        }
3374    }
3375
3376    fn add_unavailable_version(&mut self, version: Version, reason: UnavailableVersion) {
3377        // Incompatible requires-python versions are special in that we track
3378        // them as incompatible dependencies instead of marking the package version
3379        // as unavailable directly.
3380        if let UnavailableVersion::IncompatibleDist(
3381            IncompatibleDist::Source(IncompatibleSource::RequiresPython(requires_python, kind))
3382            | IncompatibleDist::Wheel(IncompatibleWheel::RequiresPython(requires_python, kind)),
3383        ) = reason
3384        {
3385            let package = &self.next;
3386            let python = self.pubgrub.package_store.alloc(PubGrubPackage::from(
3387                PubGrubPackageInner::Python(match kind {
3388                    PythonRequirementKind::Installed => PubGrubPython::Installed,
3389                    PythonRequirementKind::Target => PubGrubPython::Target,
3390                }),
3391            ));
3392            self.pubgrub
3393                .add_incompatibility(Incompatibility::from_dependency(
3394                    *package,
3395                    Range::singleton(version.clone()),
3396                    (
3397                        python,
3398                        Range::from_versions(release_specifiers_to_ranges(requires_python)),
3399                    ),
3400                ));
3401            self.pubgrub
3402                .partial_solution
3403                .add_decision(self.next, version);
3404            return;
3405        }
3406        self.pubgrub
3407            .add_incompatibility(Incompatibility::custom_version(
3408                self.next,
3409                version.clone(),
3410                UnavailableReason::Version(reason),
3411            ));
3412    }
3413
3414    /// Subset the current markers with the new markers and update the python requirements fields
3415    /// accordingly.
3416    ///
3417    /// If the fork should be dropped (e.g., because its markers can never be true for its
3418    /// Python requirement), then this returns `None`.
3419    fn with_env(mut self, env: ResolverEnvironment) -> Self {
3420        self.selected_versions.clear();
3421        self.env = env;
3422        // If the fork contains a narrowed Python requirement, apply it.
3423        if let Some(req) = self.env.narrow_python_requirement(&self.python_requirement) {
3424            debug!("Narrowed `requires-python` bound to: {}", req.target());
3425            self.python_requirement = req;
3426        }
3427        self
3428    }
3429
3430    /// Returns the URL or index for a package and version.
3431    ///
3432    /// In practice, exactly one of the returned values will be `Some`.
3433    fn source(
3434        &self,
3435        name: &PackageName,
3436        version: &Version,
3437    ) -> (Option<&VerbatimParsedUrl>, Option<&IndexUrl>) {
3438        let url = self.fork_urls.get(name);
3439        let index = url
3440            .is_none()
3441            .then(|| {
3442                self.pins
3443                    .get(name, version)
3444                    .expect("Every package should be pinned")
3445                    .index()
3446            })
3447            .flatten();
3448        (url, index)
3449    }
3450
3451    fn into_resolution(self) -> Resolution {
3452        let solution: FxHashMap<_, _> = self.pubgrub.partial_solution.extract_solution().collect();
3453        let edge_count: usize = solution
3454            .keys()
3455            .map(|package| self.pubgrub.incompatibilities[package].len())
3456            .sum();
3457        let mut edges: Vec<ResolutionDependencyEdge> = Vec::with_capacity(edge_count);
3458        for (package, self_version) in &solution {
3459            for id in &self.pubgrub.incompatibilities[package] {
3460                let incompatibility = &self.pubgrub.incompatibility_store[*id];
3461                let pubgrub::Kind::FromDependencyOf(self_package, dependency_package) =
3462                    &incompatibility.kind
3463                else {
3464                    continue;
3465                };
3466                let (self_package, dependency_package) = (*self_package, *dependency_package);
3467                let Some((self_range, dependency_range)) =
3468                    incompatibility.dependency_version_sets()
3469                else {
3470                    continue;
3471                };
3472                let dependency_range =
3473                    dependency_range.map_or_else(|| Cow::Owned(Range::empty()), Cow::Borrowed);
3474                if *package != self_package {
3475                    continue;
3476                }
3477                if !self_range.contains(self_version) {
3478                    continue;
3479                }
3480                let Some(dependency_version) = solution.get(&dependency_package) else {
3481                    continue;
3482                };
3483                if !dependency_range.contains(dependency_version) {
3484                    continue;
3485                }
3486
3487                let self_package = &self.pubgrub.package_store[self_package];
3488                let dependency_package = &self.pubgrub.package_store[dependency_package];
3489
3490                let (self_name, self_extra, self_group) = match &**self_package {
3491                    PubGrubPackageInner::Package {
3492                        name: self_name,
3493                        extra: self_extra,
3494                        group: self_group,
3495                        marker: _,
3496                    } => (Some(self_name), self_extra.as_ref(), self_group.as_ref()),
3497
3498                    PubGrubPackageInner::Root(_) => (None, None, None),
3499
3500                    _ => continue,
3501                };
3502
3503                let (self_url, self_index) = self_name
3504                    .map(|self_name| self.source(self_name, self_version))
3505                    .unwrap_or((None, None));
3506
3507                match **dependency_package {
3508                    PubGrubPackageInner::Package {
3509                        name: ref dependency_name,
3510                        extra: ref dependency_extra,
3511                        group: ref dependency_dev,
3512                        marker: ref dependency_marker,
3513                    } => {
3514                        debug_assert!(
3515                            dependency_extra.is_none(),
3516                            "Packages should depend on an extra proxy"
3517                        );
3518                        debug_assert!(
3519                            dependency_dev.is_none(),
3520                            "Packages should depend on a group proxy"
3521                        );
3522
3523                        // Ignore self-dependencies (e.g., `tensorflow-macos` depends on `tensorflow-macos`),
3524                        // but allow groups to depend on other groups, or on the package itself.
3525                        if self_group.is_none() {
3526                            if self_name == Some(dependency_name) {
3527                                continue;
3528                            }
3529                        }
3530
3531                        let (to_url, to_index) = self.source(dependency_name, dependency_version);
3532
3533                        let edge = ResolutionDependencyEdge {
3534                            from: self_name.cloned(),
3535                            from_version: self_version.clone(),
3536                            from_url: self_url.cloned(),
3537                            from_index: self_index.cloned(),
3538                            from_extra: self_extra.cloned(),
3539                            from_group: self_group.cloned(),
3540                            to: dependency_name.clone(),
3541                            to_version: dependency_version.clone(),
3542                            to_url: to_url.cloned(),
3543                            to_index: to_index.cloned(),
3544                            to_extra: dependency_extra.clone(),
3545                            to_group: dependency_dev.clone(),
3546                            marker: *dependency_marker,
3547                        };
3548                        edges.push(edge);
3549                    }
3550
3551                    PubGrubPackageInner::Marker {
3552                        name: ref dependency_name,
3553                        marker: ref dependency_marker,
3554                    } => {
3555                        // Ignore self-dependencies (e.g., `tensorflow-macos` depends on `tensorflow-macos`),
3556                        // but allow groups to depend on other groups, or on the package itself.
3557                        if self_group.is_none() {
3558                            if self_name == Some(dependency_name) {
3559                                continue;
3560                            }
3561                        }
3562
3563                        let (to_url, to_index) = self.source(dependency_name, dependency_version);
3564
3565                        let edge = ResolutionDependencyEdge {
3566                            from: self_name.cloned(),
3567                            from_version: self_version.clone(),
3568                            from_url: self_url.cloned(),
3569                            from_index: self_index.cloned(),
3570                            from_extra: self_extra.cloned(),
3571                            from_group: self_group.cloned(),
3572                            to: dependency_name.clone(),
3573                            to_version: dependency_version.clone(),
3574                            to_url: to_url.cloned(),
3575                            to_index: to_index.cloned(),
3576                            to_extra: None,
3577                            to_group: None,
3578                            marker: *dependency_marker,
3579                        };
3580                        edges.push(edge);
3581                    }
3582
3583                    PubGrubPackageInner::Extra {
3584                        name: ref dependency_name,
3585                        extra: ref dependency_extra,
3586                        marker: ref dependency_marker,
3587                    } => {
3588                        if self_group.is_none() {
3589                            debug_assert!(
3590                                self_name != Some(dependency_name),
3591                                "Extras should be flattened"
3592                            );
3593                        }
3594                        let (to_url, to_index) = self.source(dependency_name, dependency_version);
3595
3596                        // Insert an edge from the dependent package to the extra package.
3597                        let edge = ResolutionDependencyEdge {
3598                            from: self_name.cloned(),
3599                            from_version: self_version.clone(),
3600                            from_url: self_url.cloned(),
3601                            from_index: self_index.cloned(),
3602                            from_extra: self_extra.cloned(),
3603                            from_group: self_group.cloned(),
3604                            to: dependency_name.clone(),
3605                            to_version: dependency_version.clone(),
3606                            to_url: to_url.cloned(),
3607                            to_index: to_index.cloned(),
3608                            to_extra: Some(dependency_extra.clone()),
3609                            to_group: None,
3610                            marker: *dependency_marker,
3611                        };
3612                        edges.push(edge);
3613
3614                        // Insert an edge from the dependent package to the base package.
3615                        let edge = ResolutionDependencyEdge {
3616                            from: self_name.cloned(),
3617                            from_version: self_version.clone(),
3618                            from_url: self_url.cloned(),
3619                            from_index: self_index.cloned(),
3620                            from_extra: self_extra.cloned(),
3621                            from_group: self_group.cloned(),
3622                            to: dependency_name.clone(),
3623                            to_version: dependency_version.clone(),
3624                            to_url: to_url.cloned(),
3625                            to_index: to_index.cloned(),
3626                            to_extra: None,
3627                            to_group: None,
3628                            marker: *dependency_marker,
3629                        };
3630                        edges.push(edge);
3631                    }
3632
3633                    PubGrubPackageInner::Group {
3634                        name: ref dependency_name,
3635                        group: ref dependency_group,
3636                        marker: ref dependency_marker,
3637                    } => {
3638                        debug_assert!(
3639                            self_name != Some(dependency_name),
3640                            "Groups should be flattened"
3641                        );
3642
3643                        let (to_url, to_index) = self.source(dependency_name, dependency_version);
3644
3645                        // Add an edge from the dependent package to the dev package, but _not_ the
3646                        // base package.
3647                        let edge = ResolutionDependencyEdge {
3648                            from: self_name.cloned(),
3649                            from_version: self_version.clone(),
3650                            from_url: self_url.cloned(),
3651                            from_index: self_index.cloned(),
3652                            from_extra: self_extra.cloned(),
3653                            from_group: self_group.cloned(),
3654                            to: dependency_name.clone(),
3655                            to_version: dependency_version.clone(),
3656                            to_url: to_url.cloned(),
3657                            to_index: to_index.cloned(),
3658                            to_extra: None,
3659                            to_group: Some(dependency_group.clone()),
3660                            marker: *dependency_marker,
3661                        };
3662                        edges.push(edge);
3663                    }
3664
3665                    _ => {}
3666                }
3667            }
3668        }
3669
3670        let nodes = solution
3671            .into_iter()
3672            .filter_map(|(package, version)| {
3673                if let PubGrubPackageInner::Package {
3674                    name,
3675                    extra,
3676                    group,
3677                    marker: MarkerTree::TRUE,
3678                } = &*self.pubgrub.package_store[package]
3679                {
3680                    let (url, index) = self.source(name, &version);
3681                    Some((
3682                        ResolutionPackage {
3683                            name: name.clone(),
3684                            extra: extra.clone(),
3685                            dev: group.clone(),
3686                            url: url.cloned(),
3687                            index: index.cloned(),
3688                        },
3689                        version,
3690                    ))
3691                } else {
3692                    None
3693                }
3694            })
3695            .collect();
3696
3697        Resolution {
3698            nodes,
3699            edges,
3700            pins: self.pins,
3701            env: self.env,
3702        }
3703    }
3704}
3705
3706/// Fetch the metadata for an item
3707#[derive(Debug)]
3708#[expect(clippy::large_enum_variant)]
3709pub(crate) enum Request {
3710    /// A request to fetch the metadata for a package.
3711    Package(PackageName, Option<IndexMetadata>),
3712    /// A request to fetch the metadata for a built or source distribution.
3713    Dist(Dist),
3714    /// A request to fetch the metadata from an already-installed distribution.
3715    Installed(InstalledDist),
3716    /// A request to pre-fetch the metadata for a package and the best-guess distribution.
3717    Prefetch(PackageName, Range<Version>, PythonRequirement),
3718}
3719
3720impl<'a> From<ResolvedDistRef<'a>> for Request {
3721    fn from(dist: ResolvedDistRef<'a>) -> Self {
3722        // N.B. This is almost identical to `ResolvedDistRef::to_owned`, but
3723        // creates a `Request` instead of a `ResolvedDist`. There's probably
3724        // some room for DRYing this up a bit. The obvious way would be to
3725        // add a method to create a `Dist`, but a `Dist` cannot be represented
3726        // as an installed dist.
3727        match dist {
3728            ResolvedDistRef::InstallableRegistrySourceDist { sdist, prioritized } => {
3729                // This is okay because we're only here if the prioritized dist
3730                // has an sdist, so this always succeeds.
3731                let source = prioritized.source_dist().expect("a source distribution");
3732                assert_eq!(
3733                    (&sdist.name, &sdist.version),
3734                    (&source.name, &source.version),
3735                    "expected chosen sdist to match prioritized sdist"
3736                );
3737                Self::Dist(Dist::Source(SourceDist::Registry(source)))
3738            }
3739            ResolvedDistRef::InstallableRegistryBuiltDist {
3740                wheel, prioritized, ..
3741            } => {
3742                assert_eq!(
3743                    Some(&wheel.filename),
3744                    prioritized.best_wheel().map(|(wheel, _)| &wheel.filename),
3745                    "expected chosen wheel to match best wheel"
3746                );
3747                // This is okay because we're only here if the prioritized dist
3748                // has at least one wheel, so this always succeeds.
3749                let built = prioritized.built_dist().expect("at least one wheel");
3750                Self::Dist(Dist::Built(BuiltDist::Registry(built)))
3751            }
3752            ResolvedDistRef::Installed { dist } => Self::Installed(dist.clone()),
3753        }
3754    }
3755}
3756
3757impl Display for Request {
3758    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
3759        match self {
3760            Self::Package(package_name, _) => {
3761                write!(f, "Versions {package_name}")
3762            }
3763            Self::Dist(dist) => {
3764                write!(f, "Metadata {dist}")
3765            }
3766            Self::Installed(dist) => {
3767                write!(f, "Installed metadata {dist}")
3768            }
3769            Self::Prefetch(package_name, range, _) => {
3770                write!(f, "Prefetch {package_name} {range}")
3771            }
3772        }
3773    }
3774}
3775
3776#[derive(Debug)]
3777#[expect(clippy::large_enum_variant)]
3778enum Response {
3779    /// The returned metadata for a package hosted on a registry.
3780    Package(PackageName, Option<IndexUrl>, VersionsResponse),
3781    /// The returned metadata for a distribution.
3782    Dist {
3783        dist: Dist,
3784        metadata: MetadataResponse,
3785    },
3786    /// The returned metadata for an already-installed distribution.
3787    Installed {
3788        dist: InstalledDist,
3789        metadata: MetadataResponse,
3790    },
3791}
3792
3793/// Information about the dependencies for a particular package.
3794///
3795/// This effectively distills the dependency metadata of a package down into
3796/// its pubgrub specific constituent parts: each dependency package has a range
3797/// of possible versions.
3798enum Dependencies {
3799    /// Package dependencies are not available.
3800    Unavailable(UnavailableVersion),
3801    /// Container for all available package versions.
3802    ///
3803    /// Note that in universal mode, it is possible and allowed for multiple
3804    /// `PubGrubPackage` values in this list to have the same package name.
3805    /// These conflicts are resolved via [`ForkedDependencies::from_dependencies_universal`].
3806    Available(Vec<PubGrubDependency>),
3807    /// Package metadata has a `Requires-Python` specifier that is incompatible with the target.
3808    RequiresPython(VersionSpecifiers),
3809    /// Dependencies that should never result in a fork.
3810    ///
3811    /// For example, the dependencies of a `Marker` package will have the
3812    /// same name and version, but differ according to marker expressions.
3813    /// But we never want this to result in a fork.
3814    Unforkable(Vec<PubGrubDependency>),
3815}
3816
3817/// Information about the (possibly forked) dependencies for a particular
3818/// package.
3819///
3820/// This is like `Dependencies` but with an extra variant that only occurs when
3821/// a `Dependencies` list has multiple dependency specifications with the same
3822/// name and non-overlapping marker expressions (i.e., a fork occurs).
3823#[derive(Debug)]
3824enum ForkedDependencies {
3825    /// Package dependencies are not available.
3826    Unavailable(UnavailableVersion),
3827    /// No forking occurred.
3828    ///
3829    /// This is the same as `Dependencies::Available`.
3830    Unforked(Vec<PubGrubDependency>),
3831    /// Forked containers for all available package versions.
3832    ///
3833    /// Note that there is always at least two forks. If there would
3834    /// be fewer than 2 forks, then there is no fork at all and the
3835    /// `Unforked` variant is used instead.
3836    Forked {
3837        forks: Vec<Fork>,
3838        /// The package(s) with different requirements for disjoint markers.
3839        diverging_packages: BTreeSet<PackageName>,
3840    },
3841    /// Package metadata has a `Requires-Python` specifier that is incompatible with the target.
3842    RequiresPython(VersionSpecifiers),
3843}
3844
3845impl ForkedDependencies {
3846    /// Turn a flat list of dependencies into a potential set of forked
3847    /// groups of dependencies.
3848    ///
3849    /// A fork *only* occurs when there are multiple dependencies with the same
3850    /// name *and* those dependency specifications have corresponding marker
3851    /// expressions that are completely disjoint with one another.
3852    fn from_dependencies_universal(
3853        dependencies: Dependencies,
3854        env: &ResolverEnvironment,
3855        python_requirement: &PythonRequirement,
3856        conflicts: &Conflicts,
3857    ) -> Self {
3858        let deps = match dependencies {
3859            Dependencies::Available(deps) => deps,
3860            Dependencies::Unforkable(deps) => return Self::Unforked(deps),
3861            Dependencies::RequiresPython(requires_python) => {
3862                return Self::RequiresPython(requires_python);
3863            }
3864            Dependencies::Unavailable(err) => return Self::Unavailable(err),
3865        };
3866        let mut name_to_deps: BTreeMap<PackageName, Vec<PubGrubDependency>> = BTreeMap::new();
3867        for dep in deps {
3868            let name = dep
3869                .package
3870                .name()
3871                .expect("dependency always has a name")
3872                .clone();
3873            name_to_deps.entry(name).or_default().push(dep);
3874        }
3875        let (mut forks, diverging_packages) =
3876            Self::fork(name_to_deps, env, python_requirement, conflicts);
3877        if forks.is_empty() {
3878            Self::Unforked(vec![])
3879        } else if forks.len() == 1 {
3880            Self::Unforked(forks.pop().unwrap().dependencies)
3881        } else {
3882            Self::Forked {
3883                forks,
3884                diverging_packages,
3885            }
3886        }
3887    }
3888
3889    /// Noop companion to [`ForkedDependencies::from_dependencies_universal`] for non-universal
3890    /// resolutions with a fixed marker environment.
3891    fn from_dependencies_platform_specific(dependencies: Dependencies) -> Self {
3892        match dependencies {
3893            Dependencies::Available(deps) | Dependencies::Unforkable(deps) => Self::Unforked(deps),
3894            Dependencies::RequiresPython(requires_python) => Self::RequiresPython(requires_python),
3895            Dependencies::Unavailable(err) => Self::Unavailable(err),
3896        }
3897    }
3898
3899    /// Build a list of forks determined from the dependencies of a single package.
3900    ///
3901    /// Any time a marker expression is seen that is not true for all possible
3902    /// marker environments, it is possible for it to introduce a new fork.
3903    ///
3904    /// Returns the forks discovered among the dependencies and the package(s) that
3905    /// provoked at least one additional fork.
3906    fn fork(
3907        name_to_deps: BTreeMap<PackageName, Vec<PubGrubDependency>>,
3908        env: &ResolverEnvironment,
3909        python_requirement: &PythonRequirement,
3910        conflicts: &Conflicts,
3911    ) -> (Vec<Fork>, BTreeSet<PackageName>) {
3912        let python_marker = python_requirement.to_marker_tree();
3913
3914        let mut forks = vec![Fork::new(env.clone())];
3915        let mut diverging_packages = BTreeSet::new();
3916        for (name, mut deps) in name_to_deps {
3917            assert!(!deps.is_empty(), "every name has at least one dependency");
3918            // We never fork if there's only one dependency
3919            // specification for a given package name. This particular
3920            // strategy results in a "conservative" approach to forking
3921            // that gives up correctness in some cases in exchange for
3922            // more limited forking. More limited forking results in
3923            // simpler-and-easier-to-understand lock files and faster
3924            // resolving. The correctness we give up manifests when
3925            // two transitive non-sibling dependencies conflict. In
3926            // that case, we don't detect the fork ahead of time (at
3927            // present).
3928            if let [dep] = deps.as_slice() {
3929                // There's one exception: if the requirement increases the minimum-supported Python
3930                // version, we also fork in order to respect that minimum in the subsequent
3931                // resolution.
3932                //
3933                // For example, given `requires-python = ">=3.7"` and `uv ; python_version >= "3.8"`,
3934                // where uv itself only supports Python 3.8 and later, we need to fork to ensure
3935                // that the resolution can find a solution.
3936                if marker::requires_python(dep.package.marker())
3937                    .is_none_or(|bound| !python_requirement.raises(&bound))
3938                {
3939                    let dep = deps.pop().unwrap();
3940                    let marker = dep.package.marker();
3941                    for fork in &mut forks {
3942                        if fork.env.included_by_marker(marker) {
3943                            fork.add_dependency(dep.clone());
3944                        }
3945                    }
3946                    continue;
3947                }
3948            } else {
3949                // If all dependencies have the same markers, we should also avoid forking.
3950                if let Some(dep) = deps.first() {
3951                    let marker = dep.package.marker();
3952                    if deps.iter().all(|dep| marker == dep.package.marker()) {
3953                        // Unless that "same marker" is a Python requirement that is stricter than
3954                        // the current Python requirement. In that case, we need to fork to respect
3955                        // the stricter requirement.
3956                        if marker::requires_python(marker)
3957                            .is_none_or(|bound| !python_requirement.raises(&bound))
3958                        {
3959                            for dep in deps {
3960                                for fork in &mut forks {
3961                                    if fork.env.included_by_marker(marker) {
3962                                        fork.add_dependency(dep.clone());
3963                                    }
3964                                }
3965                            }
3966                            continue;
3967                        }
3968                    }
3969                }
3970            }
3971            for dep in deps {
3972                let mut forker = match ForkingPossibility::new(env, &dep) {
3973                    ForkingPossibility::Possible(forker) => forker,
3974                    ForkingPossibility::DependencyAlwaysExcluded => {
3975                        // If the markers can never be satisfied by the parent
3976                        // fork, then we can drop this dependency unceremoniously.
3977                        continue;
3978                    }
3979                    ForkingPossibility::NoForkingPossible => {
3980                        // Or, if the markers are always true, then we just
3981                        // add the dependency to every fork unconditionally.
3982                        for fork in &mut forks {
3983                            fork.add_dependency(dep.clone());
3984                        }
3985                        continue;
3986                    }
3987                };
3988                // Otherwise, we *should* need to add a new fork...
3989                diverging_packages.insert(name.clone());
3990
3991                let mut new = vec![];
3992                for fork in std::mem::take(&mut forks) {
3993                    let Some((remaining_forker, envs)) = forker.fork(&fork.env) else {
3994                        new.push(fork);
3995                        continue;
3996                    };
3997                    forker = remaining_forker;
3998
3999                    for fork_env in envs {
4000                        let mut new_fork = fork.clone();
4001                        new_fork.set_env(fork_env);
4002                        // We only add the dependency to this fork if it
4003                        // satisfies the fork's markers. Some forks are
4004                        // specifically created to exclude this dependency,
4005                        // so this isn't always true!
4006                        if forker.included(&new_fork.env) {
4007                            new_fork.add_dependency(dep.clone());
4008                        }
4009                        // Filter out any forks we created that are disjoint with our
4010                        // Python requirement.
4011                        if new_fork.env.included_by_marker(python_marker) {
4012                            new.push(new_fork);
4013                        }
4014                    }
4015                }
4016                forks = new;
4017            }
4018        }
4019        // When there is a conflicting group configuration, we need
4020        // to potentially add more forks. Each fork added contains an
4021        // exclusion list of conflicting groups where dependencies with
4022        // the corresponding package and extra name are forcefully
4023        // excluded from that group.
4024        //
4025        // We specifically iterate on conflicting groups and
4026        // potentially re-generate all forks for each one. We do it
4027        // this way in case there are multiple sets of conflicting
4028        // groups that impact the forks here.
4029        //
4030        // For example, if we have conflicting groups {x1, x2} and {x3,
4031        // x4}, we need to make sure the forks generated from one set
4032        // also account for the other set.
4033        for set in conflicts.iter() {
4034            let mut new = vec![];
4035            for fork in std::mem::take(&mut forks) {
4036                // Check if this conflict set is relevant to this fork. We need two conditions:
4037                //
4038                // 1. At least one item has dependencies in this fork (otherwise there's nothing to
4039                //    fork on).
4040                // 2. At least two items are not already excluded in this fork's environment
4041                //    (otherwise the conflict constraint is already satisfied and no fork is
4042                //    needed).
4043                let mut has_conflicting_dependency = false;
4044                for item in set.iter() {
4045                    if fork.contains_conflicting_item(item.as_ref()) {
4046                        has_conflicting_dependency = true;
4047                        diverging_packages.insert(item.package().clone());
4048                        break;
4049                    }
4050                }
4051                if !has_conflicting_dependency {
4052                    new.push(fork);
4053                    continue;
4054                }
4055
4056                // If fewer than two items in this conflict set are still possible (not already
4057                // excluded) in this fork, the conflict constraint is already satisfied by prior
4058                // forking. We can skip the full N+1 fork split if the single remaining non-excluded
4059                // item doesn't appear in any other conflict set (since it would never need its own
4060                // "excluded" variant).
4061                let non_excluded: Vec<_> = set
4062                    .iter()
4063                    .filter(|item| fork.env.included_by_group(item.as_ref()))
4064                    .collect();
4065                if non_excluded.len() < 2 {
4066                    // Check if any non-excluded item still has a live conflict in another set —
4067                    // i.e., another set where this item AND at least one other non-excluded item
4068                    // both appear. If so, we still need to fork to create the "excluded" variant
4069                    // for that item.
4070                    let dominated = non_excluded.iter().all(|item| {
4071                        !conflicts.iter().any(|other_set| {
4072                            !std::ptr::eq(set, other_set)
4073                                && other_set.contains(item.package(), item.kind().as_ref())
4074                                && other_set
4075                                    .iter()
4076                                    .filter(|other_item| {
4077                                        other_item.package() != item.package()
4078                                            || other_item.kind() != item.kind()
4079                                    })
4080                                    .any(|other_item| {
4081                                        fork.env.included_by_group(other_item.as_ref())
4082                                    })
4083                        })
4084                    });
4085                    if dominated {
4086                        // When dependencies are added to forks, we check `included_by_marker` but
4087                        // not on whether the dependency's conflict item is included by the fork's
4088                        // environment so there may be extraneous dependencies and we need to filter
4089                        // the fork to clean up dependencies gated on already-excluded extras.
4090                        let rules: Vec<_> = set
4091                            .iter()
4092                            .filter(|item| !fork.env.included_by_group(item.as_ref()))
4093                            .cloned()
4094                            .map(Err)
4095                            .collect();
4096                        if let Some(filtered) = fork.filter(rules) {
4097                            new.push(filtered);
4098                        }
4099                        continue;
4100                    }
4101                }
4102
4103                // Create a fork that excludes ALL conflicts.
4104                if let Some(fork_none) = fork.clone().filter(set.iter().cloned().map(Err)) {
4105                    new.push(fork_none);
4106                }
4107
4108                // Now create a fork for each conflicting group, where
4109                // that fork excludes every *other* conflicting group.
4110                //
4111                // So if we have conflicting extras foo, bar and baz,
4112                // then this creates three forks: one that excludes
4113                // {foo, bar}, one that excludes {foo, baz} and one
4114                // that excludes {bar, baz}.
4115                for (i, _) in set.iter().enumerate() {
4116                    let fork_allows_group = fork.clone().filter(
4117                        set.iter()
4118                            .cloned()
4119                            .enumerate()
4120                            .map(|(j, group)| if i == j { Ok(group) } else { Err(group) }),
4121                    );
4122                    if let Some(fork_allows_group) = fork_allows_group {
4123                        new.push(fork_allows_group);
4124                    }
4125                }
4126            }
4127            forks = new;
4128        }
4129        (forks, diverging_packages)
4130    }
4131}
4132
4133/// A single fork in a list of dependencies.
4134///
4135/// A fork corresponds to the full list of dependencies for a package,
4136/// but with any conflicting dependency specifications omitted. For
4137/// example, if we have `a<2 ; sys_platform == 'foo'` and `a>=2 ;
4138/// sys_platform == 'bar'`, then because the dependency specifications
4139/// have the same name and because the marker expressions are disjoint,
4140/// a fork occurs. One fork will contain `a<2` but not `a>=2`, while
4141/// the other fork will contain `a>=2` but not `a<2`.
4142#[derive(Clone, Debug)]
4143struct Fork {
4144    /// The list of dependencies for this fork, guaranteed to be conflict
4145    /// free. (i.e., There are no two packages with the same name with
4146    /// non-overlapping marker expressions.)
4147    ///
4148    /// Note that callers shouldn't mutate this sequence directly. Instead,
4149    /// they should use `add_forked_package` or `add_nonfork_package`. Namely,
4150    /// it should be impossible for a package with a marker expression that is
4151    /// disjoint from the marker expression on this fork to be added.
4152    dependencies: Vec<PubGrubDependency>,
4153    /// The conflicting groups in this fork.
4154    ///
4155    /// This exists to make some access patterns more efficient. Namely,
4156    /// it makes it easy to check whether there's a dependency with a
4157    /// particular conflicting group in this fork.
4158    conflicts: crate::FxHashbrownSet<ConflictItem>,
4159    /// The resolver environment for this fork.
4160    ///
4161    /// Principally, this corresponds to the markers in this for. So in the
4162    /// example above, the `a<2` fork would have `sys_platform == 'foo'`, while
4163    /// the `a>=2` fork would have `sys_platform == 'bar'`.
4164    ///
4165    /// If this fork was generated from another fork, then this *includes*
4166    /// the criteria from its parent. i.e., Its marker expression represents
4167    /// the intersection of the marker expression from its parent and any
4168    /// additional marker expression generated by addition forking based on
4169    /// conflicting dependency specifications.
4170    env: ResolverEnvironment,
4171}
4172
4173impl Fork {
4174    /// Create a new fork with no dependencies with the given resolver
4175    /// environment.
4176    fn new(env: ResolverEnvironment) -> Self {
4177        Self {
4178            dependencies: vec![],
4179            conflicts: crate::FxHashbrownSet::default(),
4180            env,
4181        }
4182    }
4183
4184    /// Add a dependency to this fork.
4185    fn add_dependency(&mut self, dep: PubGrubDependency) {
4186        if let Some(conflicting_item) = dep.conflicting_item() {
4187            self.conflicts.insert(conflicting_item.to_owned());
4188        }
4189        self.dependencies.push(dep);
4190    }
4191
4192    /// Sets the resolver environment to the one given.
4193    ///
4194    /// Any dependency in this fork that does not satisfy the given environment
4195    /// is removed.
4196    fn set_env(&mut self, env: ResolverEnvironment) {
4197        self.env = env;
4198        self.dependencies.retain(|dep| {
4199            let marker = dep.package.marker();
4200            if self.env.included_by_marker(marker) {
4201                return true;
4202            }
4203            if let Some(conflicting_item) = dep.conflicting_item() {
4204                self.conflicts.remove(&conflicting_item);
4205            }
4206            false
4207        });
4208    }
4209
4210    /// Returns true if any of the dependencies in this fork contain a
4211    /// dependency with the given package and extra values.
4212    fn contains_conflicting_item(&self, item: ConflictItemRef<'_>) -> bool {
4213        self.conflicts.contains(&item)
4214    }
4215
4216    /// Include or Exclude the given groups from this fork.
4217    ///
4218    /// This removes all dependencies matching the given conflicting groups.
4219    ///
4220    /// If the exclusion rules would result in a fork with an unsatisfiable
4221    /// resolver environment, then this returns `None`.
4222    fn filter(
4223        mut self,
4224        rules: impl IntoIterator<Item = Result<ConflictItem, ConflictItem>>,
4225    ) -> Option<Self> {
4226        self.env = self.env.filter_by_group(rules)?;
4227        self.dependencies.retain(|dep| {
4228            let Some(conflicting_item) = dep.conflicting_item() else {
4229                return true;
4230            };
4231            if self.env.included_by_group(conflicting_item) {
4232                return true;
4233            }
4234            match conflicting_item.kind() {
4235                // We should not filter entire projects unless they're a top-level dependency
4236                // Otherwise, we'll fail to solve for children of the project, like extras
4237                ConflictKindRef::Project => {
4238                    if dep.parent.is_some() {
4239                        return true;
4240                    }
4241                }
4242                ConflictKindRef::Group(_) => {}
4243                ConflictKindRef::Extra(_) => {}
4244            }
4245            self.conflicts.remove(&conflicting_item);
4246            false
4247        });
4248        Some(self)
4249    }
4250
4251    /// Compare forks, preferring forks with g `requires-python` requirements.
4252    fn cmp_requires_python(&self, other: &Self) -> Ordering {
4253        // A higher `requires-python` requirement indicates a _higher-priority_ fork.
4254        //
4255        // This ordering ensures that we prefer choosing the highest version for each fork based on
4256        // its `requires-python` requirement.
4257        //
4258        // The reverse would prefer choosing fewer versions, at the cost of using older package
4259        // versions on newer Python versions. For example, if reversed, we'd prefer to solve `<3.7
4260        // before solving `>=3.7`, since the resolution produced by the former might work for the
4261        // latter, but the inverse is unlikely to be true.
4262        let self_bound = self.env.requires_python().unwrap_or_default();
4263        let other_bound = other.env.requires_python().unwrap_or_default();
4264        self_bound.lower().cmp(other_bound.lower())
4265    }
4266
4267    /// Compare forks, preferring forks with upper bounds.
4268    fn cmp_upper_bounds(&self, other: &Self) -> Ordering {
4269        // We'd prefer to solve `numpy <= 2` before solving `numpy >= 1`, since the resolution
4270        // produced by the former might work for the latter, but the inverse is unlikely to be true
4271        // due to maximum version selection. (Selecting `numpy==2.0.0` would satisfy both forks, but
4272        // selecting the latest `numpy` would not.)
4273        let self_upper_bounds = self
4274            .dependencies
4275            .iter()
4276            .filter(|dep| {
4277                dep.version
4278                    .bounding_range()
4279                    .is_some_and(|(_, upper)| !matches!(upper, Bound::Unbounded))
4280            })
4281            .count();
4282        let other_upper_bounds = other
4283            .dependencies
4284            .iter()
4285            .filter(|dep| {
4286                dep.version
4287                    .bounding_range()
4288                    .is_some_and(|(_, upper)| !matches!(upper, Bound::Unbounded))
4289            })
4290            .count();
4291
4292        self_upper_bounds.cmp(&other_upper_bounds)
4293    }
4294}
4295
4296impl Eq for Fork {}
4297
4298impl PartialEq for Fork {
4299    fn eq(&self, other: &Self) -> bool {
4300        self.dependencies == other.dependencies && self.env == other.env
4301    }
4302}
4303
4304#[derive(Debug, Clone)]
4305pub(crate) struct VersionFork {
4306    /// The environment to use in the fork.
4307    env: ResolverEnvironment,
4308    /// The initial package to select in the fork.
4309    id: Id<PubGrubPackage>,
4310    /// The initial version to set for the selected package in the fork.
4311    version: Option<Version>,
4312}
4313
4314/// Enrich a [`ResolveError`] with additional information about why a given package was included.
4315fn enrich_dependency_error(
4316    error: ResolveError,
4317    id: Id<PubGrubPackage>,
4318    version: &Version,
4319    pubgrub: &State<UvDependencyProvider>,
4320) -> ResolveError {
4321    let Some(name) = pubgrub.package_store[id].name_no_root() else {
4322        return error;
4323    };
4324    let chain = DerivationChainBuilder::from_state(id, version, pubgrub).unwrap_or_default();
4325    ResolveError::Dependencies(Box::new(error), name.clone(), version.clone(), chain)
4326}
4327
4328/// Compute the set of markers for which a package is known to be relevant.
4329fn find_environments(id: Id<PubGrubPackage>, state: &State<UvDependencyProvider>) -> MarkerTree {
4330    let package = &state.package_store[id];
4331    if package.is_root() {
4332        return MarkerTree::TRUE;
4333    }
4334
4335    // First, collect the reverse-dependency closure for the package. We limit the propagation
4336    // below to this subgraph so cycles in unrelated packages don't matter here.
4337    let mut ancestors = FxHashSet::default();
4338    let mut stack = vec![id];
4339    let mut root = None;
4340    ancestors.insert(id);
4341
4342    while let Some(current) = stack.pop() {
4343        let Some(incompatibilities) = state.incompatibilities.get(&current) else {
4344            continue;
4345        };
4346
4347        for index in incompatibilities {
4348            let incompat = &state.incompatibility_store[*index];
4349            if let Kind::FromDependencyOf(parent, child) = &incompat.kind {
4350                if current != *child {
4351                    continue;
4352                }
4353                if ancestors.insert(*parent) {
4354                    if state.package_store[*parent].is_root() {
4355                        root = Some(*parent);
4356                    }
4357                    stack.push(*parent);
4358                }
4359            }
4360        }
4361    }
4362
4363    let Some(root) = root else {
4364        return MarkerTree::FALSE;
4365    };
4366
4367    // Propagate markers forward from the root through the collected subgraph. This reaches a
4368    // fixpoint even in the presence of cycles, unlike the recursive reverse walk above.
4369    let mut environments = FxHashMap::default();
4370    let mut queue = VecDeque::from([root]);
4371    environments.insert(root, MarkerTree::TRUE);
4372
4373    while let Some(current) = queue.pop_front() {
4374        let Some(current_environment) = environments.get(&current).copied() else {
4375            continue;
4376        };
4377        let Some(incompatibilities) = state.incompatibilities.get(&current) else {
4378            continue;
4379        };
4380
4381        for index in incompatibilities {
4382            let incompat = &state.incompatibility_store[*index];
4383            let Kind::FromDependencyOf(parent, child) = &incompat.kind else {
4384                continue;
4385            };
4386            if current != *parent || !ancestors.contains(child) {
4387                continue;
4388            }
4389
4390            let mut next_environment = state.package_store[*child].marker();
4391            next_environment = next_environment.and(current_environment);
4392
4393            let entry = environments.entry(*child).or_insert(MarkerTree::FALSE);
4394            let mut combined = *entry;
4395            combined = combined.or(next_environment);
4396            if combined != *entry {
4397                *entry = combined;
4398                queue.push_back(*child);
4399            }
4400        }
4401    }
4402
4403    environments.remove(&id).unwrap_or(MarkerTree::FALSE)
4404}
4405
4406#[derive(Debug, Default, Clone)]
4407struct ConflictTracker {
4408    /// How often a decision on the package was discarded due to another package decided earlier.
4409    affected: FxHashMap<Id<PubGrubPackage>, usize>,
4410    /// Package(s) to be prioritized after the next unit propagation
4411    ///
4412    /// Distilled from `affected` for fast checking in the hot loop.
4413    prioritize: Vec<Id<PubGrubPackage>>,
4414    /// How often a package was decided earlier and caused another package to be discarded.
4415    culprit: FxHashMap<Id<PubGrubPackage>, usize>,
4416    /// Package(s) to be de-prioritized after the next unit propagation
4417    ///
4418    /// Distilled from `culprit` for fast checking in the hot loop.
4419    deprioritize: Vec<Id<PubGrubPackage>>,
4420}