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:
konsti 2023-11-16 21:49:48 +01:00 committed by GitHub
parent 0d9d4f9fca
commit 1883dbdc21
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 167 additions and 120 deletions

7
Cargo.lock generated
View File

@ -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",

View File

@ -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 }

View File

@ -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
}
}

View File

@ -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.

View File

@ -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 }

View File

@ -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),

View File

@ -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 }

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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

View File

@ -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,
})) }))
} }

View File

@ -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.

View File

@ -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),