mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 13:25:00 +00:00
Add puffin venv
command to create virtual environments (#83)
Closes https://github.com/astral-sh/puffin/issues/58.
This commit is contained in:
parent
a0294a510c
commit
d0764bdc23
8 changed files with 76 additions and 28 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -1773,6 +1773,7 @@ dependencies = [
|
|||
"colored",
|
||||
"directories",
|
||||
"futures",
|
||||
"gourgeist",
|
||||
"indicatif",
|
||||
"install-wheel-rs",
|
||||
"itertools 0.11.0",
|
||||
|
@ -1791,6 +1792,7 @@ dependencies = [
|
|||
"tracing-subscriber",
|
||||
"tracing-tree",
|
||||
"url",
|
||||
"which",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -23,7 +23,12 @@ pub struct InterpreterInfo {
|
|||
}
|
||||
|
||||
/// Gets the interpreter.rs info, either cached or by running it.
|
||||
pub fn get_interpreter_info(interpreter: &Utf8Path) -> Result<InterpreterInfo, Error> {
|
||||
pub fn get_interpreter_info(
|
||||
interpreter: impl AsRef<std::path::Path>,
|
||||
) -> Result<InterpreterInfo, Error> {
|
||||
let interpreter = Utf8Path::from_path(interpreter.as_ref())
|
||||
.ok_or_else(|| Error::NonUTF8Path(interpreter.as_ref().to_path_buf()))?;
|
||||
|
||||
let cache_dir = crate_cache_dir()?.join("interpreter_info");
|
||||
|
||||
let index = seahash::hash(interpreter.as_str().as_bytes());
|
||||
|
|
|
@ -50,6 +50,8 @@ pub enum Error {
|
|||
#[source]
|
||||
err: install_wheel_rs::Error,
|
||||
},
|
||||
#[error("{0} is not a valid UTF-8 path")]
|
||||
NonUTF8Path(std::path::PathBuf),
|
||||
}
|
||||
|
||||
pub(crate) fn crate_cache_dir() -> io::Result<Utf8PathBuf> {
|
||||
|
@ -61,11 +63,16 @@ pub(crate) fn crate_cache_dir() -> io::Result<Utf8PathBuf> {
|
|||
|
||||
/// Create a virtualenv and if not bare, install `wheel`, `pip` and `setuptools`.
|
||||
pub fn create_venv(
|
||||
location: &Utf8Path,
|
||||
base_python: &Utf8Path,
|
||||
location: impl AsRef<std::path::Path>,
|
||||
base_python: impl AsRef<std::path::Path>,
|
||||
info: &InterpreterInfo,
|
||||
bare: bool,
|
||||
) -> Result<(), Error> {
|
||||
let location = Utf8Path::from_path(location.as_ref())
|
||||
.ok_or_else(|| Error::NonUTF8Path(location.as_ref().to_path_buf()))?;
|
||||
let base_python = Utf8Path::from_path(base_python.as_ref())
|
||||
.ok_or_else(|| Error::NonUTF8Path(base_python.as_ref().to_path_buf()))?;
|
||||
|
||||
let paths = create_bare_venv(location, base_python, info)?;
|
||||
|
||||
if !bare {
|
||||
|
|
|
@ -25,7 +25,7 @@ fn run() -> Result<(), gourgeist::Error> {
|
|||
let location = cli.path.unwrap_or(Utf8PathBuf::from(".venv"));
|
||||
let python = parse_python_cli(cli.python)?;
|
||||
let data = get_interpreter_info(&python)?;
|
||||
create_venv(&location, &python, &data, cli.bare)?;
|
||||
create_venv(location, &python, &data, cli.bare)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ name = "puffin"
|
|||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
gourgeist = { path = "../gourgeist" }
|
||||
install-wheel-rs = { path = "../install-wheel-rs", default-features = false }
|
||||
pep440_rs = { path = "../pep440-rs" }
|
||||
pep508_rs = { path = "../pep508-rs" }
|
||||
|
@ -34,3 +35,4 @@ tracing = { workspace = true }
|
|||
tracing-subscriber = { workspace = true }
|
||||
tracing-tree = { workspace = true }
|
||||
url = { workspace = true }
|
||||
which = { workspace = true}
|
||||
|
|
|
@ -6,6 +6,7 @@ pub(crate) use compile::compile;
|
|||
pub(crate) use freeze::freeze;
|
||||
pub(crate) use sync::{sync, SyncFlags};
|
||||
pub(crate) use uninstall::uninstall;
|
||||
pub(crate) use venv::venv;
|
||||
|
||||
mod clean;
|
||||
mod compile;
|
||||
|
@ -13,6 +14,7 @@ mod freeze;
|
|||
mod reporters;
|
||||
mod sync;
|
||||
mod uninstall;
|
||||
mod venv;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub(crate) enum ExitStatus {
|
||||
|
|
36
crates/puffin-cli/src/commands/venv.rs
Normal file
36
crates/puffin-cli/src/commands/venv.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
use std::fmt::Write;
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
use colored::Colorize;
|
||||
|
||||
use crate::commands::ExitStatus;
|
||||
use crate::printer::Printer;
|
||||
|
||||
/// Create a virtual environment.
|
||||
pub(crate) async fn venv(path: &Path, mut printer: Printer) -> Result<ExitStatus> {
|
||||
// Locate the Python interpreter.
|
||||
// TODO(charlie): Look at how Maturin discovers and ranks all the available Python interpreters.
|
||||
let executable = which::which("python3").or_else(|_| which::which("python"))?;
|
||||
let interpreter_info = gourgeist::get_interpreter_info(&executable)?;
|
||||
writeln!(
|
||||
printer,
|
||||
"Using Python interpreter: {}",
|
||||
format!("{}", executable.display()).cyan()
|
||||
)?;
|
||||
|
||||
// If the path already exists, remove it.
|
||||
tokio::fs::remove_file(path).await.ok();
|
||||
tokio::fs::remove_dir_all(path).await.ok();
|
||||
|
||||
writeln!(
|
||||
printer,
|
||||
"Creating virtual environment at: {}",
|
||||
format!("{}", path.display()).cyan()
|
||||
)?;
|
||||
|
||||
// Create the virtual environment.
|
||||
gourgeist::create_venv(path, &executable, &interpreter_info, true)?;
|
||||
|
||||
Ok(ExitStatus::Success)
|
||||
}
|
|
@ -25,6 +25,10 @@ struct Cli {
|
|||
/// Use verbose output.
|
||||
#[arg(global = true, long, short, conflicts_with = "quiet")]
|
||||
verbose: bool,
|
||||
|
||||
/// Avoid reading from or writing to the cache.
|
||||
#[arg(long)]
|
||||
no_cache: bool,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
|
@ -36,19 +40,17 @@ enum Commands {
|
|||
/// Clear the cache.
|
||||
Clean,
|
||||
/// Enumerate the installed packages in the current environment.
|
||||
Freeze(FreezeArgs),
|
||||
Freeze,
|
||||
/// Uninstall a package.
|
||||
Uninstall(UninstallArgs),
|
||||
/// Create a virtual environment.
|
||||
Venv(VenvArgs),
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
struct CompileArgs {
|
||||
/// Path to the `requirements.txt` file to compile.
|
||||
src: PathBuf,
|
||||
|
||||
/// Avoid reading from or writing to the cache.
|
||||
#[arg(long)]
|
||||
no_cache: bool,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
|
@ -56,30 +58,21 @@ struct SyncArgs {
|
|||
/// Path to the `requirements.txt` file to install.
|
||||
src: PathBuf,
|
||||
|
||||
/// Avoid reading from or writing to the cache.
|
||||
#[arg(long)]
|
||||
no_cache: bool,
|
||||
|
||||
/// Ignore any installed packages, forcing a re-installation.
|
||||
#[arg(long)]
|
||||
ignore_installed: bool,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
struct FreezeArgs {
|
||||
/// Avoid reading from or writing to the cache.
|
||||
#[arg(long)]
|
||||
no_cache: bool,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
struct UninstallArgs {
|
||||
/// The name of the package to uninstall.
|
||||
name: String,
|
||||
}
|
||||
|
||||
/// Avoid reading from or writing to the cache.
|
||||
#[arg(long)]
|
||||
no_cache: bool,
|
||||
#[derive(Args)]
|
||||
struct VenvArgs {
|
||||
/// The path to the virtual environment to create.
|
||||
name: PathBuf,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
|
@ -108,7 +101,7 @@ async fn main() -> ExitCode {
|
|||
&args.src,
|
||||
dirs.as_ref()
|
||||
.map(ProjectDirs::cache_dir)
|
||||
.filter(|_| !args.no_cache),
|
||||
.filter(|_| !cli.no_cache),
|
||||
printer,
|
||||
)
|
||||
.await
|
||||
|
@ -118,7 +111,7 @@ async fn main() -> ExitCode {
|
|||
&args.src,
|
||||
dirs.as_ref()
|
||||
.map(ProjectDirs::cache_dir)
|
||||
.filter(|_| !args.no_cache),
|
||||
.filter(|_| !cli.no_cache),
|
||||
if args.ignore_installed {
|
||||
commands::SyncFlags::IGNORE_INSTALLED
|
||||
} else {
|
||||
|
@ -131,11 +124,11 @@ async fn main() -> ExitCode {
|
|||
Commands::Clean => {
|
||||
commands::clean(dirs.as_ref().map(ProjectDirs::cache_dir), printer).await
|
||||
}
|
||||
Commands::Freeze(args) => {
|
||||
Commands::Freeze => {
|
||||
commands::freeze(
|
||||
dirs.as_ref()
|
||||
.map(ProjectDirs::cache_dir)
|
||||
.filter(|_| !args.no_cache),
|
||||
.filter(|_| !cli.no_cache),
|
||||
printer,
|
||||
)
|
||||
.await
|
||||
|
@ -145,11 +138,12 @@ async fn main() -> ExitCode {
|
|||
&args.name,
|
||||
dirs.as_ref()
|
||||
.map(ProjectDirs::cache_dir)
|
||||
.filter(|_| !args.no_cache),
|
||||
.filter(|_| !cli.no_cache),
|
||||
printer,
|
||||
)
|
||||
.await
|
||||
}
|
||||
Commands::Venv(args) => commands::venv(&args.name, printer).await,
|
||||
};
|
||||
|
||||
match result {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue