Skip to content

Commit

Permalink
Add uv toolchain find
Browse files Browse the repository at this point in the history
  • Loading branch information
zanieb committed Jun 13, 2024
1 parent 9a9ebe3 commit 1fc5643
Show file tree
Hide file tree
Showing 12 changed files with 341 additions and 9 deletions.
1 change: 1 addition & 0 deletions crates/uv-toolchain/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub use crate::discovery::{
ToolchainSources, VersionRequest,
};
pub use crate::environment::PythonEnvironment;
pub use crate::implementation::ImplementationName;
pub use crate::interpreter::Interpreter;
pub use crate::pointer_size::PointerSize;
pub use crate::prefix::Prefix;
Expand Down
8 changes: 4 additions & 4 deletions crates/uv-toolchain/src/toolchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ impl Toolchain {
cache: &Cache,
) -> Result<Self, Error> {
if let Some(python) = python {
Self::find_requested(python, system, preview, cache)
let request = ToolchainRequest::parse(python);
Self::find_requested(&request, system, preview, cache)
} else if system.is_preferred() {
Self::find_default(preview, cache)
} else {
Expand All @@ -60,14 +61,13 @@ impl Toolchain {

/// Find an installed [`Toolchain`] that satisfies a request.
pub fn find_requested(
request: &str,
request: &ToolchainRequest,
system: SystemPython,
preview: PreviewMode,
cache: &Cache,
) -> Result<Self, Error> {
let sources = ToolchainSources::from_settings(system, preview);
let request = ToolchainRequest::parse(request);
let toolchain = find_toolchain(&request, system, &sources, cache)??;
let toolchain = find_toolchain(request, system, &sources, cache)??;

Ok(toolchain)
}
Expand Down
16 changes: 16 additions & 0 deletions crates/uv/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1991,6 +1991,10 @@ pub(crate) enum ToolchainCommand {

/// Download and install a specific toolchain.
Install(ToolchainInstallArgs),

/// Search for a toolchain
#[command(disable_version_flag = true)]
Find(ToolchainFindArgs),
}

#[derive(Args)]
Expand All @@ -2014,6 +2018,18 @@ pub(crate) struct ToolchainInstallArgs {
pub(crate) target: Option<String>,
}

#[derive(Args)]
#[allow(clippy::struct_excessive_bools)]
pub(crate) struct ToolchainFindArgs {
/// The version to find.
#[arg(long)]
pub(crate) version: Option<String>,

/// The implementation to find.
#[arg(long)]
pub(crate) implementation: Option<String>,
}

#[derive(Args)]
pub(crate) struct IndexArgs {
/// The URL of the Python package index (by default: <https://pypi.org/simple>).
Expand Down
1 change: 1 addition & 0 deletions crates/uv/src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub(crate) use project::sync::sync;
#[cfg(feature = "self-update")]
pub(crate) use self_update::self_update;
pub(crate) use tool::run::run as run_tool;
pub(crate) use toolchain::find::find as toolchain_find;
pub(crate) use toolchain::install::install as toolchain_install;
pub(crate) use toolchain::list::list as toolchain_list;
use uv_cache::Cache;
Expand Down
3 changes: 2 additions & 1 deletion crates/uv/src/commands/pip/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,8 @@ pub(crate) async fn pip_compile(
SystemPython::Allowed
};
let interpreter = if let Some(python) = python.as_ref() {
Toolchain::find_requested(python, system, preview, &cache)
let request = ToolchainRequest::parse(python);
Toolchain::find_requested(&request, system, preview, &cache)
} else {
// TODO(zanieb): The split here hints at a problem with the abstraction; we should be able to use
// `Toolchain::find(...)` here.
Expand Down
3 changes: 2 additions & 1 deletion crates/uv/src/commands/project/lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ pub(crate) async fn lock(
if request.satisfied(&interpreter, cache) {
interpreter
} else {
Toolchain::find_requested(python, SystemPython::Allowed, preview, cache)?
let request = ToolchainRequest::parse(python);
Toolchain::find_requested(&request, SystemPython::Allowed, preview, cache)?
.into_interpreter()
}
} else {
Expand Down
59 changes: 59 additions & 0 deletions crates/uv/src/commands/toolchain/find.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use anyhow::Result;
use std::fmt::Write;
use std::str::FromStr;

use uv_cache::Cache;
use uv_configuration::PreviewMode;
use uv_fs::Simplified;
use uv_toolchain::{ImplementationName, SystemPython, Toolchain, ToolchainRequest, VersionRequest};
use uv_warnings::warn_user;

use crate::commands::ExitStatus;
use crate::printer::Printer;

/// Find a toolchain.
#[allow(clippy::too_many_arguments)]
pub(crate) async fn find(
version: Option<String>,
implementation: Option<String>,
preview: PreviewMode,
cache: &Cache,
printer: Printer,
) -> Result<ExitStatus> {
if preview.is_disabled() {
warn_user!("`uv toolchain find` is experimental and may change without warning.");
}

let implementation = implementation
.as_deref()
.map(ImplementationName::from_str)
.transpose()?;
let version = version
.as_deref()
.map(VersionRequest::from_str)
.transpose()?;

let request = match (version, implementation) {
(None, None) => ToolchainRequest::Any,
(Some(version), None) => ToolchainRequest::Version(version),
(Some(version), Some(implementation)) => {
ToolchainRequest::ImplementationVersion(implementation, version)
}
(None, Some(implementation)) => ToolchainRequest::Implementation(implementation),
};

let toolchain = Toolchain::find_requested(
&request,
SystemPython::Required,
PreviewMode::Enabled,
cache,
)?;

writeln!(
printer.stdout(),
"{}",
toolchain.interpreter().sys_executable().user_display()
)?;

Ok(ExitStatus::Success)
}
1 change: 1 addition & 0 deletions crates/uv/src/commands/toolchain/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub(crate) mod find;
pub(crate) mod install;
pub(crate) mod list;
18 changes: 18 additions & 0 deletions crates/uv/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,24 @@ async fn run() -> Result<ExitStatus> {
)
.await
}
Commands::Toolchain(ToolchainNamespace {
command: ToolchainCommand::Find(args),
}) => {
// Resolve the settings from the command-line arguments and workspace configuration.
let args = settings::ToolchainFindSettings::resolve(args, workspace);

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

commands::toolchain_find(
args.version,
args.implementation,
globals.preview,
&cache,
printer,
)
.await
}
}
}

Expand Down
28 changes: 26 additions & 2 deletions crates/uv/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use uv_workspace::{Combine, PipOptions, Workspace};
use crate::cli::{
ColorChoice, GlobalArgs, LockArgs, Maybe, PipCheckArgs, PipCompileArgs, PipFreezeArgs,
PipInstallArgs, PipListArgs, PipShowArgs, PipSyncArgs, PipUninstallArgs, RunArgs, SyncArgs,
ToolRunArgs, ToolchainInstallArgs, ToolchainListArgs, VenvArgs,
ToolRunArgs, ToolchainFindArgs, ToolchainInstallArgs, ToolchainListArgs, VenvArgs,
};
use crate::commands::ListFormat;

Expand Down Expand Up @@ -266,7 +266,7 @@ impl ToolchainListSettings {
}
}

/// The resolved settings to use for a `toolchain fetch` invocation.
/// The resolved settings to use for a `toolchain install` invocation.
#[allow(clippy::struct_excessive_bools)]
#[derive(Debug, Clone)]
pub(crate) struct ToolchainInstallSettings {
Expand All @@ -283,6 +283,30 @@ impl ToolchainInstallSettings {
}
}

/// The resolved settings to use for a `toolchain find` invocation.
#[allow(clippy::struct_excessive_bools)]
#[derive(Debug, Clone)]
pub(crate) struct ToolchainFindSettings {
pub(crate) version: Option<String>,
pub(crate) implementation: Option<String>,
}

impl ToolchainFindSettings {
/// Resolve the [`ToolchainFindSettings`] from the CLI and workspace configuration.
#[allow(clippy::needless_pass_by_value)]
pub(crate) fn resolve(args: ToolchainFindArgs, _workspace: Option<Workspace>) -> Self {
let ToolchainFindArgs {
version,
implementation,
} = args;

Self {
version,
implementation,
}
}
}

/// The resolved settings to use for a `sync` invocation.
#[allow(clippy::struct_excessive_bools, dead_code)]
#[derive(Debug, Clone)]
Expand Down
30 changes: 29 additions & 1 deletion crates/uv/tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
use assert_cmd::assert::{Assert, OutputAssertExt};
use assert_cmd::Command;
use assert_fs::assert::PathAssert;
use assert_fs::fixture::PathChild;
use assert_fs::fixture::{ChildPath, PathChild};
use regex::Regex;
use std::borrow::BorrowMut;
use std::env;
Expand Down Expand Up @@ -274,6 +274,34 @@ impl TestContext {
command
}

pub fn toolchains_dir(&self) -> ChildPath {
self.temp_dir.child("toolchains")
}

/// Create a `uv toolchain find` command with options shared across scenarios.
pub fn toolchain_find(&self) -> std::process::Command {
let mut command = std::process::Command::new(get_bin());
command
.arg("toolchain")
.arg("find")
.arg("--cache-dir")
.arg(self.cache_dir.path())
.env("VIRTUAL_ENV", self.venv.as_os_str())
.env("UV_NO_WRAP", "1")
.env("UV_TEST_PYTHON_PATH", "/dev/null")
.env("UV_PREVIEW", "1")
.env("UV_TOOLCHAIN_DIR", self.toolchains_dir().as_os_str())
.current_dir(&self.temp_dir);

if cfg!(all(windows, debug_assertions)) {
// TODO(konstin): Reduce stack usage in debug mode enough that the tests pass with the
// default windows stack of 1MB
command.env("UV_STACK_SIZE", (4 * 1024 * 1024).to_string());
}

command
}

/// Create a `uv run` command with options shared across scenarios.
pub fn run(&self) -> std::process::Command {
let mut command = self.run_without_exclude_newer();
Expand Down
Loading

0 comments on commit 1fc5643

Please sign in to comment.