mirror of
https://github.com/astral-sh/uv.git
synced 2025-10-01 14:31:12 +00:00
Add uv run
command (#3074)
Adds `uv run` which executes a command in your current virtual environment. This is a simple first milestone, lots of remaining work and behavior. The command is hidden.
This commit is contained in:
parent
2cee7525c7
commit
f7b83e9e83
4 changed files with 79 additions and 0 deletions
|
@ -111,6 +111,8 @@ pub(crate) enum Commands {
|
|||
/// Clear the cache, removing all entries or those linked to specific packages.
|
||||
#[command(hide = true)]
|
||||
Clean(CleanArgs),
|
||||
#[clap(hide = true)]
|
||||
Run(RunArgs),
|
||||
/// Display uv's version
|
||||
Version {
|
||||
#[arg(long, value_enum, default_value = "text")]
|
||||
|
@ -1307,6 +1309,17 @@ pub(crate) struct VenvArgs {
|
|||
pub(crate) compat_args: compat::VenvCompatArgs,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
pub(crate) struct RunArgs {
|
||||
/// Command
|
||||
pub(crate) command: String,
|
||||
|
||||
/// Arguments
|
||||
#[clap(allow_hyphen_values = true)]
|
||||
pub(crate) args: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
struct AddArgs {
|
||||
|
|
|
@ -16,6 +16,7 @@ pub(crate) use pip_list::pip_list;
|
|||
pub(crate) use pip_show::pip_show;
|
||||
pub(crate) use pip_sync::pip_sync;
|
||||
pub(crate) use pip_uninstall::pip_uninstall;
|
||||
pub(crate) use run::run;
|
||||
#[cfg(feature = "self-update")]
|
||||
pub(crate) use self_update::self_update;
|
||||
use uv_cache::Cache;
|
||||
|
@ -40,6 +41,7 @@ mod pip_show;
|
|||
mod pip_sync;
|
||||
mod pip_uninstall;
|
||||
mod reporters;
|
||||
mod run;
|
||||
#[cfg(feature = "self-update")]
|
||||
mod self_update;
|
||||
mod venv;
|
||||
|
|
63
crates/uv/src/commands/run.rs
Normal file
63
crates/uv/src/commands/run.rs
Normal file
|
@ -0,0 +1,63 @@
|
|||
use std::ffi::OsString;
|
||||
use std::{env, iter};
|
||||
|
||||
use anyhow::Result;
|
||||
use owo_colors::OwoColorize;
|
||||
use tracing::debug;
|
||||
use uv_fs::Simplified;
|
||||
use uv_interpreter::PythonEnvironment;
|
||||
|
||||
use crate::commands::ExitStatus;
|
||||
use tokio::process::Command;
|
||||
use uv_cache::Cache;
|
||||
|
||||
/// Run a command.
|
||||
#[allow(clippy::unnecessary_wraps, clippy::too_many_arguments)]
|
||||
pub(crate) async fn run(command: String, args: Vec<String>, cache: &Cache) -> Result<ExitStatus> {
|
||||
debug!("Running `{command} {}`", args.join(" "));
|
||||
|
||||
// Detect the current Python interpreter.
|
||||
// TODO(zanieb): Create ephemeral environments
|
||||
// TODO(zanieb): Accept `--python`
|
||||
let python_env = match PythonEnvironment::from_virtualenv(cache) {
|
||||
Ok(env) => Some(env),
|
||||
Err(uv_interpreter::Error::VenvNotFound) => None,
|
||||
Err(err) => return Err(err.into()),
|
||||
};
|
||||
|
||||
// Construct the command
|
||||
let mut process = Command::new(command);
|
||||
process.args(args);
|
||||
|
||||
// Set up the PATH
|
||||
if let Some(python_env) = python_env {
|
||||
debug!(
|
||||
"Using Python {} environment at {}",
|
||||
python_env.interpreter().python_version(),
|
||||
python_env.python_executable().user_display().cyan()
|
||||
);
|
||||
let new_path = if let Some(path) = std::env::var_os("PATH") {
|
||||
let python_env_path =
|
||||
iter::once(python_env.scripts().to_path_buf()).chain(env::split_paths(&path));
|
||||
env::join_paths(python_env_path)?
|
||||
} else {
|
||||
OsString::from(python_env.scripts())
|
||||
};
|
||||
|
||||
process.env("PATH", new_path);
|
||||
};
|
||||
|
||||
// Spawn and wait for completion
|
||||
// Standard input, output, and error streams are all inherited
|
||||
// TODO(zanieb): Throw a nicer error message if the command is not found
|
||||
let mut handle = process.spawn()?;
|
||||
let status = handle.wait().await?;
|
||||
|
||||
// Exit based on the result of the command
|
||||
// TODO(zanieb): Do we want to exit with the code of the child process? Probably.
|
||||
if status.success() {
|
||||
Ok(ExitStatus::Success)
|
||||
} else {
|
||||
Ok(ExitStatus::Failure)
|
||||
}
|
||||
}
|
|
@ -539,6 +539,7 @@ async fn run() -> Result<ExitStatus> {
|
|||
)
|
||||
.await
|
||||
}
|
||||
Commands::Run(args) => commands::run(args.command, args.args, &cache).await,
|
||||
#[cfg(feature = "self-update")]
|
||||
Commands::Self_(SelfNamespace {
|
||||
command: SelfCommand::Update,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue