Skip to content

Commit

Permalink
Enable unnamed requirements for direct URLs (#2569)
Browse files Browse the repository at this point in the history
## Summary

This PR enables `uv pip install` to accept unnamed requirements, as long
as the requirement ends with the wheel or source distribution archive
name. For example: `cargo run pip install
~/Downloads/anyio-4.3.0.tar.gz`.

In subsequent PRs, I'll expand the scope of supported archives and
patterns.

Part of: #313.
  • Loading branch information
charliermarsh committed Mar 21, 2024
1 parent ee211b3 commit 4d96255
Show file tree
Hide file tree
Showing 8 changed files with 251 additions and 86 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.

1 change: 1 addition & 0 deletions crates/uv/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ tracing-subscriber = { workspace = true, features = ["json"] }
tracing-tree = { workspace = true }
unicode-width = { workspace = true }
url = { workspace = true }
distribution-filename = { version = "0.0.1", path = "../distribution-filename" }

[target.'cfg(target_os = "windows")'.dependencies]
mimalloc = { version = "0.1.39" }
Expand Down
31 changes: 17 additions & 14 deletions crates/uv/src/commands/pip_compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ use crate::commands::reporters::{DownloadReporter, ResolverReporter};
use crate::commands::{elapsed, ExitStatus};
use crate::printer::Printer;
use crate::requirements::{
read_lockfile, ExtrasSpecification, RequirementsSource, RequirementsSpecification,
read_lockfile, ExtrasSpecification, NamedRequirements, RequirementsSource,
RequirementsSpecification,
};

/// Resolve a set of requirements into a set of pinned versions.
Expand Down Expand Up @@ -89,18 +90,7 @@ pub(crate) async fn pip_compile(
}

// Read all requirements from the provided sources.
let RequirementsSpecification {
project,
requirements,
constraints,
overrides,
editables,
index_url,
extra_index_urls,
no_index,
find_links,
extras: used_extras,
} = RequirementsSpecification::from_sources(
let spec = RequirementsSpecification::from_sources(
requirements,
constraints,
overrides,
Expand All @@ -113,7 +103,7 @@ pub(crate) async fn pip_compile(
if let ExtrasSpecification::Some(extras) = extras {
let mut unused_extras = extras
.iter()
.filter(|extra| !used_extras.contains(extra))
.filter(|extra| !spec.extras.contains(extra))
.collect::<Vec<_>>();
if !unused_extras.is_empty() {
unused_extras.sort_unstable();
Expand All @@ -126,6 +116,19 @@ pub(crate) async fn pip_compile(
}
}

// Convert from unnamed to named requirements.
let NamedRequirements {
project,
requirements,
constraints,
overrides,
editables,
index_url,
extra_index_urls,
no_index,
find_links,
} = NamedRequirements::from_spec(spec)?;

// Read the lockfile, if present.
let preferences = read_lockfile(output_file, upgrade).await?;

Expand Down
35 changes: 11 additions & 24 deletions crates/uv/src/commands/pip_install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ use uv_warnings::warn_user;
use crate::commands::reporters::{DownloadReporter, InstallReporter, ResolverReporter};
use crate::commands::{compile_bytecode, elapsed, ChangeEvent, ChangeEventKind, ExitStatus};
use crate::printer::Printer;
use crate::requirements::{ExtrasSpecification, RequirementsSource, RequirementsSpecification};
use crate::requirements::{
ExtrasSpecification, NamedRequirements, RequirementsSource, RequirementsSpecification,
};

use super::{DryRunEvent, Upgrade};

Expand Down Expand Up @@ -76,10 +78,10 @@ pub(crate) async fn pip_install(
dry_run: bool,
printer: Printer,
) -> Result<ExitStatus> {
let start = std::time::Instant::now();
let start = Instant::now();

// Read all requirements from the provided sources.
let RequirementsSpecification {
let NamedRequirements {
project,
requirements,
constraints,
Expand All @@ -89,25 +91,7 @@ pub(crate) async fn pip_install(
extra_index_urls,
no_index,
find_links,
extras: used_extras,
} = specification(requirements, constraints, overrides, extras, connectivity).await?;

// Check that all provided extras are used
if let ExtrasSpecification::Some(extras) = extras {
let mut unused_extras = extras
.iter()
.filter(|extra| !used_extras.contains(extra))
.collect::<Vec<_>>();
if !unused_extras.is_empty() {
unused_extras.sort_unstable();
unused_extras.dedup();
let s = if unused_extras.len() == 1 { "" } else { "s" };
return Err(anyhow!(
"Requested extra{s} not found: {}",
unused_extras.iter().join(", ")
));
}
}
} = read_requirements(requirements, constraints, overrides, extras, connectivity).await?;

// Detect the current Python interpreter.
let venv = if let Some(python) = python.as_ref() {
Expand Down Expand Up @@ -348,13 +332,13 @@ pub(crate) async fn pip_install(
}

/// Consolidate the requirements for an installation.
async fn specification(
async fn read_requirements(
requirements: &[RequirementsSource],
constraints: &[RequirementsSource],
overrides: &[RequirementsSource],
extras: &ExtrasSpecification<'_>,
connectivity: Connectivity,
) -> Result<RequirementsSpecification, Error> {
) -> Result<NamedRequirements, Error> {
// If the user requests `extras` but does not provide a pyproject toml source
if !matches!(extras, ExtrasSpecification::None)
&& !requirements
Expand Down Expand Up @@ -392,6 +376,9 @@ async fn specification(
}
}

// Convert from unnamed to named requirements.
let spec = NamedRequirements::from_spec(spec)?;

Ok(spec)
}

Expand Down
31 changes: 17 additions & 14 deletions crates/uv/src/commands/pip_sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use uv_warnings::warn_user;
use crate::commands::reporters::{DownloadReporter, FinderReporter, InstallReporter};
use crate::commands::{compile_bytecode, elapsed, ChangeEvent, ChangeEventKind, ExitStatus};
use crate::printer::Printer;
use crate::requirements::{RequirementsSource, RequirementsSpecification};
use crate::requirements::{NamedRequirements, RequirementsSource, RequirementsSpecification};

/// Install a set of locked requirements into the current Python environment.
#[allow(clippy::too_many_arguments, clippy::fn_params_excessive_bools)]
Expand Down Expand Up @@ -54,20 +54,10 @@ pub(crate) async fn pip_sync(
let start = std::time::Instant::now();

// Read all requirements from the provided sources.
let RequirementsSpecification {
project: _project,
requirements,
constraints: _constraints,
overrides: _overrides,
editables,
index_url,
extra_index_urls,
no_index,
find_links,
extras: _extras,
} = RequirementsSpecification::from_simple_sources(sources, connectivity).await?;
let spec = RequirementsSpecification::from_simple_sources(sources, connectivity).await?;

let num_requirements = requirements.len() + editables.len();
// Validate that the requirements are non-empty.
let num_requirements = spec.requirements.len() + spec.editables.len();
if num_requirements == 0 {
writeln!(printer.stderr(), "No requirements found")?;
return Ok(ExitStatus::Success);
Expand Down Expand Up @@ -107,6 +97,19 @@ pub(crate) async fn pip_sync(
}
}

// Convert from unnamed to named requirements.
let NamedRequirements {
project: _project,
requirements,
constraints: _constraints,
overrides: _overrides,
editables,
index_url,
extra_index_urls,
no_index,
find_links,
} = NamedRequirements::from_spec(spec)?;

let _lock = venv.lock()?;

// Determine the current environment markers.
Expand Down
28 changes: 15 additions & 13 deletions crates/uv/src/commands/pip_uninstall.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use uv_interpreter::PythonEnvironment;

use crate::commands::{elapsed, ExitStatus};
use crate::printer::Printer;
use crate::requirements::{RequirementsSource, RequirementsSpecification};
use crate::requirements::{NamedRequirements, RequirementsSource, RequirementsSpecification};

/// Uninstall packages from the current environment.
pub(crate) async fn pip_uninstall(
Expand All @@ -27,18 +27,7 @@ pub(crate) async fn pip_uninstall(
let start = std::time::Instant::now();

// Read all requirements from the provided sources.
let RequirementsSpecification {
project: _project,
requirements,
constraints: _constraints,
overrides: _overrides,
editables,
index_url: _index_url,
extra_index_urls: _extra_index_urls,
no_index: _no_index,
find_links: _find_links,
extras: _extras,
} = RequirementsSpecification::from_simple_sources(sources, connectivity).await?;
let spec = RequirementsSpecification::from_simple_sources(sources, connectivity).await?;

// Detect the current Python interpreter.
let venv = if let Some(python) = python.as_ref() {
Expand Down Expand Up @@ -74,6 +63,19 @@ pub(crate) async fn pip_uninstall(
}
}

// Convert from unnamed to named requirements.
let NamedRequirements {
project: _,
requirements,
constraints: _,
overrides: _,
editables,
index_url: _,
extra_index_urls: _,
no_index: _,
find_links: _,
} = NamedRequirements::from_spec(spec)?;

let _lock = venv.lock()?;

// Index the current `site-packages` directory.
Expand Down
Loading

0 comments on commit 4d96255

Please sign in to comment.