mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-08 05:45:00 +00:00
Avoid filtering preferences by --reinstall
(#3929)
## Summary In general, it's not quite right to filter preferences by `--reinstall` -- we still want to respect existing versions, we just don't want to respect _installed_ versions. But now that the installed versions and preferences are decoupled, we can remove this (`--reinstall` is enforced on the installed versions via the `Exclusions` struct that we pass to the resolver). While I was here, I also cleaned up the lockfile preference code to better match the structure for `requirements.txt`.
This commit is contained in:
parent
438b5c61d0
commit
a14fe2f6c7
6 changed files with 70 additions and 38 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -4838,6 +4838,7 @@ dependencies = [
|
||||||
name = "uv-requirements"
|
name = "uv-requirements"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anstream",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cache-key",
|
"cache-key",
|
||||||
"configparser",
|
"configparser",
|
||||||
|
|
|
@ -27,6 +27,7 @@ uv-resolver = { workspace = true, features = ["clap"] }
|
||||||
uv-types = { workspace = true }
|
uv-types = { workspace = true }
|
||||||
uv-warnings = { workspace = true }
|
uv-warnings = { workspace = true }
|
||||||
|
|
||||||
|
anstream = { workspace = true }
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
configparser = { workspace = true }
|
configparser = { workspace = true }
|
||||||
console = { workspace = true }
|
console = { workspace = true }
|
||||||
|
|
|
@ -2,21 +2,26 @@ use std::path::Path;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
|
use anstream::eprint;
|
||||||
use requirements_txt::RequirementsTxt;
|
use requirements_txt::RequirementsTxt;
|
||||||
use uv_client::{BaseClientBuilder, Connectivity};
|
use uv_client::{BaseClientBuilder, Connectivity};
|
||||||
use uv_configuration::Upgrade;
|
use uv_configuration::Upgrade;
|
||||||
use uv_resolver::{Preference, PreferenceError};
|
use uv_resolver::{Lock, Preference, PreferenceError};
|
||||||
|
|
||||||
/// Load the preferred requirements from an existing lockfile, applying the upgrade strategy.
|
use crate::ProjectWorkspace;
|
||||||
pub async fn read_lockfile(
|
|
||||||
|
/// Load the preferred requirements from an existing `requirements.txt`, applying the upgrade strategy.
|
||||||
|
pub async fn read_requirements_txt(
|
||||||
output_file: Option<&Path>,
|
output_file: Option<&Path>,
|
||||||
upgrade: Upgrade,
|
upgrade: &Upgrade,
|
||||||
) -> Result<Vec<Preference>> {
|
) -> Result<Vec<Preference>> {
|
||||||
// As an optimization, skip reading the lockfile is we're upgrading all packages anyway.
|
// As an optimization, skip reading the lockfile is we're upgrading all packages anyway.
|
||||||
let Some(output_file) = output_file
|
if upgrade.is_all() {
|
||||||
.filter(|_| !upgrade.is_all())
|
return Ok(Vec::new());
|
||||||
.filter(|output_file| output_file.exists())
|
}
|
||||||
else {
|
|
||||||
|
// If the lockfile doesn't exist, don't respect any pinned versions.
|
||||||
|
let Some(output_file) = output_file.filter(|path| path.exists()) else {
|
||||||
return Ok(Vec::new());
|
return Ok(Vec::new());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -27,6 +32,8 @@ pub async fn read_lockfile(
|
||||||
&BaseClientBuilder::new().connectivity(Connectivity::Offline),
|
&BaseClientBuilder::new().connectivity(Connectivity::Offline),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
// Map each entry in the lockfile to a preference.
|
||||||
let preferences = requirements_txt
|
let preferences = requirements_txt
|
||||||
.requirements
|
.requirements
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -47,3 +54,50 @@ pub async fn read_lockfile(
|
||||||
.collect(),
|
.collect(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Load the preferred requirements from an existing lockfile, applying the upgrade strategy.
|
||||||
|
pub async fn read_lockfile(
|
||||||
|
project: &ProjectWorkspace,
|
||||||
|
upgrade: &Upgrade,
|
||||||
|
) -> Result<Vec<Preference>> {
|
||||||
|
// As an optimization, skip reading the lockfile is we're upgrading all packages anyway.
|
||||||
|
if upgrade.is_all() {
|
||||||
|
return Ok(Vec::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
// If an existing lockfile exists, build up a set of preferences.
|
||||||
|
let lockfile = project.workspace().root().join("uv.lock");
|
||||||
|
let lock = match fs_err::tokio::read_to_string(&lockfile).await {
|
||||||
|
Ok(encoded) => match toml::from_str::<Lock>(&encoded) {
|
||||||
|
Ok(lock) => lock,
|
||||||
|
Err(err) => {
|
||||||
|
eprint!("Failed to parse lockfile; ignoring locked requirements: {err}");
|
||||||
|
return Ok(Vec::new());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
|
||||||
|
return Ok(Vec::new());
|
||||||
|
}
|
||||||
|
Err(err) => return Err(err.into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Map each entry in the lockfile to a preference.
|
||||||
|
let preferences: Vec<Preference> = lock
|
||||||
|
.distributions()
|
||||||
|
.iter()
|
||||||
|
.map(Preference::from_lock)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Apply the upgrade strategy to the requirements.
|
||||||
|
Ok(match upgrade {
|
||||||
|
// Respect all pinned versions from the existing lockfile.
|
||||||
|
Upgrade::None => preferences,
|
||||||
|
// Ignore all pinned versions from the existing lockfile.
|
||||||
|
Upgrade::All => vec![],
|
||||||
|
// Ignore pinned versions for the specified packages.
|
||||||
|
Upgrade::Packages(packages) => preferences
|
||||||
|
.into_iter()
|
||||||
|
.filter(|preference| !packages.contains(preference.name()))
|
||||||
|
.collect(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -34,8 +34,8 @@ use uv_interpreter::{
|
||||||
use uv_interpreter::{PythonVersion, SourceSelector};
|
use uv_interpreter::{PythonVersion, SourceSelector};
|
||||||
use uv_normalize::{ExtraName, PackageName};
|
use uv_normalize::{ExtraName, PackageName};
|
||||||
use uv_requirements::{
|
use uv_requirements::{
|
||||||
upgrade::read_lockfile, LookaheadResolver, NamedRequirementsResolver, RequirementsSource,
|
upgrade::read_requirements_txt, LookaheadResolver, NamedRequirementsResolver,
|
||||||
RequirementsSpecification, SourceTreeResolver,
|
RequirementsSource, RequirementsSpecification, SourceTreeResolver,
|
||||||
};
|
};
|
||||||
use uv_resolver::{
|
use uv_resolver::{
|
||||||
AnnotationStyle, DependencyMode, DisplayResolutionGraph, ExcludeNewer, Exclusions, FlatIndex,
|
AnnotationStyle, DependencyMode, DisplayResolutionGraph, ExcludeNewer, Exclusions, FlatIndex,
|
||||||
|
@ -288,7 +288,7 @@ pub(crate) async fn pip_compile(
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// Read the lockfile, if present.
|
// Read the lockfile, if present.
|
||||||
let preferences = read_lockfile(output_file, upgrade).await?;
|
let preferences = read_requirements_txt(output_file, &upgrade).await?;
|
||||||
|
|
||||||
// Resolve the flat indexes from `--find-links`.
|
// Resolve the flat indexes from `--find-links`.
|
||||||
let flat_index = {
|
let flat_index = {
|
||||||
|
|
|
@ -197,12 +197,6 @@ pub(crate) async fn resolve<InstalledPackages: InstalledPackagesProvider>(
|
||||||
// TODO(zanieb): Consider consuming these instead of cloning
|
// TODO(zanieb): Consider consuming these instead of cloning
|
||||||
let exclusions = Exclusions::new(reinstall.clone(), upgrade.clone());
|
let exclusions = Exclusions::new(reinstall.clone(), upgrade.clone());
|
||||||
|
|
||||||
// Filter out any excluded distributions from the preferences.
|
|
||||||
let preferences = preferences
|
|
||||||
.into_iter()
|
|
||||||
.filter(|dist| !exclusions.contains(dist.name()))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
// Create a manifest of the requirements.
|
// Create a manifest of the requirements.
|
||||||
let manifest = Manifest::new(
|
let manifest = Manifest::new(
|
||||||
requirements,
|
requirements,
|
||||||
|
|
|
@ -11,8 +11,9 @@ use uv_configuration::{
|
||||||
};
|
};
|
||||||
use uv_dispatch::BuildDispatch;
|
use uv_dispatch::BuildDispatch;
|
||||||
use uv_interpreter::PythonEnvironment;
|
use uv_interpreter::PythonEnvironment;
|
||||||
|
use uv_requirements::upgrade::read_lockfile;
|
||||||
use uv_requirements::ProjectWorkspace;
|
use uv_requirements::ProjectWorkspace;
|
||||||
use uv_resolver::{ExcludeNewer, FlatIndex, InMemoryIndex, Lock, OptionsBuilder, Preference};
|
use uv_resolver::{ExcludeNewer, FlatIndex, InMemoryIndex, Lock, OptionsBuilder};
|
||||||
use uv_types::{BuildIsolation, EmptyInstalledPackages, HashStrategy, InFlight};
|
use uv_types::{BuildIsolation, EmptyInstalledPackages, HashStrategy, InFlight};
|
||||||
use uv_warnings::warn_user;
|
use uv_warnings::warn_user;
|
||||||
|
|
||||||
|
@ -105,26 +106,7 @@ pub(super) async fn do_lock(
|
||||||
let options = OptionsBuilder::new().exclude_newer(exclude_newer).build();
|
let options = OptionsBuilder::new().exclude_newer(exclude_newer).build();
|
||||||
|
|
||||||
// If an existing lockfile exists, build up a set of preferences.
|
// If an existing lockfile exists, build up a set of preferences.
|
||||||
let lockfile = project.workspace().root().join("uv.lock");
|
let preferences = read_lockfile(project, &upgrade).await?;
|
||||||
let lock = match fs_err::tokio::read_to_string(&lockfile).await {
|
|
||||||
Ok(encoded) => match toml::from_str::<Lock>(&encoded) {
|
|
||||||
Ok(lock) => Some(lock),
|
|
||||||
Err(err) => {
|
|
||||||
eprint!("Failed to parse lockfile; ignoring locked requirements: {err}");
|
|
||||||
None
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => None,
|
|
||||||
Err(err) => return Err(err.into()),
|
|
||||||
};
|
|
||||||
let preferences: Vec<Preference> = lock
|
|
||||||
.map(|lock| {
|
|
||||||
lock.distributions()
|
|
||||||
.iter()
|
|
||||||
.map(Preference::from_lock)
|
|
||||||
.collect()
|
|
||||||
})
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
// Create a build dispatch.
|
// Create a build dispatch.
|
||||||
let build_dispatch = BuildDispatch::new(
|
let build_dispatch = BuildDispatch::new(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue