Skip to content

Commit

Permalink
Track supported Python range in lockfile
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed Jun 5, 2024
1 parent 191f955 commit 39b1af2
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 7 deletions.
18 changes: 15 additions & 3 deletions crates/uv-resolver/src/lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use distribution_types::{
GitSourceDist, IndexUrl, PathBuiltDist, PathSourceDist, RegistryBuiltDist, RegistryBuiltWheel,
RegistrySourceDist, RemoteSource, Resolution, ResolvedDist, ToUrlError,
};
use pep440_rs::Version;
use pep440_rs::{Version, VersionSpecifiers};
use pep508_rs::{MarkerEnvironment, MarkerTree, VerbatimUrl};
use platform_tags::{TagCompatibility, TagPriority, Tags};
use pypi_types::{HashDigest, ParsedArchiveUrl, ParsedGitUrl};
Expand All @@ -36,6 +36,8 @@ use crate::{lock, ResolutionGraph};
pub struct Lock {
version: u32,
distributions: Vec<Distribution>,
/// The range of supported Python versions.
requires_python: Option<VersionSpecifiers>,
/// A map from distribution ID to index in `distributions`.
///
/// This can be used to quickly lookup the full distribution for any ID
Expand Down Expand Up @@ -87,15 +89,18 @@ impl Lock {
}
}

let lock = Self::new(locked_dists.into_values().collect())?;
let distributions = locked_dists.into_values().collect();
let requires_python = graph.requires_python.clone();
let lock = Self::new(distributions, requires_python)?;
Ok(lock)
}

/// Initialize a [`Lock`] from a list of [`Distribution`] entries.
fn new(distributions: Vec<Distribution>) -> Result<Self, LockError> {
fn new(distributions: Vec<Distribution>, requires_python: Option<VersionSpecifiers>) -> Result<Self, LockError> {
let wire = LockWire {
version: 1,
distributions,
requires_python,
};
Self::try_from(wire)
}
Expand Down Expand Up @@ -196,13 +201,15 @@ struct LockWire {
version: u32,
#[serde(rename = "distribution")]
distributions: Vec<Distribution>,
requires_python: Option<VersionSpecifiers>,
}

impl From<Lock> for LockWire {
fn from(lock: Lock) -> LockWire {
LockWire {
version: lock.version,
distributions: lock.distributions,
requires_python: lock.requires_python,
}
}
}
Expand All @@ -215,6 +222,10 @@ impl Lock {
let mut doc = toml_edit::DocumentMut::new();
doc.insert("version", value(i64::from(self.version)));

if let Some(ref requires_python) = self.requires_python {
doc.insert("requires-python", value(requires_python.to_string()));
}

let mut distributions = ArrayOfTables::new();
for dist in &self.distributions {
let mut table = Table::new();
Expand Down Expand Up @@ -344,6 +355,7 @@ impl TryFrom<LockWire> for Lock {
Ok(Lock {
version: wire.version,
distributions: wire.distributions,
requires_python: wire.requires_python,
by_id,
})
}
Expand Down
8 changes: 8 additions & 0 deletions crates/uv-resolver/src/python_requirement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,14 @@ impl RequiresPython {
}
}
}

/// Returns the [`VersionSpecifiers`] for the [`RequiresPython`] specifier.
pub fn as_specifiers(&self) -> Option<&VersionSpecifiers> {
match self {
RequiresPython::Specifier(_) => None,
RequiresPython::Specifiers(specifiers) => Some(specifiers),
}
}
}

impl std::fmt::Display for RequiresPython {
Expand Down
16 changes: 13 additions & 3 deletions crates/uv-resolver/src/resolution/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
use distribution_types::{
Dist, DistributionMetadata, Name, ResolutionDiagnostic, VersionId, VersionOrUrlRef,
};
use pep440_rs::{Version, VersionSpecifier};
use pep440_rs::{Version, VersionSpecifier, VersionSpecifiers};
use pep508_rs::{MarkerEnvironment, MarkerTree};
use pypi_types::{ParsedUrlError, Yanked};
use uv_git::GitResolver;
Expand All @@ -20,14 +20,17 @@ use crate::pubgrub::{PubGrubDistribution, PubGrubPackageInner};
use crate::redirect::url_to_precise;
use crate::resolution::AnnotatedDist;
use crate::resolver::Resolution;
use crate::{InMemoryIndex, Manifest, MetadataResponse, ResolveError, VersionsResponse};
use crate::{InMemoryIndex, Manifest, MetadataResponse, PythonRequirement, ResolveError, VersionsResponse};
use crate::python_requirement::RequiresPython;

/// A complete resolution graph in which every node represents a pinned package and every edge
/// represents a dependency between two pinned packages.
#[derive(Debug)]
pub struct ResolutionGraph {
/// The underlying graph.
pub(crate) petgraph: Graph<AnnotatedDist, Version, Directed>,
/// The range of supported Python versions.
pub(crate) requires_python: Option<VersionSpecifiers>,
/// Any diagnostics that were encountered while building the graph.
pub(crate) diagnostics: Vec<ResolutionDiagnostic>,
}
Expand All @@ -39,9 +42,10 @@ impl ResolutionGraph {
index: &InMemoryIndex,
preferences: &Preferences,
git: &GitResolver,
python: &PythonRequirement,
resolution: Resolution,
) -> anyhow::Result<Self, ResolveError> {
// Collect all marker expressions from relevant pubgrub packages.
// Collect all marker expressions from relevant PubGrub packages.
let mut markers: FxHashMap<(&PackageName, &Version, &Option<ExtraName>), MarkerTree> =
FxHashMap::default();
for (package, versions) in &resolution.packages {
Expand Down Expand Up @@ -267,8 +271,14 @@ impl ResolutionGraph {
}
}

// Extract the `Requires-Python` range, if provided.
// TODO(charlie): Infer the supported Python range from the `Requires-Python` of the
// included packages.
let requires_python = python.target().and_then(RequiresPython::as_specifiers).cloned();

Ok(Self {
petgraph,
requires_python,
diagnostics,
})
}
Expand Down
2 changes: 1 addition & 1 deletion crates/uv-resolver/src/resolver/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
for resolution in resolutions {
combined.union(resolution);
}
ResolutionGraph::from_state(&self.index, &self.preferences, &self.git, combined)
ResolutionGraph::from_state(&self.index, &self.preferences, &self.git, &self.python_requirement, combined)
}

/// Visit a [`PubGrubPackage`] prior to selection. This should be called on a [`PubGrubPackage`]
Expand Down
1 change: 1 addition & 0 deletions crates/uv/tests/lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1038,6 +1038,7 @@ fn lock_requires_python() -> Result<()> {
assert_snapshot!(
lock, @r###"
version = 1
requires-python = ">=3.7"
[[distribution]]
name = "dataclasses"
Expand Down

0 comments on commit 39b1af2

Please sign in to comment.