Use non-editable installs for workspace member dependencies of tools

This commit is contained in:
Zanie Blue 2025-10-15 18:10:21 -05:00
parent 0959763a82
commit 561d41dadd
22 changed files with 70 additions and 3 deletions

View File

@ -588,6 +588,7 @@ impl SourceBuild {
install_path, install_path,
locations, locations,
source_strategy, source_strategy,
None,
workspace_cache, workspace_cache,
) )
.await .await
@ -1054,6 +1055,7 @@ async fn create_pep517_build_environment(
install_path, install_path,
locations, locations,
source_strategy, source_strategy,
build_context.workspace_member_editable(),
workspace_cache, workspace_cache,
) )
.await .await

View File

@ -100,6 +100,7 @@ pub struct BuildDispatch<'a> {
source_build_context: SourceBuildContext, source_build_context: SourceBuildContext,
build_extra_env_vars: FxHashMap<OsString, OsString>, build_extra_env_vars: FxHashMap<OsString, OsString>,
sources: SourceStrategy, sources: SourceStrategy,
workspace_member_editable: Option<bool>,
workspace_cache: WorkspaceCache, workspace_cache: WorkspaceCache,
concurrency: Concurrency, concurrency: Concurrency,
preview: Preview, preview: Preview,
@ -126,6 +127,7 @@ impl<'a> BuildDispatch<'a> {
hasher: &'a HashStrategy, hasher: &'a HashStrategy,
exclude_newer: ExcludeNewer, exclude_newer: ExcludeNewer,
sources: SourceStrategy, sources: SourceStrategy,
workspace_member_editable: Option<bool>,
workspace_cache: WorkspaceCache, workspace_cache: WorkspaceCache,
concurrency: Concurrency, concurrency: Concurrency,
preview: Preview, preview: Preview,
@ -152,6 +154,7 @@ impl<'a> BuildDispatch<'a> {
source_build_context: SourceBuildContext::default(), source_build_context: SourceBuildContext::default(),
build_extra_env_vars: FxHashMap::default(), build_extra_env_vars: FxHashMap::default(),
sources, sources,
workspace_member_editable,
workspace_cache, workspace_cache,
concurrency, concurrency,
preview, preview,
@ -222,6 +225,10 @@ impl BuildContext for BuildDispatch<'_> {
self.sources self.sources
} }
fn workspace_member_editable(&self) -> Option<bool> {
self.workspace_member_editable
}
fn locations(&self) -> &IndexLocations { fn locations(&self) -> &IndexLocations {
self.index_locations self.index_locations
} }

View File

