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,
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

View File

@ -100,6 +100,7 @@ pub struct BuildDispatch<'a> {
source_build_context: SourceBuildContext,
build_extra_env_vars: FxHashMap<OsString, OsString>,
sources: SourceStrategy,
workspace_member_editable: Option<bool>,
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<bool>,
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<bool> {
self.workspace_member_editable
}
fn locations(&self) -> &IndexLocations {
self.index_locations
}

View File

@ -41,6 +41,7 @@ impl BuildRequires {
install_path: &Path,
locations: &IndexLocations,
sources: SourceStrategy,
workspace_member_editable: Option<bool>,
cache: &WorkspaceCache,
) -> Result<Self, MetadataError> {
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<bool>,
) -> Result<Self, MetadataError> {
// 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 {

View File

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

View File

@ -44,6 +44,7 @@ impl LoweredRequirement {
locations: &'data IndexLocations,
workspace: &'data Workspace,
git_member: Option<&'data GitWorkspaceMember<'data>>,
workspace_member_editable: Option<bool>,
) -> impl Iterator<Item = Result<Self, LoweringError>> + 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 {

View File

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

View File

@ -47,6 +47,7 @@ impl RequiresDist {
git_member: Option<&GitWorkspaceMember<'_>>,
locations: &IndexLocations,
sources: SourceStrategy,
workspace_member_editable: Option<bool>,
cache: &WorkspaceCache,
) -> Result<Self, MetadataError> {
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<bool>,
) -> Result<Self, MetadataError> {
// 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,
)?)
}

View File

@ -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?,

View File

@ -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<bool> {
None
}
/// The index locations being searched.
fn locations(&self) -> &IndexLocations;

View File

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

View File

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

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

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

View File

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

View File

@ -1669,6 +1669,7 @@ pub(crate) async fn resolve_names(
concurrency: Concurrency,
cache: &Cache,
workspace_cache: &WorkspaceCache,
workspace_member_editable: Option<bool>,
printer: Printer,
preview: Preview,
) -> Result<Vec<Requirement>, 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<dyn ResolveLogger>,
concurrency: Concurrency,
cache: &Cache,
workspace_member_editable: Option<bool>,
printer: Printer,
preview: Preview,
) -> Result<ResolverOutput, ProjectError> {
@ -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,

View File

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

View File

@ -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,
)

View File

@ -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,
)

View File

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

View File

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