Allow uv crate to be used as a library (#4642)

This is pulled out of #4632 — a user noted that it would be useful to
use the `uv` crate from Rust. This makes it way easier to invoke `uv`
from Rust with arbitrary arguments as well as use various functionality
in the `uv` crate.

Note this is no longer needed for #4632 and is not particularly urgent.
This commit is contained in:
Zanie Blue 2024-07-10 13:15:54 -04:00 committed by GitHub
parent e0fae8e6f4
commit c14de2a92a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 93 additions and 72 deletions

7
crates/uv/src/bin/uv.rs Normal file
View file

@ -0,0 +1,7 @@
use std::process::ExitCode;
use uv::main as uv_main;
fn main() -> ExitCode {
uv_main(std::env::args_os())
}

View file

@ -1,4 +1,5 @@
use std::env;
use std::ffi::OsString;
use std::fmt::Write;
use std::io::stdout;
use std::path::PathBuf;
@ -48,76 +49,15 @@ static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
#[global_allocator]
static GLOBAL: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
mod commands;
mod logging;
mod printer;
mod settings;
mod shell;
mod version;
#[instrument]
async fn run() -> Result<ExitStatus> {
let cli = match Cli::try_parse() {
Ok(cli) => cli,
Err(mut err) => {
if let Some(ContextValue::String(subcommand)) = err.get(ContextKind::InvalidSubcommand)
{
match subcommand.as_str() {
"compile" | "lock" => {
err.insert(
ContextKind::SuggestedSubcommand,
ContextValue::String("uv pip compile".to_string()),
);
}
"sync" => {
err.insert(
ContextKind::SuggestedSubcommand,
ContextValue::String("uv pip sync".to_string()),
);
}
"install" | "add" => {
err.insert(
ContextKind::SuggestedSubcommand,
ContextValue::String("uv pip install".to_string()),
);
}
"uninstall" | "remove" => {
err.insert(
ContextKind::SuggestedSubcommand,
ContextValue::String("uv pip uninstall".to_string()),
);
}
"freeze" => {
err.insert(
ContextKind::SuggestedSubcommand,
ContextValue::String("uv pip freeze".to_string()),
);
}
"list" => {
err.insert(
ContextKind::SuggestedSubcommand,
ContextValue::String("uv pip list".to_string()),
);
}
"show" => {
err.insert(
ContextKind::SuggestedSubcommand,
ContextValue::String("uv pip show".to_string()),
);
}
"tree" => {
err.insert(
ContextKind::SuggestedSubcommand,
ContextValue::String("uv pip tree".to_string()),
);
}
_ => {}
}
}
err.exit()
}
};
pub(crate) mod commands;
pub(crate) mod logging;
pub(crate) mod printer;
pub(crate) mod settings;
pub(crate) mod shell;
pub(crate) mod version;
#[instrument(skip_all)]
async fn run(cli: Cli) -> Result<ExitStatus> {
// enable flag to pick up warnings generated by workspace loading.
if !cli.global_args.quiet {
uv_warnings::enable();
@ -1030,7 +970,81 @@ async fn run_project(
}
}
fn main() -> ExitCode {
/// The main entry point for a uv invocation.
///
/// WARNING: This entry point is not recommended for external consumption, the
/// `uv` binary interface is the official public API. When using this entry
/// point, uv assumes it is running in a process it controls and that the
/// entire process lifetime is managed by uv. Unexpected behavior may be
/// encountered if this entry pointis called multiple times in a single process.
pub fn main<I, T>(args: I) -> ExitCode
where
I: IntoIterator<Item = T>,
T: Into<OsString> + Clone,
{
// `std::env::args` is not `Send` so we parse before passing to our runtime
// https://github.com/rust-lang/rust/pull/48005
let cli = match Cli::try_parse_from(args) {
Ok(cli) => cli,
Err(mut err) => {
if let Some(ContextValue::String(subcommand)) = err.get(ContextKind::InvalidSubcommand)
{
match subcommand.as_str() {
"compile" | "lock" => {
err.insert(
ContextKind::SuggestedSubcommand,
ContextValue::String("uv pip compile".to_string()),
);
}
"sync" => {
err.insert(
ContextKind::SuggestedSubcommand,
ContextValue::String("uv pip sync".to_string()),
);
}
"install" | "add" => {
err.insert(
ContextKind::SuggestedSubcommand,
ContextValue::String("uv pip install".to_string()),
);
}
"uninstall" | "remove" => {
err.insert(
ContextKind::SuggestedSubcommand,
ContextValue::String("uv pip uninstall".to_string()),
);
}
"freeze" => {
err.insert(
ContextKind::SuggestedSubcommand,
ContextValue::String("uv pip freeze".to_string()),
);
}
"list" => {
err.insert(
ContextKind::SuggestedSubcommand,
ContextValue::String("uv pip list".to_string()),
);
}
"show" => {
err.insert(
ContextKind::SuggestedSubcommand,
ContextValue::String("uv pip show".to_string()),
);
}
"tree" => {
err.insert(
ContextKind::SuggestedSubcommand,
ContextValue::String("uv pip tree".to_string()),
);
}
_ => {}
}
}
err.exit()
}
};
// Windows has a default stack size of 1MB, which is lower than the linux and mac default.
// https://learn.microsoft.com/en-us/cpp/build/reference/stack-stack-allocations?view=msvc-170
// We support increasing the stack size to avoid stack overflows in debug mode on Windows. In
@ -1045,7 +1059,7 @@ fn main() -> ExitCode {
.build()
.expect("Failed building the Runtime");
// Box the large main future to avoid stack overflows.
let result = runtime.block_on(Box::pin(run()));
let result = runtime.block_on(Box::pin(run(cli)));
// Avoid waiting for pending tasks to complete.
//
// The resolver may have kicked off HTTP requests during resolution that
@ -1066,7 +1080,7 @@ fn main() -> ExitCode {
.build()
.expect("Failed building the Runtime");
// Box the large main future to avoid stack overflows.
let result = runtime.block_on(Box::pin(run()));
let result = runtime.block_on(Box::pin(run(cli)));
runtime.shutdown_background();
result
};