Arc-wrap PubGrubPackage for cheap cloning in pubgrub (#3688)

Pubgrub stores incompatibilities as (package name, version range)
tuples, meaning it needs to clone the package name for each
incompatibility, and each non-borrowed operation on incompatibilities.
https://github.com/astral-sh/uv/pull/3673 made me realize that
`PubGrubPackage` has gotten large (expensive to copy), so like `Version`
and other structs, i've added an `Arc` wrapper around it.

It's a pity clippy forbids `.deref()`, it's less opaque than `&**` and
has IDE support (clicking on `.deref()` jumps to the right impl).

## Benchmarks

It looks like this matters most for complex resolutions which, i assume
because they carry larger `PubGrubPackageInner::Package` and
`PubGrubPackageInner::Extra` types.

```bash
hyperfine --warmup 5 "./uv-main pip compile -q ./scripts/requirements/jupyter.in" "./uv-branch pip compile -q ./scripts/requirements/jupyter.in"
hyperfine --warmup 5 "./uv-main pip compile -q ./scripts/requirements/airflow.in" "./uv-branch pip compile -q ./scripts/requirements/airflow.in"
hyperfine --warmup 5 "./uv-main pip compile -q ./scripts/requirements/boto3.in" "./uv-branch pip compile -q ./scripts/requirements/boto3.in"
```

```
Benchmark 1: ./uv-main pip compile -q ./scripts/requirements/jupyter.in
  Time (mean ± σ):      18.2 ms ±   1.6 ms    [User: 14.4 ms, System: 26.0 ms]
  Range (min … max):    15.8 ms …  22.5 ms    181 runs

Benchmark 2: ./uv-branch pip compile -q ./scripts/requirements/jupyter.in
  Time (mean ± σ):      17.8 ms ±   1.4 ms    [User: 14.4 ms, System: 25.3 ms]
  Range (min … max):    15.4 ms …  23.1 ms    159 runs

Summary
  ./uv-branch pip compile -q ./scripts/requirements/jupyter.in ran
    1.02 ± 0.12 times faster than ./uv-main pip compile -q ./scripts/requirements/jupyter.in
```

```
Benchmark 1: ./uv-main pip compile -q ./scripts/requirements/airflow.in
  Time (mean ± σ):     153.7 ms ±   3.5 ms    [User: 165.2 ms, System: 157.6 ms]
  Range (min … max):   150.4 ms … 163.0 ms    19 runs

Benchmark 2: ./uv-branch pip compile -q ./scripts/requirements/airflow.in
  Time (mean ± σ):     123.9 ms ±   4.6 ms    [User: 152.4 ms, System: 133.8 ms]
  Range (min … max):   118.4 ms … 138.1 ms    24 runs

Summary
  ./uv-branch pip compile -q ./scripts/requirements/airflow.in ran
    1.24 ± 0.05 times faster than ./uv-main pip compile -q ./scripts/requirements/airflow.in
```

```
Benchmark 1: ./uv-main pip compile -q ./scripts/requirements/boto3.in
  Time (mean ± σ):     327.0 ms ±   3.8 ms    [User: 344.5 ms, System: 71.6 ms]
  Range (min … max):   322.7 ms … 334.6 ms    10 runs

Benchmark 2: ./uv-branch pip compile -q ./scripts/requirements/boto3.in
  Time (mean ± σ):     311.2 ms ±   3.1 ms    [User: 339.3 ms, System: 63.1 ms]
  Range (min … max):   307.8 ms … 317.0 ms    10 runs

Summary
  ./uv-branch pip compile -q ./scripts/requirements/boto3.in ran
    1.05 ± 0.02 times faster than ./uv-main pip compile -q ./scripts/requirements/boto3.in
```

<!--
Thank you for contributing to uv! To help us out with reviewing, please
consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->
This commit is contained in:
konsti 2024-05-21 13:49:35 +02:00 committed by GitHub
parent fbae55019e
commit 76418f5bdf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 159 additions and 122 deletions

View file

@ -37,6 +37,7 @@ anstream = { workspace = true }
anyhow = { workspace = true } anyhow = { workspace = true }
chrono = { workspace = true } chrono = { workspace = true }
clap = { workspace = true, features = ["derive"], optional = true } clap = { workspace = true, features = ["derive"], optional = true }
dashmap = { workspace = true }
derivative = { workspace = true } derivative = { workspace = true }
either = { workspace = true } either = { workspace = true }
futures = { workspace = true } futures = { workspace = true }
@ -56,7 +57,6 @@ tokio = { workspace = true }
tokio-stream = { workspace = true } tokio-stream = { workspace = true }
tracing = { workspace = true } tracing = { workspace = true }
url = { workspace = true } url = { workspace = true }
dashmap = { workspace = true }
[dev-dependencies] [dev-dependencies]
uv-interpreter = { workspace = true } uv-interpreter = { workspace = true }

View file

@ -16,7 +16,7 @@ use uv_normalize::PackageName;
use crate::candidate_selector::CandidateSelector; use crate::candidate_selector::CandidateSelector;
use crate::dependency_provider::UvDependencyProvider; use crate::dependency_provider::UvDependencyProvider;
use crate::pubgrub::{PubGrubPackage, PubGrubPython, PubGrubReportFormatter}; use crate::pubgrub::{PubGrubPackage, PubGrubPackageInner, PubGrubPython, PubGrubReportFormatter};
use crate::python_requirement::PythonRequirement; use crate::python_requirement::PythonRequirement;
use crate::resolver::{ use crate::resolver::{
FxOnceMap, IncompletePackage, UnavailablePackage, UnavailableReason, VersionsResponse, FxOnceMap, IncompletePackage, UnavailablePackage, UnavailableReason, VersionsResponse,
@ -114,7 +114,7 @@ impl<T> From<tokio::sync::mpsc::error::SendError<T>> for ResolveError {
} }
/// Given a [`DerivationTree`], collapse any [`External::FromDependencyOf`] incompatibilities /// Given a [`DerivationTree`], collapse any [`External::FromDependencyOf`] incompatibilities
/// wrap an [`PubGrubPackage::Extra`] package. /// wrap an [`PubGrubPackageInner::Extra`] package.
fn collapse_extra_proxies( fn collapse_extra_proxies(
derivation_tree: &mut DerivationTree<PubGrubPackage, Range<Version>, UnavailableReason>, derivation_tree: &mut DerivationTree<PubGrubPackage, Range<Version>, UnavailableReason>,
) { ) {
@ -126,22 +126,16 @@ fn collapse_extra_proxies(
Arc::make_mut(&mut derived.cause2), Arc::make_mut(&mut derived.cause2),
) { ) {
( (
DerivationTree::External(External::FromDependencyOf( DerivationTree::External(External::FromDependencyOf(package, ..)),
PubGrubPackage::Extra { .. },
..,
)),
ref mut cause, ref mut cause,
) => { ) if matches!(&**package, PubGrubPackageInner::Extra { .. }) => {
collapse_extra_proxies(cause); collapse_extra_proxies(cause);
*derivation_tree = cause.clone(); *derivation_tree = cause.clone();
} }
( (
ref mut cause, ref mut cause,
DerivationTree::External(External::FromDependencyOf( DerivationTree::External(External::FromDependencyOf(package, ..)),
PubGrubPackage::Extra { .. }, ) if matches!(&**package, PubGrubPackageInner::Extra { .. }) => {
..,
)),
) => {
collapse_extra_proxies(cause); collapse_extra_proxies(cause);
*derivation_tree = cause.clone(); *derivation_tree = cause.clone();
} }
@ -241,22 +235,22 @@ impl NoSolutionError {
) -> Self { ) -> Self {
let mut available_versions = IndexMap::default(); let mut available_versions = IndexMap::default();
for package in self.derivation_tree.packages() { for package in self.derivation_tree.packages() {
match package { match &**package {
PubGrubPackage::Root(_) => {} PubGrubPackageInner::Root(_) => {}
PubGrubPackage::Python(PubGrubPython::Installed) => { PubGrubPackageInner::Python(PubGrubPython::Installed) => {
available_versions.insert( available_versions.insert(
package.clone(), package.clone(),
BTreeSet::from([python_requirement.installed().deref().clone()]), BTreeSet::from([python_requirement.installed().deref().clone()]),
); );
} }
PubGrubPackage::Python(PubGrubPython::Target) => { PubGrubPackageInner::Python(PubGrubPython::Target) => {
available_versions.insert( available_versions.insert(
package.clone(), package.clone(),
BTreeSet::from([python_requirement.target().deref().clone()]), BTreeSet::from([python_requirement.target().deref().clone()]),
); );
} }
PubGrubPackage::Extra { .. } => {} PubGrubPackageInner::Extra { .. } => {}
PubGrubPackage::Package { name, .. } => { PubGrubPackageInner::Package { name, .. } => {
// Avoid including available versions for packages that exist in the derivation // Avoid including available versions for packages that exist in the derivation
// tree, but were never visited during resolution. We _may_ have metadata for // tree, but were never visited during resolution. We _may_ have metadata for
// these packages, but it's non-deterministic, and omitting them ensures that // these packages, but it's non-deterministic, and omitting them ensures that
@ -304,7 +298,7 @@ impl NoSolutionError {
) -> Self { ) -> Self {
let mut new = FxHashMap::default(); let mut new = FxHashMap::default();
for package in self.derivation_tree.packages() { for package in self.derivation_tree.packages() {
if let PubGrubPackage::Package { name, .. } = package { if let PubGrubPackageInner::Package { name, .. } = &**package {
if let Some(reason) = unavailable_packages.get(name) { if let Some(reason) = unavailable_packages.get(name) {
new.insert(name.clone(), reason.clone()); new.insert(name.clone(), reason.clone());
} }
@ -322,7 +316,7 @@ impl NoSolutionError {
) -> Self { ) -> Self {
let mut new = FxHashMap::default(); let mut new = FxHashMap::default();
for package in self.derivation_tree.packages() { for package in self.derivation_tree.packages() {
if let PubGrubPackage::Package { name, .. } = package { if let PubGrubPackageInner::Package { name, .. } = &**package {
if let Some(versions) = incomplete_packages.get(name) { if let Some(versions) = incomplete_packages.get(name) {
for entry in versions.iter() { for entry in versions.iter() {
let (version, reason) = entry.pair(); let (version, reason) = entry.pair();

View file

@ -10,7 +10,7 @@ use uv_configuration::{Constraints, Overrides};
use uv_normalize::{ExtraName, PackageName}; use uv_normalize::{ExtraName, PackageName};
use crate::pubgrub::specifier::PubGrubSpecifier; use crate::pubgrub::specifier::PubGrubSpecifier;
use crate::pubgrub::PubGrubPackage; use crate::pubgrub::{PubGrubPackage, PubGrubPackageInner};
use crate::resolver::{Locals, Urls}; use crate::resolver::{Locals, Urls};
use crate::ResolveError; use crate::ResolveError;
@ -102,8 +102,8 @@ fn add_requirements(
})) { })) {
let PubGrubRequirement { package, version } = result?; let PubGrubRequirement { package, version } = result?;
match &package { match &*package {
PubGrubPackage::Package { name, .. } => { PubGrubPackageInner::Package { name, .. } => {
// Detect self-dependencies. // Detect self-dependencies.
if source_name.is_some_and(|source_name| source_name == name) { if source_name.is_some_and(|source_name| source_name == name) {
warn!("{name} has a dependency on itself"); warn!("{name} has a dependency on itself");
@ -112,7 +112,7 @@ fn add_requirements(
dependencies.push((package.clone(), version.clone())); dependencies.push((package.clone(), version.clone()));
} }
PubGrubPackage::Extra { name, extra, .. } => { PubGrubPackageInner::Extra { name, extra, .. } => {
// Recursively add the dependencies of the current package (e.g., `black` depending on // Recursively add the dependencies of the current package (e.g., `black` depending on
// `black[colorama]`). // `black[colorama]`).
if source_name.is_some_and(|source_name| source_name == name) { if source_name.is_some_and(|source_name| source_name == name) {
@ -158,7 +158,7 @@ fn add_requirements(
PubGrubRequirement::from_constraint(constraint, urls, locals)?; PubGrubRequirement::from_constraint(constraint, urls, locals)?;
// Ignore self-dependencies. // Ignore self-dependencies.
if let PubGrubPackage::Package { name, .. } = &package { if let PubGrubPackageInner::Package { name, .. } = &*package {
// Detect self-dependencies. // Detect self-dependencies.
if source_name.is_some_and(|source_name| source_name == name) { if source_name.is_some_and(|source_name| source_name == name) {
warn!("{name} has a dependency on itself"); warn!("{name} has a dependency on itself");
@ -249,12 +249,12 @@ impl PubGrubRequirement {
} }
Ok(Self { Ok(Self {
package: PubGrubPackage::Package { package: PubGrubPackage::from(PubGrubPackageInner::Package {
name: requirement.name.clone(), name: requirement.name.clone(),
extra, extra,
marker: None, marker: None,
url: Some(expected.clone()), url: Some(expected.clone()),
}, }),
version: Range::full(), version: Range::full(),
}) })
} }
@ -275,12 +275,12 @@ impl PubGrubRequirement {
} }
Ok(Self { Ok(Self {
package: PubGrubPackage::Package { package: PubGrubPackage::from(PubGrubPackageInner::Package {
name: requirement.name.clone(), name: requirement.name.clone(),
extra, extra,
marker: None, marker: None,
url: Some(expected.clone()), url: Some(expected.clone()),
}, }),
version: Range::full(), version: Range::full(),
}) })
} }
@ -301,12 +301,12 @@ impl PubGrubRequirement {
} }
Ok(Self { Ok(Self {
package: PubGrubPackage::Package { package: PubGrubPackage::from(PubGrubPackageInner::Package {
name: requirement.name.clone(), name: requirement.name.clone(),
extra, extra,
marker: None, marker: None,
url: Some(expected.clone()), url: Some(expected.clone()),
}, }),
version: Range::full(), version: Range::full(),
}) })
} }

View file

@ -1,6 +1,6 @@
pub(crate) use crate::pubgrub::dependencies::{PubGrubDependencies, PubGrubRequirement}; pub(crate) use crate::pubgrub::dependencies::{PubGrubDependencies, PubGrubRequirement};
pub(crate) use crate::pubgrub::distribution::PubGrubDistribution; pub(crate) use crate::pubgrub::distribution::PubGrubDistribution;
pub(crate) use crate::pubgrub::package::{PubGrubPackage, PubGrubPython}; pub(crate) use crate::pubgrub::package::{PubGrubPackage, PubGrubPackageInner, PubGrubPython};
pub(crate) use crate::pubgrub::priority::{PubGrubPriorities, PubGrubPriority}; pub(crate) use crate::pubgrub::priority::{PubGrubPriorities, PubGrubPriority};
pub(crate) use crate::pubgrub::report::PubGrubReportFormatter; pub(crate) use crate::pubgrub::report::PubGrubReportFormatter;
pub(crate) use crate::pubgrub::specifier::PubGrubSpecifier; pub(crate) use crate::pubgrub::specifier::PubGrubSpecifier;

View file

@ -1,9 +1,36 @@
use distribution_types::VerbatimParsedUrl; use distribution_types::VerbatimParsedUrl;
use pep508_rs::MarkerTree; use pep508_rs::MarkerTree;
use std::fmt::{Display, Formatter};
use std::ops::Deref;
use std::sync::Arc;
use uv_normalize::{ExtraName, PackageName}; use uv_normalize::{ExtraName, PackageName};
use crate::resolver::Urls; use crate::resolver::Urls;
/// [`Arc`] wrapper around [`PubGrubPackageInner`] to make cloning (inside PubGrub) cheap.
#[derive(Debug, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct PubGrubPackage(Arc<PubGrubPackageInner>);
impl Deref for PubGrubPackage {
type Target = PubGrubPackageInner;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Display for PubGrubPackage {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Display::fmt(&self.0, f)
}
}
impl From<PubGrubPackageInner> for PubGrubPackage {
fn from(package: PubGrubPackageInner) -> Self {
Self(Arc::new(package))
}
}
/// A PubGrub-compatible wrapper around a "Python package", with two notable characteristics: /// A PubGrub-compatible wrapper around a "Python package", with two notable characteristics:
/// ///
/// 1. Includes a [`PubGrubPackage::Root`] variant, to satisfy PubGrub's requirement that a /// 1. Includes a [`PubGrubPackage::Root`] variant, to satisfy PubGrub's requirement that a
@ -12,7 +39,7 @@ use crate::resolver::Urls;
/// package (e.g., `black[colorama]`), and mark it as a dependency of the real package (e.g., /// package (e.g., `black[colorama]`), and mark it as a dependency of the real package (e.g.,
/// `black`). We then discard the virtual packages at the end of the resolution process. /// `black`). We then discard the virtual packages at the end of the resolution process.
#[derive(Debug, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)] #[derive(Debug, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub enum PubGrubPackage { pub enum PubGrubPackageInner {
/// The root package, which is used to start the resolution process. /// The root package, which is used to start the resolution process.
Root(Option<PackageName>), Root(Option<PackageName>),
/// A Python version. /// A Python version.
@ -91,19 +118,19 @@ impl PubGrubPackage {
) -> Self { ) -> Self {
let url = urls.get(&name).cloned(); let url = urls.get(&name).cloned();
if let Some(extra) = extra { if let Some(extra) = extra {
Self::Extra { Self(Arc::new(PubGrubPackageInner::Extra {
name, name,
extra, extra,
marker, marker,
url, url,
} }))
} else { } else {
Self::Package { Self(Arc::new(PubGrubPackageInner::Package {
name, name,
extra, extra,
marker, marker,
url, url,
} }))
} }
} }
} }
@ -116,7 +143,7 @@ pub enum PubGrubPython {
Target, Target,
} }
impl std::fmt::Display for PubGrubPackage { impl std::fmt::Display for PubGrubPackageInner {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
Self::Root(name) => { Self::Root(name) => {

View file

@ -7,6 +7,7 @@ use pep440_rs::Version;
use uv_normalize::PackageName; use uv_normalize::PackageName;
use crate::pubgrub::package::PubGrubPackage; use crate::pubgrub::package::PubGrubPackage;
use crate::pubgrub::PubGrubPackageInner;
/// A prioritization map to guide the PubGrub resolution process. /// A prioritization map to guide the PubGrub resolution process.
/// ///
@ -24,14 +25,14 @@ impl PubGrubPriorities {
/// Add a [`PubGrubPackage`] to the priority map. /// Add a [`PubGrubPackage`] to the priority map.
pub(crate) fn insert(&mut self, package: &PubGrubPackage, version: &Range<Version>) { pub(crate) fn insert(&mut self, package: &PubGrubPackage, version: &Range<Version>) {
let next = self.0.len(); let next = self.0.len();
match package { match &**package {
PubGrubPackage::Root(_) => {} PubGrubPackageInner::Root(_) => {}
PubGrubPackage::Python(_) => {} PubGrubPackageInner::Python(_) => {}
PubGrubPackage::Extra { PubGrubPackageInner::Extra {
name, url: None, .. name, url: None, ..
} }
| PubGrubPackage::Package { | PubGrubPackageInner::Package {
name, url: None, .. name, url: None, ..
} => { } => {
match self.0.entry(name.clone()) { match self.0.entry(name.clone()) {
@ -66,10 +67,10 @@ impl PubGrubPriorities {
} }
} }
} }
PubGrubPackage::Extra { PubGrubPackageInner::Extra {
name, url: Some(_), .. name, url: Some(_), ..
} }
| PubGrubPackage::Package { | PubGrubPackageInner::Package {
name, url: Some(_), .. name, url: Some(_), ..
} => { } => {
match self.0.entry(name.clone()) { match self.0.entry(name.clone()) {
@ -101,11 +102,11 @@ impl PubGrubPriorities {
/// Return the [`PubGrubPriority`] of the given package, if it exists. /// Return the [`PubGrubPriority`] of the given package, if it exists.
pub(crate) fn get(&self, package: &PubGrubPackage) -> Option<PubGrubPriority> { pub(crate) fn get(&self, package: &PubGrubPackage) -> Option<PubGrubPriority> {
match package { match &**package {
PubGrubPackage::Root(_) => Some(PubGrubPriority::Root), PubGrubPackageInner::Root(_) => Some(PubGrubPriority::Root),
PubGrubPackage::Python(_) => Some(PubGrubPriority::Root), PubGrubPackageInner::Python(_) => Some(PubGrubPriority::Root),
PubGrubPackage::Extra { name, .. } => self.0.get(name).copied(), PubGrubPackageInner::Extra { name, .. } => self.0.get(name).copied(),
PubGrubPackage::Package { name, .. } => self.0.get(name).copied(), PubGrubPackageInner::Package { name, .. } => self.0.get(name).copied(),
} }
} }
} }

View file

@ -19,7 +19,7 @@ use crate::candidate_selector::CandidateSelector;
use crate::python_requirement::PythonRequirement; use crate::python_requirement::PythonRequirement;
use crate::resolver::{IncompletePackage, UnavailablePackage, UnavailableReason}; use crate::resolver::{IncompletePackage, UnavailablePackage, UnavailableReason};
use super::PubGrubPackage; use super::{PubGrubPackage, PubGrubPackageInner};
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct PubGrubReportFormatter<'a> { pub(crate) struct PubGrubReportFormatter<'a> {
@ -44,7 +44,7 @@ impl ReportFormatter<PubGrubPackage, Range<Version>, UnavailableReason>
format!("we are solving dependencies of {package} {version}") format!("we are solving dependencies of {package} {version}")
} }
External::NoVersions(package, set) => { External::NoVersions(package, set) => {
if matches!(package, PubGrubPackage::Python(_)) { if matches!(&**package, PubGrubPackageInner::Python(_)) {
if let Some(python) = self.python_requirement { if let Some(python) = self.python_requirement {
if python.target() == python.installed() { if python.target() == python.installed() {
// Simple case, the installed version is the same as the target version // Simple case, the installed version is the same as the target version
@ -107,11 +107,11 @@ impl ReportFormatter<PubGrubPackage, Range<Version>, UnavailableReason>
} }
} }
} }
External::Custom(package, set, reason) => match package { External::Custom(package, set, reason) => match &**package {
PubGrubPackage::Root(Some(name)) => { PubGrubPackageInner::Root(Some(name)) => {
format!("{name} cannot be used because {reason}") format!("{name} cannot be used because {reason}")
} }
PubGrubPackage::Root(None) => { PubGrubPackageInner::Root(None) => {
format!("your requirements cannot be used because {reason}") format!("your requirements cannot be used because {reason}")
} }
_ => match reason { _ => match reason {
@ -131,12 +131,12 @@ impl ReportFormatter<PubGrubPackage, Range<Version>, UnavailableReason>
External::FromDependencyOf(package, package_set, dependency, dependency_set) => { External::FromDependencyOf(package, package_set, dependency, dependency_set) => {
let package_set = self.simplify_set(package_set, package); let package_set = self.simplify_set(package_set, package);
let dependency_set = self.simplify_set(dependency_set, dependency); let dependency_set = self.simplify_set(dependency_set, dependency);
match package { match &**package {
PubGrubPackage::Root(Some(name)) => format!( PubGrubPackageInner::Root(Some(name)) => format!(
"{name} depends on {}", "{name} depends on {}",
PackageRange::dependency(dependency, &dependency_set) PackageRange::dependency(dependency, &dependency_set)
), ),
PubGrubPackage::Root(None) => format!( PubGrubPackageInner::Root(None) => format!(
"you require {}", "you require {}",
PackageRange::dependency(dependency, &dependency_set) PackageRange::dependency(dependency, &dependency_set)
), ),
@ -157,15 +157,22 @@ impl ReportFormatter<PubGrubPackage, Range<Version>, UnavailableReason>
// by package first. // by package first.
terms_vec.sort_by(|&(pkg1, _), &(pkg2, _)| pkg1.cmp(pkg2)); terms_vec.sort_by(|&(pkg1, _), &(pkg2, _)| pkg1.cmp(pkg2));
match terms_vec.as_slice() { match terms_vec.as_slice() {
[] | [(PubGrubPackage::Root(_), _)] => "the requirements are unsatisfiable".into(), [] => "the requirements are unsatisfiable".into(),
[(package @ PubGrubPackage::Package { .. }, Term::Positive(range))] => { [(root, _)] if matches!(&**(*root), PubGrubPackageInner::Root(_)) => {
"the requirements are unsatisfiable".into()
}
[(package, Term::Positive(range))]
if matches!(&**(*package), PubGrubPackageInner::Package { .. }) =>
{
let range = self.simplify_set(range, package); let range = self.simplify_set(range, package);
format!( format!(
"{} cannot be used", "{} cannot be used",
PackageRange::compatibility(package, &range) PackageRange::compatibility(package, &range)
) )
} }
[(package @ PubGrubPackage::Package { .. }, Term::Negative(range))] => { [(package, Term::Negative(range))]
if matches!(&**(*package), PubGrubPackageInner::Package { .. }) =>
{
let range = self.simplify_set(range, package); let range = self.simplify_set(range, package);
format!( format!(
"{} must be used", "{} must be used",
@ -339,13 +346,13 @@ impl PubGrubReportFormatter<'_> {
let dependency_set2 = self.simplify_set(dependency_set2, dependency2); let dependency_set2 = self.simplify_set(dependency_set2, dependency2);
let dependency2 = PackageRange::dependency(dependency2, &dependency_set2); let dependency2 = PackageRange::dependency(dependency2, &dependency_set2);
match package1 { match &**package1 {
PubGrubPackage::Root(Some(name)) => format!( PubGrubPackageInner::Root(Some(name)) => format!(
"{name} depends on {}and {}", "{name} depends on {}and {}",
Padded::new("", &dependency1, " "), Padded::new("", &dependency1, " "),
dependency2, dependency2,
), ),
PubGrubPackage::Root(None) => format!( PubGrubPackageInner::Root(None) => format!(
"you require {}and {}", "you require {}and {}",
Padded::new("", &dependency1, " "), Padded::new("", &dependency1, " "),
dependency2, dependency2,
@ -402,7 +409,7 @@ impl PubGrubReportFormatter<'_> {
) -> IndexSet<PubGrubHint> { ) -> IndexSet<PubGrubHint> {
/// Returns `true` if pre-releases were allowed for a package. /// Returns `true` if pre-releases were allowed for a package.
fn allowed_prerelease(package: &PubGrubPackage, selector: &CandidateSelector) -> bool { fn allowed_prerelease(package: &PubGrubPackage, selector: &CandidateSelector) -> bool {
let PubGrubPackage::Package { name, .. } = package else { let PubGrubPackageInner::Package { name, .. } = &**package else {
return false; return false;
}; };
selector.prerelease_strategy().allows(name) selector.prerelease_strategy().allows(name)
@ -460,7 +467,7 @@ impl PubGrubReportFormatter<'_> {
let no_find_links = let no_find_links =
index_locations.flat_index().peekable().peek().is_none(); index_locations.flat_index().peekable().peek().is_none();
if let PubGrubPackage::Package { name, .. } = package { if let PubGrubPackageInner::Package { name, .. } = &**package {
// Add hints due to the package being entirely unavailable. // Add hints due to the package being entirely unavailable.
match unavailable_packages.get(name) { match unavailable_packages.get(name) {
Some(UnavailablePackage::NoIndex) => { Some(UnavailablePackage::NoIndex) => {
@ -970,9 +977,9 @@ impl<T: std::fmt::Display> std::fmt::Display for Padded<'_, T> {
} }
fn fmt_package(package: &PubGrubPackage) -> String { fn fmt_package(package: &PubGrubPackage) -> String {
match package { match &**package {
PubGrubPackage::Root(Some(name)) => name.to_string(), PubGrubPackageInner::Root(Some(name)) => name.to_string(),
PubGrubPackage::Root(None) => "you require".to_string(), PubGrubPackageInner::Root(None) => "you require".to_string(),
_ => format!("{package}"), _ => format!("{package}"),
} }
} }

View file

@ -18,7 +18,7 @@ use crate::dependency_provider::UvDependencyProvider;
use crate::editables::Editables; use crate::editables::Editables;
use crate::pins::FilePins; use crate::pins::FilePins;
use crate::preferences::Preferences; use crate::preferences::Preferences;
use crate::pubgrub::{PubGrubDistribution, PubGrubPackage}; use crate::pubgrub::{PubGrubDistribution, PubGrubPackageInner};
use crate::redirect::url_to_precise; use crate::redirect::url_to_precise;
use crate::resolution::AnnotatedDist; use crate::resolution::AnnotatedDist;
use crate::resolver::FxOnceMap; use crate::resolver::FxOnceMap;
@ -55,8 +55,8 @@ impl ResolutionGraph {
let mut extras = FxHashMap::default(); let mut extras = FxHashMap::default();
let mut diagnostics = Vec::new(); let mut diagnostics = Vec::new();
for (package, version) in selection { for (package, version) in selection {
match package { match &**package {
PubGrubPackage::Package { PubGrubPackageInner::Package {
name, name,
extra: Some(extra), extra: Some(extra),
marker: None, marker: None,
@ -95,7 +95,7 @@ impl ResolutionGraph {
}); });
} }
} }
PubGrubPackage::Package { PubGrubPackageInner::Package {
name, name,
extra: Some(extra), extra: Some(extra),
marker: None, marker: None,
@ -159,8 +159,8 @@ impl ResolutionGraph {
FxHashMap::with_capacity_and_hasher(selection.len(), BuildHasherDefault::default()); FxHashMap::with_capacity_and_hasher(selection.len(), BuildHasherDefault::default());
for (package, version) in selection { for (package, version) in selection {
match package { match &**package {
PubGrubPackage::Package { PubGrubPackageInner::Package {
name, name,
extra: None, extra: None,
marker: None, marker: None,
@ -229,7 +229,7 @@ impl ResolutionGraph {
}); });
inverse.insert(name, index); inverse.insert(name, index);
} }
PubGrubPackage::Package { PubGrubPackageInner::Package {
name, name,
extra: None, extra: None,
marker: None, marker: None,
@ -328,16 +328,16 @@ impl ResolutionGraph {
continue; continue;
} }
let PubGrubPackage::Package { let PubGrubPackageInner::Package {
name: self_name, .. name: self_name, ..
} = self_package } = &**self_package
else { else {
continue; continue;
}; };
let PubGrubPackage::Package { let PubGrubPackageInner::Package {
name: dependency_name, name: dependency_name,
.. ..
} = dependency_package } = &**dependency_package
else { else {
continue; continue;
}; };

View file

@ -10,7 +10,7 @@ use distribution_types::DistributionMetadata;
use pep440_rs::Version; use pep440_rs::Version;
use crate::candidate_selector::{CandidateDist, CandidateSelector}; use crate::candidate_selector::{CandidateDist, CandidateSelector};
use crate::pubgrub::PubGrubPackage; use crate::pubgrub::{PubGrubPackage, PubGrubPackageInner};
use crate::resolver::Request; use crate::resolver::Request;
use crate::{InMemoryIndex, ResolveError, VersionsResponse}; use crate::{InMemoryIndex, ResolveError, VersionsResponse};
@ -53,12 +53,12 @@ impl BatchPrefetcher {
index: &InMemoryIndex, index: &InMemoryIndex,
selector: &CandidateSelector, selector: &CandidateSelector,
) -> anyhow::Result<(), ResolveError> { ) -> anyhow::Result<(), ResolveError> {
let PubGrubPackage::Package { let PubGrubPackageInner::Package {
name, name,
extra: None, extra: None,
marker: None, marker: None,
url: None, url: None,
} = &next } = &**next
else { else {
return Ok(()); return Ok(());
}; };
@ -163,7 +163,10 @@ impl BatchPrefetcher {
/// Each time we tried a version for a package, we register that here. /// Each time we tried a version for a package, we register that here.
pub(crate) fn version_tried(&mut self, package: PubGrubPackage) { pub(crate) fn version_tried(&mut self, package: PubGrubPackage) {
// Only track base packages, no virtual packages from extras. // Only track base packages, no virtual packages from extras.
if matches!(package, PubGrubPackage::Package { extra: Some(_), .. }) { if matches!(
&*package,
PubGrubPackageInner::Package { extra: Some(_), .. }
) {
return; return;
} }
*self.tried_versions.entry(package).or_default() += 1; *self.tried_versions.entry(package).or_default() += 1;

View file

@ -43,8 +43,8 @@ use crate::manifest::Manifest;
use crate::pins::FilePins; use crate::pins::FilePins;
use crate::preferences::Preferences; use crate::preferences::Preferences;
use crate::pubgrub::{ use crate::pubgrub::{
PubGrubDependencies, PubGrubDistribution, PubGrubPackage, PubGrubPriorities, PubGrubPython, PubGrubDependencies, PubGrubDistribution, PubGrubPackage, PubGrubPackageInner,
PubGrubRequirement, PubGrubSpecifier, PubGrubPriorities, PubGrubPython, PubGrubRequirement, PubGrubSpecifier,
}; };
use crate::python_requirement::PythonRequirement; use crate::python_requirement::PythonRequirement;
use crate::resolution::ResolutionGraph; use crate::resolution::ResolutionGraph;
@ -401,7 +401,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
visited: &mut FxHashSet<PackageName>, visited: &mut FxHashSet<PackageName>,
request_sink: Sender<Request>, request_sink: Sender<Request>,
) -> Result<ResolutionGraph, ResolveError> { ) -> Result<ResolutionGraph, ResolveError> {
let root = PubGrubPackage::Root(self.project.clone()); let root = PubGrubPackage::from(PubGrubPackageInner::Root(self.project.clone()));
let mut prefetcher = BatchPrefetcher::default(); let mut prefetcher = BatchPrefetcher::default();
let mut state = SolveState { let mut state = SolveState {
pubgrub: State::init(root.clone(), MIN_VERSION.clone()), pubgrub: State::init(root.clone(), MIN_VERSION.clone()),
@ -480,7 +480,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
.expect("a package was chosen but we don't have a term."); .expect("a package was chosen but we don't have a term.");
// Check if the decision was due to the package being unavailable // Check if the decision was due to the package being unavailable
if let PubGrubPackage::Package { ref name, .. } = state.next { if let PubGrubPackageInner::Package { ref name, .. } = &*state.next {
if let Some(entry) = self.unavailable_packages.get(name) { if let Some(entry) = self.unavailable_packages.get(name) {
state state
.pubgrub .pubgrub
@ -530,7 +530,10 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
.add_incompatibility(Incompatibility::from_dependency( .add_incompatibility(Incompatibility::from_dependency(
package.clone(), package.clone(),
Range::singleton(version.clone()), Range::singleton(version.clone()),
(PubGrubPackage::Python(kind), python_version.clone()), (
PubGrubPackage::from(PubGrubPackageInner::Python(kind)),
python_version.clone(),
),
)); ));
} }
state state
@ -633,11 +636,11 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
package: &PubGrubPackage, package: &PubGrubPackage,
request_sink: &Sender<Request>, request_sink: &Sender<Request>,
) -> Result<(), ResolveError> { ) -> Result<(), ResolveError> {
match package { match &**package {
PubGrubPackage::Root(_) => {} PubGrubPackageInner::Root(_) => {}
PubGrubPackage::Python(_) => {} PubGrubPackageInner::Python(_) => {}
PubGrubPackage::Extra { .. } => {} PubGrubPackageInner::Extra { .. } => {}
PubGrubPackage::Package { PubGrubPackageInner::Package {
name, url: None, .. name, url: None, ..
} => { } => {
// Verify that the package is allowed under the hash-checking policy. // Verify that the package is allowed under the hash-checking policy.
@ -650,7 +653,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
request_sink.blocking_send(Request::Package(name.clone()))?; request_sink.blocking_send(Request::Package(name.clone()))?;
} }
} }
PubGrubPackage::Package { PubGrubPackageInner::Package {
name, name,
url: Some(url), url: Some(url),
.. ..
@ -684,12 +687,12 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
// Iterate over the potential packages, and fetch file metadata for any of them. These // Iterate over the potential packages, and fetch file metadata for any of them. These
// represent our current best guesses for the versions that we _might_ select. // represent our current best guesses for the versions that we _might_ select.
for (package, range) in packages { for (package, range) in packages {
let PubGrubPackage::Package { let PubGrubPackageInner::Package {
name, name,
extra: None, extra: None,
marker: None, marker: None,
url: None, url: None,
} = package } = &**package
else { else {
continue; continue;
}; };
@ -711,10 +714,12 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
visited: &mut FxHashSet<PackageName>, visited: &mut FxHashSet<PackageName>,
request_sink: &Sender<Request>, request_sink: &Sender<Request>,
) -> Result<Option<ResolverVersion>, ResolveError> { ) -> Result<Option<ResolverVersion>, ResolveError> {
match package { match &**package {
PubGrubPackage::Root(_) => Ok(Some(ResolverVersion::Available(MIN_VERSION.clone()))), PubGrubPackageInner::Root(_) => {
Ok(Some(ResolverVersion::Available(MIN_VERSION.clone())))
}
PubGrubPackage::Python(PubGrubPython::Installed) => { PubGrubPackageInner::Python(PubGrubPython::Installed) => {
let version = self.python_requirement.installed(); let version = self.python_requirement.installed();
if range.contains(version) { if range.contains(version) {
Ok(Some(ResolverVersion::Available(version.deref().clone()))) Ok(Some(ResolverVersion::Available(version.deref().clone())))
@ -723,7 +728,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
} }
} }
PubGrubPackage::Python(PubGrubPython::Target) => { PubGrubPackageInner::Python(PubGrubPython::Target) => {
let version = self.python_requirement.target(); let version = self.python_requirement.target();
if range.contains(version) { if range.contains(version) {
Ok(Some(ResolverVersion::Available(version.deref().clone()))) Ok(Some(ResolverVersion::Available(version.deref().clone())))
@ -732,12 +737,12 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
} }
} }
PubGrubPackage::Extra { PubGrubPackageInner::Extra {
name, name,
url: Some(url), url: Some(url),
.. ..
} }
| PubGrubPackage::Package { | PubGrubPackageInner::Package {
name, name,
url: Some(url), url: Some(url),
.. ..
@ -833,10 +838,10 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
Ok(Some(ResolverVersion::Available(version.clone()))) Ok(Some(ResolverVersion::Available(version.clone())))
} }
PubGrubPackage::Extra { PubGrubPackageInner::Extra {
name, url: None, .. name, url: None, ..
} }
| PubGrubPackage::Package { | PubGrubPackageInner::Package {
name, url: None, .. name, url: None, ..
} => { } => {
// Wait for the metadata to be available. // Wait for the metadata to be available.
@ -916,7 +921,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
let version = candidate.version().clone(); let version = candidate.version().clone();
// Emit a request to fetch the metadata for this version. // Emit a request to fetch the metadata for this version.
if matches!(package, PubGrubPackage::Package { .. }) { if matches!(&**package, PubGrubPackageInner::Package { .. }) {
if self.index.distributions().register(candidate.version_id()) { if self.index.distributions().register(candidate.version_id()) {
let request = Request::from(dist.for_resolution()); let request = Request::from(dist.for_resolution());
request_sink.blocking_send(request)?; request_sink.blocking_send(request)?;
@ -937,8 +942,8 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
priorities: &mut PubGrubPriorities, priorities: &mut PubGrubPriorities,
request_sink: &Sender<Request>, request_sink: &Sender<Request>,
) -> Result<Dependencies, ResolveError> { ) -> Result<Dependencies, ResolveError> {
match package { match &**package {
PubGrubPackage::Root(_) => { PubGrubPackageInner::Root(_) => {
// Add the root requirements. // Add the root requirements.
let dependencies = PubGrubDependencies::from_requirements( let dependencies = PubGrubDependencies::from_requirements(
&self.requirements, &self.requirements,
@ -1021,9 +1026,9 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
Ok(Dependencies::Available(dependencies.into())) Ok(Dependencies::Available(dependencies.into()))
} }
PubGrubPackage::Python(_) => Ok(Dependencies::Available(Vec::default())), PubGrubPackageInner::Python(_) => Ok(Dependencies::Available(Vec::default())),
PubGrubPackage::Package { PubGrubPackageInner::Package {
name, name,
extra, extra,
marker: _marker, marker: _marker,
@ -1192,28 +1197,28 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
} }
// Add a dependency on both the extra and base package. // Add a dependency on both the extra and base package.
PubGrubPackage::Extra { PubGrubPackageInner::Extra {
name, name,
extra, extra,
marker: _marker, marker: _marker,
url, url,
} => Ok(Dependencies::Available(vec![ } => Ok(Dependencies::Available(vec![
( (
PubGrubPackage::Package { PubGrubPackage::from(PubGrubPackageInner::Package {
name: name.clone(), name: name.clone(),
extra: None, extra: None,
marker: None, marker: None,
url: url.clone(), url: url.clone(),
}, }),
Range::singleton(version.clone()), Range::singleton(version.clone()),
), ),
( (
PubGrubPackage::Package { PubGrubPackage::from(PubGrubPackageInner::Package {
name: name.clone(), name: name.clone(),
extra: Some(extra.clone()), extra: Some(extra.clone()),
marker: None, marker: None,
url: url.clone(), url: url.clone(),
}, }),
Range::singleton(version.clone()), Range::singleton(version.clone()),
), ),
])), ])),
@ -1438,18 +1443,18 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
fn on_progress(&self, package: &PubGrubPackage, version: &Version) { fn on_progress(&self, package: &PubGrubPackage, version: &Version) {
if let Some(reporter) = self.reporter.as_ref() { if let Some(reporter) = self.reporter.as_ref() {
match package { match &**package {
PubGrubPackage::Root(_) => {} PubGrubPackageInner::Root(_) => {}
PubGrubPackage::Python(_) => {} PubGrubPackageInner::Python(_) => {}
PubGrubPackage::Extra { .. } => {} PubGrubPackageInner::Extra { .. } => {}
PubGrubPackage::Package { PubGrubPackageInner::Package {
name, name,
url: Some(url), url: Some(url),
.. ..
} => { } => {
reporter.on_progress(name, &VersionOrUrlRef::Url(&url.verbatim)); reporter.on_progress(name, &VersionOrUrlRef::Url(&url.verbatim));
} }
PubGrubPackage::Package { PubGrubPackageInner::Package {
name, url: None, .. name, url: None, ..
} => { } => {
reporter.on_progress(name, &VersionOrUrlRef::Version(version)); reporter.on_progress(name, &VersionOrUrlRef::Version(version));