Skip to content

Commit

Permalink
simplify marker expressions when deserializing lockfile
Browse files Browse the repository at this point in the history
  • Loading branch information
ibraheemdev committed Aug 8, 2024
1 parent 690c39c commit 8360e37
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 1 deletion.
1 change: 1 addition & 0 deletions crates/uv-resolver/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ textwrap = { workspace = true }
thiserror = { workspace = true }
tokio = { workspace = true }
tokio-stream = { workspace = true }
toml = { workspace = true }
toml_edit = { workspace = true }
tracing = { workspace = true }
url = { workspace = true }
Expand Down
48 changes: 48 additions & 0 deletions crates/uv-resolver/src/lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use std::sync::Arc;
use either::Either;
use itertools::Itertools;
use petgraph::visit::EdgeRef;
use pubgrub::Range;
use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
use toml_edit::{value, Array, ArrayOfTables, InlineTable, Item, Table, Value};
use url::Url;
Expand Down Expand Up @@ -77,6 +78,53 @@ pub struct Lock {
}

impl Lock {
/// Deserialize the [`Lock`] from a TOML string.
pub fn from_toml(s: &str) -> Result<Lock, toml::de::Error> {
let mut lock: Lock = toml::from_str(s)?;

// Simplify all marker expressions based on the requires-python bound.
//
// This is necessary to ensure the a `Lock` deserialized from a lockfile compares
// equally to a newly created `Lock`.
// TODO(ibraheem): we should only simplify python versions when serializing or ensure
// the requires-python bound is enforced on construction to avoid this step.
if let Some(requires_python) = &lock.requires_python {
let python_version = Range::from(requires_python.bound_major_minor().clone());
let python_full_version = Range::from(requires_python.bound().clone());

for package in &mut lock.packages {
for dep in &mut package.dependencies {
if let Some(marker) = &mut dep.marker {
*marker = marker.clone().simplify_python_versions(
python_version.clone(),
python_full_version.clone(),
);
}
}

for dep in package.optional_dependencies.values_mut().flatten() {
if let Some(marker) = &mut dep.marker {
*marker = marker.clone().simplify_python_versions(
python_version.clone(),
python_full_version.clone(),
);
}
}

for dep in package.dev_dependencies.values_mut().flatten() {
if let Some(marker) = &mut dep.marker {
*marker = marker.clone().simplify_python_versions(
python_version.clone(),
python_full_version.clone(),
);
}
}
}
}

Ok(lock)
}

/// Initialize a [`Lock`] from a [`ResolutionGraph`].
pub fn from_resolution_graph(graph: &ResolutionGraph) -> Result<Self, LockError> {
let mut locked_dists = BTreeMap::new();
Expand Down
2 changes: 1 addition & 1 deletion crates/uv/src/commands/project/lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -590,7 +590,7 @@ pub(crate) async fn commit(lock: &Lock, workspace: &Workspace) -> Result<(), Pro
/// Returns `Ok(None)` if the lockfile does not exist.
pub(crate) async fn read(workspace: &Workspace) -> Result<Option<Lock>, ProjectError> {
match fs_err::tokio::read_to_string(&workspace.install_path().join("uv.lock")).await {
Ok(encoded) => match toml::from_str::<Lock>(&encoded) {
Ok(encoded) => match Lock::from_toml(&encoded) {
Ok(lock) => Ok(Some(lock)),
Err(err) => {
eprint!("Failed to parse lockfile; ignoring locked requirements: {err}");
Expand Down

0 comments on commit 8360e37

Please sign in to comment.