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"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anyhow",
|
||||
"cache-key",
|
||||
"configparser",
|
||||
|
|
|
@ -27,6 +27,7 @@ uv-resolver = { workspace = true, features = ["clap"] }
|
|||
uv-types = { workspace = true }
|
||||
uv-warnings = { workspace = true }
|
||||
|
||||
anstream = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
configparser = { workspace = true }
|
||||
console = { workspace = true }
|
||||
|
|
|
@ -2,21 +2,26 @@ use std::path::Path;
|
|||
|
||||
use anyhow::Result;
|
||||
|
||||
use anstream::eprint;
|
||||
use requirements_txt::RequirementsTxt;
|
||||
use uv_client::{BaseClientBuilder, Connectivity};
|
||||
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.
|
||||
pub async fn read_lockfile(
|
||||
use crate::ProjectWorkspace;
|
||||
|
||||
/// Load the preferred requirements from an existing `requirements.txt`, applying the upgrade strategy.
|
||||
pub async fn read_requirements_txt(
|
||||
output_file: Option<&Path>,
|
||||
upgrade: Upgrade,
|
||||
upgrade: &Upgrade,
|
||||
) -> Result<Vec<Preference>> {
|
||||
// As an optimization, skip reading the lockfile is we're upgrading all packages anyway.
|
||||
let Some(output_file) = output_file
|
||||
.filter(|_| !upgrade.is_all())
|
||||
.filter(|output_file| output_file.exists())
|
||||
else {
|
||||
if upgrade.is_all() {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
|
||||
// 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());
|
||||
};
|
||||
|
||||
|
@ -27,6 +32,8 @@ pub async fn read_lockfile(
|
|||
&BaseClientBuilder::new().connectivity(Connectivity::Offline),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Map each entry in the lockfile to a preference.
|
||||
let preferences = requirements_txt
|
||||
.requirements
|
||||
.into_iter()
|
||||
|
@ -47,3 +54,50 @@ pub async fn read_lockfile(
|
|||
.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_normalize::{ExtraName, PackageName};
|
||||
use uv_requirements::{
|
||||
upgrade::read_lockfile, LookaheadResolver, NamedRequirementsResolver, RequirementsSource,
|
||||
RequirementsSpecification, SourceTreeResolver,
|
||||
upgrade::read_requirements_txt, LookaheadResolver, NamedRequirementsResolver,
|
||||
RequirementsSource, RequirementsSpecification, SourceTreeResolver,
|
||||
};
|
||||
use uv_resolver::{
|
||||
AnnotationStyle, DependencyMode, DisplayResolutionGraph, ExcludeNewer, Exclusions, FlatIndex,
|
||||
|
@ -288,7 +288,7 @@ pub(crate) async fn pip_compile(
|
|||
.build();
|
||||
|
||||
// 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`.
|
||||
let flat_index = {
|
||||
|
|
|
@ -197,12 +197,6 @@ pub(crate) async fn resolve<InstalledPackages: InstalledPackagesProvider>(
|
|||
// TODO(zanieb): Consider consuming these instead of cloning
|
||||
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.
|
||||
let manifest = Manifest::new(
|
||||
requirements,
|
||||
|
|
|
@ -11,8 +11,9 @@ use uv_configuration::{
|
|||
};
|
||||
use uv_dispatch::BuildDispatch;
|
||||
use uv_interpreter::PythonEnvironment;
|
||||
use uv_requirements::upgrade::read_lockfile;
|
||||
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_warnings::warn_user;
|
||||
|
||||
|
@ -105,26 +106,7 @@ pub(super) async fn do_lock(
|
|||
let options = OptionsBuilder::new().exclude_newer(exclude_newer).build();
|
||||
|
||||
// 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) => 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();
|
||||
let preferences = read_lockfile(project, &upgrade).await?;
|
||||
|
||||
// Create a build dispatch.
|
||||
let build_dispatch = BuildDispatch::new(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue