diff --git a/crates/uv-build-frontend/src/lib.rs b/crates/uv-build-frontend/src/lib.rs index 1ef439788..13335614b 100644 --- a/crates/uv-build-frontend/src/lib.rs +++ b/crates/uv-build-frontend/src/lib.rs @@ -588,6 +588,7 @@ impl SourceBuild { install_path, locations, source_strategy, + None, workspace_cache, ) .await @@ -1054,6 +1055,7 @@ async fn create_pep517_build_environment( install_path, locations, source_strategy, + build_context.workspace_member_editable(), workspace_cache, ) .await diff --git a/crates/uv-dispatch/src/lib.rs b/crates/uv-dispatch/src/lib.rs index 1b8570bab..8d609eca2 100644 --- a/crates/uv-dispatch/src/lib.rs +++ b/crates/uv-dispatch/src/lib.rs @@ -100,6 +100,7 @@ pub struct BuildDispatch<'a> { source_build_context: SourceBuildContext, build_extra_env_vars: FxHashMap, sources: SourceStrategy, + workspace_member_editable: Option, workspace_cache: WorkspaceCache, concurrency: Concurrency, preview: Preview, @@ -126,6 +127,7 @@ impl<'a> BuildDispatch<'a> { hasher: &'a HashStrategy, exclude_newer: ExcludeNewer, sources: SourceStrategy, + workspace_member_editable: Option, workspace_cache: WorkspaceCache, concurrency: Concurrency, preview: Preview, @@ -152,6 +154,7 @@ impl<'a> BuildDispatch<'a> { source_build_context: SourceBuildContext::default(), build_extra_env_vars: FxHashMap::default(), sources, + workspace_member_editable, workspace_cache, concurrency, preview, @@ -222,6 +225,10 @@ impl BuildContext for BuildDispatch<'_> { self.sources } + fn workspace_member_editable(&self) -> Option { + self.workspace_member_editable + } + fn locations(&self) -> &IndexLocations { self.index_locations } diff --git a/crates/uv-distribution/src/metadata/build_requires.rs b/crates/uv-distribution/src/metadata/build_requires.rs index 723ca4f96..c790d9496 100644 --- a/crates/uv-distribution/src/metadata/build_requires.rs +++ b/crates/uv-distribution/src/metadata/build_requires.rs @@ -41,6 +41,7 @@ impl BuildRequires { install_path: &Path, locations: &IndexLocations, sources: SourceStrategy, + workspace_member_editable: Option, cache: &WorkspaceCache, ) -> Result { let discovery = match sources { @@ -56,7 +57,7 @@ impl BuildRequires { return Ok(Self::from_metadata23(metadata)); }; - Self::from_project_workspace(metadata, &project_workspace, locations, sources) + Self::from_project_workspace(metadata, &project_workspace, locations, sources, workspace_member_editable) } /// Lower the `build-system.requires` field from a `pyproject.toml` file. @@ -65,6 +66,7 @@ impl BuildRequires { project_workspace: &ProjectWorkspace, locations: &IndexLocations, source_strategy: SourceStrategy, + workspace_member_editable: Option, ) -> Result { // Collect any `tool.uv.index` entries. let empty = vec![]; @@ -114,6 +116,7 @@ impl BuildRequires { locations, project_workspace.workspace(), None, + workspace_member_editable, ) .map(move |requirement| match requirement { Ok(requirement) => Ok(requirement.into_inner()), @@ -186,6 +189,7 @@ impl BuildRequires { locations, workspace, None, + None, ) .map(move |requirement| match requirement { Ok(requirement) => Ok(requirement.into_inner()), @@ -271,6 +275,7 @@ impl LoweredExtraBuildDependencies { index_locations, workspace, None, + None, ) .map(move |requirement| { match requirement { diff --git a/crates/uv-distribution/src/metadata/dependency_groups.rs b/crates/uv-distribution/src/metadata/dependency_groups.rs index d12e0651d..a4f3468e8 100644 --- a/crates/uv-distribution/src/metadata/dependency_groups.rs +++ b/crates/uv-distribution/src/metadata/dependency_groups.rs @@ -156,6 +156,7 @@ impl SourcedDependencyGroups { locations, project.workspace(), git_member, + None, ) .map(move |requirement| match requirement { Ok(requirement) => Ok(requirement.into_inner()), diff --git a/crates/uv-distribution/src/metadata/lowering.rs b/crates/uv-distribution/src/metadata/lowering.rs index 627e2d4dd..67feaaaed 100644 --- a/crates/uv-distribution/src/metadata/lowering.rs +++ b/crates/uv-distribution/src/metadata/lowering.rs @@ -44,6 +44,7 @@ impl LoweredRequirement { locations: &'data IndexLocations, workspace: &'data Workspace, git_member: Option<&'data GitWorkspaceMember<'data>>, + workspace_member_editable: Option, ) -> impl Iterator> + use<'data> + 'data { // Identify the source from the `tool.uv.sources` table. let (sources, origin) = if let Some(source) = project_sources.get(&requirement.name) { @@ -313,7 +314,7 @@ impl LoweredRequirement { RequirementSource::Directory { install_path: install_path.into_boxed_path(), url, - editable: Some(editability.unwrap_or(true)), + editable: Some(editability.unwrap_or(workspace_member_editable.unwrap_or(true))), r#virtual: Some(false), } } else { diff --git a/crates/uv-distribution/src/metadata/mod.rs b/crates/uv-distribution/src/metadata/mod.rs index 3557de15e..7c5d28ebb 100644 --- a/crates/uv-distribution/src/metadata/mod.rs +++ b/crates/uv-distribution/src/metadata/mod.rs @@ -90,6 +90,7 @@ impl Metadata { git_source: Option<&GitWorkspaceMember<'_>>, locations: &IndexLocations, sources: SourceStrategy, + workspace_member_editable: Option, cache: &WorkspaceCache, ) -> Result { // Lower the requirements. @@ -111,6 +112,7 @@ impl Metadata { git_source, locations, sources, + workspace_member_editable, cache, ) .await?; diff --git a/crates/uv-distribution/src/metadata/requires_dist.rs b/crates/uv-distribution/src/metadata/requires_dist.rs index eb0c007f9..4f6346626 100644 --- a/crates/uv-distribution/src/metadata/requires_dist.rs +++ b/crates/uv-distribution/src/metadata/requires_dist.rs @@ -47,6 +47,7 @@ impl RequiresDist { git_member: Option<&GitWorkspaceMember<'_>>, locations: &IndexLocations, sources: SourceStrategy, + workspace_member_editable: Option, cache: &WorkspaceCache, ) -> Result { let discovery = DiscoveryOptions { @@ -69,7 +70,7 @@ impl RequiresDist { return Ok(Self::from_metadata23(metadata)); }; - Self::from_project_workspace(metadata, &project_workspace, git_member, locations, sources) + Self::from_project_workspace(metadata, &project_workspace, git_member, locations, sources, workspace_member_editable) } fn from_project_workspace( @@ -78,6 +79,7 @@ impl RequiresDist { git_member: Option<&GitWorkspaceMember<'_>>, locations: &IndexLocations, source_strategy: SourceStrategy, + workspace_member_editable: Option, ) -> Result { // Collect any `tool.uv.index` entries. let empty = vec![]; @@ -140,6 +142,7 @@ impl RequiresDist { locations, project_workspace.workspace(), git_member, + workspace_member_editable, ) .map( move |requirement| match requirement { @@ -182,6 +185,7 @@ impl RequiresDist { locations, project_workspace.workspace(), git_member, + workspace_member_editable, ) .map(move |requirement| match requirement { Ok(requirement) => Ok(requirement.into_inner()), @@ -468,6 +472,7 @@ mod test { None, &IndexLocations::default(), SourceStrategy::default(), + None, )?) } diff --git a/crates/uv-distribution/src/source/mod.rs b/crates/uv-distribution/src/source/mod.rs index 1a22ada94..b0a5da43c 100644 --- a/crates/uv-distribution/src/source/mod.rs +++ b/crates/uv-distribution/src/source/mod.rs @@ -1264,6 +1264,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { None, self.build_context.locations(), self.build_context.sources(), + self.build_context.workspace_member_editable(), self.build_context.workspace_cache(), ) .await?, @@ -1317,6 +1318,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { None, self.build_context.locations(), self.build_context.sources(), + self.build_context.workspace_member_editable(), self.build_context.workspace_cache(), ) .await?, @@ -1366,6 +1368,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { None, self.build_context.locations(), self.build_context.sources(), + self.build_context.workspace_member_editable(), self.build_context.workspace_cache(), ) .await?, @@ -1427,6 +1430,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { None, self.build_context.locations(), self.build_context.sources(), + self.build_context.workspace_member_editable(), self.build_context.workspace_cache(), ) .await?, @@ -1501,6 +1505,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { None, self.build_context.locations(), self.build_context.sources(), + self.build_context.workspace_member_editable(), self.build_context.workspace_cache(), ) .await?; @@ -1801,6 +1806,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { Some(&git_member), self.build_context.locations(), self.build_context.sources(), + self.build_context.workspace_member_editable(), self.build_context.workspace_cache(), ) .await?, @@ -1834,6 +1840,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { Some(&git_member), self.build_context.locations(), self.build_context.sources(), + self.build_context.workspace_member_editable(), self.build_context.workspace_cache(), ) .await?, @@ -1886,6 +1893,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { Some(&git_member), self.build_context.locations(), self.build_context.sources(), + self.build_context.workspace_member_editable(), self.build_context.workspace_cache(), ) .await?, @@ -1947,6 +1955,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { Some(&git_member), self.build_context.locations(), self.build_context.sources(), + self.build_context.workspace_member_editable(), self.build_context.workspace_cache(), ) .await?, diff --git a/crates/uv-types/src/traits.rs b/crates/uv-types/src/traits.rs index 6054ba302..941f6712a 100644 --- a/crates/uv-types/src/traits.rs +++ b/crates/uv-types/src/traits.rs @@ -99,6 +99,15 @@ pub trait BuildContext { /// Whether to incorporate `tool.uv.sources` when resolving requirements. fn sources(&self) -> SourceStrategy; + /// Whether workspace members should be installed as editable. + /// + /// Returns `None` for default behavior (editable), or `Some(false)` to force non-editable + /// installations. This is primarily used by `uv tool install` to ensure workspace dependencies + /// are vendored rather than linked via `.pth` files. + fn workspace_member_editable(&self) -> Option { + None + } + /// The index locations being searched. fn locations(&self) -> &IndexLocations; diff --git a/crates/uv/src/commands/build_frontend.rs b/crates/uv/src/commands/build_frontend.rs index 08b5a32df..e6517a8a0 100644 --- a/crates/uv/src/commands/build_frontend.rs +++ b/crates/uv/src/commands/build_frontend.rs @@ -612,6 +612,7 @@ async fn build_package( &hasher, exclude_newer, sources, + None, workspace_cache, concurrency, preview, diff --git a/crates/uv/src/commands/pip/compile.rs b/crates/uv/src/commands/pip/compile.rs index aa89884ea..fe769f4ac 100644 --- a/crates/uv/src/commands/pip/compile.rs +++ b/crates/uv/src/commands/pip/compile.rs @@ -511,6 +511,7 @@ pub(crate) async fn pip_compile( &build_hashes, exclude_newer.clone(), sources, + None, WorkspaceCache::default(), concurrency, preview, diff --git a/crates/uv/src/commands/pip/install.rs b/crates/uv/src/commands/pip/install.rs index b513faa79..72206b0a0 100644 --- a/crates/uv/src/commands/pip/install.rs +++ b/crates/uv/src/commands/pip/install.rs @@ -478,6 +478,7 @@ pub(crate) async fn pip_install( &build_hasher, exclude_newer.clone(), sources, + None, WorkspaceCache::default(), concurrency, preview, @@ -613,6 +614,7 @@ pub(crate) async fn pip_install( &build_hasher, exclude_newer.clone(), sources, + None, WorkspaceCache::default(), concurrency, preview, diff --git a/crates/uv/src/commands/pip/sync.rs b/crates/uv/src/commands/pip/sync.rs index 0cc7c9d9a..cdc536fb7 100644 --- a/crates/uv/src/commands/pip/sync.rs +++ b/crates/uv/src/commands/pip/sync.rs @@ -387,6 +387,7 @@ pub(crate) async fn pip_sync( &build_hasher, exclude_newer.clone(), sources, + None, WorkspaceCache::default(), concurrency, preview, @@ -524,6 +525,7 @@ pub(crate) async fn pip_sync( &build_hasher, exclude_newer.clone(), sources, + None, WorkspaceCache::default(), concurrency, preview, diff --git a/crates/uv/src/commands/project/add.rs b/crates/uv/src/commands/project/add.rs index cc06ce97b..51e7d651b 100644 --- a/crates/uv/src/commands/project/add.rs +++ b/crates/uv/src/commands/project/add.rs @@ -467,6 +467,7 @@ pub(crate) async fn add( &build_hasher, settings.resolver.exclude_newer.clone(), sources, + None, // No workspace caching since `uv add` changes the workspace definition. WorkspaceCache::default(), concurrency, diff --git a/crates/uv/src/commands/project/environment.rs b/crates/uv/src/commands/project/environment.rs index e8ecc6bc6..0e291d394 100644 --- a/crates/uv/src/commands/project/environment.rs +++ b/crates/uv/src/commands/project/environment.rs @@ -139,6 +139,7 @@ impl CachedEnvironment { resolve, concurrency, cache, + None, printer, preview, ) diff --git a/crates/uv/src/commands/project/lock.rs b/crates/uv/src/commands/project/lock.rs index 5363a365a..6e8dcb987 100644 --- a/crates/uv/src/commands/project/lock.rs +++ b/crates/uv/src/commands/project/lock.rs @@ -734,6 +734,7 @@ async fn do_lock( &build_hasher, exclude_newer.clone(), *sources, + None, workspace_cache.clone(), concurrency, preview, diff --git a/crates/uv/src/commands/project/mod.rs b/crates/uv/src/commands/project/mod.rs index 785874c6e..4b89df01a 100644 --- a/crates/uv/src/commands/project/mod.rs +++ b/crates/uv/src/commands/project/mod.rs @@ -1669,6 +1669,7 @@ pub(crate) async fn resolve_names( concurrency: Concurrency, cache: &Cache, workspace_cache: &WorkspaceCache, + workspace_member_editable: Option, printer: Printer, preview: Preview, ) -> Result, uv_requirements::Error> { @@ -1771,6 +1772,7 @@ pub(crate) async fn resolve_names( &build_hasher, exclude_newer.clone(), *sources, + workspace_member_editable, workspace_cache.clone(), concurrency, preview, @@ -1842,6 +1844,7 @@ pub(crate) async fn resolve_environment( logger: Box, concurrency: Concurrency, cache: &Cache, + workspace_member_editable: Option, printer: Printer, preview: Preview, ) -> Result { @@ -1982,6 +1985,7 @@ pub(crate) async fn resolve_environment( &build_hasher, exclude_newer.clone(), *sources, + workspace_member_editable, workspace_cache, concurrency, preview, @@ -2120,6 +2124,7 @@ pub(crate) async fn sync_environment( &build_hasher, exclude_newer.clone(), sources, + None, workspace_cache, concurrency, preview, @@ -2351,6 +2356,7 @@ pub(crate) async fn update_environment( &build_hasher, exclude_newer.clone(), *sources, + None, workspace_cache, concurrency, preview, diff --git a/crates/uv/src/commands/project/sync.rs b/crates/uv/src/commands/project/sync.rs index 1225f3757..bc4f52cfa 100644 --- a/crates/uv/src/commands/project/sync.rs +++ b/crates/uv/src/commands/project/sync.rs @@ -777,6 +777,7 @@ pub(super) async fn do_sync( &build_hasher, exclude_newer.clone(), sources, + None, workspace_cache.clone(), concurrency, preview, diff --git a/crates/uv/src/commands/tool/install.rs b/crates/uv/src/commands/tool/install.rs index 0f35ad70c..17f922f83 100644 --- a/crates/uv/src/commands/tool/install.rs +++ b/crates/uv/src/commands/tool/install.rs @@ -145,6 +145,7 @@ pub(crate) async fn install( concurrency, &cache, &workspace_cache, + Some(false), printer, preview, ) @@ -270,6 +271,7 @@ pub(crate) async fn install( concurrency, &cache, &workspace_cache, + Some(false), printer, preview, ) @@ -295,6 +297,7 @@ pub(crate) async fn install( concurrency, &cache, &workspace_cache, + Some(false), printer, preview, ) @@ -516,6 +519,7 @@ pub(crate) async fn install( Box::new(DefaultResolveLogger), concurrency, &cache, + Some(false), printer, preview, ) @@ -571,6 +575,7 @@ pub(crate) async fn install( Box::new(DefaultResolveLogger), concurrency, &cache, + Some(false), printer, preview, ) diff --git a/crates/uv/src/commands/tool/run.rs b/crates/uv/src/commands/tool/run.rs index 918d014f9..5a723a125 100644 --- a/crates/uv/src/commands/tool/run.rs +++ b/crates/uv/src/commands/tool/run.rs @@ -801,6 +801,7 @@ async fn get_or_create_environment( concurrency, cache, &workspace_cache, + None, printer, preview, ) @@ -892,6 +893,7 @@ async fn get_or_create_environment( concurrency, cache, &workspace_cache, + None, printer, preview, ) @@ -918,6 +920,7 @@ async fn get_or_create_environment( concurrency, cache, &workspace_cache, + None, printer, preview, ) diff --git a/crates/uv/src/commands/tool/upgrade.rs b/crates/uv/src/commands/tool/upgrade.rs index 330673c24..f1c1ad6f9 100644 --- a/crates/uv/src/commands/tool/upgrade.rs +++ b/crates/uv/src/commands/tool/upgrade.rs @@ -355,6 +355,7 @@ async fn upgrade_tool( Box::new(SummaryResolveLogger), concurrency, cache, + None, printer, preview, ) diff --git a/crates/uv/src/commands/venv.rs b/crates/uv/src/commands/venv.rs index 13fe8fb51..142e4926e 100644 --- a/crates/uv/src/commands/venv.rs +++ b/crates/uv/src/commands/venv.rs @@ -275,6 +275,7 @@ pub(crate) async fn venv( &build_hasher, exclude_newer, sources, + None, workspace_cache, concurrency, preview,