Skip to content

Commit

Permalink
Add index URLs when provided via uv add --index / --default-index
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed Sep 27, 2024
1 parent 3c4c842 commit 2186b50
Show file tree
Hide file tree
Showing 6 changed files with 792 additions and 3 deletions.
4 changes: 4 additions & 0 deletions crates/uv-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,10 @@ impl<T> Maybe<T> {
Maybe::None => None,
}
}

pub fn is_some(&self) -> bool {
matches!(self, Maybe::Some(_))
}
}

/// Parse a string into an [`IndexUrl`], mapping the empty string to `None`.
Expand Down
74 changes: 73 additions & 1 deletion crates/uv-workspace/src/pyproject_mut.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use distribution_types::Index;
use itertools::Itertools;
use pep440_rs::{Version, VersionSpecifier, VersionSpecifiers};
use pep508_rs::{ExtraName, MarkerTree, PackageName, Requirement, VersionOrUrl};
use std::path::Path;
use std::str::FromStr;
use std::{fmt, mem};
use thiserror::Error;
use toml_edit::{Array, DocumentMut, Item, RawString, Table, TomlError, Value};
use toml_edit::{Array, ArrayOfTables, DocumentMut, Item, RawString, Table, TomlError, Value};
use uv_fs::PortablePath;

use crate::pyproject::{DependencyType, Source};
Expand Down Expand Up @@ -190,6 +191,77 @@ impl PyProjectTomlMut {
Ok(edit)
}

/// Add an [`Index`] to `tool.uv.index`.
pub fn add_index(&mut self, index: &Index) -> Result<(), Error> {
let existing = self
.doc
.entry("tool")
.or_insert(implicit())
.as_table_mut()
.ok_or(Error::MalformedSources)?
.entry("uv")
.or_insert(implicit())
.as_table_mut()
.ok_or(Error::MalformedSources)?
.entry("index")
.or_insert(Item::ArrayOfTables(ArrayOfTables::new()))
.as_array_of_tables()
.ok_or(Error::MalformedSources)?;

let mut table = Table::new();
if let Some(name) = index.name.as_ref() {
table.insert("name", toml_edit::value(name.to_string()));
}
table.insert("url", toml_edit::value(index.url.to_string()));
if index.default {
table.insert("default", toml_edit::value(true));
}

// Push the item to the table.
let mut updated = ArrayOfTables::new();
updated.push(table);
for table in existing {
// If there's another index with the same name, replace it.
if table
.get("name")
.is_some_and(|name| name.as_str() == index.name.as_deref())
{
continue;
}

// If there's another default index, remove it.
if index.default
&& table
.get("default")
.is_some_and(|default| default.as_bool() == Some(true))
{
continue;
}

// If there's another index with the same URL, replace it.
if table
.get("url")
.is_some_and(|url| url.as_str() == Some(index.url.url().as_str()))
{
continue;
}

updated.push(table.clone());
}
self.doc
.entry("tool")
.or_insert(implicit())
.as_table_mut()
.ok_or(Error::MalformedSources)?
.entry("uv")
.or_insert(implicit())
.as_table_mut()
.ok_or(Error::MalformedSources)?
.insert("index", Item::ArrayOfTables(updated));

Ok(())
}

/// Adds a dependency to `project.optional-dependencies`.
///
/// Returns `true` if the dependency was added, `false` if it was updated.
Expand Down
8 changes: 7 additions & 1 deletion crates/uv/src/commands/project/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use rustc_hash::{FxBuildHasher, FxHashMap};
use tracing::debug;

use cache_key::RepositoryUrl;
use distribution_types::UnresolvedRequirement;
use distribution_types::{Index, UnresolvedRequirement};
use pep508_rs::{ExtraName, Requirement, UnnamedRequirement, VersionOrUrl};
use pypi_types::{redact_git_credentials, ParsedUrl, RequirementSource, VerbatimParsedUrl};
use uv_auth::{store_credentials, store_credentials_from_url, Credentials};
Expand Down Expand Up @@ -59,6 +59,7 @@ pub(crate) async fn add(
editable: Option<bool>,
dependency_type: DependencyType,
raw_sources: bool,
indexes: Vec<Index>,
rev: Option<String>,
tag: Option<String>,
branch: Option<String>,
Expand Down Expand Up @@ -465,6 +466,11 @@ pub(crate) async fn add(
});
}

// Add any indexes that were provided on the command-line.
for index in indexes {
toml.add_index(&index)?;
}

let content = toml.to_string();

// Save the modified `pyproject.toml` or script.
Expand Down
1 change: 1 addition & 0 deletions crates/uv/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1353,6 +1353,7 @@ async fn run_project(
args.editable,
args.dependency_type,
args.raw_sources,
args.indexes,
args.rev,
args.tag,
args.branch,
Expand Down
47 changes: 47 additions & 0 deletions crates/uv/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -803,6 +803,7 @@ pub(crate) struct AddSettings {
pub(crate) script: Option<PathBuf>,
pub(crate) python: Option<String>,
pub(crate) refresh: Refresh,
pub(crate) indexes: Vec<Index>,
pub(crate) settings: ResolverInstallerSettings,
}

Expand Down Expand Up @@ -841,6 +842,51 @@ impl AddSettings {
DependencyType::Production
};

// Track the `--index` and `--default-index` arguments from the command-line.
let indexes = installer
.index_args
.default_index
.clone()
.and_then(Maybe::into_option)
.into_iter()
.chain(
installer
.index_args
.index
.clone()
.into_iter()
.flatten()
.filter_map(Maybe::into_option),
)
.collect::<Vec<_>>();

// If the user passed an `--index-url` or `--extra-index-url`, warn.
if installer
.index_args
.index_url
.as_ref()
.is_some_and(Maybe::is_some)
{
if script.is_some() {
warn_user_once!("Indexes specified via `--index-url` will not be persisted to the script; use `--default-index` instead.");
} else {
warn_user_once!("Indexes specified via `--index-url` will not be persisted to the `pyproject.toml` file; use `--default-index` instead.");
}
}

if installer
.index_args
.extra_index_url
.as_ref()
.is_some_and(|extra_index_url| extra_index_url.iter().any(Maybe::is_some))
{
if script.is_some() {
warn_user_once!("Indexes specified via `--extra-index-url` will not be persisted to the script; use `--index` instead.");
} else {
warn_user_once!("Indexes specified via `--extra-index-url` will not be persisted to the `pyproject.toml` file; use `--index` instead.");
}
}

Self {
locked,
frozen,
Expand All @@ -855,6 +901,7 @@ impl AddSettings {
package,
script,
python,
indexes,
editable: flag(editable, no_editable),
extras: extra.unwrap_or_default(),
refresh: Refresh::from(refresh),
Expand Down
Loading

0 comments on commit 2186b50

Please sign in to comment.