Skip to content

Commit

Permalink
Respect explicit vs. implicit indexes in more places
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed Sep 27, 2024
1 parent 67461d9 commit 2a8d89e
Show file tree
Hide file tree
Showing 21 changed files with 321 additions and 119 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 12 additions & 2 deletions crates/distribution-types/src/index.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{IndexUrl, IndexUrlError};
use std::str::FromStr;
use thiserror::Error;

use crate::{IndexUrl, IndexUrlError};
use url::Url;

#[derive(Debug, Clone, Hash, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
Expand Down Expand Up @@ -92,6 +92,16 @@ impl Index {
default: false,
}
}

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

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

impl FromStr for Index {
Expand Down
129 changes: 79 additions & 50 deletions crates/distribution-types/src/index_url.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ use crate::{Index, Verbatim};

static PYPI_URL: LazyLock<Url> = LazyLock::new(|| Url::parse("https://pypi.org/simple").unwrap());

static DEFAULT_INDEX_URL: LazyLock<IndexUrl> =
LazyLock::new(|| IndexUrl::Pypi(VerbatimUrl::from_url(PYPI_URL.clone())));
static DEFAULT_INDEX_URL: LazyLock<Index> = LazyLock::new(|| {
Index::from_index_url(IndexUrl::Pypi(VerbatimUrl::from_url(PYPI_URL.clone())))
});

/// The URL of an index to use for fetching packages (e.g., PyPI).
#[derive(Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
Expand Down Expand Up @@ -297,7 +298,8 @@ impl From<VerbatimUrl> for FlatIndexLocation {

/// The index locations to use for fetching packages. By default, uses the PyPI index.
///
/// From a pip perspective, this type merges `--index-url`, `--extra-index-url`, and `--find-links`.
/// From a pip perspective, this type merges `--index-url`, `--extra-index-url`, and `--find-links`,
/// along with the uv-specific `--index` and `--default-index` options.
#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
pub struct IndexLocations {
Expand Down Expand Up @@ -344,51 +346,64 @@ impl IndexLocations {
}

impl<'a> IndexLocations {
/// Return the primary [`IndexUrl`] entry.
/// Return the default [`Index`] entry.
///
/// If `--no-index` is set, return `None`.
///
/// If no index is provided, use the `PyPI` index.
pub fn index(&'a self) -> Option<&'a IndexUrl> {
pub fn default_index(&'a self) -> Option<&'a Index> {
if self.no_index {
None
} else {
let mut seen = FxHashSet::default();
self.indexes
.iter()
.find_map(|index| {
if !index.default || index.explicit {
None
} else {
Some(&index.url)
}
})
.filter(move |index| index.name.as_ref().map_or(true, |name| seen.insert(name)))
.find(|index| index.default && !index.explicit)
.or_else(|| Some(&DEFAULT_INDEX_URL))
}
}

/// Return an iterator over the extra [`IndexUrl`] entries.
pub fn extra_index(&'a self) -> impl Iterator<Item = &'a IndexUrl> + 'a {
/// Return an iterator over the implicit [`Index`] entries.
pub fn implicit_indexes(&'a self) -> impl Iterator<Item = &'a Index> + 'a {
if self.no_index {
Either::Left(std::iter::empty())
} else {
Either::Right(self.indexes.iter().filter_map(|index| {
if index.default || index.explicit {
None
} else {
Some(&index.url)
}
}))
let mut seen = FxHashSet::default();
Either::Right(
self.indexes
.iter()
.filter(move |index| index.name.as_ref().map_or(true, |name| seen.insert(name)))
.filter(|index| !(index.default || index.explicit)),
)
}
}

/// Return an iterator over all [`IndexUrl`] entries in order.
/// Return an iterator over the explicit [`Index`] entries.
pub fn explicit_indexes(&'a self) -> impl Iterator<Item = &'a Index> + 'a {
if self.no_index {
Either::Left(std::iter::empty())
} else {
let mut seen = FxHashSet::default();
Either::Right(
self.indexes
.iter()
.filter(move |index| index.name.as_ref().map_or(true, |name| seen.insert(name)))
.filter(|index| index.explicit),
)
}
}

/// Return an iterator over all [`Index`] entries in order.
///
/// Prioritizes the extra indexes over the main index.
/// Explicit indexes are excluded.
///
/// Prioritizes the extra indexes over the default index.
///
/// If `no_index` was enabled, then this always returns an empty
/// iterator.
pub fn indexes(&'a self) -> impl Iterator<Item = &'a IndexUrl> + 'a {
self.extra_index().chain(self.index())
pub fn indexes(&'a self) -> impl Iterator<Item = &'a Index> + 'a {
self.implicit_indexes().chain(self.default_index())
}

/// Return an iterator over the [`FlatIndexLocation`] entries.
Expand All @@ -409,58 +424,72 @@ impl<'a> IndexLocations {
}
}

/// Return an iterator over all [`Url`] entries.
pub fn urls(&'a self) -> impl Iterator<Item = &'a Url> + 'a {
self.indexes()
.map(IndexUrl::url)
/// Return an iterator over all allowed [`IndexUrl`] entries.
///
/// This includes both explicit and implicit indexes, as well as the default index.
///
/// If `no_index` was enabled, then this always returns an empty
/// iterator.
pub fn allowed_indexes(&'a self) -> impl Iterator<Item = &'a Index> + 'a {
self.explicit_indexes()
.chain(self.implicit_indexes())
.chain(self.default_index())
}

/// Return an iterator over all allowed [`Url`] entries.
///
/// This includes both explicit and implicit index URLs, as well as the default index.
///
/// If `no_index` was enabled, then this always returns an empty
/// iterator.
pub fn allowed_urls(&'a self) -> impl Iterator<Item = &'a Url> + 'a {
self.allowed_indexes()
.map(Index::raw_url)
.chain(self.flat_index().map(FlatIndexLocation::url))
}
}

/// The index URLs to use for fetching packages.
///
/// From a pip perspective, this type merges `--index-url` and `--extra-index-url`.
/// From a pip perspective, this type merges `--index-url` and `--extra-index-url`, along with the
/// uv-specific `--index` and `--default-index` options.
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct IndexUrls {
indexes: Vec<Index>,
no_index: bool,
}

impl<'a> IndexUrls {
/// Return the primary [`IndexUrl`] entry.
/// Return the default [`Index`] entry.
///
/// If `--no-index` is set, return `None`.
///
/// If no index is provided, use the `PyPI` index.
fn index(&'a self) -> Option<&'a IndexUrl> {
fn default_index(&'a self) -> Option<&'a Index> {
if self.no_index {
None
} else {
let mut seen = FxHashSet::default();
self.indexes
.iter()
.find_map(|index| {
if !index.default || index.explicit {
None
} else {
Some(&index.url)
}
})
.filter(move |index| index.name.as_ref().map_or(true, |name| seen.insert(name)))
.find(|index| index.default && !index.explicit)
.or_else(|| Some(&DEFAULT_INDEX_URL))
}
}

/// Return an iterator over the extra [`IndexUrl`] entries.
fn extra_index(&'a self) -> impl Iterator<Item = &'a IndexUrl> + 'a {
/// Return an iterator over the implicit [`Index`] entries.
fn implicit_indexes(&'a self) -> impl Iterator<Item = &'a Index> + 'a {
if self.no_index {
Either::Left(std::iter::empty())
} else {
Either::Right(self.indexes.iter().filter_map(|index| {
if index.default || index.explicit {
None
} else {
Some(&index.url)
}
}))
let mut seen = FxHashSet::default();
Either::Right(
self.indexes
.iter()
.filter(move |index| index.name.as_ref().map_or(true, |name| seen.insert(name)))
.filter(|index| !(index.default || index.explicit)),
)
}
}

Expand All @@ -471,8 +500,8 @@ impl<'a> IndexUrls {
///
/// If `no_index` was enabled, then this always returns an empty
/// iterator.
pub fn indexes(&'a self) -> impl Iterator<Item = &'a IndexUrl> + 'a {
self.extra_index().chain(self.index())
pub fn indexes(&'a self) -> impl Iterator<Item = &'a Index> + 'a {
self.implicit_indexes().chain(self.default_index())
}
}

Expand Down
4 changes: 2 additions & 2 deletions crates/distribution-types/src/resolution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ impl From<&ResolvedDist> for Requirement {
wheels.best_wheel().filename.version.clone(),
),
),
index: None,
index: Some(wheels.best_wheel().index.url().clone()),
},
Dist::Built(BuiltDist::DirectUrl(wheel)) => {
let mut location = wheel.url.to_url();
Expand All @@ -211,7 +211,7 @@ impl From<&ResolvedDist> for Requirement {
specifier: pep440_rs::VersionSpecifiers::from(
pep440_rs::VersionSpecifier::equals_version(sdist.version.clone()),
),
index: None,
index: Some(sdist.index.url().clone()),
},
Dist::Source(SourceDist::DirectUrl(sdist)) => {
let mut location = sdist.url.to_url();
Expand Down
4 changes: 2 additions & 2 deletions crates/uv-client/src/registry_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use url::Url;

use distribution_filename::{DistFilename, SourceDistFilename, WheelFilename};
use distribution_types::{
BuiltDist, File, FileLocation, IndexCapabilities, IndexUrl, IndexUrls, Name,
BuiltDist, File, FileLocation, Index, IndexCapabilities, IndexUrl, IndexUrls, Name,
};
use pep440_rs::Version;
use pep508_rs::MarkerEnvironment;
Expand Down Expand Up @@ -210,7 +210,7 @@ impl RegistryClient {
let indexes = if let Some(index) = index {
Either::Left(std::iter::once(index))
} else {
Either::Right(self.index_urls.indexes())
Either::Right(self.index_urls.indexes().map(Index::url))
};

let mut it = indexes.peekable();
Expand Down
1 change: 1 addition & 0 deletions crates/uv-distribution/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ uv-workspace = { workspace = true }
anyhow = { workspace = true }
fs-err = { workspace = true }
futures = { workspace = true }
indexmap = { workspace = true }
nanoid = { workspace = true }
owo-colors = { workspace = true }
reqwest = { workspace = true }
Expand Down
Loading

0 comments on commit 2a8d89e

Please sign in to comment.