Skip to content

Commit

Permalink
Add a lock target abstraction
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed Dec 25, 2024
1 parent 7c47a45 commit b90829c
Show file tree
Hide file tree
Showing 11 changed files with 329 additions and 194 deletions.
60 changes: 28 additions & 32 deletions crates/uv-resolver/src/lock/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ use uv_pypi_types::{
Requirement, RequirementSource,
};
use uv_types::{BuildContext, HashStrategy};
use uv_workspace::Workspace;
use uv_workspace::WorkspaceMember;

use crate::fork_strategy::ForkStrategy;
pub use crate::lock::installable::Installable;
Expand Down Expand Up @@ -916,7 +916,8 @@ impl Lock {
/// Convert the [`Lock`] to a [`Resolution`] using the given marker environment, tags, and root.
pub async fn satisfies<Context: BuildContext>(
&self,
workspace: &Workspace,
root: &Path,
packages: &BTreeMap<PackageName, WorkspaceMember>,
members: &[PackageName],
requirements: &[Requirement],
constraints: &[Requirement],
Expand Down Expand Up @@ -944,7 +945,7 @@ impl Lock {
// Validate that the member sources have not changed.
{
// E.g., that they've switched from virtual to non-virtual or vice versa.
for (name, member) in workspace.packages() {
for (name, member) in packages {
let expected = !member.pyproject_toml().is_package();
let actual = self
.find_by_name(name)
Expand All @@ -957,7 +958,7 @@ impl Lock {
}

// E.g., that the version has changed.
for (name, member) in workspace.packages() {
for (name, member) in packages {
let Some(expected) = member
.pyproject_toml()
.project
Expand Down Expand Up @@ -986,14 +987,14 @@ impl Lock {
let expected: BTreeSet<_> = requirements
.iter()
.cloned()
.map(|requirement| normalize_requirement(requirement, workspace))
.map(|requirement| normalize_requirement(requirement, root))
.collect::<Result<_, _>>()?;
let actual: BTreeSet<_> = self
.manifest
.requirements
.iter()
.cloned()
.map(|requirement| normalize_requirement(requirement, workspace))
.map(|requirement| normalize_requirement(requirement, root))
.collect::<Result<_, _>>()?;
if expected != actual {
return Ok(SatisfiesResult::MismatchedConstraints(expected, actual));
Expand All @@ -1005,14 +1006,14 @@ impl Lock {
let expected: BTreeSet<_> = constraints
.iter()
.cloned()
.map(|requirement| normalize_requirement(requirement, workspace))
.map(|requirement| normalize_requirement(requirement, root))
.collect::<Result<_, _>>()?;
let actual: BTreeSet<_> = self
.manifest
.constraints
.iter()
.cloned()
.map(|requirement| normalize_requirement(requirement, workspace))
.map(|requirement| normalize_requirement(requirement, root))
.collect::<Result<_, _>>()?;
if expected != actual {
return Ok(SatisfiesResult::MismatchedConstraints(expected, actual));
Expand All @@ -1024,14 +1025,14 @@ impl Lock {
let expected: BTreeSet<_> = overrides
.iter()
.cloned()
.map(|requirement| normalize_requirement(requirement, workspace))
.map(|requirement| normalize_requirement(requirement, root))
.collect::<Result<_, _>>()?;
let actual: BTreeSet<_> = self
.manifest
.overrides
.iter()
.cloned()
.map(|requirement| normalize_requirement(requirement, workspace))
.map(|requirement| normalize_requirement(requirement, root))
.collect::<Result<_, _>>()?;
if expected != actual {
return Ok(SatisfiesResult::MismatchedOverrides(expected, actual));
Expand All @@ -1049,7 +1050,7 @@ impl Lock {
requirements
.iter()
.cloned()
.map(|requirement| normalize_requirement(requirement, workspace))
.map(|requirement| normalize_requirement(requirement, root))
.collect::<Result<_, _>>()?,
))
})
Expand All @@ -1065,7 +1066,7 @@ impl Lock {
requirements
.iter()
.cloned()
.map(|requirement| normalize_requirement(requirement, workspace))
.map(|requirement| normalize_requirement(requirement, root))
.collect::<Result<_, _>>()?,
))
})
Expand Down Expand Up @@ -1111,7 +1112,7 @@ impl Lock {
IndexUrl::Pypi(_) | IndexUrl::Url(_) => None,
IndexUrl::Path(url) => {
let path = url.to_file_path().ok()?;
let path = relative_to(&path, workspace.install_path())
let path = relative_to(&path, root)
.or_else(|_| std::path::absolute(path))
.ok()?;
Some(path)
Expand All @@ -1121,7 +1122,7 @@ impl Lock {
});

// Add the workspace packages to the queue.
for root_name in workspace.packages().keys() {
for root_name in packages.keys() {
let root = self
.find_by_name(root_name)
.expect("found too many packages matching root");
Expand Down Expand Up @@ -1170,7 +1171,7 @@ impl Lock {

// Get the metadata for the distribution.
let dist = package.to_dist(
workspace.install_path(),
root,
// When validating, it's okay to use wheels that don't match the current platform.
TagPolicy::Preferred(tags),
// When validating, it's okay to use (e.g.) a source distribution with `--no-build`.
Expand Down Expand Up @@ -1233,14 +1234,14 @@ impl Lock {
let expected: BTreeSet<_> = metadata
.requires_dist
.into_iter()
.map(|requirement| normalize_requirement(requirement, workspace))
.map(|requirement| normalize_requirement(requirement, root))
.collect::<Result<_, _>>()?;
let actual: BTreeSet<_> = package
.metadata
.requires_dist
.iter()
.cloned()
.map(|requirement| normalize_requirement(requirement, workspace))
.map(|requirement| normalize_requirement(requirement, root))
.collect::<Result<_, _>>()?;

if expected != actual {
Expand All @@ -1264,7 +1265,7 @@ impl Lock {
group,
requirements
.into_iter()
.map(|requirement| normalize_requirement(requirement, workspace))
.map(|requirement| normalize_requirement(requirement, root))
.collect::<Result<_, _>>()?,
))
})
Expand All @@ -1280,7 +1281,7 @@ impl Lock {
requirements
.iter()
.cloned()
.map(|requirement| normalize_requirement(requirement, workspace))
.map(|requirement| normalize_requirement(requirement, root))
.collect::<Result<_, _>>()?,
))
})
Expand Down Expand Up @@ -1465,23 +1466,23 @@ impl ResolverManifest {
}

/// Convert the manifest to a relative form using the given workspace.
pub fn relative_to(self, workspace: &Workspace) -> Result<Self, io::Error> {
pub fn relative_to(self, root: &Path) -> Result<Self, io::Error> {
Ok(Self {
members: self.members,
requirements: self
.requirements
.into_iter()
.map(|requirement| requirement.relative_to(workspace.install_path()))
.map(|requirement| requirement.relative_to(root))
.collect::<Result<BTreeSet<_>, _>>()?,
constraints: self
.constraints
.into_iter()
.map(|requirement| requirement.relative_to(workspace.install_path()))
.map(|requirement| requirement.relative_to(root))
.collect::<Result<BTreeSet<_>, _>>()?,
overrides: self
.overrides
.into_iter()
.map(|requirement| requirement.relative_to(workspace.install_path()))
.map(|requirement| requirement.relative_to(root))
.collect::<Result<BTreeSet<_>, _>>()?,
dependency_groups: self
.dependency_groups
Expand All @@ -1491,7 +1492,7 @@ impl ResolverManifest {
group,
requirements
.into_iter()
.map(|requirement| requirement.relative_to(workspace.install_path()))
.map(|requirement| requirement.relative_to(root))
.collect::<Result<BTreeSet<_>, _>>()?,
))
})
Expand Down Expand Up @@ -3874,10 +3875,7 @@ fn normalize_url(mut url: Url) -> UrlString {
/// 2. Ensures that the lock and install paths are appropriately framed with respect to the
/// current [`Workspace`].
/// 3. Removes the `origin` field, which is only used in `requirements.txt`.
fn normalize_requirement(
requirement: Requirement,
workspace: &Workspace,
) -> Result<Requirement, LockError> {
fn normalize_requirement(requirement: Requirement, root: &Path) -> Result<Requirement, LockError> {
match requirement.source {
RequirementSource::Git {
mut repository,
Expand Down Expand Up @@ -3919,8 +3917,7 @@ fn normalize_requirement(
ext,
url: _,
} => {
let install_path =
uv_fs::normalize_path_buf(workspace.install_path().join(&install_path));
let install_path = uv_fs::normalize_path_buf(root.join(&install_path));
let url = VerbatimUrl::from_absolute_path(&install_path)
.map_err(LockErrorKind::RequirementVerbatimUrl)?;

Expand All @@ -3943,8 +3940,7 @@ fn normalize_requirement(
r#virtual,
url: _,
} => {
let install_path =
uv_fs::normalize_path_buf(workspace.install_path().join(&install_path));
let install_path = uv_fs::normalize_path_buf(root.join(&install_path));
let url = VerbatimUrl::from_absolute_path(&install_path)
.map_err(LockErrorKind::RequirementVerbatimUrl)?;

Expand Down
4 changes: 2 additions & 2 deletions crates/uv-workspace/src/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ impl Workspace {
.any(|member| *member.root() == self.install_path)
}

/// Returns the set of requirements that include all packages in the workspace.
/// Returns the set of all workspace members.
pub fn members_requirements(&self) -> impl Iterator<Item = Requirement> + '_ {
self.packages.values().filter_map(|member| {
let url = VerbatimUrl::from_absolute_path(&member.root)
Expand Down Expand Up @@ -309,7 +309,7 @@ impl Workspace {
})
}

/// Returns the set of requirements that include all packages in the workspace.
/// Returns the set of all workspace member dependency groups.
pub fn group_requirements(&self) -> impl Iterator<Item = Requirement> + '_ {
self.packages.values().filter_map(|member| {
let url = VerbatimUrl::from_absolute_path(&member.root)
Expand Down
9 changes: 5 additions & 4 deletions crates/uv/src/commands/project/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ use crate::commands::pip::loggers::{
use crate::commands::pip::operations::Modifications;
use crate::commands::project::install_target::InstallTarget;
use crate::commands::project::lock::LockMode;
use crate::commands::project::lock_target::LockTarget;
use crate::commands::project::{
init_script_python_requirement, lock, ProjectError, ProjectInterpreter, ScriptInterpreter,
init_script_python_requirement, ProjectError, ProjectInterpreter, ScriptInterpreter,
};
use crate::commands::reporters::{PythonDownloadReporter, ResolverReporter};
use crate::commands::{diagnostics, project, ExitStatus};
Expand Down Expand Up @@ -611,7 +612,7 @@ pub(crate) async fn add(
let project_root = project.root().to_path_buf();
let workspace_root = project.workspace().install_path().clone();
let existing_pyproject_toml = project.pyproject_toml().as_ref().to_vec();
let existing_uv_lock = lock::read_bytes(project.workspace()).await?;
let existing_uv_lock = LockTarget::from(project.workspace()).read_bytes().await?;

// Update the `pypackage.toml` in-memory.
let project = project
Expand Down Expand Up @@ -715,7 +716,7 @@ async fn lock_and_sync(

let mut lock = project::lock::do_safe_lock(
mode,
project.workspace(),
project.workspace().into(),
settings.into(),
bounds,
&state,
Expand Down Expand Up @@ -834,7 +835,7 @@ async fn lock_and_sync(
// the addition of the minimum version specifiers.
lock = project::lock::do_safe_lock(
mode,
project.workspace(),
project.workspace().into(),
settings.into(),
bounds,
&state,
Expand Down
2 changes: 1 addition & 1 deletion crates/uv/src/commands/project/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ pub(crate) async fn export(
// Lock the project.
let lock = match do_safe_lock(
mode,
project.workspace(),
project.workspace().into(),
settings.as_ref(),
LowerBound::Warn,
&state,
Expand Down
Loading

0 comments on commit b90829c

Please sign in to comment.