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