Skip to content

Commit

Permalink
Respect named --index and --default-index values in `tool.uv.sour…
Browse files Browse the repository at this point in the history
…ces` (#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 #7899.
  • Loading branch information
charliermarsh authored Oct 15, 2024
1 parent a034a8b commit 2153c6a
Show file tree
Hide file tree
Showing 23 changed files with 370 additions and 133 deletions.
9 changes: 8 additions & 1 deletion crates/uv-build-frontend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use tracing::{debug, info_span, instrument, Instrument};
pub use crate::error::{Error, MissingHeaderCause};
use uv_configuration::{BuildKind, BuildOutput, ConfigSettings, SourceStrategy};
use uv_distribution::{LowerBound, RequiresDist};
use uv_distribution_types::Resolution;
use uv_distribution_types::{IndexLocations, Resolution};
use uv_fs::{rename_with_retry, PythonExt, Simplified};
use uv_pep440::Version;
use uv_pep508::PackageName;
Expand Down Expand Up @@ -250,6 +250,7 @@ impl SourceBuild {
build_context: &impl BuildContext,
source_build_context: SourceBuildContext,
version_id: Option<String>,
locations: &IndexLocations,
source_strategy: SourceStrategy,
config_settings: ConfigSettings,
build_isolation: BuildIsolation<'_>,
Expand All @@ -272,6 +273,7 @@ impl SourceBuild {
let (pep517_backend, project) = Self::extract_pep517_backend(
&source_tree,
fallback_package_name,
locations,
source_strategy,
&default_backend,
)
Expand Down Expand Up @@ -371,6 +373,7 @@ impl SourceBuild {
package_name.as_ref(),
package_version.as_ref(),
version_id.as_deref(),
locations,
source_strategy,
build_kind,
level,
Expand Down Expand Up @@ -433,6 +436,7 @@ impl SourceBuild {
async fn extract_pep517_backend(
source_tree: &Path,
package_name: Option<&PackageName>,
locations: &IndexLocations,
source_strategy: SourceStrategy,
default_backend: &Pep517Backend,
) -> Result<(Pep517Backend, Option<Project>), Box<Error>> {
Expand Down Expand Up @@ -465,6 +469,7 @@ impl SourceBuild {
let requires_dist = RequiresDist::from_project_maybe_workspace(
requires_dist,
source_tree,
locations,
source_strategy,
LowerBound::Allow,
)
Expand Down Expand Up @@ -803,6 +808,7 @@ async fn create_pep517_build_environment(
package_name: Option<&PackageName>,
package_version: Option<&Version>,
version_id: Option<&str>,
locations: &IndexLocations,
source_strategy: SourceStrategy,
build_kind: BuildKind,
level: BuildOutput,
Expand Down Expand Up @@ -915,6 +921,7 @@ async fn create_pep517_build_environment(
let requires_dist = RequiresDist::from_project_maybe_workspace(
requires_dist,
source_tree,
locations,
source_strategy,
LowerBound::Allow,
)
Expand Down
77 changes: 59 additions & 18 deletions crates/uv-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use uv_configuration::{
ConfigSettingEntry, ExportFormat, IndexStrategy, KeyringProviderType, PackageNameSpecifier,
TargetTriple, TrustedHost, TrustedPublishing, VersionControlSystem,
};
use uv_distribution_types::{Index, IndexUrl};
use uv_distribution_types::{Index, IndexUrl, Origin, PipExtraIndex, PipFindLinks, PipIndex};
use uv_normalize::{ExtraName, PackageName};
use uv_pep508::Requirement;
use uv_pypi_types::VerbatimParsedUrl;
Expand Down Expand Up @@ -774,41 +774,82 @@ impl<T> Maybe<T> {
}
}

/// Parse a string into an [`IndexUrl`], mapping the empty string to `None`.
fn parse_index_url(input: &str) -> Result<Maybe<IndexUrl>, String> {
/// Parse an `--index-url` argument into an [`PipIndex`], mapping the empty string to `None`.
fn parse_index_url(input: &str) -> Result<Maybe<PipIndex>, String> {
if input.is_empty() {
Ok(Maybe::None)
} else {
match IndexUrl::from_str(input) {
Ok(url) => Ok(Maybe::Some(url)),
Err(err) => Err(err.to_string()),
}
IndexUrl::from_str(input)
.map(Index::from_index_url)
.map(|index| Index {
origin: Some(Origin::Cli),
..index
})
.map(PipIndex::from)
.map(Maybe::Some)
.map_err(|err| err.to_string())
}
}

/// Parse an `--extra-index-url` argument into an [`PipExtraIndex`], mapping the empty string to `None`.
fn parse_extra_index_url(input: &str) -> Result<Maybe<PipExtraIndex>, String> {
if input.is_empty() {
Ok(Maybe::None)
} else {
IndexUrl::from_str(input)
.map(Index::from_extra_index_url)
.map(|index| Index {
origin: Some(Origin::Cli),
..index
})
.map(PipExtraIndex::from)
.map(Maybe::Some)
.map_err(|err| err.to_string())
}
}

/// Parse a `--find-links` argument into an [`PipFindLinks`], mapping the empty string to `None`.
fn parse_find_links(input: &str) -> Result<Maybe<PipFindLinks>, String> {
if input.is_empty() {
Ok(Maybe::None)
} else {
IndexUrl::from_str(input)
.map(Index::from_find_links)
.map(|index| Index {
origin: Some(Origin::Cli),
..index
})
.map(PipFindLinks::from)
.map(Maybe::Some)
.map_err(|err| err.to_string())
}
}

/// Parse a string into an [`Index`], mapping the empty string to `None`.
fn parse_index_source(input: &str) -> Result<Maybe<Index>, String> {
/// Parse an `--index` argument into an [`Index`], mapping the empty string to `None`.
fn parse_index(input: &str) -> Result<Maybe<Index>, String> {
if input.is_empty() {
Ok(Maybe::None)
} else {
match Index::from_str(input) {
Ok(index) => Ok(Maybe::Some(Index {
default: false,
origin: Some(Origin::Cli),
..index
})),
Err(err) => Err(err.to_string()),
}
}
}

/// Parse a string into an [`Index`], mapping the empty string to `None`.
fn parse_default_index_source(input: &str) -> Result<Maybe<Index>, String> {
/// Parse a `--default-index` argument into an [`Index`], mapping the empty string to `None`.
fn parse_default_index(input: &str) -> Result<Maybe<Index>, String> {
if input.is_empty() {
Ok(Maybe::None)
} else {
match Index::from_str(input) {
Ok(index) => Ok(Maybe::Some(Index {
default: true,
origin: Some(Origin::Cli),
..index
})),
Err(err) => Err(err.to_string()),
Expand Down Expand Up @@ -3839,7 +3880,7 @@ pub struct IndexArgs {
/// All indexes provided via this flag take priority over the index specified by
/// `--default-index` (which defaults to PyPI). When multiple `--index` flags are
/// provided, earlier values take priority.
#[arg(long, env = "UV_INDEX", value_delimiter = ' ', value_parser = parse_index_source, help_heading = "Index options")]
#[arg(long, env = "UV_INDEX", value_delimiter = ' ', value_parser = parse_index, help_heading = "Index options")]
pub index: Option<Vec<Maybe<Index>>>,

/// The URL of the default package index (by default: <https://pypi.org/simple>).
Expand All @@ -3849,7 +3890,7 @@ pub struct IndexArgs {
///
/// The index given by this flag is given lower priority than all other indexes specified via
/// the `--index` flag.
#[arg(long, env = "UV_DEFAULT_INDEX", value_parser = parse_default_index_source, help_heading = "Index options")]
#[arg(long, env = "UV_DEFAULT_INDEX", value_parser = parse_default_index, help_heading = "Index options")]
pub default_index: Option<Maybe<Index>>,

/// (Deprecated: use `--default-index` instead) The URL of the Python package index (by default: <https://pypi.org/simple>).
Expand All @@ -3860,7 +3901,7 @@ pub struct IndexArgs {
/// The index given by this flag is given lower priority than all other
/// indexes specified via the `--extra-index-url` flag.
#[arg(long, short, env = EnvVars::UV_INDEX_URL, value_parser = parse_index_url, help_heading = "Index options")]
pub index_url: Option<Maybe<IndexUrl>>,
pub index_url: Option<Maybe<PipIndex>>,

/// (Deprecated: use `--index` instead) Extra URLs of package indexes to use, in addition to `--index-url`.
///
Expand All @@ -3870,8 +3911,8 @@ pub struct IndexArgs {
/// All indexes provided via this flag take priority over the index specified by
/// `--index-url` (which defaults to PyPI). When multiple `--extra-index-url` flags are
/// provided, earlier values take priority.
#[arg(long, env = EnvVars::UV_EXTRA_INDEX_URL, value_delimiter = ' ', value_parser = parse_index_url, help_heading = "Index options")]
pub extra_index_url: Option<Vec<Maybe<IndexUrl>>>,
#[arg(long, env = EnvVars::UV_EXTRA_INDEX_URL, value_delimiter = ' ', value_parser = parse_extra_index_url, help_heading = "Index options")]
pub extra_index_url: Option<Vec<Maybe<PipExtraIndex>>>,

/// Locations to search for candidate distributions, in addition to those found in the registry
/// indexes.
Expand All @@ -3885,10 +3926,10 @@ pub struct IndexArgs {
long,
short,
env = EnvVars::UV_FIND_LINKS,
value_parser = parse_index_url,
value_parser = parse_find_links,
help_heading = "Index options"
)]
pub find_links: Option<Vec<Maybe<IndexUrl>>>,
pub find_links: Option<Vec<Maybe<PipFindLinks>>>,

/// Ignore the registry index (e.g., PyPI), instead relying on direct URL dependencies and those
/// provided via `--find-links`.
Expand Down
9 changes: 6 additions & 3 deletions crates/uv-cli/src/options.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use uv_cache::Refresh;
use uv_configuration::ConfigSettings;
use uv_distribution_types::{PipExtraIndex, PipFindLinks, PipIndex};
use uv_resolver::PrereleaseMode;
use uv_settings::{Combine, PipOptions, ResolverInstallerOptions, ResolverOptions};

Expand Down Expand Up @@ -201,18 +202,20 @@ impl From<IndexArgs> for PipOptions {
.combine(
index.map(|index| index.into_iter().filter_map(Maybe::into_option).collect()),
),
index_url: index_url.and_then(Maybe::into_option),
extra_index_url: extra_index_url.map(|extra_index_url| {
extra_index_url
index_url: index_url.and_then(Maybe::into_option).map(PipIndex::from),
extra_index_url: extra_index_url.map(|extra_index_urls| {
extra_index_urls
.into_iter()
.filter_map(Maybe::into_option)
.map(PipExtraIndex::from)
.collect()
}),
no_index: if no_index { Some(true) } else { None },
find_links: find_links.map(|find_links| {
find_links
.into_iter()
.filter_map(Maybe::into_option)
.map(PipFindLinks::from)
.collect()
}),
..PipOptions::default()
Expand Down
3 changes: 2 additions & 1 deletion crates/uv-dispatch/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ impl<'a> BuildContext for BuildDispatch<'a> {
self.sources
}

fn index_locations(&self) -> &IndexLocations {
fn locations(&self) -> &IndexLocations {
self.index_locations
}

Expand Down Expand Up @@ -350,6 +350,7 @@ impl<'a> BuildContext for BuildDispatch<'a> {
self,
self.source_build_context.clone(),
version_id,
self.index_locations,
sources,
self.config_settings.clone(),
self.build_isolation,
Expand Down
24 changes: 23 additions & 1 deletion crates/uv-distribution-types/src/index.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use crate::{IndexUrl, IndexUrlError};
use std::str::FromStr;
use thiserror::Error;
use url::Url;
use uv_auth::Credentials;

use crate::origin::Origin;
use crate::{IndexUrl, IndexUrlError};

#[derive(Debug, Clone, Hash, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct Index {
Expand Down Expand Up @@ -52,6 +54,9 @@ pub struct Index {
/// is given the highest priority when resolving packages.
#[serde(default)]
pub default: bool,
/// The origin of the index (e.g., a CLI flag, a user-level configuration file, etc.).
#[serde(skip)]
pub origin: Option<Origin>,
// /// The type of the index.
// ///
// /// Indexes can either be PEP 503-compliant (i.e., a registry implementing the Simple API) or
Expand Down Expand Up @@ -81,6 +86,7 @@ impl Index {
name: None,
explicit: false,
default: true,
origin: None,
}
}

Expand All @@ -91,6 +97,7 @@ impl Index {
name: None,
explicit: false,
default: false,
origin: None,
}
}

Expand All @@ -101,14 +108,27 @@ impl Index {
name: None,
explicit: false,
default: false,
origin: None,
}
}

/// Set the [`Origin`] of the index.
#[must_use]
pub fn with_origin(mut self, origin: Origin) -> Self {
self.origin = Some(origin);
self
}

/// Return the [`IndexUrl`] of the index.
pub fn url(&self) -> &IndexUrl {
&self.url
}

/// Consume the [`Index`] and return the [`IndexUrl`].
pub fn into_url(self) -> IndexUrl {
self.url
}

/// Return the raw [`URL`] of the index.
pub fn raw_url(&self) -> &Url {
self.url.url()
Expand Down Expand Up @@ -145,6 +165,7 @@ impl FromStr for Index {
url,
explicit: false,
default: false,
origin: None,
});
}
}
Expand All @@ -156,6 +177,7 @@ impl FromStr for Index {
url,
explicit: false,
default: false,
origin: None,
})
}
}
Expand Down
4 changes: 4 additions & 0 deletions crates/uv-distribution-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ pub use crate::id::*;
pub use crate::index::*;
pub use crate::index_url::*;
pub use crate::installed::*;
pub use crate::origin::*;
pub use crate::pip_index::*;
pub use crate::prioritized_distribution::*;
pub use crate::resolution::*;
pub use crate::resolved::*;
Expand All @@ -79,6 +81,8 @@ mod id;
mod index;
mod index_url;
mod installed;
mod origin;
mod pip_index;
mod prioritized_distribution;
mod resolution;
mod resolved;
Expand Down
12 changes: 12 additions & 0 deletions crates/uv-distribution-types/src/origin.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/// The origin of a piece of configuration.
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
pub enum Origin {
/// The setting was provided via the CLI.
Cli,
/// The setting was provided via a user-level configuration file.
User,
/// The setting was provided via a project-level configuration file.
Project,
/// The setting was provided via a `requirements.txt` file.
RequirementsTxt,
}
Loading

0 comments on commit 2153c6a

Please sign in to comment.