Share in-flight map across resolutions (#932)

## Summary

This PR fixes a subtle bug in `pip install` when using `--reinstall`. If
a package depends on a build system directly (e.g., `waitress` depends
on `setuptools`), and then you have other packages that also need the
build system to build a source distribution, right now, we don't share
the `OnceMap` between those cases.

This lifts the `InFlight` tracking up a level, so that it's initialized
once per command, then shared everywhere.

## Test Plan

I'm having trouble coming up with an identical test-case and hesitant to
add this slow test to the suite... But if you run `pip install
--reinstall` with:

```
waitress @ git+https://github.com/zanieb/waitress
devpi-server @ git+https://github.com/zanieb/devpi#subdirectory=server
```

It fails consistently on `main` and passes here.
This commit is contained in:
Charlie Marsh 2024-01-15 13:11:22 -05:00 committed by GitHub
parent 249ca10765
commit 116da6b7de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 61 additions and 39 deletions

View File

@ -26,7 +26,7 @@ use puffin_normalize::ExtraName;
use puffin_resolver::{ use puffin_resolver::{
DisplayResolutionGraph, Manifest, PreReleaseMode, ResolutionMode, ResolutionOptions, Resolver, DisplayResolutionGraph, Manifest, PreReleaseMode, ResolutionMode, ResolutionOptions, Resolver,
}; };
use puffin_traits::SetupPyStrategy; use puffin_traits::{InFlight, SetupPyStrategy};
use requirements_txt::EditableRequirement; use requirements_txt::EditableRequirement;
use crate::commands::reporters::{DownloadReporter, ResolverReporter}; use crate::commands::reporters::{DownloadReporter, ResolverReporter};
@ -154,6 +154,9 @@ pub(crate) async fn pip_compile(
FlatIndex::from_entries(entries, &tags) FlatIndex::from_entries(entries, &tags)
}; };
// Track in-flight downloads, builds, etc., across resolutions.
let in_flight = InFlight::default();
let options = ResolutionOptions::new(resolution_mode, prerelease_mode, exclude_newer); let options = ResolutionOptions::new(resolution_mode, prerelease_mode, exclude_newer);
let build_dispatch = BuildDispatch::new( let build_dispatch = BuildDispatch::new(
&client, &client,
@ -161,6 +164,7 @@ pub(crate) async fn pip_compile(
&interpreter, &interpreter,
&index_locations, &index_locations,
&flat_index, &flat_index,
&in_flight,
interpreter.sys_executable().to_path_buf(), interpreter.sys_executable().to_path_buf(),
setup_py, setup_py,
no_build, no_build,
@ -227,13 +231,6 @@ pub(crate) async fn pip_compile(
editable_metadata, editable_metadata,
); );
// Resolve the flat indexes from `--find-links`.
let flat_index = {
let client = FlatIndexClient::new(&client, &cache);
let entries = client.fetch(index_locations.flat_indexes()).await?;
FlatIndex::from_entries(entries, &tags)
};
// Resolve the dependencies. // Resolve the dependencies.
let resolver = Resolver::new( let resolver = Resolver::new(
manifest, manifest,

View File

@ -28,7 +28,7 @@ use puffin_normalize::PackageName;
use puffin_resolver::{ use puffin_resolver::{
Manifest, PreReleaseMode, ResolutionGraph, ResolutionMode, ResolutionOptions, Resolver, Manifest, PreReleaseMode, ResolutionGraph, ResolutionMode, ResolutionOptions, Resolver,
}; };
use puffin_traits::{OnceMap, SetupPyStrategy}; use puffin_traits::{InFlight, SetupPyStrategy};
use requirements_txt::EditableRequirement; use requirements_txt::EditableRequirement;
use crate::commands::reporters::{DownloadReporter, InstallReporter, ResolverReporter}; use crate::commands::reporters::{DownloadReporter, InstallReporter, ResolverReporter};
@ -144,6 +144,9 @@ pub(crate) async fn pip_install(
FlatIndex::from_entries(entries, tags) FlatIndex::from_entries(entries, tags)
}; };
// Track in-flight downloads, builds, etc., across resolutions.
let in_flight = InFlight::default();
let options = ResolutionOptions::new(resolution_mode, prerelease_mode, exclude_newer); let options = ResolutionOptions::new(resolution_mode, prerelease_mode, exclude_newer);
let build_dispatch = BuildDispatch::new( let build_dispatch = BuildDispatch::new(
@ -152,6 +155,7 @@ pub(crate) async fn pip_install(
&interpreter, &interpreter,
&index_locations, &index_locations,
&flat_index, &flat_index,
&in_flight,
venv.python_executable(), venv.python_executable(),
setup_py, setup_py,
no_build, no_build,
@ -221,6 +225,7 @@ pub(crate) async fn pip_install(
&index_locations, &index_locations,
tags, tags,
&client, &client,
&in_flight,
&build_dispatch, &build_dispatch,
&cache, &cache,
&venv, &venv,
@ -420,6 +425,7 @@ async fn install(
index_urls: &IndexLocations, index_urls: &IndexLocations,
tags: &Tags, tags: &Tags,
client: &RegistryClient, client: &RegistryClient,
in_flight: &InFlight,
build_dispatch: &BuildDispatch<'_>, build_dispatch: &BuildDispatch<'_>,
cache: &Cache, cache: &Cache,
venv: &Virtualenv, venv: &Virtualenv,
@ -488,8 +494,9 @@ async fn install(
let downloader = Downloader::new(cache, tags, client, build_dispatch) let downloader = Downloader::new(cache, tags, client, build_dispatch)
.with_reporter(DownloadReporter::from(printer).with_length(remote.len() as u64)); .with_reporter(DownloadReporter::from(printer).with_length(remote.len() as u64));
// STOPSHIP(charlie): This needs to be shared!
let wheels = downloader let wheels = downloader
.download(remote, &OnceMap::default()) .download(remote, in_flight)
.await .await
.context("Failed to download distributions")?; .context("Failed to download distributions")?;

View File

@ -14,7 +14,7 @@ use puffin_client::{FlatIndex, FlatIndexClient, RegistryClient, RegistryClientBu
use puffin_dispatch::BuildDispatch; use puffin_dispatch::BuildDispatch;
use puffin_installer::{Downloader, InstallPlan, Reinstall, ResolvedEditable, SitePackages}; use puffin_installer::{Downloader, InstallPlan, Reinstall, ResolvedEditable, SitePackages};
use puffin_interpreter::Virtualenv; use puffin_interpreter::Virtualenv;
use puffin_traits::{OnceMap, SetupPyStrategy}; use puffin_traits::{InFlight, SetupPyStrategy};
use pypi_types::Yanked; use pypi_types::Yanked;
use requirements_txt::EditableRequirement; use requirements_txt::EditableRequirement;
@ -70,6 +70,9 @@ pub(crate) async fn pip_sync(
FlatIndex::from_entries(entries, tags) FlatIndex::from_entries(entries, tags)
}; };
// Track in-flight downloads, builds, etc., across resolutions.
let in_flight = InFlight::default();
// Prep the build context. // Prep the build context.
let build_dispatch = BuildDispatch::new( let build_dispatch = BuildDispatch::new(
&client, &client,
@ -77,6 +80,7 @@ pub(crate) async fn pip_sync(
venv.interpreter(), venv.interpreter(),
&index_locations, &index_locations,
&flat_index, &flat_index,
&in_flight,
venv.python_executable(), venv.python_executable(),
setup_py, setup_py,
no_build, no_build,
@ -211,7 +215,7 @@ pub(crate) async fn pip_sync(
.with_reporter(DownloadReporter::from(printer).with_length(remote.len() as u64)); .with_reporter(DownloadReporter::from(printer).with_length(remote.len() as u64));
let wheels = downloader let wheels = downloader
.download(remote, &OnceMap::default()) .download(remote, &in_flight)
.await .await
.context("Failed to download distributions")?; .context("Failed to download distributions")?;

View File

@ -15,7 +15,7 @@ use puffin_cache::Cache;
use puffin_client::{FlatIndex, FlatIndexClient, RegistryClientBuilder}; use puffin_client::{FlatIndex, FlatIndexClient, RegistryClientBuilder};
use puffin_dispatch::BuildDispatch; use puffin_dispatch::BuildDispatch;
use puffin_interpreter::Interpreter; use puffin_interpreter::Interpreter;
use puffin_traits::{BuildContext, SetupPyStrategy}; use puffin_traits::{BuildContext, InFlight, SetupPyStrategy};
use crate::commands::ExitStatus; use crate::commands::ExitStatus;
use crate::printer::Printer; use crate::printer::Printer;
@ -139,6 +139,9 @@ async fn venv_impl(
FlatIndex::from_entries(entries, tags) FlatIndex::from_entries(entries, tags)
}; };
// Track in-flight downloads, builds, etc., across resolutions.
let in_flight = InFlight::default();
// Prep the build context. // Prep the build context.
let build_dispatch = BuildDispatch::new( let build_dispatch = BuildDispatch::new(
&client, &client,
@ -146,6 +149,7 @@ async fn venv_impl(
interpreter, interpreter,
index_locations, index_locations,
&flat_index, &flat_index,
&in_flight,
venv.python_executable(), venv.python_executable(),
SetupPyStrategy::default(), SetupPyStrategy::default(),
true, true,

View File

@ -12,7 +12,7 @@ use puffin_cache::{Cache, CacheArgs};
use puffin_client::{FlatIndex, RegistryClientBuilder}; use puffin_client::{FlatIndex, RegistryClientBuilder};
use puffin_dispatch::BuildDispatch; use puffin_dispatch::BuildDispatch;
use puffin_interpreter::Virtualenv; use puffin_interpreter::Virtualenv;
use puffin_traits::{BuildContext, BuildKind, SetupPyStrategy}; use puffin_traits::{BuildContext, BuildKind, InFlight, SetupPyStrategy};
#[derive(Parser)] #[derive(Parser)]
pub(crate) struct BuildArgs { pub(crate) struct BuildArgs {
@ -57,6 +57,7 @@ pub(crate) async fn build(args: BuildArgs) -> Result<PathBuf> {
let index_urls = IndexLocations::default(); let index_urls = IndexLocations::default();
let flat_index = FlatIndex::default(); let flat_index = FlatIndex::default();
let setup_py = SetupPyStrategy::default(); let setup_py = SetupPyStrategy::default();
let in_flight = InFlight::default();
let build_dispatch = BuildDispatch::new( let build_dispatch = BuildDispatch::new(
&client, &client,
@ -64,6 +65,7 @@ pub(crate) async fn build(args: BuildArgs) -> Result<PathBuf> {
venv.interpreter(), venv.interpreter(),
&index_urls, &index_urls,
&flat_index, &flat_index,
&in_flight,
venv.python_executable(), venv.python_executable(),
setup_py, setup_py,
false, false,

View File

@ -25,7 +25,7 @@ use puffin_installer::Downloader;
use puffin_interpreter::Virtualenv; use puffin_interpreter::Virtualenv;
use puffin_normalize::PackageName; use puffin_normalize::PackageName;
use puffin_resolver::DistFinder; use puffin_resolver::DistFinder;
use puffin_traits::{BuildContext, OnceMap, SetupPyStrategy}; use puffin_traits::{BuildContext, InFlight, SetupPyStrategy};
#[derive(Parser)] #[derive(Parser)]
pub(crate) struct InstallManyArgs { pub(crate) struct InstallManyArgs {
@ -62,6 +62,7 @@ pub(crate) async fn install_many(args: InstallManyArgs) -> Result<()> {
let index_locations = IndexLocations::default(); let index_locations = IndexLocations::default();
let flat_index = FlatIndex::default(); let flat_index = FlatIndex::default();
let setup_py = SetupPyStrategy::default(); let setup_py = SetupPyStrategy::default();
let in_flight = InFlight::default();
let tags = venv.interpreter().tags()?; let tags = venv.interpreter().tags()?;
let build_dispatch = BuildDispatch::new( let build_dispatch = BuildDispatch::new(
@ -70,6 +71,7 @@ pub(crate) async fn install_many(args: InstallManyArgs) -> Result<()> {
venv.interpreter(), venv.interpreter(),
&index_locations, &index_locations,
&flat_index, &flat_index,
&in_flight,
venv.python_executable(), venv.python_executable(),
setup_py, setup_py,
args.no_build, args.no_build,
@ -141,7 +143,7 @@ async fn install_chunk(
let mut registry_index = RegistryWheelIndex::new(build_dispatch.cache(), tags, index_locations); let mut registry_index = RegistryWheelIndex::new(build_dispatch.cache(), tags, index_locations);
let (cached, uncached): (Vec<_>, Vec<_>) = dists.into_iter().partition_map(|dist| { let (cached, uncached): (Vec<_>, Vec<_>) = dists.into_iter().partition_map(|dist| {
// We always want the wheel for the latest version not whatever matching is in cache // We always want the wheel for the latest version not whatever matching is in cache.
let VersionOrUrl::Version(version) = dist.version_or_url() else { let VersionOrUrl::Version(version) = dist.version_or_url() else {
unreachable!(); unreachable!();
}; };
@ -155,7 +157,7 @@ async fn install_chunk(
info!("Cached: {}, Uncached {}", cached.len(), uncached.len()); info!("Cached: {}, Uncached {}", cached.len(), uncached.len());
let downloader = Downloader::new(build_dispatch.cache(), tags, client, build_dispatch); let downloader = Downloader::new(build_dispatch.cache(), tags, client, build_dispatch);
let in_flight = OnceMap::default(); let in_flight = InFlight::default();
let fetches: Vec<_> = futures::stream::iter(uncached) let fetches: Vec<_> = futures::stream::iter(uncached)
.map(|dist| downloader.get_wheel(dist, &in_flight)) .map(|dist| downloader.get_wheel(dist, &in_flight))
.buffer_unordered(50) .buffer_unordered(50)

View File

@ -17,7 +17,7 @@ use puffin_client::{FlatIndex, FlatIndexClient, RegistryClientBuilder};
use puffin_dispatch::BuildDispatch; use puffin_dispatch::BuildDispatch;
use puffin_interpreter::Virtualenv; use puffin_interpreter::Virtualenv;
use puffin_resolver::{Manifest, ResolutionOptions, Resolver}; use puffin_resolver::{Manifest, ResolutionOptions, Resolver};
use puffin_traits::SetupPyStrategy; use puffin_traits::{InFlight, SetupPyStrategy};
#[derive(ValueEnum, Default, Clone)] #[derive(ValueEnum, Default, Clone)]
pub(crate) enum ResolveCliFormat { pub(crate) enum ResolveCliFormat {
@ -65,6 +65,7 @@ pub(crate) async fn resolve_cli(args: ResolveCliArgs) -> Result<()> {
let entries = client.fetch(index_locations.flat_indexes()).await?; let entries = client.fetch(index_locations.flat_indexes()).await?;
FlatIndex::from_entries(entries, venv.interpreter().tags()?) FlatIndex::from_entries(entries, venv.interpreter().tags()?)
}; };
let in_flight = InFlight::default();
let build_dispatch = BuildDispatch::new( let build_dispatch = BuildDispatch::new(
&client, &client,
@ -72,6 +73,7 @@ pub(crate) async fn resolve_cli(args: ResolveCliArgs) -> Result<()> {
venv.interpreter(), venv.interpreter(),
&index_locations, &index_locations,
&flat_index, &flat_index,
&in_flight,
venv.python_executable(), venv.python_executable(),
SetupPyStrategy::default(), SetupPyStrategy::default(),
args.no_build, args.no_build,

View File

@ -20,7 +20,7 @@ use puffin_client::{FlatIndex, RegistryClient, RegistryClientBuilder};
use puffin_dispatch::BuildDispatch; use puffin_dispatch::BuildDispatch;
use puffin_interpreter::Virtualenv; use puffin_interpreter::Virtualenv;
use puffin_normalize::PackageName; use puffin_normalize::PackageName;
use puffin_traits::{BuildContext, SetupPyStrategy}; use puffin_traits::{BuildContext, InFlight, SetupPyStrategy};
#[derive(Parser)] #[derive(Parser)]
pub(crate) struct ResolveManyArgs { pub(crate) struct ResolveManyArgs {
@ -76,6 +76,7 @@ pub(crate) async fn resolve_many(args: ResolveManyArgs) -> Result<()> {
let index_locations = IndexLocations::default(); let index_locations = IndexLocations::default();
let flat_index = FlatIndex::default(); let flat_index = FlatIndex::default();
let setup_py = SetupPyStrategy::default(); let setup_py = SetupPyStrategy::default();
let in_flight = InFlight::default();
let build_dispatch = BuildDispatch::new( let build_dispatch = BuildDispatch::new(
&client, &client,
@ -83,6 +84,7 @@ pub(crate) async fn resolve_many(args: ResolveManyArgs) -> Result<()> {
venv.interpreter(), venv.interpreter(),
&index_locations, &index_locations,
&flat_index, &flat_index,
&in_flight,
venv.python_executable(), venv.python_executable(),
setup_py, setup_py,
args.no_build, args.no_build,

View File

@ -9,7 +9,7 @@ use anyhow::{bail, Context, Result};
use itertools::Itertools; use itertools::Itertools;
use tracing::{debug, instrument}; use tracing::{debug, instrument};
use distribution_types::{CachedDist, DistributionId, IndexLocations, Name, Resolution}; use distribution_types::{IndexLocations, Name, Resolution};
use pep508_rs::Requirement; use pep508_rs::Requirement;
use puffin_build::{SourceBuild, SourceBuildContext}; use puffin_build::{SourceBuild, SourceBuildContext};
use puffin_cache::Cache; use puffin_cache::Cache;
@ -17,7 +17,7 @@ use puffin_client::{FlatIndex, RegistryClient};
use puffin_installer::{Downloader, InstallPlan, Installer, Reinstall, SitePackages}; use puffin_installer::{Downloader, InstallPlan, Installer, Reinstall, SitePackages};
use puffin_interpreter::{Interpreter, Virtualenv}; use puffin_interpreter::{Interpreter, Virtualenv};
use puffin_resolver::{Manifest, ResolutionOptions, Resolver}; use puffin_resolver::{Manifest, ResolutionOptions, Resolver};
use puffin_traits::{BuildContext, BuildKind, OnceMap, SetupPyStrategy}; use puffin_traits::{BuildContext, BuildKind, InFlight, SetupPyStrategy};
/// The main implementation of [`BuildContext`], used by the CLI, see [`BuildContext`] /// The main implementation of [`BuildContext`], used by the CLI, see [`BuildContext`]
/// documentation. /// documentation.
@ -27,12 +27,12 @@ pub struct BuildDispatch<'a> {
interpreter: &'a Interpreter, interpreter: &'a Interpreter,
index_locations: &'a IndexLocations, index_locations: &'a IndexLocations,
flat_index: &'a FlatIndex, flat_index: &'a FlatIndex,
in_flight: &'a InFlight,
base_python: PathBuf, base_python: PathBuf,
setup_py: SetupPyStrategy, setup_py: SetupPyStrategy,
no_build: bool, no_build: bool,
source_build_context: SourceBuildContext, source_build_context: SourceBuildContext,
options: ResolutionOptions, options: ResolutionOptions,
in_flight: OnceMap<DistributionId, Result<CachedDist, String>>,
} }
impl<'a> BuildDispatch<'a> { impl<'a> BuildDispatch<'a> {
@ -43,6 +43,7 @@ impl<'a> BuildDispatch<'a> {
interpreter: &'a Interpreter, interpreter: &'a Interpreter,
index_locations: &'a IndexLocations, index_locations: &'a IndexLocations,
flat_index: &'a FlatIndex, flat_index: &'a FlatIndex,
in_flight: &'a InFlight,
base_python: PathBuf, base_python: PathBuf,
setup_py: SetupPyStrategy, setup_py: SetupPyStrategy,
no_build: bool, no_build: bool,
@ -53,12 +54,12 @@ impl<'a> BuildDispatch<'a> {
interpreter, interpreter,
index_locations, index_locations,
flat_index, flat_index,
in_flight,
base_python, base_python,
setup_py, setup_py,
no_build, no_build,
source_build_context: SourceBuildContext::default(), source_build_context: SourceBuildContext::default(),
options: ResolutionOptions::default(), options: ResolutionOptions::default(),
in_flight: OnceMap::default(),
} }
} }
@ -184,7 +185,7 @@ impl<'a> BuildContext for BuildDispatch<'a> {
); );
downloader downloader
.download(remote, &self.in_flight) .download(remote, self.in_flight)
.await .await
.context("Failed to download and build distributions")? .context("Failed to download and build distributions")?
}; };

View File

@ -7,14 +7,12 @@ use tokio::task::JoinError;
use tracing::{instrument, warn}; use tracing::{instrument, warn};
use url::Url; use url::Url;
use distribution_types::{ use distribution_types::{CachedDist, Dist, Identifier, LocalEditable, RemoteSource, SourceDist};
CachedDist, Dist, DistributionId, Identifier, LocalEditable, RemoteSource, SourceDist,
};
use platform_tags::Tags; use platform_tags::Tags;
use puffin_cache::Cache; use puffin_cache::Cache;
use puffin_client::RegistryClient; use puffin_client::RegistryClient;
use puffin_distribution::{DistributionDatabase, DistributionDatabaseError, LocalWheel, Unzip}; use puffin_distribution::{DistributionDatabase, DistributionDatabaseError, LocalWheel, Unzip};
use puffin_traits::{BuildContext, OnceMap}; use puffin_traits::{BuildContext, InFlight};
use crate::editable::BuiltEditable; use crate::editable::BuiltEditable;
@ -66,7 +64,7 @@ impl<'a, Context: BuildContext + Send + Sync> Downloader<'a, Context> {
pub fn download_stream<'stream>( pub fn download_stream<'stream>(
&'stream self, &'stream self,
distributions: Vec<Dist>, distributions: Vec<Dist>,
in_flight: &'stream OnceMap<DistributionId, Result<CachedDist, String>>, in_flight: &'stream InFlight,
) -> impl Stream<Item = Result<CachedDist, Error>> + 'stream { ) -> impl Stream<Item = Result<CachedDist, Error>> + 'stream {
futures::stream::iter(distributions) futures::stream::iter(distributions)
.map(|dist| async { .map(|dist| async {
@ -86,7 +84,7 @@ impl<'a, Context: BuildContext + Send + Sync> Downloader<'a, Context> {
pub async fn download( pub async fn download(
&self, &self,
mut distributions: Vec<Dist>, mut distributions: Vec<Dist>,
in_flight: &OnceMap<DistributionId, Result<CachedDist, String>>, in_flight: &InFlight,
) -> Result<Vec<CachedDist>, Error> { ) -> Result<Vec<CachedDist>, Error> {
// Sort the distributions by size. // Sort the distributions by size.
distributions distributions
@ -154,13 +152,9 @@ impl<'a, Context: BuildContext + Send + Sync> Downloader<'a, Context> {
/// Download, build, and unzip a single wheel. /// Download, build, and unzip a single wheel.
#[instrument(skip_all, fields(name = % dist, size = ? dist.size(), url = dist.file().map(|file| file.url.to_string()).unwrap_or_default()))] #[instrument(skip_all, fields(name = % dist, size = ? dist.size(), url = dist.file().map(|file| file.url.to_string()).unwrap_or_default()))]
pub async fn get_wheel( pub async fn get_wheel(&self, dist: Dist, in_flight: &InFlight) -> Result<CachedDist, Error> {
&self,
dist: Dist,
in_flight: &OnceMap<DistributionId, Result<CachedDist, String>>,
) -> Result<CachedDist, Error> {
let id = dist.distribution_id(); let id = dist.distribution_id();
let wheel = if in_flight.register(&id) { let wheel = if in_flight.downloads.register(&id) {
let download: LocalWheel = self let download: LocalWheel = self
.database .database
.get_or_build_wheel(dist.clone()) .get_or_build_wheel(dist.clone())
@ -169,16 +163,17 @@ impl<'a, Context: BuildContext + Send + Sync> Downloader<'a, Context> {
let result = Self::unzip_wheel(download).await; let result = Self::unzip_wheel(download).await;
match result { match result {
Ok(cached) => { Ok(cached) => {
in_flight.done(id, Ok(cached.clone())); in_flight.downloads.done(id, Ok(cached.clone()));
cached cached
} }
Err(err) => { Err(err) => {
in_flight.done(id, Err(err.to_string())); in_flight.downloads.done(id, Err(err.to_string()));
return Err(err); return Err(err);
} }
} }
} else { } else {
in_flight in_flight
.downloads
.wait(&id) .wait(&id)
.await .await
.value() .value()

View File

@ -6,7 +6,7 @@ use std::path::{Path, PathBuf};
use anyhow::Result; use anyhow::Result;
use distribution_types::Resolution; use distribution_types::{CachedDist, DistributionId, Resolution};
pub use once_map::OnceMap; pub use once_map::OnceMap;
use pep508_rs::Requirement; use pep508_rs::Requirement;
use puffin_cache::Cache; use puffin_cache::Cache;
@ -124,6 +124,12 @@ pub trait SourceBuildTrait {
-> impl Future<Output = Result<String>> + Send + 'a; -> impl Future<Output = Result<String>> + Send + 'a;
} }
#[derive(Default)]
pub struct InFlight {
/// The in-flight distribution downloads.
pub downloads: OnceMap<DistributionId, Result<CachedDist, String>>,
}
/// The strategy to use when building source distributions that lack a `pyproject.toml`. /// The strategy to use when building source distributions that lack a `pyproject.toml`.
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub enum SetupPyStrategy { pub enum SetupPyStrategy {