@ -41,6 +41,7 @@ impl BuildRequires {
install_path: &Path, install_path: &Path,
locations: &IndexLocations, locations: &IndexLocations,
sources: SourceStrategy, sources: SourceStrategy,
workspace_member_editable: Option<bool>,
cache: &WorkspaceCache, cache: &WorkspaceCache,
) -> Result<Self, MetadataError> { ) -> Result<Self, MetadataError> {
let discovery = match sources { let discovery = match sources {
@ -56,7 +57,7 @@ impl BuildRequires {
return Ok(Self::from_metadata23(metadata)); 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. /// Lower the `build-system.requires` field from a `pyproject.toml` file.
@ -65,6 +66,7 @@ impl BuildRequires {
project_workspace: &ProjectWorkspace, project_workspace: &ProjectWorkspace,
locations: &IndexLocations, locations: &IndexLocations,
source_strategy: SourceStrategy, source_strategy: SourceStrategy,
workspace_member_editable: Option<bool>,
) -> Result<Self, MetadataError> { ) -> Result<Self, MetadataError> {
// Collect any `tool.uv.index` entries. // Collect any `tool.uv.index` entries.
let empty = vec![]; let empty = vec![];
@ -114,6 +116,7 @@ impl BuildRequires {
locations, locations,
project_workspace.workspace(), project_workspace.workspace(),
None, None,
workspace_member_editable,
) )
.map(move |requirement| match requirement { .map(move |requirement| match requirement {
Ok(requirement) => Ok(requirement.into_inner()), Ok(requirement) => Ok(requirement.into_inner()),
@ -186,6 +189,7 @@ impl BuildRequires {
locations, locations,
workspace, workspace,
None, None,
None,
) )
.map(move |requirement| match requirement { .map(move |requirement| match requirement {
Ok(requirement) => Ok(requirement.into_inner()), Ok(requirement) => Ok(requirement.into_inner()),
@ -271,6 +275,7 @@ impl LoweredExtraBuildDependencies {
index_locations, index_locations,
workspace, workspace,
None, None,
None,
) )
.map(move |requirement| { .map(move |requirement| {
match requirement { match requirement {

View File

@ -156,6 +156,7 @@ impl SourcedDependencyGroups {
locations, locations,
project.workspace(), project.workspace(),
git_member, git_member,
None,
) )
.map(move |requirement| match requirement { .map(move |requirement| match requirement {
Ok(requirement) => Ok(requirement.into_inner()), Ok(requirement) => Ok(requirement.into_inner()),

View File

@ -44,6 +44,7 @@ impl LoweredRequirement {
locations: &'data IndexLocations, locations: &'data IndexLocations,
workspace: &'data Workspace, workspace: &'data Workspace,
git_member: Option<&'data GitWorkspaceMember<'data>>, git_member: Option<&'data GitWorkspaceMember<'data>>,
workspace_member_editable: Option<bool>,
) -> impl Iterator<Item = Result<Self, LoweringError>> + use<'data> + 'data { ) -> impl Iterator<Item = Result<Self, LoweringError>> + use<'data> + 'data {
// Identify the source from the `tool.uv.sources` table. // Identify the source from the `tool.uv.sources` table.
let (sources, origin) = if let Some(source) = project_sources.get(&requirement.name) { let (sources, origin) = if let Some(source) = project_sources.get(&requirement.name) {
@ -313,7 +314,7 @@ impl LoweredRequirement {
RequirementSource::Directory { RequirementSource::Directory {
install_path: install_path.into_boxed_path(), install_path: install_path.into_boxed_path(),
url, url,
editable: Some(editability.unwrap_or(true)), editable: Some(editability.unwrap_or(workspace_member_editable.unwrap_or(true))),
r#virtual: Some(false), r#virtual: Some(false),
} }
} else { } else {

View File

@ -90,6 +90,7 @@ impl Metadata {
git_source: Option<&GitWorkspaceMember<'_>>, git_source: Option<&GitWorkspaceMember<'_>>,
locations: &IndexLocations, locations: &IndexLocations,
sources: SourceStrategy, sources: SourceStrategy,
workspace_member_editable: Option<bool>,
cache: &WorkspaceCache, cache: &WorkspaceCache,
) -> Result<Self, MetadataError> { ) -> Result<Self, MetadataError> {
// Lower the requirements. // Lower the requirements.
@ -111,6 +112,7 @@ impl Metadata {
git_source, git_source,
locations, locations,
sources, sources,
workspace_member_editable,
cache, cache,
) )
.await?; .await?;

View File

@ -47,6 +47,7 @@ impl RequiresDist {
git_member: Option<&GitWorkspaceMember<'_>>, git_member: Option<&GitWorkspaceMember<'_>>,
locations: &IndexLocations, locations: &IndexLocations,
sources: SourceStrategy, sources: SourceStrategy,
workspace_member_editable: Option<bool>,
cache: &WorkspaceCache, cache: &WorkspaceCache,
) -> Result<Self, MetadataError> { ) -> Result<Self, MetadataError> {
let discovery = DiscoveryOptions { let discovery = DiscoveryOptions {
@ -69,7 +70,7 @@ impl RequiresDist {
return Ok(Self::from_metadata23(metadata)); 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( fn from_project_workspace(
@ -78,6 +79,7 @@ impl RequiresDist {
git_member: Option<&GitWorkspaceMember<'_>>, git_member: Option<&GitWorkspaceMember<'_>>,
locations: &IndexLocations, locations: &IndexLocations,
source_strategy: SourceStrategy, source_strategy: SourceStrategy,
workspace_member_editable: Option<bool>,
) -> Result<Self, MetadataError> { ) -> Result<Self, MetadataError> {
// Collect any `tool.uv.index` entries. // Collect any `tool.uv.index` entries.
let empty = vec![]; let empty = vec![];
@ -140,6 +142,7 @@ impl RequiresDist {
locations, locations,
project_workspace.workspace(), project_workspace.workspace(),
git_member, git_member,
workspace_member_editable,
) )
.map( .map(
move |requirement| match requirement { move |requirement| match requirement {
@ -182,6 +185,7 @@ impl RequiresDist {
locations, locations,
project_workspace.workspace(), project_workspace.workspace(),
git_member, git_member,
workspace_member_editable,
) )
.map(move |requirement| match requirement { .map(move |requirement| match requirement {
Ok(requirement) => Ok(requirement.into_inner()), Ok(requirement) => Ok(requirement.into_inner()),
@ -468,6 +472,7 @@ mod test {
None, None,
&IndexLocations::default(), &IndexLocations::default(),
SourceStrategy::default(), SourceStrategy::default(),
None,
)?) )?)
} }

View File

@ -1264,6 +1264,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
None, None,
self.build_context.locations(), self.build_context.locations(),
self.build_context.sources(), self.build_context.sources(),
self.build_context.workspace_member_editable(),
self.build_context.workspace_cache(), self.build_context.workspace_cache(),
) )
.await?, .await?,
@ -1317,6 +1318,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
None, None,
self.build_context.locations(), self.build_context.locations(),
self.build_context.sources(), self.build_context.sources(),
self.build_context.workspace_member_editable(),
self.build_context.workspace_cache(), self.build_context.workspace_cache(),
) )
.await?, .await?,
@ -1366,6 +1368,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
None, None,
self.build_context.locations(), self.build_context.locations(),
self.build_context.sources(), self.build_context.sources(),
self.build_context.workspace_member_editable(),
self.build_context.workspace_cache(), self.build_context.workspace_cache(),
) )
.await?, .await?,
@ -1427,6 +1430,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
None, None,
self.build_context.locations(), self.build_context.locations(),
self.build_context.sources(), self.build_context.sources(),
self.build_context.workspace_member_editable(),
self.build_context.workspace_cache(), self.build_context.workspace_cache(),
) )
.await?, .await?,
@ -1501,6 +1505,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
None, None,
self.build_context.locations(), self.build_context.locations(),
self.build_context.sources(), self.build_context.sources(),
self.build_context.workspace_member_editable(),
self.build_context.workspace_cache(), self.build_context.workspace_cache(),
) )
.await?; .await?;
@ -1801,6 +1806,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
Some(&git_member), Some(&git_member),
self.build_context.locations(), self.build_context.locations(),
self.build_context.sources(), self.build_context.sources(),
self.build_context.workspace_member_editable(),
self.build_context.workspace_cache(), self.build_context.workspace_cache(),
) )
.await?, .await?,
@ -1834,6 +1840,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
Some(&git_member), Some(&git_member),
self.build_context.locations(), self.build_context.locations(),
self.build_context.sources(), self.build_context.sources(),
self.build_context.workspace_member_editable(),
self.build_context.workspace_cache(), self.build_context.workspace_cache(),
) )
.await?, .await?,
@ -1886,6 +1893,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
Some(&git_member), Some(&git_member),
self.build_context.locations(), self.build_context.locations(),
self.build_context.sources(), self.build_context.sources(),
self.build_context.workspace_member_editable(),
self.build_context.workspace_cache(), self.build_context.workspace_cache(),
) )
.await?, .await?,
@ -1947,6 +1955,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
Some(&git_member), Some(&git_member),
self.build_context.locations(), self.build_context.locations(),
self.build_context.sources(), self.build_context.sources(),
self.build_context.workspace_member_editable(),
self.build_context.workspace_cache(), self.build_context.workspace_cache(),
) )
.await?, .await?,

View File

@ -99,6 +99,15 @@ pub trait BuildContext {
/// Whether to incorporate `tool.uv.sources` when resolving requirements. /// Whether to incorporate `tool.uv.sources` when resolving requirements.
fn sources(&self) -> SourceStrategy; 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<bool> {
None
}
/// The index locations being searched. /// The index locations being searched.
fn locations(&self) -> &IndexLocations; fn locations(&self) -> &IndexLocations;

View File

@ -612,6 +612,7 @@ async fn build_package(
&hasher, &hasher,
exclude_newer, exclude_newer,
sources, sources,
None,
workspace_cache, workspace_cache,
concurrency, concurrency,
preview, preview,

View File

@ -511,6 +511,7 @@ pub(crate) async fn pip_compile(
&build_hashes, &build_hashes,
exclude_newer.clone(), exclude_newer.clone(),
sources, sources,
None,
WorkspaceCache::default(), WorkspaceCache::default(),
concurrency, concurrency,
preview, preview,

View File

@ -478,6 +478,7 @@ pub(crate) async fn pip_install(
&build_hasher, &build_hasher,
exclude_newer.clone(), exclude_newer.clone(),
sources, sources,
None,
WorkspaceCache::default(), WorkspaceCache::default(),
concurrency, concurrency,
preview, preview,
@ -613,6 +614,7 @@ pub(crate) async fn pip_install(
&build_hasher, &build_hasher,
exclude_newer.clone(), exclude_newer.clone(),
sources, sources,
None,
WorkspaceCache::default(), WorkspaceCache::default(),
concurrency, concurrency,
preview, preview,

View File

@ -387,6 +387,7 @@ pub(crate) async fn pip_sync(
&build_hasher, &build_hasher,
exclude_newer.clone(), exclude_newer.clone(),
sources, sources,
None,
WorkspaceCache::default(), WorkspaceCache::default(),
concurrency, concurrency,
preview, preview,
@ -524,6 +525,7 @@ pub(crate) async fn pip_sync(
&build_hasher, &build_hasher,
exclude_newer.clone(), exclude_newer.clone(),
sources, sources,
None,
WorkspaceCache::default(), WorkspaceCache::default(),
concurrency, concurrency,
preview, preview,

View File

@ -467,6 +467,7 @@ pub(crate) async fn add(
&build_hasher, &build_hasher,
settings.resolver.exclude_newer.clone(), settings.resolver.exclude_newer.clone(),
sources, sources,
None,
// No workspace caching since `uv add` changes the workspace definition. // No workspace caching since `uv add` changes the workspace definition.
WorkspaceCache::default(), WorkspaceCache::default(),
concurrency, concurrency,

View File

@ -139,6 +139,7 @@ impl CachedEnvironment {
resolve, resolve,
concurrency, concurrency,
cache, cache,
None,
printer, printer,
preview, preview,
) )

View File

@ -734,6 +734,7 @@ async fn do_lock(
&build_hasher, &build_hasher,
exclude_newer.clone(), exclude_newer.clone(),
*sources, *sources,
None,
workspace_cache.clone(), workspace_cache.clone(),
concurrency, concurrency,
preview, preview,

View File

@ -1669,6 +1669,7 @@ pub(crate) async fn resolve_names(
concurrency: Concurrency, concurrency: Concurrency,
cache: &Cache, cache: &Cache,
workspace_cache: &WorkspaceCache, workspace_cache: &WorkspaceCache,
workspace_member_editable: Option<bool>,
printer: Printer, printer: Printer,
preview: Preview, preview: Preview,
) -> Result<Vec<Requirement>, uv_requirements::Error> { ) -> Result<Vec<Requirement>, uv_requirements::Error> {
@ -1771,6 +1772,7 @@ pub(crate) async fn resolve_names(
&build_hasher, &build_hasher,
exclude_newer.clone(), exclude_newer.clone(),
*sources, *sources,
workspace_member_editable,
workspace_cache.clone(), workspace_cache.clone(),
concurrency, concurrency,
preview, preview,
@ -1842,6 +1844,7 @@ pub(crate) async fn resolve_environment(
logger: Box<dyn ResolveLogger>, logger: Box<dyn ResolveLogger>,
concurrency: Concurrency, concurrency: Concurrency,
cache: &Cache, cache: &Cache,
workspace_member_editable: Option<bool>,
printer: Printer, printer: Printer,
preview: Preview, preview: Preview,
) -> Result<ResolverOutput, ProjectError> { ) -> Result<ResolverOutput, ProjectError> {
@ -1982,6 +1985,7 @@ pub(crate) async fn resolve_environment(
&build_hasher, &build_hasher,
exclude_newer.clone(), exclude_newer.clone(),
*sources, *sources,
workspace_member_editable,
workspace_cache, workspace_cache,
concurrency, concurrency,
preview, preview,
@ -2120,6 +2124,7 @@ pub(crate) async fn sync_environment(
&build_hasher, &build_hasher,
exclude_newer.clone(), exclude_newer.clone(),
sources, sources,
None,
workspace_cache, workspace_cache,
concurrency, concurrency,
preview, preview,
@ -2351,6 +2356,7 @@ pub(crate) async fn update_environment(
&build_hasher, &build_hasher,
exclude_newer.clone(), exclude_newer.clone(),
*sources, *sources,
None,
workspace_cache, workspace_cache,
concurrency, concurrency,
preview, preview,

View File

@ -777,6 +777,7 @@ pub(super) async fn do_sync(
&build_hasher, &build_hasher,
exclude_newer.clone(), exclude_newer.clone(),
sources, sources,
None,
workspace_cache.clone(), workspace_cache.clone(),
concurrency, concurrency,
preview, preview,

View File

@ -145,6 +145,7 @@ pub(crate) async fn install(
concurrency, concurrency,
&cache, &cache,
&workspace_cache, &workspace_cache,
Some(false),
printer, printer,
preview, preview,
) )
@ -270,6 +271,7 @@ pub(crate) async fn install(
concurrency, concurrency,
&cache, &cache,
&workspace_cache, &workspace_cache,
Some(false),
printer, printer,
preview, preview,
) )
@ -295,6 +297,7 @@ pub(crate) async fn install(
concurrency, concurrency,
&cache, &cache,
&workspace_cache, &workspace_cache,
Some(false),
printer, printer,
preview, preview,
) )
@ -516,6 +519,7 @@ pub(crate) async fn install(
Box::new(DefaultResolveLogger), Box::new(DefaultResolveLogger),
concurrency, concurrency,
&cache, &cache,
Some(false),
printer, printer,
preview, preview,
) )
@ -571,6 +575,7 @@ pub(crate) async fn install(
Box::new(DefaultResolveLogger), Box::new(DefaultResolveLogger),
concurrency, concurrency,
&cache, &cache,
Some(false),
printer, printer,
preview, preview,
) )

View File

@ -801,6 +801,7 @@ async fn get_or_create_environment(
concurrency, concurrency,
cache, cache,
&workspace_cache, &workspace_cache,
None,
printer, printer,
preview, preview,
) )
@ -892,6 +893,7 @@ async fn get_or_create_environment(
concurrency, concurrency,
cache, cache,
&workspace_cache, &workspace_cache,
None,
printer, printer,
preview, preview,
) )
@ -918,6 +920,7 @@ async fn get_or_create_environment(
concurrency, concurrency,
cache, cache,
&workspace_cache, &workspace_cache,
None,
printer, printer,
preview, preview,
) )

View File

@ -355,6 +355,7 @@ async fn upgrade_tool(
Box::new(SummaryResolveLogger), Box::new(SummaryResolveLogger),
concurrency, concurrency,
cache, cache,
None,
printer, printer,
preview, preview,
) )

View File

@ -275,6 +275,7 @@ pub(crate) async fn venv(
&build_hasher, &build_hasher,
exclude_newer, exclude_newer,
sources, sources,
None,
workspace_cache, workspace_cache,
concurrency, concurrency,
preview, preview,