Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions crates/uv-build-frontend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,7 @@ impl SourceBuild {
let requires_dist = RequiresDist::from_project_maybe_workspace(
requires_dist,
install_path,
None,
locations,
source_strategy,
LowerBound::Allow,
Expand Down Expand Up @@ -927,6 +928,7 @@ async fn create_pep517_build_environment(
let requires_dist = RequiresDist::from_project_maybe_workspace(
requires_dist,
install_path,
None,
locations,
source_strategy,
LowerBound::Allow,
Expand Down
26 changes: 24 additions & 2 deletions crates/uv-distribution/src/metadata/lowering.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use either::Either;
use std::collections::BTreeMap;
use std::io;
use std::path::{Path, PathBuf};

use either::Either;
use thiserror::Error;
use url::Url;

use uv_configuration::LowerBound;
use uv_distribution_filename::DistExtension;
use uv_distribution_types::{Index, IndexLocations, IndexName, Origin};
Expand All @@ -16,6 +18,8 @@ use uv_warnings::warn_user_once;
use uv_workspace::pyproject::{PyProjectToml, Source, Sources};
use uv_workspace::Workspace;

use crate::metadata::GitWorkspaceMember;

#[derive(Debug, Clone)]
pub struct LoweredRequirement(Requirement);

Expand All @@ -38,6 +42,7 @@ impl LoweredRequirement {
locations: &'data IndexLocations,
workspace: &'data Workspace,
lower_bound: LowerBound,
git_member: Option<&'data GitWorkspaceMember<'data>>,
) -> impl Iterator<Item = Result<Self, LoweringError>> + 'data {
let (source, origin) = if let Some(source) = project_sources.get(&requirement.name) {
(Some(source), RequirementOrigin::Project)
Expand Down Expand Up @@ -216,7 +221,24 @@ impl LoweredRequirement {
))
})?;

let source = if member.pyproject_toml().is_package() {
let source = if let Some(git_member) = &git_member {
// If the workspace comes from a git dependency, all workspace
// members need to be git deps, too.
let subdirectory =
uv_fs::relative_to(member.root(), git_member.fetch_root)
.expect("Workspace member must be relative");
RequirementSource::Git {
repository: git_member.git_source.git.repository().clone(),
reference: git_member.git_source.git.reference().clone(),
precise: git_member.git_source.git.precise(),
subdirectory: if subdirectory == PathBuf::new() {
None
} else {
Some(subdirectory)
},
url,
}
} else if member.pyproject_toml().is_package() {
RequirementSource::Directory {
install_path,
url,
Expand Down
24 changes: 18 additions & 6 deletions crates/uv-distribution/src/metadata/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::path::Path;
use thiserror::Error;

use uv_configuration::{LowerBound, SourceStrategy};
use uv_distribution_types::IndexLocations;
use uv_distribution_types::{GitSourceUrl, IndexLocations};
use uv_normalize::{ExtraName, GroupName, PackageName};
use uv_pep440::{Version, VersionSpecifiers};
use uv_pypi_types::{HashDigest, ResolutionMetadata};
Expand Down Expand Up @@ -65,23 +65,26 @@ impl Metadata {
pub async fn from_workspace(
metadata: ResolutionMetadata,
install_path: &Path,
git_source: Option<&GitWorkspaceMember<'_>>,
locations: &IndexLocations,
sources: SourceStrategy,
bounds: LowerBound,
) -> Result<Self, MetadataError> {
// Lower the requirements.
let requires_dist = uv_pypi_types::RequiresDist {
name: metadata.name,
requires_dist: metadata.requires_dist,
provides_extras: metadata.provides_extras,
};
let RequiresDist {
name,
requires_dist,
provides_extras,
dependency_groups,
} = RequiresDist::from_project_maybe_workspace(
uv_pypi_types::RequiresDist {
name: metadata.name,
requires_dist: metadata.requires_dist,
provides_extras: metadata.provides_extras,
},
requires_dist,
install_path,
git_source,
locations,
sources,
bounds,
Expand Down Expand Up @@ -133,3 +136,12 @@ impl From<Metadata> for ArchiveMetadata {
}
}
}

/// A workspace member from a checked-out Git repo.
#[derive(Debug, Clone)]
pub struct GitWorkspaceMember<'a> {
/// The root of the checkout, which may be the root of the workspace or may be above the
/// workspace root.
pub fetch_root: &'a Path,
pub git_source: &'a GitSourceUrl<'a>,
}
25 changes: 21 additions & 4 deletions crates/uv-distribution/src/metadata/requires_dist.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::collections::BTreeMap;
use std::path::Path;

use crate::metadata::{LoweredRequirement, MetadataError};
use crate::metadata::{GitWorkspaceMember, LoweredRequirement, MetadataError};
use crate::Metadata;
use uv_configuration::{LowerBound, SourceStrategy};
use uv_distribution_types::IndexLocations;
Expand Down Expand Up @@ -39,22 +39,35 @@ impl RequiresDist {
pub async fn from_project_maybe_workspace(
metadata: uv_pypi_types::RequiresDist,
install_path: &Path,
git_member: Option<&GitWorkspaceMember<'_>>,
locations: &IndexLocations,
sources: SourceStrategy,
lower_bound: LowerBound,
) -> Result<Self, MetadataError> {
// TODO(konsti): Limit discovery for Git checkouts to Git root.
// TODO(konsti): Cache workspace discovery.
let discovery_options = if let Some(git_member) = &git_member {
DiscoveryOptions {
stop_discovery_at: Some(
git_member
.fetch_root
.parent()
.expect("git checkout has a parent"),
),
..Default::default()
}
} else {
DiscoveryOptions::default()
};
let Some(project_workspace) =
ProjectWorkspace::from_maybe_project_root(install_path, &DiscoveryOptions::default())
.await?
ProjectWorkspace::from_maybe_project_root(install_path, &discovery_options).await?
else {
return Ok(Self::from_metadata23(metadata));
};

Self::from_project_workspace(
metadata,
&project_workspace,
git_member,
locations,
sources,
lower_bound,
Expand All @@ -64,6 +77,7 @@ impl RequiresDist {
fn from_project_workspace(
metadata: uv_pypi_types::RequiresDist,
project_workspace: &ProjectWorkspace,
git_member: Option<&GitWorkspaceMember<'_>>,
locations: &IndexLocations,
source_strategy: SourceStrategy,
lower_bound: LowerBound,
Expand Down Expand Up @@ -142,6 +156,7 @@ impl RequiresDist {
locations,
project_workspace.workspace(),
lower_bound,
git_member,
)
.map(move |requirement| {
match requirement {
Expand Down Expand Up @@ -196,6 +211,7 @@ impl RequiresDist {
locations,
project_workspace.workspace(),
lower_bound,
git_member,
)
.map(move |requirement| match requirement {
Ok(requirement) => Ok(requirement.into_inner()),
Expand Down Expand Up @@ -266,6 +282,7 @@ mod test {
Ok(RequiresDist::from_project_workspace(
requires_dist,
&project_workspace,
None,
&IndexLocations::default(),
SourceStrategy::default(),
LowerBound::default(),
Expand Down
15 changes: 14 additions & 1 deletion crates/uv-distribution/src/source/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ use zip::ZipArchive;

use crate::distribution_database::ManagedClient;
use crate::error::Error;
use crate::metadata::{ArchiveMetadata, Metadata};
use crate::metadata::{ArchiveMetadata, GitWorkspaceMember, Metadata};
use crate::reporter::Facade;
use crate::source::built_wheel_metadata::BuiltWheelMetadata;
use crate::source::revision::Revision;
Expand Down Expand Up @@ -389,6 +389,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
let requires_dist = RequiresDist::from_project_maybe_workspace(
requires_dist,
project_root,
None,
self.build_context.locations(),
self.build_context.sources(),
self.build_context.bounds(),
Expand Down Expand Up @@ -1083,6 +1084,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
Metadata::from_workspace(
metadata,
resource.install_path.as_ref(),
None,
self.build_context.locations(),
self.build_context.sources(),
self.build_context.bounds(),
Expand Down Expand Up @@ -1119,6 +1121,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
Metadata::from_workspace(
metadata,
resource.install_path.as_ref(),
None,
self.build_context.locations(),
self.build_context.sources(),
self.build_context.bounds(),
Expand Down Expand Up @@ -1150,6 +1153,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
Metadata::from_workspace(
metadata,
resource.install_path.as_ref(),
None,
self.build_context.locations(),
self.build_context.sources(),
self.build_context.bounds(),
Expand Down Expand Up @@ -1197,6 +1201,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
Metadata::from_workspace(
metadata,
resource.install_path.as_ref(),
None,
self.build_context.locations(),
self.build_context.sources(),
self.build_context.bounds(),
Expand Down Expand Up @@ -1378,10 +1383,15 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
if let Some(metadata) =
Self::read_static_metadata(source, fetch.path(), resource.subdirectory).await?
{
let git_member = GitWorkspaceMember {
fetch_root: fetch.path(),
git_source: resource,
};
return Ok(ArchiveMetadata::from(
Metadata::from_workspace(
metadata,
&path,
Some(&git_member),
self.build_context.locations(),
self.build_context.sources(),
self.build_context.bounds(),
Expand Down Expand Up @@ -1410,6 +1420,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
Metadata::from_workspace(
metadata,
&path,
None,
self.build_context.locations(),
self.build_context.sources(),
self.build_context.bounds(),
Expand Down Expand Up @@ -1442,6 +1453,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
Metadata::from_workspace(
metadata,
&path,
None,
self.build_context.locations(),
self.build_context.sources(),
self.build_context.bounds(),
Expand Down Expand Up @@ -1489,6 +1501,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
Metadata::from_workspace(
metadata,
fetch.path(),
None,
self.build_context.locations(),
self.build_context.sources(),
self.build_context.bounds(),
Expand Down
Loading
Loading