Skip to content

Commit

Permalink
Support requirements.txt files in uv tool install and uv tool run
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed Jul 23, 2024
1 parent 68c27cb commit 646e6d2
Show file tree
Hide file tree
Showing 9 changed files with 232 additions and 42 deletions.
10 changes: 9 additions & 1 deletion crates/uv-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2208,10 +2208,14 @@ pub struct ToolRunArgs {
#[arg(long)]
pub from: Option<String>,

/// Include the following extra requirements.
/// Run with the given packages installed.
#[arg(long)]
pub with: Vec<String>,

/// Run with all packages listed in the given `requirements.txt` files.
#[arg(long, value_parser = parse_maybe_file_path)]
pub with_requirements: Vec<Maybe<PathBuf>>,

#[command(flatten)]
pub installer: ResolverInstallerArgs,

Expand Down Expand Up @@ -2252,6 +2256,10 @@ pub struct ToolInstallArgs {
#[arg(long)]
pub with: Vec<String>,

/// Run all requirements listed in the given `requirements.txt` files.
#[arg(long, value_parser = parse_maybe_file_path)]
pub with_requirements: Vec<Maybe<PathBuf>>,

#[command(flatten)]
pub installer: ResolverInstallerArgs,

Expand Down
1 change: 1 addition & 0 deletions crates/uv/src/commands/pip/uninstall.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ pub(crate) async fn pip_uninstall(
printer: Printer,
) -> Result<ExitStatus> {
let start = std::time::Instant::now();

let client_builder = BaseClientBuilder::new()
.connectivity(connectivity)
.native_tls(native_tls)
Expand Down
21 changes: 10 additions & 11 deletions crates/uv/src/commands/tool/common.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use distribution_types::{InstalledDist, Name};
use pypi_types::Requirement;
use uv_cache::Cache;
use uv_client::Connectivity;
use uv_client::{BaseClientBuilder, Connectivity};
use uv_configuration::{Concurrency, PreviewMode};
use uv_installer::SitePackages;
use uv_python::{Interpreter, PythonEnvironment};
use uv_requirements::RequirementsSpecification;
use uv_requirements::{RequirementsSource, RequirementsSpecification};
use uv_tool::entrypoint_paths;

use crate::commands::{project, SharedState};
Expand All @@ -14,7 +14,7 @@ use crate::settings::ResolverInstallerSettings;

/// Resolve any [`UnnamedRequirements`].
pub(super) async fn resolve_requirements(
requirements: impl Iterator<Item = &str>,
requirements: &[RequirementsSource],
interpreter: &Interpreter,
settings: &ResolverInstallerSettings,
state: &SharedState,
Expand All @@ -25,18 +25,17 @@ pub(super) async fn resolve_requirements(
cache: &Cache,
printer: Printer,
) -> anyhow::Result<Vec<Requirement>> {
let client_builder = BaseClientBuilder::new()
.connectivity(connectivity)
.native_tls(native_tls);

// Parse the requirements.
let requirements = {
let mut parsed = vec![];
for requirement in requirements {
parsed.push(RequirementsSpecification::parse_package(requirement)?);
}
parsed
};
let spec =
RequirementsSpecification::from_simple_sources(requirements, &client_builder).await?;

// Resolve the parsed requirements.
project::resolve_names(
requirements,
spec.requirements,
interpreter,
settings,
state,
Expand Down
44 changes: 23 additions & 21 deletions crates/uv/src/commands/tool/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ use uv_python::{
EnvironmentPreference, PythonEnvironment, PythonFetch, PythonInstallation, PythonPreference,
PythonRequest,
};
use uv_requirements::RequirementsSpecification;
use uv_requirements::{RequirementsSource, RequirementsSpecification};
use uv_shell::Shell;
use uv_tool::{entrypoint_paths, find_executable_directory, InstalledTools, Tool, ToolEntrypoint};
use uv_warnings::{warn_user, warn_user_once};

use crate::commands::reporters::PythonDownloadReporter;
use crate::commands::tool::common::resolve_requirements;
use crate::commands::{
project::{resolve_environment, sync_environment, update_environment},
project::{resolve_environment, resolve_names, sync_environment, update_environment},
tool::common::matching_packages,
};
use crate::commands::{ExitStatus, SharedState};
Expand All @@ -41,8 +41,8 @@ use crate::settings::ResolverInstallerSettings;
pub(crate) async fn install(
package: String,
from: Option<String>,
with: &[RequirementsSource],
python: Option<String>,
with: Vec<String>,
force: bool,
settings: ResolverInstallerSettings,
preview: PreviewMode,
Expand Down Expand Up @@ -91,21 +91,23 @@ pub(crate) async fn install(
bail!("Package requirement (`{from}`) provided with `--from` conflicts with install request (`{package}`)", from = from.cyan(), package = package.cyan())
};

let from_requirement = resolve_requirements(
std::iter::once(from.as_str()),
&interpreter,
&settings,
&state,
preview,
connectivity,
concurrency,
native_tls,
cache,
printer,
)
.await?
.pop()
.unwrap();
let from_requirement = {
resolve_names(
vec![RequirementsSpecification::parse_package(&from)?],
&interpreter,
&settings,
&state,
preview,
connectivity,
concurrency,
native_tls,
cache,
printer,
)
.await?
.pop()
.unwrap()
};

// Check if the positional name conflicts with `--from`.
if from_requirement.name != package {
Expand All @@ -119,8 +121,8 @@ pub(crate) async fn install(

from_requirement
} else {
resolve_requirements(
std::iter::once(package.as_str()),
resolve_names(
vec![RequirementsSpecification::parse_package(&package)?],
&interpreter,
&settings,
&state,
Expand All @@ -142,7 +144,7 @@ pub(crate) async fn install(
requirements.push(from.clone());
requirements.extend(
resolve_requirements(
with.iter().map(String::as_str),
with,
&interpreter,
&settings,
&state,
Expand Down
17 changes: 10 additions & 7 deletions crates/uv/src/commands/tool/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,15 @@ use uv_python::{
EnvironmentPreference, PythonEnvironment, PythonFetch, PythonInstallation, PythonPreference,
PythonRequest,
};
use uv_requirements::{RequirementsSource, RequirementsSpecification};
use uv_tool::{entrypoint_paths, InstalledTools};
use uv_warnings::{warn_user, warn_user_once};

use crate::commands::reporters::PythonDownloadReporter;
use crate::commands::tool::common::resolve_requirements;
use crate::commands::{project::environment::CachedEnvironment, tool::common::matching_packages};
use crate::commands::{
project, project::environment::CachedEnvironment, tool::common::matching_packages,
};
use crate::commands::{ExitStatus, SharedState};
use crate::printer::Printer;
use crate::settings::ResolverInstallerSettings;
Expand All @@ -54,7 +57,7 @@ impl Display for ToolRunCommand {
pub(crate) async fn run(
command: ExternalCommand,
from: Option<String>,
with: Vec<String>,
with: &[RequirementsSource],
python: Option<String>,
settings: ResolverInstallerSettings,
invocation_source: ToolRunCommand,
Expand Down Expand Up @@ -86,7 +89,7 @@ pub(crate) async fn run(
// Get or create a compatible environment in which to execute the tool.
let (from, environment) = get_or_create_environment(
&from,
&with,
with,
python.as_deref(),
&settings,
isolated,
Expand Down Expand Up @@ -273,7 +276,7 @@ fn warn_executable_not_provided_by_package(
/// [`PythonEnvironment`]. Otherwise, gets or creates a [`CachedEnvironment`].
async fn get_or_create_environment(
from: &str,
with: &[String],
with: &[RequirementsSource],
python: Option<&str>,
settings: &ResolverInstallerSettings,
isolated: bool,
Expand Down Expand Up @@ -312,8 +315,8 @@ async fn get_or_create_environment(

// Resolve the `from` requirement.
let from = {
resolve_requirements(
std::iter::once(from),
project::resolve_names(
vec![RequirementsSpecification::parse_package(from)?],
&interpreter,
settings,
&state,
Expand All @@ -335,7 +338,7 @@ async fn get_or_create_environment(
requirements.push(from.clone());
requirements.extend(
resolve_requirements(
with.iter().map(String::as_str),
with,
&interpreter,
settings,
&state,
Expand Down
27 changes: 25 additions & 2 deletions crates/uv/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -618,10 +618,22 @@ async fn run(cli: Cli) -> Result<ExitStatus> {

// Initialize the cache.
let cache = cache.init()?.with_refresh(args.refresh);

let requirements = args
.with
.into_iter()
.map(RequirementsSource::from_package)
.chain(
args.with_requirements
.into_iter()
.map(RequirementsSource::from_requirements_file),
)
.collect::<Vec<_>>();

commands::tool_run(
args.command,
args.from,
args.with,
&requirements,
args.python,
args.settings,
invocation_source,
Expand All @@ -647,11 +659,22 @@ async fn run(cli: Cli) -> Result<ExitStatus> {
// Initialize the cache.
let cache = cache.init()?.with_refresh(args.refresh);

let requirements = args
.with
.into_iter()
.map(RequirementsSource::from_package)
.chain(
args.with_requirements
.into_iter()
.map(RequirementsSource::from_requirements_file),
)
.collect::<Vec<_>>();

commands::tool_install(
args.package,
args.from,
&requirements,
args.python,
args.with,
args.force,
args.settings,
globals.preview,
Expand Down
12 changes: 12 additions & 0 deletions crates/uv/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ pub(crate) struct ToolRunSettings {
pub(crate) command: ExternalCommand,
pub(crate) from: Option<String>,
pub(crate) with: Vec<String>,
pub(crate) with_requirements: Vec<PathBuf>,
pub(crate) python: Option<String>,
pub(crate) refresh: Refresh,
pub(crate) settings: ResolverInstallerSettings,
Expand All @@ -261,6 +262,7 @@ impl ToolRunSettings {
command,
from,
with,
with_requirements,
installer,
build,
refresh,
Expand All @@ -271,6 +273,10 @@ impl ToolRunSettings {
command,
from,
with,
with_requirements: with_requirements
.into_iter()
.filter_map(Maybe::into_option)
.collect(),
python,
refresh: Refresh::from(refresh),
settings: ResolverInstallerSettings::combine(
Expand All @@ -288,6 +294,7 @@ pub(crate) struct ToolInstallSettings {
pub(crate) package: String,
pub(crate) from: Option<String>,
pub(crate) with: Vec<String>,
pub(crate) with_requirements: Vec<PathBuf>,
pub(crate) python: Option<String>,
pub(crate) refresh: Refresh,
pub(crate) settings: ResolverInstallerSettings,
Expand All @@ -302,6 +309,7 @@ impl ToolInstallSettings {
package,
from,
with,
with_requirements,
installer,
force,
build,
Expand All @@ -313,6 +321,10 @@ impl ToolInstallSettings {
package,
from,
with,
with_requirements: with_requirements
.into_iter()
.filter_map(Maybe::into_option)
.collect(),
python,
force,
refresh: Refresh::from(refresh),
Expand Down
Loading

0 comments on commit 646e6d2

Please sign in to comment.