Skip to content

Commit

Permalink
Add semantics toggle
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed May 22, 2024
1 parent e2bf5d8 commit 90944eb
Show file tree
Hide file tree
Showing 6 changed files with 201 additions and 56 deletions.
7 changes: 6 additions & 1 deletion crates/uv-resolver/src/manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ impl Manifest {

// Include direct requirements, with constraints and overrides applied.
DependencyMode::Direct => Either::Right(
self.overrides.apply(& self.requirements)
self.overrides.apply(&self.requirements)
.chain(self.constraints.requirements())
.chain(self.overrides.requirements())
.filter(move |requirement| requirement.evaluate_markers(markers, &[]))),
Expand Down Expand Up @@ -210,4 +210,9 @@ impl Manifest {
) -> impl Iterator<Item = &Requirement> {
self.constraints.apply(self.overrides.apply(requirements))
}

/// Returns the number of input requirements.
pub fn num_requirements(&self) -> usize {
self.requirements.len() + self.editables.len()
}
}
2 changes: 2 additions & 0 deletions crates/uv/src/commands/pip/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ use uv_resolver::{
use uv_types::{BuildIsolation, HashStrategy, InFlight};

use crate::commands::pip::operations;
use crate::commands::pip::operations::Modifications;
use crate::commands::reporters::ResolverReporter;
use crate::commands::{elapsed, ExitStatus};
use crate::editables::ResolvedEditables;
Expand Down Expand Up @@ -474,6 +475,7 @@ pub(crate) async fn pip_install(
&resolution,
&editables,
site_packages,
Modifications::Sufficient,
&reinstall,
&no_binary,
link_mode,
Expand Down
131 changes: 94 additions & 37 deletions crates/uv/src/commands/pip/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,21 +198,32 @@ pub(crate) async fn resolve(
);

// Resolve the dependencies.
let resolver = Resolver::new(
manifest,
options,
&python_requirement,
Some(markers),
tags,
flat_index,
index,
hasher,
build_dispatch,
site_packages,
DistributionDatabase::new(client, build_dispatch, concurrency.downloads),
)?
.with_reporter(ResolverReporter::from(printer));
let resolution = resolver.resolve().await?;
let resolution = {
// If possible, create a bound on the progress bar.
let reporter = match options.dependency_mode {
DependencyMode::Transitive => ResolverReporter::from(printer),
DependencyMode::Direct => {
ResolverReporter::from(printer).with_length(manifest.num_requirements() as u64)
}
};

let resolver = Resolver::new(
manifest,
options,
&python_requirement,
Some(markers),
tags,
flat_index,
index,
hasher,
build_dispatch,
site_packages,
DistributionDatabase::new(client, build_dispatch, concurrency.downloads),
)?
.with_reporter(reporter);

resolver.resolve().await?
};

let s = if resolution.len() == 1 { "" } else { "s" };
writeln!(
Expand Down Expand Up @@ -240,12 +251,28 @@ pub(crate) async fn resolve(
Ok(resolution)
}

#[derive(Debug, Clone, Copy)]
pub(crate) enum Modifications {
/// Use `pip install` semantics, whereby existing installations are left as-is, unless they are
/// marked for re-installation or upgrade.
///
/// Ensures that the resulting environment is sufficient to meet the requirements, but without
/// any unnecessary changes.
Sufficient,
/// Use `pip sync` semantics, whereby any existing, extraneous installations are removed.
///
/// Ensures that the resulting environment is an exact match for the requirements, but may
/// result in more changes than necessary.
Exact,
}

/// Install a set of requirements into the current environment.
#[allow(clippy::too_many_arguments)]
pub(crate) async fn install(
resolution: &Resolution,
editables: &[ResolvedEditable],
site_packages: SitePackages,
modifications: Modifications,
reinstall: &Reinstall,
no_binary: &NoBinary,
link_mode: LinkMode,
Expand Down Expand Up @@ -283,18 +310,24 @@ pub(crate) async fn install(
.context("Failed to determine installation plan")?;

if dry_run {
return report_dry_run(resolution, plan, start, printer);
return report_dry_run(resolution, plan, modifications, start, printer);
}

let Plan {
cached,
remote,
reinstalls,
extraneous: _,
extraneous,
} = plan;

// If we're in `install` mode, ignore any extraneous distributions.
let extraneous = match modifications {
Modifications::Sufficient => vec![],
Modifications::Exact => extraneous,
};

// Nothing to do.
if remote.is_empty() && cached.is_empty() && reinstalls.is_empty() {
if remote.is_empty() && cached.is_empty() && reinstalls.is_empty() && extraneous.is_empty() {
let s = if resolution.len() == 1 { "" } else { "s" };
writeln!(
printer.stderr(),
Expand Down Expand Up @@ -354,9 +387,11 @@ pub(crate) async fn install(
wheels
};

// Remove any existing installations.
if !reinstalls.is_empty() {
for dist_info in &reinstalls {
// Remove any upgraded or extraneous installations.
if !extraneous.is_empty() || !reinstalls.is_empty() {
let start = std::time::Instant::now();

for dist_info in extraneous.iter().chain(reinstalls.iter()) {
match uv_installer::uninstall(dist_info).await {
Ok(summary) => {
debug!(
Expand All @@ -379,6 +414,22 @@ pub(crate) async fn install(
Err(err) => return Err(err.into()),
}
}

let s = if extraneous.len() + reinstalls.len() == 1 {
""
} else {
"s"
};
writeln!(
printer.stderr(),
"{}",
format!(
"Uninstalled {} in {}",
format!("{} package{}", extraneous.len() + reinstalls.len(), s).bold(),
elapsed(start.elapsed())
)
.dimmed()
)?;
}

// Install the resolved distributions.
Expand Down Expand Up @@ -407,8 +458,9 @@ pub(crate) async fn install(
compile_bytecode(venv, cache, printer).await?;
}

for event in reinstalls
for event in extraneous
.into_iter()
.chain(reinstalls.into_iter())
.map(|distribution| ChangeEvent {
dist: LocalDist::from(distribution),
kind: ChangeEventKind::Removed,
Expand Down Expand Up @@ -481,18 +533,25 @@ pub(crate) async fn install(
fn report_dry_run(
resolution: &Resolution,
plan: Plan,
modifications: Modifications,
start: std::time::Instant,
printer: Printer,
) -> Result<(), Error> {
let Plan {
cached,
remote,
reinstalls,
extraneous: _,
extraneous,
} = plan;

// If we're in `install` mode, ignore any extraneous distributions.
let extraneous = match modifications {
Modifications::Sufficient => vec![],
Modifications::Exact => extraneous,
};

// Nothing to do.
if remote.is_empty() && cached.is_empty() && reinstalls.is_empty() {
if remote.is_empty() && cached.is_empty() && reinstalls.is_empty() && extraneous.is_empty() {
let s = if resolution.len() == 1 { "" } else { "s" };
writeln!(
printer.stderr(),
Expand Down Expand Up @@ -536,15 +595,19 @@ fn report_dry_run(
remote
};

// Remove any existing installations.
if !reinstalls.is_empty() {
let s = if reinstalls.len() == 1 { "" } else { "s" };
// Remove any upgraded or extraneous installations.
if !extraneous.is_empty() || !reinstalls.is_empty() {
let s = if extraneous.len() + reinstalls.len() == 1 {
""
} else {
"s"
};
writeln!(
printer.stderr(),
"{}",
format!(
"Would uninstall {}",
format!("{} package{}", reinstalls.len(), s).bold(),
format!("{} package{}", extraneous.len() + reinstalls.len(), s).bold(),
)
.dimmed()
)?;
Expand All @@ -562,8 +625,9 @@ fn report_dry_run(
)?;
}

for event in reinstalls
for event in extraneous
.into_iter()
.chain(reinstalls.into_iter())
.map(|distribution| DryRunEvent {
name: distribution.name().clone(),
version: distribution.installed_version().to_string(),
Expand Down Expand Up @@ -613,8 +677,7 @@ pub(crate) fn validate(
printer: Printer,
) -> Result<(), Error> {
let site_packages = SitePackages::from_executable(venv)?;
let diagnostics = site_packages.diagnostics()?;
for diagnostic in diagnostics {
for diagnostic in site_packages.diagnostics()? {
// Only surface diagnostics that are "relevant" to the current resolution.
if resolution
.packages()
Expand All @@ -640,12 +703,6 @@ pub(crate) enum Error {
#[error(transparent)]
Uninstall(#[from] uv_installer::UninstallError),

#[error(transparent)]
Client(#[from] uv_client::Error),

#[error(transparent)]
Platform(#[from] platform_tags::PlatformError),

#[error(transparent)]
Hash(#[from] uv_types::HashStrategyError),

Expand Down
2 changes: 2 additions & 0 deletions crates/uv/src/commands/pip/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ use uv_resolver::{
use uv_types::{BuildIsolation, HashStrategy, InFlight};

use crate::commands::pip::operations;
use crate::commands::pip::operations::Modifications;
use crate::commands::reporters::ResolverReporter;
use crate::commands::ExitStatus;
use crate::editables::ResolvedEditables;
Expand Down Expand Up @@ -415,6 +416,7 @@ pub(crate) async fn pip_sync(
&resolution,
&editables,
site_packages,
Modifications::Exact,
reinstall,
&no_binary,
link_mode,
Expand Down
Loading

0 comments on commit 90944eb

Please sign in to comment.