Skip to main content

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                            None,
489                            installation,
490                            tags,
491                            config_settings,
492                            config_settings_package,
493                            extra_build_requires,
494                            extra_build_variables,
495                        ) {
496                            RequirementSatisfaction::Mismatch
497                            | RequirementSatisfaction::OutOfDate
498                            | RequirementSatisfaction::CacheInvalid => {
499                                return Ok(SatisfiesResult::Unsatisfied(requirement.to_string()));
500                            }
501                            RequirementSatisfaction::Satisfied => {}
502                        }
503                    }
504
505                    // Validate that the installed version satisfies the constraints.
506                    for constraint in constraints.get(name).into_iter().flatten() {
507                        if constraint.evaluate_markers(Some(markers), &[]) {
508                            match RequirementSatisfaction::check(
509                                name,
510                                distribution,
511                                &constraint.source,
512                                None,
513                                installation,
514                                tags,
515                                config_settings,
516                                config_settings_package,
517                                extra_build_requires,
518                                extra_build_variables,
519                            ) {
520                                RequirementSatisfaction::Mismatch
521                                | RequirementSatisfaction::OutOfDate
522                                | RequirementSatisfaction::CacheInvalid => {
523                                    return Ok(SatisfiesResult::Unsatisfied(
524                                        requirement.to_string(),
525                                    ));
526                                }
527                                RequirementSatisfaction::Satisfied => {}
528                            }
529                        }
530                    }
531
532                    // Recurse into the dependencies.
533                    let metadata = distribution
534                        .read_metadata()
535                        .with_context(|| format!("Failed to read metadata for: {distribution}"))?;
536
537                    // Add the dependencies to the queue.
538                    for dependency in &metadata.requires_dist {
539                        let dependency = Requirement::from(dependency.clone());
540                        if let Some(r#overrides) = overrides.get(&dependency.name) {
541                            for dependency in r#overrides {
542                                if dependency.evaluate_markers(Some(markers), &requirement.extras) {
543                                    if seen.insert((*dependency).clone()) {
544                                        stack.push(Cow::Borrowed(*dependency));
545                                    }
546                                }
547                            }
548                        } else {
549                            if dependency.evaluate_markers(Some(markers), &requirement.extras) {
550                                if seen.insert(dependency.clone()) {
551                                    stack.push(Cow::Owned(dependency));
552                                }
553                            }
554                        }
555                    }
556                }
557                _ => {
558                    // There are multiple installed distributions for the same package.
559                    return Ok(SatisfiesResult::Unsatisfied(requirement.to_string()));
560                }
561            }
562        }
563
564        Ok(SatisfiesResult::Fresh {
565            recursive_requirements: seen,
566        })
567    }
568}
569
570#[derive(Debug, Clone, Copy, PartialEq, Eq)]
571pub enum InstallationStrategy {
572    /// A permissive installation strategy, which accepts existing installations even if the source
573    /// type differs, as in the `pip` and `uv pip` CLIs.
574    ///
575    /// In this strategy, packages that are already installed in the environment may be reused if
576    /// they implicitly match the requirements. For example, if the user installs `./path/to/idna`,
577    /// then runs `uv pip install anyio` (which depends on `idna`), the existing `idna` installation
578    /// will be reused if its version matches the requirement, even though it was installed from a
579    /// path and is being implicitly requested from a registry.
580    Permissive,
581
582    /// A strict installation strategy, which requires that existing installations match the source
583    /// type, as in the `uv sync` CLI.
584    ///
585    /// This strategy enforces that the installation source must match the requirement source.
586    /// It prevents reusing packages that were installed from different sources, ensuring
587    /// declarative and reproducible environments.
588    Strict,
589}
590
591/// We check if all requirements are already satisfied, recursing through the requirements tree.
592#[derive(Debug)]
593pub enum SatisfiesResult {
594    /// All requirements are recursively satisfied.
595    Fresh {
596        /// The flattened set (transitive closure) of all requirements checked.
597        recursive_requirements: FxHashSet<Requirement>,
598    },
599    /// We found an unsatisfied requirement. Since we exit early, we only know about the first
600    /// unsatisfied requirement.
601    Unsatisfied(String),
602}
603
604impl IntoIterator for SitePackages {
605    type Item = InstalledDist;
606    type IntoIter = Flatten<std::vec::IntoIter<Option<InstalledDist>>>;
607
608    fn into_iter(self) -> Self::IntoIter {
609        self.distributions.into_iter().flatten()
610    }
611}
612
613#[derive(Debug)]
614pub enum SitePackagesDiagnostic {
615    MetadataUnavailable {
616        /// The package that is missing metadata.
617        package: PackageName,
618        /// The path to the package.
619        path: PathBuf,
620    },
621    TagsUnavailable {
622        /// The package that is missing tags.
623        package: PackageName,
624        /// The path to the package.
625        path: PathBuf,
626    },
627    IncompatiblePythonVersion {
628        /// The package that requires a different version of Python.
629        package: PackageName,
630        /// The version of Python that is installed.
631        version: Version,
632        /// The version of Python that is required.
633        requires_python: VersionSpecifiers,
634    },
635    IncompatiblePlatform {
636        /// The package that was built for a different platform.
637        package: PackageName,
638    },
639    MissingDependency {
640        /// The package that is missing a dependency.
641        package: PackageName,
642        /// The dependency that is missing.
643        requirement: uv_pep508::Requirement<VerbatimParsedUrl>,
644    },
645    IncompatibleDependency {
646        /// The package that has an incompatible dependency.
647        package: PackageName,
648        /// The version of the package that is installed.
649        version: Version,
650        /// The dependency that is incompatible.
651        requirement: uv_pep508::Requirement<VerbatimParsedUrl>,
652    },
653    DuplicatePackage {
654        /// The package that has multiple installed distributions.
655        package: PackageName,
656        /// The installed versions of the package.
657        paths: Vec<PathBuf>,
658    },
659}
660
661impl Diagnostic for SitePackagesDiagnostic {
662    /// Convert the diagnostic into a user-facing message.
663    fn message(&self) -> String {
664        match self {
665            Self::MetadataUnavailable { package, path } => format!(
666                "The package `{package}` is broken or incomplete (unable to read `METADATA`). Consider recreating the virtualenv, or removing the package directory at: {}.",
667                path.display(),
668            ),
669            Self::TagsUnavailable { package, path } => format!(
670                "The package `{package}` is broken or incomplete (unable to read `WHEEL` file). Consider recreating the virtualenv, or removing the package directory at: {}.",
671                path.display(),
672            ),
673            Self::IncompatiblePythonVersion {
674                package,
675                version,
676                requires_python,
677            } => format!(
678                "The package `{package}` requires Python {requires_python}, but `{version}` is installed"
679            ),
680            Self::IncompatiblePlatform { package } => {
681                format!("The package `{package}` was built for a different platform")
682            }
683            Self::MissingDependency {
684                package,
685                requirement,
686            } => {
687                format!("The package `{package}` requires `{requirement}`, but it's not installed")
688            }
689            Self::IncompatibleDependency {
690                package,
691                version,
692                requirement,
693            } => format!(
694                "The package `{package}` requires `{requirement}`, but `{version}` is installed"
695            ),
696            Self::DuplicatePackage { package, paths } => {
697                let mut paths = paths.clone();
698                paths.sort();
699                format!(
700                    "The package `{package}` has multiple installed distributions: {}",
701                    paths.iter().fold(String::new(), |acc, path| acc
702                        + &format!("\n  - {}", path.display()))
703                )
704            }
705        }
706    }
707
708    /// Returns `true` if the [`PackageName`] is involved in this diagnostic.
709    fn includes(&self, name: &PackageName) -> bool {
710        match self {
711            Self::MetadataUnavailable { package, .. } => name == package,
712            Self::TagsUnavailable { package, .. } => name == package,
713            Self::IncompatiblePythonVersion { package, .. } => name == package,
714            Self::IncompatiblePlatform { package } => name == package,
715            Self::MissingDependency { package, .. } => name == package,
716            Self::IncompatibleDependency {
717                package,
718                requirement,
719                ..
720            } => name == package || &requirement.name == name,
721            Self::DuplicatePackage { package, .. } => name == package,
722        }
723    }
724}
725
726impl InstalledPackagesProvider for SitePackages {
727    fn iter(&self) -> impl Iterator<Item = &InstalledDist> {
728        self.iter()
729    }
730
731    fn get_packages(&self, name: &PackageName) -> Vec<&InstalledDist> {
732        self.get_packages(name)
733    }
734}