mirror of https://github.com/astral-sh/uv
Always¹ clear temporary directories (#437)
Always¹ clear the temporary directories we create. * Clear source dist downloads: Previously, the temporary directories would remain in the cache dir, now they are cleared properly * Clear wheel file downloads: Delete the `.whl` file, we only need to cache the unpacked wheel * Consistent handling of cache arguments: Abstract the handling for CLI cache args away, again making sure we remove the `--no-cache` temp dir. There are no more `into_path()` calls that persist `TempDir`s that i could find. ¹Assuming drop is run, and deleting the directory doesn't silently error.
This commit is contained in:
parent
0d9d4f9fca
commit
1883dbdc21
|
|
@ -2330,8 +2330,11 @@ dependencies = [
|
||||||
name = "puffin-cache"
|
name = "puffin-cache"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
"directories",
|
||||||
"hex",
|
"hex",
|
||||||
"seahash",
|
"seahash",
|
||||||
|
"tempfile",
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -2348,7 +2351,6 @@ dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
"colored",
|
"colored",
|
||||||
"directories",
|
|
||||||
"fs-err",
|
"fs-err",
|
||||||
"futures",
|
"futures",
|
||||||
"gourgeist",
|
"gourgeist",
|
||||||
|
|
@ -2365,6 +2367,7 @@ dependencies = [
|
||||||
"platform-tags",
|
"platform-tags",
|
||||||
"predicates",
|
"predicates",
|
||||||
"pubgrub",
|
"pubgrub",
|
||||||
|
"puffin-cache",
|
||||||
"puffin-client",
|
"puffin-client",
|
||||||
"puffin-dispatch",
|
"puffin-dispatch",
|
||||||
"puffin-distribution",
|
"puffin-distribution",
|
||||||
|
|
@ -2425,7 +2428,6 @@ dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
"colored",
|
"colored",
|
||||||
"directories",
|
|
||||||
"distribution-filename",
|
"distribution-filename",
|
||||||
"fs-err",
|
"fs-err",
|
||||||
"futures",
|
"futures",
|
||||||
|
|
@ -2437,6 +2439,7 @@ dependencies = [
|
||||||
"platform-host",
|
"platform-host",
|
||||||
"platform-tags",
|
"platform-tags",
|
||||||
"puffin-build",
|
"puffin-build",
|
||||||
|
"puffin-cache",
|
||||||
"puffin-client",
|
"puffin-client",
|
||||||
"puffin-dispatch",
|
"puffin-dispatch",
|
||||||
"puffin-interpreter",
|
"puffin-interpreter",
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,9 @@ authors = { workspace = true }
|
||||||
license = { workspace = true }
|
license = { workspace = true }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
clap = { workspace = true, features = ["derive"], optional = true }
|
||||||
|
directories = { workspace = true }
|
||||||
hex = { workspace = true }
|
hex = { workspace = true }
|
||||||
seahash = { workspace = true }
|
seahash = { workspace = true }
|
||||||
|
tempfile = { workspace = true }
|
||||||
url = { workspace = true }
|
url = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
#![cfg(feature = "clap")]
|
||||||
|
|
||||||
|
use std::io;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use clap::Parser;
|
||||||
|
use directories::ProjectDirs;
|
||||||
|
use tempfile::{tempdir, TempDir};
|
||||||
|
|
||||||
|
#[derive(Parser, Debug, Clone)]
|
||||||
|
pub struct CacheArgs {
|
||||||
|
/// Avoid reading from or writing to the cache.
|
||||||
|
#[arg(global = true, long, short)]
|
||||||
|
no_cache: bool,
|
||||||
|
|
||||||
|
/// Path to the cache directory.
|
||||||
|
#[arg(global = true, long, env = "PUFFIN_CACHE_DIR")]
|
||||||
|
cache_dir: Option<PathBuf>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CacheDir {
|
||||||
|
/// The cache directory.
|
||||||
|
cache_dir: PathBuf,
|
||||||
|
/// A temporary cache directory, if the user requested `--no-cache`. Included to ensure that
|
||||||
|
/// the temporary directory exists for the length of the operation, but is dropped at the end
|
||||||
|
/// as appropriate.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
tempdir: Option<TempDir>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<CacheArgs> for CacheDir {
|
||||||
|
type Error = io::Error;
|
||||||
|
|
||||||
|
/// Prefer, in order:
|
||||||
|
/// 1. A temporary cache directory, if the user requested `--no-cache`.
|
||||||
|
/// 2. The specific cache directory specified by the user via `--cache-dir` or `PUFFIN_CACHE_DIR`.
|
||||||
|
/// 3. The system-appropriate cache directory.
|
||||||
|
/// 4. A `.puffin_cache` directory in the current working directory.
|
||||||
|
fn try_from(value: CacheArgs) -> Result<Self, Self::Error> {
|
||||||
|
let project_dirs = ProjectDirs::from("", "", "puffin");
|
||||||
|
if value.no_cache {
|
||||||
|
let tempdir = tempdir()?;
|
||||||
|
let cache_dir = tempdir.path().to_path_buf();
|
||||||
|
Ok(Self {
|
||||||
|
cache_dir,
|
||||||
|
tempdir: Some(tempdir),
|
||||||
|
})
|
||||||
|
} else if let Some(cache_dir) = value.cache_dir {
|
||||||
|
Ok(Self {
|
||||||
|
cache_dir,
|
||||||
|
tempdir: None,
|
||||||
|
})
|
||||||
|
} else if let Some(project_dirs) = project_dirs {
|
||||||
|
Ok(Self {
|
||||||
|
cache_dir: project_dirs.cache_dir().to_path_buf(),
|
||||||
|
tempdir: None,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Ok(Self {
|
||||||
|
cache_dir: PathBuf::from(".puffin_cache"),
|
||||||
|
tempdir: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CacheDir {
|
||||||
|
pub fn path(&self) -> &PathBuf {
|
||||||
|
&self.cache_dir
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,10 +3,13 @@ use std::hash::Hasher;
|
||||||
use seahash::SeaHasher;
|
use seahash::SeaHasher;
|
||||||
|
|
||||||
pub use canonical_url::{CanonicalUrl, RepositoryUrl};
|
pub use canonical_url::{CanonicalUrl, RepositoryUrl};
|
||||||
|
#[cfg(feature = "clap")]
|
||||||
|
pub use cli::{CacheArgs, CacheDir};
|
||||||
pub use digest::digest;
|
pub use digest::digest;
|
||||||
|
|
||||||
mod cache_key;
|
mod cache_key;
|
||||||
mod canonical_url;
|
mod canonical_url;
|
||||||
|
mod cli;
|
||||||
mod digest;
|
mod digest;
|
||||||
|
|
||||||
/// A trait for types that can be hashed in a stable way across versions and platforms.
|
/// A trait for types that can be hashed in a stable way across versions and platforms.
|
||||||
|
|
|
||||||
|
|
@ -20,16 +20,17 @@ pep440_rs = { path = "../pep440-rs" }
|
||||||
pep508_rs = { path = "../pep508-rs" }
|
pep508_rs = { path = "../pep508-rs" }
|
||||||
platform-host = { path = "../platform-host" }
|
platform-host = { path = "../platform-host" }
|
||||||
platform-tags = { path = "../platform-tags" }
|
platform-tags = { path = "../platform-tags" }
|
||||||
|
puffin-cache = { path = "../puffin-cache" }
|
||||||
puffin-client = { path = "../puffin-client" }
|
puffin-client = { path = "../puffin-client" }
|
||||||
puffin-dispatch = { path = "../puffin-dispatch" }
|
puffin-dispatch = { path = "../puffin-dispatch" }
|
||||||
puffin-distribution = { path = "../puffin-distribution" }
|
puffin-distribution = { path = "../puffin-distribution" }
|
||||||
puffin-installer = { path = "../puffin-installer" }
|
puffin-installer = { path = "../puffin-installer" }
|
||||||
puffin-interpreter = { path = "../puffin-interpreter" }
|
puffin-interpreter = { path = "../puffin-interpreter" }
|
||||||
puffin-normalize = { path = "../puffin-normalize" }
|
puffin-normalize = { path = "../puffin-normalize" }
|
||||||
pypi-types = { path = "../pypi-types" }
|
|
||||||
requirements-txt = { path = "../requirements-txt" }
|
|
||||||
puffin-resolver = { path = "../puffin-resolver", features = ["clap"] }
|
puffin-resolver = { path = "../puffin-resolver", features = ["clap"] }
|
||||||
puffin-workspace = { path = "../puffin-workspace" }
|
puffin-workspace = { path = "../puffin-workspace" }
|
||||||
|
pypi-types = { path = "../pypi-types" }
|
||||||
|
requirements-txt = { path = "../requirements-txt" }
|
||||||
|
|
||||||
anstream = { workspace = true }
|
anstream = { workspace = true }
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
|
|
@ -38,7 +39,6 @@ cacache = { workspace = true }
|
||||||
chrono = { workspace = true }
|
chrono = { workspace = true }
|
||||||
clap = { workspace = true, features = ["derive"] }
|
clap = { workspace = true, features = ["derive"] }
|
||||||
colored = { workspace = true }
|
colored = { workspace = true }
|
||||||
directories = { workspace = true }
|
|
||||||
fs-err = { workspace = true, features = ["tokio"] }
|
fs-err = { workspace = true, features = ["tokio"] }
|
||||||
futures = { workspace = true }
|
futures = { workspace = true }
|
||||||
indicatif = { workspace = true }
|
indicatif = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
use std::borrow::Cow;
|
use std::path::PathBuf;
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
use std::process::ExitCode;
|
use std::process::ExitCode;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
|
@ -7,10 +6,9 @@ use anyhow::Result;
|
||||||
use chrono::{DateTime, Days, NaiveDate, NaiveTime, Utc};
|
use chrono::{DateTime, Days, NaiveDate, NaiveTime, Utc};
|
||||||
use clap::{Args, Parser, Subcommand};
|
use clap::{Args, Parser, Subcommand};
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use directories::ProjectDirs;
|
|
||||||
use tempfile::tempdir;
|
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
use puffin_cache::{CacheArgs, CacheDir};
|
||||||
use puffin_normalize::{ExtraName, PackageName};
|
use puffin_normalize::{ExtraName, PackageName};
|
||||||
use puffin_resolver::{PreReleaseMode, ResolutionMode};
|
use puffin_resolver::{PreReleaseMode, ResolutionMode};
|
||||||
use requirements::ExtrasSpecification;
|
use requirements::ExtrasSpecification;
|
||||||
|
|
@ -58,13 +56,8 @@ struct Cli {
|
||||||
#[arg(global = true, long, short, conflicts_with = "quiet")]
|
#[arg(global = true, long, short, conflicts_with = "quiet")]
|
||||||
verbose: bool,
|
verbose: bool,
|
||||||
|
|
||||||
/// Avoid reading from or writing to the cache.
|
#[command(flatten)]
|
||||||
#[arg(global = true, long, short)]
|
cache_args: CacheArgs,
|
||||||
no_cache: bool,
|
|
||||||
|
|
||||||
/// Path to the cache directory.
|
|
||||||
#[arg(global = true, long, env = "PUFFIN_CACHE_DIR")]
|
|
||||||
cache_dir: Option<PathBuf>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
|
|
@ -256,21 +249,7 @@ async fn inner() -> Result<ExitStatus> {
|
||||||
printer::Printer::Default
|
printer::Printer::Default
|
||||||
};
|
};
|
||||||
|
|
||||||
// Prefer, in order:
|
let cache_dir = CacheDir::try_from(cli.cache_args)?;
|
||||||
// 1. A temporary cache directory, if the user requested `--no-cache`.
|
|
||||||
// 2. The specific cache directory specified by the user via `--cache-dir` or `PUFFIN_CACHE_DIR`.
|
|
||||||
// 3. The system-appropriate cache directory.
|
|
||||||
// 4. A `.puffin_cache` directory in the current working directory.
|
|
||||||
let project_dirs = ProjectDirs::from("", "", "puffin");
|
|
||||||
let cache_dir = if cli.no_cache {
|
|
||||||
Cow::Owned(tempdir()?.into_path())
|
|
||||||
} else if let Some(cache_dir) = cli.cache_dir {
|
|
||||||
Cow::Owned(cache_dir)
|
|
||||||
} else if let Some(project_dirs) = project_dirs.as_ref() {
|
|
||||||
Cow::Borrowed(project_dirs.cache_dir())
|
|
||||||
} else {
|
|
||||||
Cow::Borrowed(Path::new(".puffin_cache"))
|
|
||||||
};
|
|
||||||
|
|
||||||
match cli.command {
|
match cli.command {
|
||||||
Commands::PipCompile(args) => {
|
Commands::PipCompile(args) => {
|
||||||
|
|
@ -307,7 +286,7 @@ async fn inner() -> Result<ExitStatus> {
|
||||||
args.no_build,
|
args.no_build,
|
||||||
args.python_version,
|
args.python_version,
|
||||||
args.exclude_newer,
|
args.exclude_newer,
|
||||||
&cache_dir,
|
cache_dir.path(),
|
||||||
printer,
|
printer,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|
@ -325,7 +304,7 @@ async fn inner() -> Result<ExitStatus> {
|
||||||
args.link_mode.unwrap_or_default(),
|
args.link_mode.unwrap_or_default(),
|
||||||
index_urls,
|
index_urls,
|
||||||
args.no_build,
|
args.no_build,
|
||||||
&cache_dir,
|
cache_dir.path(),
|
||||||
printer,
|
printer,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|
@ -337,10 +316,10 @@ async fn inner() -> Result<ExitStatus> {
|
||||||
.map(RequirementsSource::from)
|
.map(RequirementsSource::from)
|
||||||
.chain(args.requirement.into_iter().map(RequirementsSource::from))
|
.chain(args.requirement.into_iter().map(RequirementsSource::from))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
commands::pip_uninstall(&sources, &cache_dir, printer).await
|
commands::pip_uninstall(&sources, cache_dir.path(), printer).await
|
||||||
}
|
}
|
||||||
Commands::Clean => commands::clean(&cache_dir, printer),
|
Commands::Clean => commands::clean(cache_dir.path(), printer),
|
||||||
Commands::Freeze => commands::freeze(&cache_dir, printer),
|
Commands::Freeze => commands::freeze(cache_dir.path(), printer),
|
||||||
Commands::Venv(args) => commands::venv(&args.name, args.python.as_deref(), printer),
|
Commands::Venv(args) => commands::venv(&args.name, args.python.as_deref(), printer),
|
||||||
Commands::Add(args) => commands::add(&args.name, printer),
|
Commands::Add(args) => commands::add(&args.name, printer),
|
||||||
Commands::Remove(args) => commands::remove(&args.name, printer),
|
Commands::Remove(args) => commands::remove(&args.name, printer),
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ pep508_rs = { path = "../pep508-rs" }
|
||||||
platform-host = { path = "../platform-host" }
|
platform-host = { path = "../platform-host" }
|
||||||
platform-tags = { path = "../platform-tags" }
|
platform-tags = { path = "../platform-tags" }
|
||||||
puffin-build = { path = "../puffin-build" }
|
puffin-build = { path = "../puffin-build" }
|
||||||
|
puffin-cache = { path = "../puffin-cache", features = ["clap"] }
|
||||||
puffin-client = { path = "../puffin-client" }
|
puffin-client = { path = "../puffin-client" }
|
||||||
puffin-dispatch = { path = "../puffin-dispatch" }
|
puffin-dispatch = { path = "../puffin-dispatch" }
|
||||||
puffin-interpreter = { path = "../puffin-interpreter" }
|
puffin-interpreter = { path = "../puffin-interpreter" }
|
||||||
|
|
@ -27,7 +28,6 @@ anstream = { workspace = true }
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
clap = { workspace = true, features = ["derive"] }
|
clap = { workspace = true, features = ["derive"] }
|
||||||
colored = { workspace = true }
|
colored = { workspace = true }
|
||||||
directories = { workspace = true }
|
|
||||||
fs-err = { workspace = true }
|
fs-err = { workspace = true }
|
||||||
futures = { workspace = true }
|
futures = { workspace = true }
|
||||||
indicatif = { workspace = true }
|
indicatif = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,10 @@ use std::path::PathBuf;
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use directories::ProjectDirs;
|
|
||||||
use fs_err as fs;
|
use fs_err as fs;
|
||||||
|
|
||||||
use platform_host::Platform;
|
use platform_host::Platform;
|
||||||
|
use puffin_cache::{CacheArgs, CacheDir};
|
||||||
use puffin_client::RegistryClientBuilder;
|
use puffin_client::RegistryClientBuilder;
|
||||||
use puffin_dispatch::BuildDispatch;
|
use puffin_dispatch::BuildDispatch;
|
||||||
use puffin_interpreter::Virtualenv;
|
use puffin_interpreter::Virtualenv;
|
||||||
|
|
@ -25,6 +25,8 @@ pub(crate) struct BuildArgs {
|
||||||
sdist: PathBuf,
|
sdist: PathBuf,
|
||||||
/// The subdirectory to build within the source distribution.
|
/// The subdirectory to build within the source distribution.
|
||||||
subdirectory: Option<PathBuf>,
|
subdirectory: Option<PathBuf>,
|
||||||
|
#[command(flatten)]
|
||||||
|
cache_args: CacheArgs,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build a source distribution to a wheel
|
/// Build a source distribution to a wheel
|
||||||
|
|
@ -36,19 +38,14 @@ pub(crate) async fn build(args: BuildArgs) -> Result<PathBuf> {
|
||||||
env::current_dir()?
|
env::current_dir()?
|
||||||
};
|
};
|
||||||
|
|
||||||
let project_dirs = ProjectDirs::from("", "", "puffin");
|
let cache_dir = CacheDir::try_from(args.cache_args)?;
|
||||||
let cache = project_dirs
|
|
||||||
.as_ref()
|
|
||||||
.map(|project_dirs| project_dirs.cache_dir().to_path_buf())
|
|
||||||
.or_else(|| Some(tempfile::tempdir().ok()?.into_path()))
|
|
||||||
.unwrap_or_else(|| PathBuf::from(".puffin_cache"));
|
|
||||||
|
|
||||||
let platform = Platform::current()?;
|
let platform = Platform::current()?;
|
||||||
let venv = Virtualenv::from_env(platform, Some(&cache))?;
|
let venv = Virtualenv::from_env(platform, Some(cache_dir.path()))?;
|
||||||
|
|
||||||
let build_dispatch = BuildDispatch::new(
|
let build_dispatch = BuildDispatch::new(
|
||||||
RegistryClientBuilder::new(cache.clone()).build(),
|
RegistryClientBuilder::new(cache_dir.path().clone()).build(),
|
||||||
cache,
|
cache_dir.path().clone(),
|
||||||
venv.interpreter_info().clone(),
|
venv.interpreter_info().clone(),
|
||||||
fs::canonicalize(venv.python_executable())?,
|
fs::canonicalize(venv.python_executable())?,
|
||||||
false,
|
false,
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,11 @@ use std::path::PathBuf;
|
||||||
|
|
||||||
use anstream::println;
|
use anstream::println;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use directories::ProjectDirs;
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
use pep508_rs::Requirement;
|
use pep508_rs::Requirement;
|
||||||
use platform_host::Platform;
|
use platform_host::Platform;
|
||||||
|
use puffin_cache::{CacheArgs, CacheDir};
|
||||||
use puffin_client::RegistryClientBuilder;
|
use puffin_client::RegistryClientBuilder;
|
||||||
use puffin_dispatch::BuildDispatch;
|
use puffin_dispatch::BuildDispatch;
|
||||||
use puffin_interpreter::Virtualenv;
|
use puffin_interpreter::Virtualenv;
|
||||||
|
|
@ -25,21 +25,18 @@ pub(crate) struct ResolveCliArgs {
|
||||||
/// cached wheels of already built source distributions will be reused.
|
/// cached wheels of already built source distributions will be reused.
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
no_build: bool,
|
no_build: bool,
|
||||||
|
#[command(flatten)]
|
||||||
|
cache_args: CacheArgs,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn resolve_cli(args: ResolveCliArgs) -> anyhow::Result<()> {
|
pub(crate) async fn resolve_cli(args: ResolveCliArgs) -> anyhow::Result<()> {
|
||||||
let project_dirs = ProjectDirs::from("", "", "puffin");
|
let cache_dir = CacheDir::try_from(args.cache_args)?;
|
||||||
let cache = project_dirs
|
|
||||||
.as_ref()
|
|
||||||
.map(|project_dirs| project_dirs.cache_dir().to_path_buf())
|
|
||||||
.or_else(|| Some(tempfile::tempdir().ok()?.into_path()))
|
|
||||||
.unwrap_or_else(|| PathBuf::from(".puffin_cache"));
|
|
||||||
|
|
||||||
let platform = Platform::current()?;
|
let platform = Platform::current()?;
|
||||||
let venv = Virtualenv::from_env(platform, Some(&cache))?;
|
let venv = Virtualenv::from_env(platform, Some(cache_dir.path()))?;
|
||||||
let build_dispatch = BuildDispatch::new(
|
let build_dispatch = BuildDispatch::new(
|
||||||
RegistryClientBuilder::new(cache.clone()).build(),
|
RegistryClientBuilder::new(cache_dir.path().clone()).build(),
|
||||||
cache.clone(),
|
cache_dir.path().clone(),
|
||||||
venv.interpreter_info().clone(),
|
venv.interpreter_info().clone(),
|
||||||
fs::canonicalize(venv.python_executable())?,
|
fs::canonicalize(venv.python_executable())?,
|
||||||
args.no_build,
|
args.no_build,
|
||||||
|
|
|
||||||
|
|
@ -2,32 +2,29 @@ use std::path::PathBuf;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use directories::ProjectDirs;
|
|
||||||
use fs_err as fs;
|
use fs_err as fs;
|
||||||
use futures::stream::FuturesUnordered;
|
use futures::stream::FuturesUnordered;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use indicatif::ProgressStyle;
|
use indicatif::ProgressStyle;
|
||||||
use tokio::sync::Semaphore;
|
|
||||||
use tokio::time::Instant;
|
|
||||||
use tracing::{info, info_span, span, Level, Span};
|
|
||||||
use tracing_indicatif::span_ext::IndicatifSpanExt;
|
|
||||||
|
|
||||||
use pep508_rs::Requirement;
|
use pep508_rs::Requirement;
|
||||||
use platform_host::Platform;
|
use platform_host::Platform;
|
||||||
|
use puffin_cache::{CacheArgs, CacheDir};
|
||||||
use puffin_client::RegistryClientBuilder;
|
use puffin_client::RegistryClientBuilder;
|
||||||
use puffin_dispatch::BuildDispatch;
|
use puffin_dispatch::BuildDispatch;
|
||||||
use puffin_interpreter::Virtualenv;
|
use puffin_interpreter::Virtualenv;
|
||||||
use puffin_traits::BuildContext;
|
use puffin_traits::BuildContext;
|
||||||
|
use tokio::sync::Semaphore;
|
||||||
|
use tokio::time::Instant;
|
||||||
|
use tracing::{info, info_span, span, Level, Span};
|
||||||
|
use tracing_indicatif::span_ext::IndicatifSpanExt;
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
pub(crate) struct ResolveManyArgs {
|
pub(crate) struct ResolveManyArgs {
|
||||||
list: PathBuf,
|
list: PathBuf,
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
limit: Option<usize>,
|
limit: Option<usize>,
|
||||||
/// Path to the cache directory.
|
|
||||||
#[arg(global = true, long, env = "PUFFIN_CACHE_DIR")]
|
|
||||||
cache_dir: Option<PathBuf>,
|
|
||||||
/// Don't build source distributions. This means resolving will not run arbitrary code. The
|
/// Don't build source distributions. This means resolving will not run arbitrary code. The
|
||||||
/// cached wheels of already built source distributions will be reused.
|
/// cached wheels of already built source distributions will be reused.
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
|
|
@ -35,19 +32,12 @@ pub(crate) struct ResolveManyArgs {
|
||||||
/// Run this many tasks in parallel
|
/// Run this many tasks in parallel
|
||||||
#[clap(long, default_value = "50")]
|
#[clap(long, default_value = "50")]
|
||||||
num_tasks: usize,
|
num_tasks: usize,
|
||||||
|
#[command(flatten)]
|
||||||
|
cache_args: CacheArgs,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn resolve_many(args: ResolveManyArgs) -> anyhow::Result<()> {
|
pub(crate) async fn resolve_many(args: ResolveManyArgs) -> Result<()> {
|
||||||
let project_dirs = ProjectDirs::from("", "", "puffin");
|
let cache_dir = CacheDir::try_from(args.cache_args)?;
|
||||||
let cache = args
|
|
||||||
.cache_dir
|
|
||||||
.or_else(|| {
|
|
||||||
project_dirs
|
|
||||||
.as_ref()
|
|
||||||
.map(|project_dirs| project_dirs.cache_dir().to_path_buf())
|
|
||||||
})
|
|
||||||
.or_else(|| Some(tempfile::tempdir().ok()?.into_path()))
|
|
||||||
.unwrap_or_else(|| PathBuf::from(".puffin_cache"));
|
|
||||||
|
|
||||||
let data = fs::read_to_string(&args.list)?;
|
let data = fs::read_to_string(&args.list)?;
|
||||||
let lines = data.lines().map(Requirement::from_str);
|
let lines = data.lines().map(Requirement::from_str);
|
||||||
|
|
@ -58,10 +48,10 @@ pub(crate) async fn resolve_many(args: ResolveManyArgs) -> anyhow::Result<()> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let platform = Platform::current()?;
|
let platform = Platform::current()?;
|
||||||
let venv = Virtualenv::from_env(platform, Some(&cache))?;
|
let venv = Virtualenv::from_env(platform, Some(cache_dir.path()))?;
|
||||||
let build_dispatch = BuildDispatch::new(
|
let build_dispatch = BuildDispatch::new(
|
||||||
RegistryClientBuilder::new(cache.clone()).build(),
|
RegistryClientBuilder::new(cache_dir.path().clone()).build(),
|
||||||
cache.clone(),
|
cache_dir.path().clone(),
|
||||||
venv.interpreter_info().clone(),
|
venv.interpreter_info().clone(),
|
||||||
fs::canonicalize(venv.python_executable())?,
|
fs::canonicalize(venv.python_executable())?,
|
||||||
args.no_build,
|
args.no_build,
|
||||||
|
|
|
||||||
|
|
@ -1,39 +1,24 @@
|
||||||
use std::borrow::Cow;
|
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use directories::ProjectDirs;
|
|
||||||
use tempfile::tempdir;
|
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
use distribution_filename::WheelFilename;
|
use distribution_filename::WheelFilename;
|
||||||
|
use puffin_cache::{CacheArgs, CacheDir};
|
||||||
use puffin_client::RegistryClientBuilder;
|
use puffin_client::RegistryClientBuilder;
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
pub(crate) struct WheelMetadataArgs {
|
pub(crate) struct WheelMetadataArgs {
|
||||||
url: Url,
|
url: Url,
|
||||||
/// Avoid reading from or writing to the cache.
|
#[command(flatten)]
|
||||||
#[arg(global = true, long, short)]
|
cache_args: CacheArgs,
|
||||||
no_cache: bool,
|
|
||||||
/// Path to the cache directory.
|
|
||||||
#[arg(global = true, long, env = "PUFFIN_CACHE_DIR")]
|
|
||||||
cache_dir: Option<PathBuf>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn wheel_metadata(args: WheelMetadataArgs) -> anyhow::Result<()> {
|
pub(crate) async fn wheel_metadata(args: WheelMetadataArgs) -> Result<()> {
|
||||||
let project_dirs = ProjectDirs::from("", "", "puffin");
|
let cache_dir = CacheDir::try_from(args.cache_args)?;
|
||||||
// https://github.com/astral-sh/puffin/issues/366
|
|
||||||
let cache_dir = if args.no_cache {
|
let client = RegistryClientBuilder::new(cache_dir.path().clone()).build();
|
||||||
Cow::Owned(tempdir()?.into_path())
|
|
||||||
} else if let Some(cache_dir) = args.cache_dir {
|
|
||||||
Cow::Owned(cache_dir)
|
|
||||||
} else if let Some(project_dirs) = project_dirs.as_ref() {
|
|
||||||
Cow::Borrowed(project_dirs.cache_dir())
|
|
||||||
} else {
|
|
||||||
Cow::Borrowed(Path::new(".puffin_cache"))
|
|
||||||
};
|
|
||||||
let client = RegistryClientBuilder::new(cache_dir).build();
|
|
||||||
|
|
||||||
let filename = WheelFilename::from_str(
|
let filename = WheelFilename::from_str(
|
||||||
args.url
|
args.url
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,7 @@ async fn build_sdist<T: BuildContext + Send + Sync>(
|
||||||
Ok(WheelDownload::Disk(DiskWheel {
|
Ok(WheelDownload::Disk(DiskWheel {
|
||||||
dist: dist.dist,
|
dist: dist.dist,
|
||||||
path: wheel_filename,
|
path: wheel_filename,
|
||||||
|
temp_dir: None,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use bytesize::ByteSize;
|
use bytesize::ByteSize;
|
||||||
|
use tempfile::TempDir;
|
||||||
use tokio::task::JoinSet;
|
use tokio::task::JoinSet;
|
||||||
use tokio_util::compat::FuturesAsyncReadCompatExt;
|
use tokio_util::compat::FuturesAsyncReadCompatExt;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
@ -146,15 +147,16 @@ async fn fetch(
|
||||||
debug!("Fetching disk-based wheel from registry: {dist} ({size})");
|
debug!("Fetching disk-based wheel from registry: {dist} ({size})");
|
||||||
|
|
||||||
// Download the wheel to a temporary file.
|
// Download the wheel to a temporary file.
|
||||||
let temp_dir = tempfile::tempdir_in(cache)?.into_path();
|
let temp_dir = tempfile::tempdir_in(cache)?;
|
||||||
let wheel_filename = &wheel.file.filename;
|
let wheel_filename = &wheel.file.filename;
|
||||||
let wheel_file = temp_dir.join(wheel_filename);
|
let wheel_file = temp_dir.path().join(wheel_filename);
|
||||||
let mut writer = tokio::fs::File::create(&wheel_file).await?;
|
let mut writer = tokio::fs::File::create(&wheel_file).await?;
|
||||||
tokio::io::copy(&mut reader.compat(), &mut writer).await?;
|
tokio::io::copy(&mut reader.compat(), &mut writer).await?;
|
||||||
|
|
||||||
Ok(Download::Wheel(WheelDownload::Disk(DiskWheel {
|
Ok(Download::Wheel(WheelDownload::Disk(DiskWheel {
|
||||||
dist,
|
dist,
|
||||||
path: wheel_file,
|
path: wheel_file,
|
||||||
|
temp_dir: Some(temp_dir),
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -166,15 +168,16 @@ async fn fetch(
|
||||||
let reader = client.stream_external(&wheel.url).await?;
|
let reader = client.stream_external(&wheel.url).await?;
|
||||||
|
|
||||||
// Download the wheel to a temporary file.
|
// Download the wheel to a temporary file.
|
||||||
let temp_dir = tempfile::tempdir_in(cache)?.into_path();
|
let temp_dir = tempfile::tempdir_in(cache)?;
|
||||||
let wheel_filename = wheel.filename()?;
|
let wheel_filename = wheel.filename()?;
|
||||||
let wheel_file = temp_dir.join(wheel_filename);
|
let wheel_file = temp_dir.path().join(wheel_filename);
|
||||||
let mut writer = tokio::fs::File::create(&wheel_file).await?;
|
let mut writer = tokio::fs::File::create(&wheel_file).await?;
|
||||||
tokio::io::copy(&mut reader.compat(), &mut writer).await?;
|
tokio::io::copy(&mut reader.compat(), &mut writer).await?;
|
||||||
|
|
||||||
Ok(Download::Wheel(WheelDownload::Disk(DiskWheel {
|
Ok(Download::Wheel(WheelDownload::Disk(DiskWheel {
|
||||||
dist,
|
dist,
|
||||||
path: wheel_file,
|
path: wheel_file,
|
||||||
|
temp_dir: Some(temp_dir),
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -188,9 +191,9 @@ async fn fetch(
|
||||||
let reader = client.stream_external(&url).await?;
|
let reader = client.stream_external(&url).await?;
|
||||||
|
|
||||||
// Download the source distribution.
|
// Download the source distribution.
|
||||||
let temp_dir = tempfile::tempdir_in(cache)?.into_path();
|
let temp_dir = tempfile::tempdir_in(cache)?;
|
||||||
let sdist_filename = sdist.filename()?;
|
let sdist_filename = sdist.filename()?;
|
||||||
let sdist_file = temp_dir.join(sdist_filename);
|
let sdist_file = temp_dir.path().join(sdist_filename);
|
||||||
let mut writer = tokio::fs::File::create(&sdist_file).await?;
|
let mut writer = tokio::fs::File::create(&sdist_file).await?;
|
||||||
tokio::io::copy(&mut reader.compat(), &mut writer).await?;
|
tokio::io::copy(&mut reader.compat(), &mut writer).await?;
|
||||||
|
|
||||||
|
|
@ -198,6 +201,7 @@ async fn fetch(
|
||||||
dist,
|
dist,
|
||||||
sdist_file,
|
sdist_file,
|
||||||
subdirectory: None,
|
subdirectory: None,
|
||||||
|
temp_dir: Some(temp_dir),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -210,9 +214,9 @@ async fn fetch(
|
||||||
let mut reader = tokio::io::BufReader::new(reader.compat());
|
let mut reader = tokio::io::BufReader::new(reader.compat());
|
||||||
|
|
||||||
// Download the source distribution.
|
// Download the source distribution.
|
||||||
let temp_dir = tempfile::tempdir_in(cache)?.into_path();
|
let temp_dir = tempfile::tempdir_in(cache)?;
|
||||||
let sdist_filename = sdist.filename()?;
|
let sdist_filename = sdist.filename()?;
|
||||||
let sdist_file = temp_dir.join(sdist_filename);
|
let sdist_file = temp_dir.path().join(sdist_filename);
|
||||||
let mut writer = tokio::fs::File::create(&sdist_file).await?;
|
let mut writer = tokio::fs::File::create(&sdist_file).await?;
|
||||||
tokio::io::copy(&mut reader, &mut writer).await?;
|
tokio::io::copy(&mut reader, &mut writer).await?;
|
||||||
|
|
||||||
|
|
@ -220,6 +224,7 @@ async fn fetch(
|
||||||
dist,
|
dist,
|
||||||
sdist_file,
|
sdist_file,
|
||||||
subdirectory,
|
subdirectory,
|
||||||
|
temp_dir: Some(temp_dir),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -238,6 +243,7 @@ async fn fetch(
|
||||||
dist,
|
dist,
|
||||||
sdist_file,
|
sdist_file,
|
||||||
subdirectory,
|
subdirectory,
|
||||||
|
temp_dir: None,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -267,6 +273,9 @@ pub struct DiskWheel {
|
||||||
pub(crate) dist: Dist,
|
pub(crate) dist: Dist,
|
||||||
/// The path to the downloaded wheel.
|
/// The path to the downloaded wheel.
|
||||||
pub(crate) path: PathBuf,
|
pub(crate) path: PathBuf,
|
||||||
|
/// The download location, to be dropped after use.
|
||||||
|
#[allow(dead_code)] // We only want the drop implementation
|
||||||
|
pub(crate) temp_dir: Option<TempDir>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A downloaded wheel.
|
/// A downloaded wheel.
|
||||||
|
|
@ -287,7 +296,7 @@ impl WheelDownload {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A downloaded source distribution.
|
/// A downloaded source distribution.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug)]
|
||||||
pub struct SourceDistDownload {
|
pub struct SourceDistDownload {
|
||||||
/// The remote distribution from which this source distribution was downloaded.
|
/// The remote distribution from which this source distribution was downloaded.
|
||||||
pub(crate) dist: Dist,
|
pub(crate) dist: Dist,
|
||||||
|
|
@ -295,6 +304,10 @@ pub struct SourceDistDownload {
|
||||||
pub(crate) sdist_file: PathBuf,
|
pub(crate) sdist_file: PathBuf,
|
||||||
/// The subdirectory within the archive or directory.
|
/// The subdirectory within the archive or directory.
|
||||||
pub(crate) subdirectory: Option<PathBuf>,
|
pub(crate) subdirectory: Option<PathBuf>,
|
||||||
|
/// We can't use source dist archives, we build them into wheels which we persist and then drop
|
||||||
|
/// the source distribution. This field is non for git dependencies, which we keep in the cache.
|
||||||
|
#[allow(dead_code)] // We only keep it for the drop impl
|
||||||
|
pub(crate) temp_dir: Option<TempDir>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A downloaded distribution, either a wheel or a source distribution.
|
/// A downloaded distribution, either a wheel or a source distribution.
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ impl<'a, T: BuildContext> SourceDistFetcher<'a, T> {
|
||||||
bail!("Building source distributions is disabled");
|
bail!("Building source distributions is disabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
let (sdist_file, subdirectory) = match dist {
|
let (temp_dir, sdist_file, subdirectory) = match dist {
|
||||||
SourceDist::Registry(sdist) => {
|
SourceDist::Registry(sdist) => {
|
||||||
debug!(
|
debug!(
|
||||||
"Fetching source distribution from registry: {}",
|
"Fetching source distribution from registry: {}",
|
||||||
|
|
@ -89,13 +89,13 @@ impl<'a, T: BuildContext> SourceDistFetcher<'a, T> {
|
||||||
let reader = client.stream_external(&url).await?;
|
let reader = client.stream_external(&url).await?;
|
||||||
|
|
||||||
// Download the source distribution.
|
// Download the source distribution.
|
||||||
let temp_dir = tempfile::tempdir_in(self.build_context.cache())?.into_path();
|
let temp_dir = tempfile::tempdir_in(self.build_context.cache())?;
|
||||||
let sdist_filename = sdist.filename()?;
|
let sdist_filename = sdist.filename()?;
|
||||||
let sdist_file = temp_dir.join(sdist_filename);
|
let sdist_file = temp_dir.path().join(sdist_filename);
|
||||||
let mut writer = tokio::fs::File::create(&sdist_file).await?;
|
let mut writer = tokio::fs::File::create(&sdist_file).await?;
|
||||||
tokio::io::copy(&mut reader.compat(), &mut writer).await?;
|
tokio::io::copy(&mut reader.compat(), &mut writer).await?;
|
||||||
|
|
||||||
(sdist_file, None)
|
(Some(temp_dir), sdist_file, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
SourceDist::DirectUrl(sdist) => {
|
SourceDist::DirectUrl(sdist) => {
|
||||||
|
|
@ -107,13 +107,13 @@ impl<'a, T: BuildContext> SourceDistFetcher<'a, T> {
|
||||||
let mut reader = tokio::io::BufReader::new(reader.compat());
|
let mut reader = tokio::io::BufReader::new(reader.compat());
|
||||||
|
|
||||||
// Download the source distribution.
|
// Download the source distribution.
|
||||||
let temp_dir = tempfile::tempdir_in(self.build_context.cache())?.into_path();
|
let temp_dir = tempfile::tempdir_in(self.build_context.cache())?;
|
||||||
let sdist_filename = sdist.filename()?;
|
let sdist_filename = sdist.filename()?;
|
||||||
let sdist_file = temp_dir.join(sdist_filename);
|
let sdist_file = temp_dir.path().join(sdist_filename);
|
||||||
let mut writer = tokio::fs::File::create(&sdist_file).await?;
|
let mut writer = tokio::fs::File::create(&sdist_file).await?;
|
||||||
tokio::io::copy(&mut reader, &mut writer).await?;
|
tokio::io::copy(&mut reader, &mut writer).await?;
|
||||||
|
|
||||||
(sdist_file, subdirectory)
|
(Some(temp_dir), sdist_file, subdirectory)
|
||||||
}
|
}
|
||||||
|
|
||||||
SourceDist::Git(sdist) => {
|
SourceDist::Git(sdist) => {
|
||||||
|
|
@ -131,7 +131,7 @@ impl<'a, T: BuildContext> SourceDistFetcher<'a, T> {
|
||||||
.await??
|
.await??
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
(sdist_file, subdirectory)
|
(None, sdist_file, subdirectory)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -154,6 +154,10 @@ impl<'a, T: BuildContext> SourceDistFetcher<'a, T> {
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
if let Some(temp_dir) = temp_dir {
|
||||||
|
temp_dir.close()?;
|
||||||
|
}
|
||||||
|
|
||||||
// Read the metadata from the wheel.
|
// Read the metadata from the wheel.
|
||||||
let wheel = CachedWheel::new(
|
let wheel = CachedWheel::new(
|
||||||
wheel_dir.join(&disk_filename),
|
wheel_dir.join(&disk_filename),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue