Skip to content

Commit

Permalink
Validate early
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed Nov 1, 2024
1 parent c08da2b commit b3180cc
Show file tree
Hide file tree
Showing 26 changed files with 337 additions and 249 deletions.
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions crates/uv-resolver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ pub use exclude_newer::ExcludeNewer;
pub use exclusions::Exclusions;
pub use flat_index::{FlatDistributions, FlatIndex};
pub use lock::{
Lock, LockError, LockVersion, RequirementsTxtExport, ResolverManifest, SatisfiesResult,
TreeDisplay, VERSION, InstallTarget,
InstallTarget, Lock, LockError, LockVersion, RequirementsTxtExport, ResolverManifest,
SatisfiesResult, TreeDisplay, VERSION,
};
pub use manifest::Manifest;
pub use options::{Flexibility, Options, OptionsBuilder};
Expand Down
29 changes: 13 additions & 16 deletions crates/uv-resolver/src/lock/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ use toml_edit::{value, Array, ArrayOfTables, InlineTable, Item, Table, Value};
use url::Url;

pub use crate::lock::requirements_txt::RequirementsTxtExport;
pub use crate::lock::tree::TreeDisplay;
pub use crate::lock::target::InstallTarget;
pub use crate::lock::tree::TreeDisplay;
use crate::requires_python::SimplifiedMarkerTree;
use crate::resolution::{AnnotatedDist, ResolutionGraphNode};
use crate::{
Expand Down Expand Up @@ -45,9 +45,10 @@ use uv_pypi_types::{
};
use uv_types::{BuildContext, HashStrategy};
use uv_workspace::dependency_groups::DependencyGroupError;
use uv_workspace::{Workspace};
use uv_workspace::Workspace;

mod requirements_txt;
mod target;
mod tree;

/// The current version of the lockfile format.
Expand Down Expand Up @@ -547,19 +548,16 @@ impl Lock {

/// Return the workspace root used to generate this lock.
pub fn root(&self) -> Option<&PackageName> {
self.packages
.iter()
.find_map(|package| {
let (Source::Editable(path) | Source::Virtual(path)) = &package.id.source
else {
return None;
};
if path == Path::new("") {
Some(&package.id.name)
} else {
None
}
})
self.packages.iter().find_map(|package| {
let (Source::Editable(path) | Source::Virtual(path)) = &package.id.source else {
return None;
};
if path == Path::new("") {
Some(&package.id.name)
} else {
None
}
})
}

/// Returns the supported environments that were used to generate this
Expand Down Expand Up @@ -4236,4 +4234,3 @@ fn each_element_on_its_line_array(elements: impl Iterator<Item = impl Into<Value

#[cfg(test)]
mod tests;
mod target;
20 changes: 10 additions & 10 deletions crates/uv-resolver/src/lock/requirements_txt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@ use petgraph::{Directed, Graph};
use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
use url::Url;

use crate::graph_ops::marker_reachability;
use crate::lock::{Package, PackageId, Source};
use crate::{Lock, LockError};
use uv_configuration::{DevGroupsManifest, EditableMode, ExtrasSpecification, InstallOptions};
use uv_distribution_filename::{DistExtension, SourceDistExtension};
use uv_fs::Simplified;
use uv_git::GitReference;
use uv_normalize::ExtraName;
use uv_pep508::MarkerTree;
use uv_pypi_types::{ParsedArchiveUrl, ParsedGitUrl};
use uv_workspace::InstallTarget;

use crate::graph_ops::marker_reachability;
use crate::lock::{Package, PackageId, Source};
use crate::{InstallTarget, LockError};

type LockGraph<'lock> = Graph<Node<'lock>, Edge, Directed>;

Expand All @@ -34,15 +34,14 @@ pub struct RequirementsTxtExport<'lock> {

impl<'lock> RequirementsTxtExport<'lock> {
pub fn from_lock(
lock: &'lock Lock,
target: InstallTarget<'lock>,
extras: &ExtrasSpecification,
dev: &DevGroupsManifest,
editable: EditableMode,
hashes: bool,
install_options: &'lock InstallOptions,
) -> Result<Self, LockError> {
let size_guess = lock.packages.len();
let size_guess = target.lock().packages.len();
let mut petgraph = LockGraph::with_capacity(size_guess, size_guess);
let mut inverse = FxHashMap::with_capacity_and_hasher(size_guess, FxBuildHasher);

Expand All @@ -53,7 +52,8 @@ impl<'lock> RequirementsTxtExport<'lock> {

// Add the workspace package to the queue.
for root_name in target.packages() {
let dist = lock
let dist = target
.lock()
.find_by_name(root_name)
.expect("found too many packages matching root")
.expect("could not find root");
Expand Down Expand Up @@ -88,7 +88,7 @@ impl<'lock> RequirementsTxtExport<'lock> {
// Add any development dependencies.
for group in dev.iter() {
for dep in dist.dependency_groups.get(group).into_iter().flatten() {
let dep_dist = lock.find_by_id(&dep.package_id);
let dep_dist = target.lock().find_by_id(&dep.package_id);

// Add the dependency to the graph.
if let Entry::Vacant(entry) = inverse.entry(&dep.package_id) {
Expand Down Expand Up @@ -135,7 +135,7 @@ impl<'lock> RequirementsTxtExport<'lock> {
};

for dep in deps {
let dep_dist = lock.find_by_id(&dep.package_id);
let dep_dist = target.lock().find_by_id(&dep.package_id);

// Add the dependency to the graph.
if let Entry::Vacant(entry) = inverse.entry(&dep.package_id) {
Expand Down Expand Up @@ -175,7 +175,7 @@ impl<'lock> RequirementsTxtExport<'lock> {
install_options.include_package(
&package.id.name,
target.project_name(),
lock.members(),
target.lock().members(),
)
})
.map(|(index, package)| Requirement {
Expand Down
65 changes: 37 additions & 28 deletions crates/uv-resolver/src/lock/target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,47 +10,54 @@ use uv_pypi_types::{ResolverMarkerEnvironment, VerbatimParsedUrl};
use uv_workspace::dependency_groups::{DependencyGroupError, FlatDependencyGroups};
use uv_workspace::Workspace;

use crate::{Lock, LockError};
use crate::lock::{LockErrorKind, Package, TagPolicy};
use crate::{Lock, LockError};

/// A target that can be installed.
/// A target that can be installed from a lockfile.
#[derive(Debug, Copy, Clone)]
pub enum InstallTarget<'env> {
/// A project (which could be a workspace root or member).
Project { workspace: &'env Workspace, name: &'env PackageName, lock: &'env Lock },
Project {
workspace: &'env Workspace,
name: &'env PackageName,
lock: &'env Lock,
},
/// An entire workspace.
Workspace { workspace: &'env Workspace, lock: &'env Lock },
/// A (legacy) workspace with a non-project root.
NonProjectWorkspace { workspace: &'env Workspace, lock: &'env Lock},
Workspace {
workspace: &'env Workspace,
lock: &'env Lock,
},
/// An entire workspace with a (legacy) non-project root.
NonProjectWorkspace {
workspace: &'env Workspace,
lock: &'env Lock,
},
}

impl<'env> InstallTarget<'env> {
/// Return the [`Workspace`] of the target.
pub fn workspace(&self) -> &Workspace {
pub fn workspace(&self) -> &'env Workspace {
match self {
Self::Project { workspace, ..} => workspace,
Self::Workspace { workspace, ..} => workspace,
Self::NonProjectWorkspace { workspace, ..} => workspace,
Self::Project { workspace, .. } => workspace,
Self::Workspace { workspace, .. } => workspace,
Self::NonProjectWorkspace { workspace, .. } => workspace,
}
}


/// Return the [`Lock`] of the target.
pub fn lock(&self) -> &Lock {
pub fn lock(&self) -> &'env Lock {
match self {
Self::Project { lock, ..} => lock,
Self::Workspace { lock, ..} => lock,
Self::NonProjectWorkspace { lock, ..} => lock,
Self::Project { lock, .. } => lock,
Self::Workspace { lock, .. } => lock,
Self::NonProjectWorkspace { lock, .. } => lock,
}
}

/// Return the [`PackageName`] of the target.
pub fn packages(&self) -> impl Iterator<Item = &PackageName> {
match self {
Self::Project { name, ..} => Either::Right(Either::Left(std::iter::once(*name))),
Self::NonProjectWorkspace { lock, .. } => {
Either::Left(lock.members().into_iter())
}
Self::Project { name, .. } => Either::Right(Either::Left(std::iter::once(*name))),
Self::NonProjectWorkspace { lock, .. } => Either::Left(lock.members().iter()),
Self::Workspace { lock, .. } => {
// Identify the workspace members.
//
Expand All @@ -59,9 +66,9 @@ impl<'env> InstallTarget<'env> {
if lock.members().is_empty() {
Either::Right(Either::Right(lock.root().into_iter()))
} else {
Either::Left(lock.members().into_iter())
Either::Left(lock.members().iter())
}
},
}
}
}

Expand All @@ -79,7 +86,7 @@ impl<'env> InstallTarget<'env> {
match self {
Self::Project { .. } => Ok(BTreeMap::default()),
Self::Workspace { .. } => Ok(BTreeMap::default()),
Self::NonProjectWorkspace { workspace, .. }=> {
Self::NonProjectWorkspace { workspace, .. } => {
// For non-projects, we might have `dependency-groups` or `tool.uv.dev-dependencies`
// that are attached to the workspace root (which isn't a member).

Expand Down Expand Up @@ -129,9 +136,9 @@ impl<'env> InstallTarget<'env> {
/// Return the [`PackageName`] of the target, if available.
pub fn project_name(&self) -> Option<&PackageName> {
match self {
Self::Project { name, ..} => Some(name),
Self::Workspace {.. } => None,
Self::NonProjectWorkspace {.. } => None,
Self::Project { name, .. } => Some(name),
Self::Workspace { .. } => None,
Self::NonProjectWorkspace { .. } => None,
}
}

Expand All @@ -150,7 +157,8 @@ impl<'env> InstallTarget<'env> {

// Add the workspace packages to the queue.
for root_name in self.packages() {
let root = self.lock()
let root = self
.lock()
.find_by_name(root_name)
.map_err(|_| LockErrorKind::MultipleRootPackages {
name: root_name.clone(),
Expand Down Expand Up @@ -206,7 +214,8 @@ impl<'env> InstallTarget<'env> {
for dependency in groups.get(group).into_iter().flatten() {
if dependency.marker.evaluate(marker_env, &[]) {
let root_name = &dependency.name;
let root = self.lock()
let root = self
.lock()
.find_by_markers(root_name, marker_env)
.map_err(|_| LockErrorKind::MultipleRootPackages {
name: root_name.clone(),
Expand Down Expand Up @@ -266,4 +275,4 @@ impl<'env> InstallTarget<'env> {
let diagnostics = vec![];
Ok(Resolution::new(map, hashes, diagnostics))
}
}
}
1 change: 0 additions & 1 deletion crates/uv-workspace/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ uv-pypi-types = { workspace = true }
uv-static = { workspace = true }
uv-warnings = { workspace = true }

either = { workspace = true }
fs-err = { workspace = true }
glob = { workspace = true }
itertools = { workspace = true }
Expand Down
4 changes: 2 additions & 2 deletions crates/uv-workspace/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pub use workspace::{
check_nested_workspaces, DiscoveryOptions, MemberDiscovery, ProjectWorkspace,
VirtualProject, Workspace, WorkspaceError, WorkspaceMember,
check_nested_workspaces, DiscoveryOptions, MemberDiscovery, ProjectWorkspace, VirtualProject,
Workspace, WorkspaceError, WorkspaceMember,
};

pub mod dependency_groups;
Expand Down
3 changes: 1 addition & 2 deletions crates/uv-workspace/src/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use crate::pyproject::{
DependencyGroups, Project, PyProjectToml, PyprojectTomlError, Sources, ToolUvSources,
ToolUvWorkspace,
};
use either::Either;
use glob::{glob, GlobError, PatternError};
use rustc_hash::FxHashSet;
use std::collections::{BTreeMap, BTreeSet};
Expand All @@ -15,7 +14,7 @@ use uv_distribution_types::Index;
use uv_fs::{Simplified, CWD};
use uv_normalize::{GroupName, PackageName, DEV_DEPENDENCIES};
use uv_pep508::{MarkerTree, RequirementOrigin, VerbatimUrl};
use uv_pypi_types::{Requirement, RequirementSource, SupportedEnvironments, VerbatimParsedUrl};
use uv_pypi_types::{Requirement, RequirementSource, SupportedEnvironments};
use uv_static::EnvVars;
use uv_warnings::{warn_user, warn_user_once};

Expand Down
18 changes: 10 additions & 8 deletions crates/uv/src/commands/project/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ use uv_types::{BuildIsolation, HashStrategy};
use uv_warnings::warn_user_once;
use uv_workspace::pyproject::{DependencyType, Source, SourceError};
use uv_workspace::pyproject_mut::{ArrayEdit, DependencyTarget, PyProjectTomlMut};
use uv_workspace::{DiscoveryOptions, VirtualProject, Workspace};
use uv_workspace::{DiscoveryOptions, VirtualProject, Workspace};

use crate::commands::pip::loggers::{
DefaultInstallLogger, DefaultResolveLogger, SummaryResolveLogger,
Expand Down Expand Up @@ -871,18 +871,20 @@ async fn lock_and_sync(

// Identify the installation target.
let target = match &project {
VirtualProject::Project(project) => {
InstallTarget::Project { workspace: project.workspace(), name: project.project_name(), lock: &lock }
}
VirtualProject::NonProject(workspace) => {
InstallTarget::NonProjectWorkspace { workspace, lock: &lock }
}
VirtualProject::Project(project) => InstallTarget::Project {
workspace: project.workspace(),
name: project.project_name(),
lock: &lock,
},
VirtualProject::NonProject(workspace) => InstallTarget::NonProjectWorkspace {
workspace,
lock: &lock,
},
};

project::sync::do_sync(
target,
venv,
&lock,
&extras,
&DevGroupsManifest::from_spec(dev),
EditableMode::Editable,
Expand Down
Loading

0 comments on commit b3180cc

Please sign in to comment.