From fc0f6374061094db10e2534d99d87f442c00599e Mon Sep 17 00:00:00 2001 From: Tim de Jager Date: Thu, 31 Jul 2025 13:42:27 +0200 Subject: [PATCH] Make the `BuildDispatch` interpreter method async (#14956) This is a bit of a weird request, but in [pixi](https://pixi.sh) we are making use of this function to lazily instantiate a conda environment. Well, in actuality we are using a shim to the `BuildDispatch` to actually to only create a conda prefix, if some package needs to be built during the resolution phase. Otherwise we can resolve everything without an enviroment containing a python intepreter. We are using a method now - that uses the runtime to run async code inside this function, as `interpreter` is the first method called on a `BuildContext` when running a source build - using `tokio::Handle::block_on`. However was causing a deadlock in very specific situations, me and @baszalmstra + @wolfv have investigated this thoroughly, but have not been able to find the root cause. It would hang in a part of the uv code that hits the index, but that is **after** all of our initialization *and the blocking call* was completed. Changing this to be fully async fixes the problem, this requires this method to be async though. We get that this is not necessarily required, and we might find a workaround, but I wanted to try it this way first. Thanks! --- crates/uv-dispatch/src/lib.rs | 2 +- crates/uv-distribution/src/source/mod.rs | 6 +++++- crates/uv-types/src/traits.rs | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/crates/uv-dispatch/src/lib.rs b/crates/uv-dispatch/src/lib.rs index ddc2d5ed5..606836a06 100644 --- a/crates/uv-dispatch/src/lib.rs +++ b/crates/uv-dispatch/src/lib.rs @@ -175,7 +175,7 @@ impl<'a> BuildDispatch<'a> { impl BuildContext for BuildDispatch<'_> { type SourceDistBuilder = SourceBuild; - fn interpreter(&self) -> &Interpreter { + async fn interpreter(&self) -> &Interpreter { self.interpreter } diff --git a/crates/uv-distribution/src/source/mod.rs b/crates/uv-distribution/src/source/mod.rs index f269c1b87..3d9361797 100644 --- a/crates/uv-distribution/src/source/mod.rs +++ b/crates/uv-distribution/src/source/mod.rs @@ -2386,11 +2386,13 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { let base_python = if cfg!(unix) { self.build_context .interpreter() + .await .find_base_python() .map_err(Error::BaseInterpreter)? } else { self.build_context .interpreter() + .await .to_base_python() .map_err(Error::BaseInterpreter)? }; @@ -2485,7 +2487,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { // Ensure that the _installed_ Python version is compatible with the `requires-python` // specifier. if let Some(requires_python) = source.requires_python() { - let installed = self.build_context.interpreter().python_version(); + let installed = self.build_context.interpreter().await.python_version(); let target = release_specifiers_to_ranges(requires_python.clone()) .bounding_range() .map(|bounding_range| bounding_range.0.cloned()) @@ -2507,11 +2509,13 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { let base_python = if cfg!(unix) { self.build_context .interpreter() + .await .find_base_python() .map_err(Error::BaseInterpreter)? } else { self.build_context .interpreter() + .await .to_base_python() .map_err(Error::BaseInterpreter)? }; diff --git a/crates/uv-types/src/traits.rs b/crates/uv-types/src/traits.rs index 875742da9..68e6583ae 100644 --- a/crates/uv-types/src/traits.rs +++ b/crates/uv-types/src/traits.rs @@ -62,8 +62,10 @@ use crate::BuildArena; pub trait BuildContext { type SourceDistBuilder: SourceBuildTrait; + // Note: this function is async deliberately, because downstream code may need to + // run async code to get the interpreter, to resolve the Python version. /// Return a reference to the interpreter. - fn interpreter(&self) -> &Interpreter; + fn interpreter(&self) -> impl Future + '_; /// Return a reference to the cache. fn cache(&self) -> &Cache;