mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 21:35:00 +00:00

This PR makes the cache non-optional in most of Puffin, which simplifies the code, allows us to reuse the cache within a single command (even with `--no-cache`), and also allows us to use the cache for disk storage across an invocation. I left the cache as optional for the `Virtualenv` and `InterpreterInfo` abstractions, since those are generic enough that it seems nice to have a non-cached version, but it's kind of arbitrary.
94 lines
2.5 KiB
Rust
94 lines
2.5 KiB
Rust
use std::fmt::Write;
|
|
use std::path::Path;
|
|
|
|
use anyhow::Result;
|
|
use colored::Colorize;
|
|
use fs_err as fs;
|
|
use miette::{Diagnostic, IntoDiagnostic};
|
|
use platform_host::Platform;
|
|
use puffin_interpreter::InterpreterInfo;
|
|
use thiserror::Error;
|
|
|
|
use crate::commands::ExitStatus;
|
|
use crate::printer::Printer;
|
|
|
|
/// Create a virtual environment.
|
|
#[allow(clippy::unnecessary_wraps)]
|
|
pub(crate) fn venv(
|
|
path: &Path,
|
|
base_python: Option<&Path>,
|
|
printer: Printer,
|
|
) -> Result<ExitStatus> {
|
|
match venv_impl(path, base_python, printer) {
|
|
Ok(status) => Ok(status),
|
|
Err(err) => {
|
|
#[allow(clippy::print_stderr)]
|
|
{
|
|
eprint!("{err:?}");
|
|
}
|
|
Ok(ExitStatus::Failure)
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Error, Debug, Diagnostic)]
|
|
enum VenvError {
|
|
#[error("Unable to find a Python interpreter")]
|
|
#[diagnostic(code(puffin::venv::python_not_found))]
|
|
PythonNotFound,
|
|
|
|
#[error("Failed to extract Python interpreter info")]
|
|
#[diagnostic(code(puffin::venv::interpreter))]
|
|
InterpreterError(#[source] anyhow::Error),
|
|
|
|
#[error("Failed to create virtual environment")]
|
|
#[diagnostic(code(puffin::venv::creation))]
|
|
CreationError(#[source] gourgeist::Error),
|
|
}
|
|
|
|
/// Create a virtual environment.
|
|
fn venv_impl(
|
|
path: &Path,
|
|
base_python: Option<&Path>,
|
|
mut printer: Printer,
|
|
) -> miette::Result<ExitStatus> {
|
|
// Locate the Python interpreter.
|
|
let base_python = if let Some(base_python) = base_python {
|
|
fs::canonicalize(base_python).into_diagnostic()?
|
|
} else {
|
|
fs::canonicalize(
|
|
which::which_global("python3")
|
|
.or_else(|_| which::which_global("python"))
|
|
.map_err(|_| VenvError::PythonNotFound)?,
|
|
)
|
|
.into_diagnostic()?
|
|
};
|
|
|
|
let platform = Platform::current().into_diagnostic()?;
|
|
let interpreter_info = InterpreterInfo::query(&base_python, platform, None)
|
|
.map_err(VenvError::InterpreterError)?;
|
|
|
|
writeln!(
|
|
printer,
|
|
"Using Python interpreter: {}",
|
|
format!("{}", base_python.display()).cyan()
|
|
)
|
|
.into_diagnostic()?;
|
|
|
|
// If the path already exists, remove it.
|
|
fs::remove_file(path).ok();
|
|
fs::remove_dir_all(path).ok();
|
|
|
|
writeln!(
|
|
printer,
|
|
"Creating virtual environment at: {}",
|
|
format!("{}", path.display()).cyan()
|
|
)
|
|
.into_diagnostic()?;
|
|
|
|
// Create the virtual environment.
|
|
gourgeist::create_venv(path, &base_python, &interpreter_info, true)
|
|
.map_err(VenvError::CreationError)?;
|
|
|
|
Ok(ExitStatus::Success)
|
|
}
|