uv_installer/
site_packages.rs

1use std::borrow::Cow;
2use std::collections::BTreeSet;
3use std::iter::Flatten;
4use std::path::PathBuf;
5
6use anyhow::{Context, Result};
7use fs_err as fs;
8use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
9
10use uv_distribution_types::{
11    ConfigSettings, Diagnostic, ExtraBuildRequires, ExtraBuildVariables, InstalledDist,
12    InstalledDistKind, Name, NameRequirementSpecification, PackageConfigSettings, Requirement,
13    UnresolvedRequirement, UnresolvedRequirementSpecification,
14};
15use uv_fs::Simplified;
16use uv_normalize::PackageName;
17use uv_pep440::{Version, VersionSpecifiers};
18use uv_pep508::VersionOrUrl;
19use uv_platform_tags::Tags;
20use uv_pypi_types::{ResolverMarkerEnvironment, VerbatimParsedUrl};
21use uv_python::{Interpreter, PythonEnvironment};
22use uv_redacted::DisplaySafeUrl;
23use uv_types::InstalledPackagesProvider;
24use uv_warnings::warn_user;
25
26use crate::satisfies::RequirementSatisfaction;
27
28/// An index over the packages installed in an environment.
29///
30/// Packages are indexed by both name and (for editable installs) URL.
31#[derive(Debug, Clone)]
32pub struct SitePackages {
33    interpreter: Interpreter,
34    /// The vector of all installed distributions. The `by_name` and `by_url` indices index into
35    /// this vector. The vector may contain `None` values, which represent distributions that were
36    /// removed from the virtual environment.
37    distributions: Vec<Option<InstalledDist>>,
38    /// The installed distributions, keyed by name. Although the Python runtime does not support it,
39    /// it is possible to have multiple distributions with the same name to be present in the
40    /// virtual environment, which we handle gracefully.
41    by_name: FxHashMap<PackageName, Vec<usize>>,
42    /// The installed editable distributions, keyed by URL.
43    by_url: FxHashMap<DisplaySafeUrl, Vec<usize>>,
44}
45
46impl SitePackages {
47    /// Build an index of installed packages from the given Python environment.
48    pub fn from_environment(environment: &PythonEnvironment) -> Result<Self> {
49        Self::from_interpreter(environment.interpreter())
50    }
51
52    /// Build an index of installed packages from the given Python executable.
53    pub fn from_interpreter(interpreter: &Interpreter) -> Result<Self> {
54        let mut distributions: Vec<Option<InstalledDist>> = Vec::new();
55        let mut by_name = FxHashMap::default();
56        let mut by_url = FxHashMap::default();
57
58        for site_packages in interpreter.site_packages() {
59            // Read the site-packages directory.
60            let site_packages = match fs::read_dir(site_packages.as_ref()) {
61                Ok(read_dir) => {
62                    // Collect sorted directory paths; `read_dir` is not stable across platforms
63                    let dist_likes: BTreeSet<_> = read_dir
64                        .filter_map(|read_dir| match read_dir {
65                            Ok(entry) => match entry.file_type() {
66                                Ok(file_type) => (file_type.is_dir()
67                                    || entry
68                                        .path()
69                                        .extension()
70                                        .is_some_and(|ext| ext == "egg-link" || ext == "egg-info"))
71                                .then_some(Ok(entry.path())),
72                                Err(err) => Some(Err(err)),
73                            },
74                            Err(err) => Some(Err(err)),
75                        })
76                        .collect::<Result<_, std::io::Error>>()
77                        .with_context(|| {
78                            format!(
79                                "Failed to read site-packages directory contents: {}",
80                                site_packages.user_display()
81                            )
82                        })?;
83                    dist_likes
84                }
85                Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
86                    return Ok(Self {
87                        interpreter: interpreter.clone(),
88                        distributions,
89                        by_name,
90                        by_url,
91                    });
92                }
93                Err(err) => return Err(err).context("Failed to read site-packages directory"),
94            };
95
96            // Index all installed packages by name.
97            for path in site_packages {
98                let dist_info = match InstalledDist::try_from_path(&path) {
99                    Ok(Some(dist_info)) => dist_info,
100                    Ok(None) => continue,
101                    Err(_)
102                        if path.file_name().is_some_and(|name| {
103                            name.to_str().is_some_and(|name| name.starts_with('~'))
104                        }) =>
105                    {
106                        warn_user!(
107                            "Ignoring dangling temporary directory: `{}`",
108                            path.simplified_display().cyan()
109                        );
110                        continue;
111                    }
112                    Err(err) => {
113                        return Err(err).context(format!(
114                            "Failed to read metadata from: `{}`",
115                            path.simplified_display()
116                        ));
117                    }
118                };
119
120                let idx = distributions.len();
121
122                // Index the distribution by name.
123                by_name
124                    .entry(dist_info.name().clone())
125                    .or_default()
126                    .push(idx);
127
128                // Index the distribution by URL.
129                if let InstalledDistKind::Url(dist) = &dist_info.kind {
130                    by_url.entry(dist.url.clone()).or_default().push(idx);
131                }
132
133                // Add the distribution to the database.
134                distributions.push(Some(dist_info));
135            }
136        }
137
138        Ok(Self {
139            interpreter: interpreter.clone(),
140            distributions,
141            by_name,
142            by_url,
143        })
144    }
145
146    /// Returns the [`Interpreter`] used to install the packages.
147    pub fn interpreter(&self) -> &Interpreter {
148        &self.interpreter
149    }
150
151    /// Returns an iterator over the installed distributions.
152    pub fn iter(&self) -> impl Iterator<Item = &InstalledDist> {
153        self.distributions.iter().flatten()
154    }
155
156    /// Returns the installed distributions for a given package.
157    pub fn get_packages(&self, name: &PackageName) -> Vec<&InstalledDist> {
158        let Some(indexes) = self.by_name.get(name) else {
159            return Vec::new();
160        };
161        indexes
162            .iter()
163            .flat_map(|&index| &self.distributions[index])
164            .collect()
165    }
166
167    /// Remove the given packages from the index, returning all installed versions, if any.
168    pub fn remove_packages(&mut self, name: &PackageName) -> Vec<InstalledDist> {
169        let Some(indexes) = self.by_name.get(name) else {
170            return Vec::new();
171        };
172        indexes
173            .iter()
174            .filter_map(|index| std::mem::take(&mut self.distributions[*index]))
175            .collect()
176    }
177
178    /// Returns the distributions installed from the given URL, if any.
179    pub fn get_urls(&self, url: &DisplaySafeUrl) -> Vec<&InstalledDist> {
180        let Some(indexes) = self.by_url.get(url) else {
181            return Vec::new();
182        };
183        indexes
184            .iter()
185            .flat_map(|&index| &self.distributions[index])
186            .collect()
187    }
188
189    /// Returns `true` if there are any installed packages.
190    pub fn any(&self) -> bool {
191        self.distributions.iter().any(Option::is_some)
192    }
193
194    /// Validate the installed packages in the virtual environment.
195    pub fn diagnostics(
196        &self,
197        markers: &ResolverMarkerEnvironment,
198        tags: &Tags,
199    ) -> Result<Vec<SitePackagesDiagnostic>> {
200        let mut diagnostics = Vec::new();
201
202        for (package, indexes) in &self.by_name {
203            let mut distributions = indexes.iter().flat_map(|index| &self.distributions[*index]);
204
205            // Find the installed distribution for the given package.
206            let Some(distribution) = distributions.next() else {
207                continue;
208            };
209
210            if let Some(conflict) = distributions.next() {
211                // There are multiple installed distributions for the same package.
212                diagnostics.push(SitePackagesDiagnostic::DuplicatePackage {
213                    package: package.clone(),
214                    paths: std::iter::once(distribution.install_path().to_owned())
215                        .chain(std::iter::once(conflict.install_path().to_owned()))
216                        .chain(distributions.map(|dist| dist.install_path().to_owned()))
217                        .collect(),
218                });
219                continue;
220            }
221
222            for index in indexes {
223                let Some(distribution) = &self.distributions[*index] else {
224                    continue;
225                };
226
227                // Determine the dependencies for the given package.
228                let Ok(metadata) = distribution.read_metadata() else {
229                    diagnostics.push(SitePackagesDiagnostic::MetadataUnavailable {
230                        package: package.clone(),
231                        path: distribution.install_path().to_owned(),
232                    });
233                    continue;
234                };
235
236                // Verify that the package is compatible with the current Python version.
237                if let Some(requires_python) = metadata.requires_python.as_ref() {
238                    if !requires_python.contains(markers.python_full_version()) {
239                        diagnostics.push(SitePackagesDiagnostic::IncompatiblePythonVersion {
240                            package: package.clone(),
241                            version: self.interpreter.python_version().clone(),
242                            requires_python: requires_python.clone(),
243                        });
244                    }
245                }
246
247                // Verify that the package is compatible with the current tags.
248                match distribution.read_tags() {
249                    Ok(Some(wheel_tags)) => {
250                        if !wheel_tags.is_compatible(tags) {
251                            // TODO(charlie): Show the expanded tag hint, that explains _why_ it doesn't match.
252                            diagnostics.push(SitePackagesDiagnostic::IncompatiblePlatform {
253                                package: package.clone(),
254                            });
255                        }
256                    }
257                    Ok(None) => {}
258                    Err(_) => {
259                        diagnostics.push(SitePackagesDiagnostic::TagsUnavailable {
260                            package: package.clone(),
261                            path: distribution.install_path().to_owned(),
262                        });
263                    }
264                }
265
266                // Verify that the dependencies are installed.
267                for dependency in &metadata.requires_dist {
268                    if !dependency.evaluate_markers(markers, &[]) {
269                        continue;
270                    }
271
272                    let installed = self.get_packages(&dependency.name);
273                    match installed.as_slice() {
274                        [] => {
275                            // No version installed.
276                            diagnostics.push(SitePackagesDiagnostic::MissingDependency {
277                                package: package.clone(),
278                                requirement: dependency.clone(),
279                            });
280                        }
281                        [installed] => {
282                            match &dependency.version_or_url {
283                                None | Some(VersionOrUrl::Url(_)) => {
284                                    // Nothing to do (accept any installed version).
285                                }
286                                Some(VersionOrUrl::VersionSpecifier(version_specifier)) => {
287                                    // The installed version doesn't satisfy the requirement.
288                                    if !version_specifier.contains(installed.version()) {
289                                        diagnostics.push(
290                                            SitePackagesDiagnostic::IncompatibleDependency {
291                                                package: package.clone(),
292                                                version: installed.version().clone(),
293                                                requirement: dependency.clone(),
294                                            },
295                                        );
296                                    }
297                                }
298                            }
299                        }
300                        _ => {
301                            // There are multiple installed distributions for the same package.
302                        }
303                    }
304                }
305            }
306        }
307
308        Ok(diagnostics)
309    }
310
311    /// Returns if the installed packages satisfy the given requirements.
312    pub fn satisfies_spec(
313        &self,
314        requirements: &[UnresolvedRequirementSpecification],
315        constraints: &[NameRequirementSpecification],
316        overrides: &[UnresolvedRequirementSpecification],
317        installation: InstallationStrategy,
318        markers: &ResolverMarkerEnvironment,
319        tags: &Tags,
320        config_settings: &ConfigSettings,
321        config_settings_package: &PackageConfigSettings,
322        extra_build_requires: &ExtraBuildRequires,
323        extra_build_variables: &ExtraBuildVariables,
324    ) -> Result<SatisfiesResult> {
325        // First, map all unnamed requirements to named requirements.
326        let requirements = {
327            let mut named = Vec::with_capacity(requirements.len());
328            for requirement in requirements {
329                match &requirement.requirement {
330                    UnresolvedRequirement::Named(requirement) => {
331                        named.push(Cow::Borrowed(requirement));
332                    }
333                    UnresolvedRequirement::Unnamed(requirement) => {
334                        match self.get_urls(requirement.url.verbatim.raw()).as_slice() {
335                            [] => {
336                                return Ok(SatisfiesResult::Unsatisfied(
337                                    requirement.url.verbatim.raw().to_string(),
338                                ));
339                            }
340                            [distribution] => {
341                                let requirement = uv_pep508::Requirement {
342                                    name: distribution.name().clone(),
343                                    version_or_url: Some(VersionOrUrl::Url(
344                                        requirement.url.clone(),
345                                    )),
346                                    marker: requirement.marker,
347                                    extras: requirement.extras.clone(),
348                                    origin: requirement.origin.clone(),
349                                };
350                                named.push(Cow::Owned(Requirement::from(requirement)));
351                            }
352                            _ => {
353                                return Ok(SatisfiesResult::Unsatisfied(
354                                    requirement.url.verbatim.raw().to_string(),
355                                ));
356                            }
357                        }
358                    }
359                }
360            }
361            named
362        };
363
364        // Second, map all overrides to named requirements. We assume that all overrides are
365        // relevant.
366        let overrides = {
367            let mut named = Vec::with_capacity(overrides.len());
368            for requirement in overrides {
369                match &requirement.requirement {
370                    UnresolvedRequirement::Named(requirement) => {
371                        named.push(Cow::Borrowed(requirement));
372                    }
373                    UnresolvedRequirement::Unnamed(requirement) => {
374                        match self.get_urls(requirement.url.verbatim.raw()).as_slice() {
375                            [] => {
376                                return Ok(SatisfiesResult::Unsatisfied(
377                                    requirement.url.verbatim.raw().to_string(),
378                                ));
379                            }
380                            [distribution] => {
381                                let requirement = uv_pep508::Requirement {
382                                    name: distribution.name().clone(),
383                                    version_or_url: Some(VersionOrUrl::Url(
384                                        requirement.url.clone(),
385                                    )),
386                                    marker: requirement.marker,
387                                    extras: requirement.extras.clone(),
388                                    origin: requirement.origin.clone(),
389                                };
390                                named.push(Cow::Owned(Requirement::from(requirement)));
391                            }
392                            _ => {
393                                return Ok(SatisfiesResult::Unsatisfied(
394                                    requirement.url.verbatim.raw().to_string(),
395                                ));
396                            }
397                        }
398                    }
399                }
400            }
401            named
402        };
403
404        self.satisfies_requirements(
405            requirements.iter().map(Cow::as_ref),
406            constraints.iter().map(|constraint| &constraint.requirement),
407            overrides.iter().map(Cow::as_ref),
408            installation,
409            markers,
410            tags,
411            config_settings,
412            config_settings_package,
413            extra_build_requires,
414            extra_build_variables,
415        )
416    }
417
418    /// Like [`SitePackages::satisfies_spec`], but with resolved names for all requirements.
419    pub fn satisfies_requirements<'a>(
420        &self,
421        requirements: impl ExactSizeIterator<Item = &'a Requirement>,
422        constraints: impl Iterator<Item = &'a Requirement>,
423        overrides: impl Iterator<Item = &'a Requirement>,
424        installation: InstallationStrategy,
425        markers: &ResolverMarkerEnvironment,
426        tags: &Tags,
427        config_settings: &ConfigSettings,
428        config_settings_package: &PackageConfigSettings,
429        extra_build_requires: &ExtraBuildRequires,
430        extra_build_variables: &ExtraBuildVariables,
431    ) -> Result<SatisfiesResult> {
432        // Collect the constraints and overrides by package name.
433        let constraints: FxHashMap<&PackageName, Vec<&Requirement>> =
434            constraints.fold(FxHashMap::default(), |mut constraints, constraint| {
435                constraints
436                    .entry(&constraint.name)
437                    .or_default()
438                    .push(constraint);
439                constraints
440            });
441        let overrides: FxHashMap<&PackageName, Vec<&Requirement>> =
442            overrides.fold(FxHashMap::default(), |mut overrides, r#override| {
443                overrides
444                    .entry(&r#override.name)
445                    .or_default()
446                    .push(r#override);
447                overrides
448            });
449
450        let mut stack = Vec::with_capacity(requirements.len());
451        let mut seen = FxHashSet::with_capacity_and_hasher(requirements.len(), FxBuildHasher);
452
453        // Add the direct requirements to the queue.
454        for requirement in requirements {
455            if let Some(r#overrides) = overrides.get(&requirement.name) {
456                for dependency in r#overrides {
457                    if dependency.evaluate_markers(Some(markers), &[]) {
458                        if seen.insert((*dependency).clone()) {
459                            stack.push(Cow::Borrowed(*dependency));
460                        }
461                    }
462                }
463            } else {
464                if requirement.evaluate_markers(Some(markers), &[]) {
465                    if seen.insert(requirement.clone()) {
466                        stack.push(Cow::Borrowed(requirement));
467                    }
468                }
469            }
470        }
471
472        // Verify that all non-editable requirements are met.
473        while let Some(requirement) = stack.pop() {
474            let name = &requirement.name;
475            let installed = self.get_packages(name);
476            match installed.as_slice() {
477                [] => {
478                    // The package isn't installed.
479                    return Ok(SatisfiesResult::Unsatisfied(requirement.to_string()));
480                }
481                [distribution] => {
482                    // Validate that the requirement is satisfied.
483                    if requirement.evaluate_markers(Some(markers), &[]) {
484                        match RequirementSatisfaction::check(
485                            name,
486                            distribution,
487                            &requirement.source,
488                            installation,
489                            tags,
490                            config_settings,
491                            config_settings_package,
492                            extra_build_requires,
493                            extra_build_variables,
494                        ) {
495                            RequirementSatisfaction::Mismatch
496                            | RequirementSatisfaction::OutOfDate
497                            | RequirementSatisfaction::CacheInvalid => {
498                                return Ok(SatisfiesResult::Unsatisfied(requirement.to_string()));
499                            }
500                            RequirementSatisfaction::Satisfied => {}
501                        }
502                    }
503
504                    // Validate that the installed version satisfies the constraints.
505                    for constraint in constraints.get(name).into_iter().flatten() {
506                        if constraint.evaluate_markers(Some(markers), &[]) {
507                            match RequirementSatisfaction::check(
508                                name,
509                                distribution,
510                                &constraint.source,
511                                installation,
512                                tags,
513                                config_settings,
514                                config_settings_package,
515                                extra_build_requires,
516                                extra_build_variables,
517                            ) {
518                                RequirementSatisfaction::Mismatch
519                                | RequirementSatisfaction::OutOfDate
520                                | RequirementSatisfaction::CacheInvalid => {
521                                    return Ok(SatisfiesResult::Unsatisfied(
522                                        requirement.to_string(),
523                                    ));
524                                }
525                                RequirementSatisfaction::Satisfied => {}
526                            }
527                        }
528                    }
529
530                    // Recurse into the dependencies.
531                    let metadata = distribution
532                        .read_metadata()
533                        .with_context(|| format!("Failed to read metadata for: {distribution}"))?;
534
535                    // Add the dependencies to the queue.
536                    for dependency in &metadata.requires_dist {
537                        let dependency = Requirement::from(dependency.clone());
538                        if let Some(r#overrides) = overrides.get(&dependency.name) {
539                            for dependency in r#overrides {
540                                if dependency.evaluate_markers(Some(markers), &requirement.extras) {
541                                    if seen.insert((*dependency).clone()) {
542                                        stack.push(Cow::Borrowed(*dependency));
543                                    }
544                                }
545                            }
546                        } else {
547                            if dependency.evaluate_markers(Some(markers), &requirement.extras) {
548                                if seen.insert(dependency.clone()) {
549                                    stack.push(Cow::Owned(dependency));
550                                }
551                            }
552                        }
553                    }
554                }
555                _ => {
556                    // There are multiple installed distributions for the same package.
557                    return Ok(SatisfiesResult::Unsatisfied(requirement.to_string()));
558                }
559            }
560        }
561
562        Ok(SatisfiesResult::Fresh {
563            recursive_requirements: seen,
564        })
565    }
566}
567
568#[derive(Debug, Clone, Copy, PartialEq, Eq)]
569pub enum InstallationStrategy {
570    /// A permissive installation strategy, which accepts existing installations even if the source
571    /// type differs, as in the `pip` and `uv pip` CLIs.
572    ///
573    /// In this strategy, packages that are already installed in the environment may be reused if
574    /// they implicitly match the requirements. For example, if the user installs `./path/to/idna`,
575    /// then runs `uv pip install anyio` (which depends on `idna`), the existing `idna` installation
576    /// will be reused if its version matches the requirement, even though it was installed from a
577    /// path and is being implicitly requested from a registry.
578    Permissive,
579
580    /// A strict installation strategy, which requires that existing installations match the source
581    /// type, as in the `uv sync` CLI.
582    ///
583    /// This strategy enforces that the installation source must match the requirement source.
584    /// It prevents reusing packages that were installed from different sources, ensuring
585    /// declarative and reproducible environments.
586    Strict,
587}
588
589/// We check if all requirements are already satisfied, recursing through the requirements tree.
590#[derive(Debug)]
591pub enum SatisfiesResult {
592    /// All requirements are recursively satisfied.
593    Fresh {
594        /// The flattened set (transitive closure) of all requirements checked.
595        recursive_requirements: FxHashSet<Requirement>,
596    },
597    /// We found an unsatisfied requirement. Since we exit early, we only know about the first
598    /// unsatisfied requirement.
599    Unsatisfied(String),
600}
601
602impl IntoIterator for SitePackages {
603    type Item = InstalledDist;
604    type IntoIter = Flatten<std::vec::IntoIter<Option<InstalledDist>>>;
605
606    fn into_iter(self) -> Self::IntoIter {
607        self.distributions.into_iter().flatten()
608    }
609}
610
611#[derive(Debug)]
612pub enum SitePackagesDiagnostic {
613    MetadataUnavailable {
614        /// The package that is missing metadata.
615        package: PackageName,
616        /// The path to the package.
617        path: PathBuf,
618    },
619    TagsUnavailable {
620        /// The package that is missing tags.
621        package: PackageName,
622        /// The path to the package.
623        path: PathBuf,
624    },
625    IncompatiblePythonVersion {
626        /// The package that requires a different version of Python.
627        package: PackageName,
628        /// The version of Python that is installed.
629        version: Version,
630        /// The version of Python that is required.
631        requires_python: VersionSpecifiers,
632    },
633    IncompatiblePlatform {
634        /// The package that was built for a different platform.
635        package: PackageName,
636    },
637    MissingDependency {
638        /// The package that is missing a dependency.
639        package: PackageName,
640        /// The dependency that is missing.
641        requirement: uv_pep508::Requirement<VerbatimParsedUrl>,
642    },
643    IncompatibleDependency {
644        /// The package that has an incompatible dependency.
645        package: PackageName,
646        /// The version of the package that is installed.
647        version: Version,
648        /// The dependency that is incompatible.
649        requirement: uv_pep508::Requirement<VerbatimParsedUrl>,
650    },
651    DuplicatePackage {
652        /// The package that has multiple installed distributions.
653        package: PackageName,
654        /// The installed versions of the package.
655        paths: Vec<PathBuf>,
656    },
657}
658
659impl Diagnostic for SitePackagesDiagnostic {
660    /// Convert the diagnostic into a user-facing message.
661    fn message(&self) -> String {
662        match self {
663            Self::MetadataUnavailable { package, path } => format!(
664                "The package `{package}` is broken or incomplete (unable to read `METADATA`). Consider recreating the virtualenv, or removing the package directory at: {}.",
665                path.display(),
666            ),
667            Self::TagsUnavailable { package, path } => format!(
668                "The package `{package}` is broken or incomplete (unable to read `WHEEL` file). Consider recreating the virtualenv, or removing the package directory at: {}.",
669                path.display(),
670            ),
671            Self::IncompatiblePythonVersion {
672                package,
673                version,
674                requires_python,
675            } => format!(
676                "The package `{package}` requires Python {requires_python}, but `{version}` is installed"
677            ),
678            Self::IncompatiblePlatform { package } => {
679                format!("The package `{package}` was built for a different platform")
680            }
681            Self::MissingDependency {
682                package,
683                requirement,
684            } => {
685                format!("The package `{package}` requires `{requirement}`, but it's not installed")
686            }
687            Self::IncompatibleDependency {
688                package,
689                version,
690                requirement,
691            } => format!(
692                "The package `{package}` requires `{requirement}`, but `{version}` is installed"
693            ),
694            Self::DuplicatePackage { package, paths } => {
695                let mut paths = paths.clone();
696                paths.sort();
697                format!(
698                    "The package `{package}` has multiple installed distributions: {}",
699                    paths.iter().fold(String::new(), |acc, path| acc
700                        + &format!("\n  - {}", path.display()))
701                )
702            }
703        }
704    }
705
706    /// Returns `true` if the [`PackageName`] is involved in this diagnostic.
707    fn includes(&self, name: &PackageName) -> bool {
708        match self {
709            Self::MetadataUnavailable { package, .. } => name == package,
710            Self::TagsUnavailable { package, .. } => name == package,
711            Self::IncompatiblePythonVersion { package, .. } => name == package,
712            Self::IncompatiblePlatform { package } => name == package,
713            Self::MissingDependency { package, .. } => name == package,
714            Self::IncompatibleDependency {
715                package,
716                requirement,
717                ..
718            } => name == package || &requirement.name == name,
719            Self::DuplicatePackage { package, .. } => name == package,
720        }
721    }
722}
723
724impl InstalledPackagesProvider for SitePackages {
725    fn iter(&self) -> impl Iterator<Item = &InstalledDist> {
726        self.iter()
727    }
728
729    fn get_packages(&self, name: &PackageName) -> Vec<&InstalledDist> {
730        self.get_packages(name)
731    }
732}