mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-04 10:58:28 +00:00
Respect named --index
and --default-index
values in tool.uv.sources
(#7910)
## Summary If you pass a named index via the CLI, you can now reference it as a named source. This required some surprisingly large refactors, since we now need to be able to track whether a given index was provided on the CLI vs. elsewhere (since, e.g., we don't want users to be able to reference named indexes defined in global configuration). Closes https://github.com/astral-sh/uv/issues/7899.
This commit is contained in:
parent
a034a8b83b
commit
2153c6ac0d
23 changed files with 370 additions and 133 deletions
|
@ -6,7 +6,7 @@ use thiserror::Error;
|
|||
use url::Url;
|
||||
|
||||
use uv_distribution_filename::DistExtension;
|
||||
use uv_distribution_types::Index;
|
||||
use uv_distribution_types::{Index, IndexLocations, Origin};
|
||||
use uv_git::GitReference;
|
||||
use uv_normalize::PackageName;
|
||||
use uv_pep440::VersionSpecifiers;
|
||||
|
@ -20,7 +20,7 @@ use uv_workspace::Workspace;
|
|||
pub struct LoweredRequirement(Requirement);
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum Origin {
|
||||
enum RequirementOrigin {
|
||||
/// The `tool.uv.sources` were read from the project.
|
||||
Project,
|
||||
/// The `tool.uv.sources` were read from the workspace root.
|
||||
|
@ -35,15 +35,16 @@ impl LoweredRequirement {
|
|||
project_dir: &'data Path,
|
||||
project_sources: &'data BTreeMap<PackageName, Sources>,
|
||||
project_indexes: &'data [Index],
|
||||
locations: &'data IndexLocations,
|
||||
workspace: &'data Workspace,
|
||||
lower_bound: LowerBound,
|
||||
) -> impl Iterator<Item = Result<LoweredRequirement, LoweringError>> + 'data {
|
||||
) -> impl Iterator<Item = Result<Self, LoweringError>> + 'data {
|
||||
let (source, origin) = if let Some(source) = project_sources.get(&requirement.name) {
|
||||
(Some(source), Origin::Project)
|
||||
(Some(source), RequirementOrigin::Project)
|
||||
} else if let Some(source) = workspace.sources().get(&requirement.name) {
|
||||
(Some(source), Origin::Workspace)
|
||||
(Some(source), RequirementOrigin::Workspace)
|
||||
} else {
|
||||
(None, Origin::Project)
|
||||
(None, RequirementOrigin::Project)
|
||||
};
|
||||
let source = source.cloned();
|
||||
|
||||
|
@ -155,16 +156,14 @@ impl LoweredRequirement {
|
|||
Source::Registry { index, marker } => {
|
||||
// Identify the named index from either the project indexes or the workspace indexes,
|
||||
// in that order.
|
||||
let Some(index) = project_indexes
|
||||
.iter()
|
||||
let Some(index) = locations
|
||||
.indexes()
|
||||
.filter(|index| matches!(index.origin, Some(Origin::Cli)))
|
||||
.chain(project_indexes.iter())
|
||||
.chain(workspace.indexes().iter())
|
||||
.find(|Index { name, .. }| {
|
||||
name.as_ref().is_some_and(|name| *name == index)
|
||||
})
|
||||
.or_else(|| {
|
||||
workspace.indexes().iter().find(|Index { name, .. }| {
|
||||
name.as_ref().is_some_and(|name| *name == index)
|
||||
})
|
||||
})
|
||||
.map(|Index { url: index, .. }| index.clone())
|
||||
else {
|
||||
return Err(LoweringError::MissingIndex(
|
||||
|
@ -260,7 +259,8 @@ impl LoweredRequirement {
|
|||
dir: &'data Path,
|
||||
sources: &'data BTreeMap<PackageName, Sources>,
|
||||
indexes: &'data [Index],
|
||||
) -> impl Iterator<Item = Result<LoweredRequirement, LoweringError>> + 'data {
|
||||
locations: &'data IndexLocations,
|
||||
) -> impl Iterator<Item = Result<Self, LoweringError>> + 'data {
|
||||
let source = sources.get(&requirement.name).cloned();
|
||||
|
||||
let Some(source) = source else {
|
||||
|
@ -332,7 +332,7 @@ impl LoweredRequirement {
|
|||
}
|
||||
let source = path_source(
|
||||
PathBuf::from(path),
|
||||
Origin::Project,
|
||||
RequirementOrigin::Project,
|
||||
dir,
|
||||
dir,
|
||||
editable.unwrap_or(false),
|
||||
|
@ -340,8 +340,10 @@ impl LoweredRequirement {
|
|||
(source, marker)
|
||||
}
|
||||
Source::Registry { index, marker } => {
|
||||
let Some(index) = indexes
|
||||
.iter()
|
||||
let Some(index) = locations
|
||||
.indexes()
|
||||
.filter(|index| matches!(index.origin, Some(Origin::Cli)))
|
||||
.chain(indexes.iter())
|
||||
.find(|Index { name, .. }| {
|
||||
name.as_ref().is_some_and(|name| *name == index)
|
||||
})
|
||||
|
@ -508,15 +510,15 @@ fn registry_source(
|
|||
/// Convert a path string to a file or directory source.
|
||||
fn path_source(
|
||||
path: impl AsRef<Path>,
|
||||
origin: Origin,
|
||||
origin: RequirementOrigin,
|
||||
project_dir: &Path,
|
||||
workspace_root: &Path,
|
||||
editable: bool,
|
||||
) -> Result<RequirementSource, LoweringError> {
|
||||
let path = path.as_ref();
|
||||
let base = match origin {
|
||||
Origin::Project => project_dir,
|
||||
Origin::Workspace => workspace_root,
|
||||
RequirementOrigin::Project => project_dir,
|
||||
RequirementOrigin::Workspace => workspace_root,
|
||||
};
|
||||
let url = VerbatimUrl::from_path(path, base)?.with_given(path.to_string_lossy());
|
||||
let install_path = url.to_file_path().map_err(|()| {
|
||||
|
|
|
@ -4,6 +4,7 @@ use std::path::Path;
|
|||
use thiserror::Error;
|
||||
|
||||
use uv_configuration::SourceStrategy;
|
||||
use uv_distribution_types::IndexLocations;
|
||||
use uv_normalize::{ExtraName, GroupName, PackageName};
|
||||
use uv_pep440::{Version, VersionSpecifiers};
|
||||
use uv_pypi_types::{HashDigest, ResolutionMetadata};
|
||||
|
@ -61,6 +62,7 @@ impl Metadata {
|
|||
pub async fn from_workspace(
|
||||
metadata: ResolutionMetadata,
|
||||
install_path: &Path,
|
||||
locations: &IndexLocations,
|
||||
sources: SourceStrategy,
|
||||
) -> Result<Self, MetadataError> {
|
||||
// Lower the requirements.
|
||||
|
@ -76,6 +78,7 @@ impl Metadata {
|
|||
provides_extras: metadata.provides_extras,
|
||||
},
|
||||
install_path,
|
||||
locations,
|
||||
sources,
|
||||
LowerBound::Warn,
|
||||
)
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use crate::metadata::lowering::LowerBound;
|
||||
use crate::metadata::{LoweredRequirement, MetadataError};
|
||||
use crate::Metadata;
|
||||
|
||||
use crate::metadata::lowering::LowerBound;
|
||||
use std::collections::BTreeMap;
|
||||
use std::path::Path;
|
||||
use uv_configuration::SourceStrategy;
|
||||
use uv_distribution_types::IndexLocations;
|
||||
use uv_normalize::{ExtraName, GroupName, PackageName, DEV_DEPENDENCIES};
|
||||
use uv_workspace::pyproject::ToolUvSources;
|
||||
use uv_workspace::{DiscoveryOptions, ProjectWorkspace};
|
||||
|
@ -38,6 +39,7 @@ impl RequiresDist {
|
|||
pub async fn from_project_maybe_workspace(
|
||||
metadata: uv_pypi_types::RequiresDist,
|
||||
install_path: &Path,
|
||||
locations: &IndexLocations,
|
||||
sources: SourceStrategy,
|
||||
lower_bound: LowerBound,
|
||||
) -> Result<Self, MetadataError> {
|
||||
|
@ -50,18 +52,25 @@ impl RequiresDist {
|
|||
return Ok(Self::from_metadata23(metadata));
|
||||
};
|
||||
|
||||
Self::from_project_workspace(metadata, &project_workspace, sources, lower_bound)
|
||||
Self::from_project_workspace(
|
||||
metadata,
|
||||
&project_workspace,
|
||||
locations,
|
||||
sources,
|
||||
lower_bound,
|
||||
)
|
||||
}
|
||||
|
||||
fn from_project_workspace(
|
||||
metadata: uv_pypi_types::RequiresDist,
|
||||
project_workspace: &ProjectWorkspace,
|
||||
locations: &IndexLocations,
|
||||
source_strategy: SourceStrategy,
|
||||
lower_bound: LowerBound,
|
||||
) -> Result<Self, MetadataError> {
|
||||
// Collect any `tool.uv.index` entries.
|
||||
let empty = vec![];
|
||||
let indexes = match source_strategy {
|
||||
let project_indexes = match source_strategy {
|
||||
SourceStrategy::Enabled => project_workspace
|
||||
.current_project()
|
||||
.pyproject_toml()
|
||||
|
@ -75,7 +84,7 @@ impl RequiresDist {
|
|||
|
||||
// Collect any `tool.uv.sources` and `tool.uv.dev_dependencies` from `pyproject.toml`.
|
||||
let empty = BTreeMap::default();
|
||||
let sources = match source_strategy {
|
||||
let project_sources = match source_strategy {
|
||||
SourceStrategy::Enabled => project_workspace
|
||||
.current_project()
|
||||
.pyproject_toml()
|
||||
|
@ -107,8 +116,9 @@ impl RequiresDist {
|
|||
requirement,
|
||||
&metadata.name,
|
||||
project_workspace.project_root(),
|
||||
sources,
|
||||
indexes,
|
||||
project_sources,
|
||||
project_indexes,
|
||||
locations,
|
||||
project_workspace.workspace(),
|
||||
lower_bound,
|
||||
)
|
||||
|
@ -141,8 +151,9 @@ impl RequiresDist {
|
|||
requirement,
|
||||
&metadata.name,
|
||||
project_workspace.project_root(),
|
||||
sources,
|
||||
indexes,
|
||||
project_sources,
|
||||
project_indexes,
|
||||
locations,
|
||||
project_workspace.workspace(),
|
||||
lower_bound,
|
||||
)
|
||||
|
@ -188,6 +199,7 @@ mod test {
|
|||
use indoc::indoc;
|
||||
use insta::assert_snapshot;
|
||||
use uv_configuration::SourceStrategy;
|
||||
use uv_distribution_types::IndexLocations;
|
||||
use uv_workspace::pyproject::PyProjectToml;
|
||||
use uv_workspace::{DiscoveryOptions, ProjectWorkspace};
|
||||
|
||||
|
@ -214,7 +226,8 @@ mod test {
|
|||
Ok(RequiresDist::from_project_workspace(
|
||||
requires_dist,
|
||||
&project_workspace,
|
||||
SourceStrategy::Enabled,
|
||||
&IndexLocations::default(),
|
||||
SourceStrategy::default(),
|
||||
LowerBound::Warn,
|
||||
)?)
|
||||
}
|
||||
|
|
|
@ -388,6 +388,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
|
|||
let requires_dist = RequiresDist::from_project_maybe_workspace(
|
||||
requires_dist,
|
||||
project_root,
|
||||
self.build_context.locations(),
|
||||
self.build_context.sources(),
|
||||
LowerBound::Warn,
|
||||
)
|
||||
|
@ -1086,6 +1087,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
|
|||
Metadata::from_workspace(
|
||||
metadata,
|
||||
resource.install_path.as_ref(),
|
||||
self.build_context.locations(),
|
||||
self.build_context.sources(),
|
||||
)
|
||||
.await?,
|
||||
|
@ -1120,6 +1122,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
|
|||
Metadata::from_workspace(
|
||||
metadata,
|
||||
resource.install_path.as_ref(),
|
||||
self.build_context.locations(),
|
||||
self.build_context.sources(),
|
||||
)
|
||||
.await?,
|
||||
|
@ -1149,6 +1152,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
|
|||
Metadata::from_workspace(
|
||||
metadata,
|
||||
resource.install_path.as_ref(),
|
||||
self.build_context.locations(),
|
||||
self.build_context.sources(),
|
||||
)
|
||||
.await?,
|
||||
|
@ -1194,6 +1198,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
|
|||
Metadata::from_workspace(
|
||||
metadata,
|
||||
resource.install_path.as_ref(),
|
||||
self.build_context.locations(),
|
||||
self.build_context.sources(),
|
||||
)
|
||||
.await?,
|
||||
|
@ -1374,7 +1379,13 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
|
|||
Self::read_static_metadata(source, fetch.path(), resource.subdirectory).await?
|
||||
{
|
||||
return Ok(ArchiveMetadata::from(
|
||||
Metadata::from_workspace(metadata, &path, self.build_context.sources()).await?,
|
||||
Metadata::from_workspace(
|
||||
metadata,
|
||||
&path,
|
||||
self.build_context.locations(),
|
||||
self.build_context.sources(),
|
||||
)
|
||||
.await?,
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -1395,7 +1406,13 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
|
|||
|
||||
debug!("Using cached metadata for: {source}");
|
||||
return Ok(ArchiveMetadata::from(
|
||||
Metadata::from_workspace(metadata, &path, self.build_context.sources()).await?,
|
||||
Metadata::from_workspace(
|
||||
metadata,
|
||||
&path,
|
||||
self.build_context.locations(),
|
||||
self.build_context.sources(),
|
||||
)
|
||||
.await?,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -1420,7 +1437,13 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
|
|||
.map_err(Error::CacheWrite)?;
|
||||
|
||||
return Ok(ArchiveMetadata::from(
|
||||
Metadata::from_workspace(metadata, &path, self.build_context.sources()).await?,
|
||||
Metadata::from_workspace(
|
||||
metadata,
|
||||
&path,
|
||||
self.build_context.locations(),
|
||||
self.build_context.sources(),
|
||||
)
|
||||
.await?,
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -1460,7 +1483,13 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
|
|||
.map_err(Error::CacheWrite)?;
|
||||
|
||||
Ok(ArchiveMetadata::from(
|
||||
Metadata::from_workspace(metadata, fetch.path(), self.build_context.sources()).await?,
|
||||
Metadata::from_workspace(
|
||||
metadata,
|
||||
fetch.path(),
|
||||
self.build_context.locations(),
|
||||
self.build_context.sources(),
|
||||
)
|
||||
.await?,
|
||||
))
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue