11use futures:: FutureExt ;
22use pyo3:: { prelude:: * , IntoPyObjectExt } ;
3- use std:: { future:: Future , sync:: Arc } ;
3+ use std:: { future:: Future , sync:: Arc , sync :: OnceLock } ;
44use tokio:: { runtime:: Builder as RuntimeBuilder , task:: JoinHandle } ;
55
66#[ cfg( unix) ]
@@ -151,7 +151,7 @@ pub(crate) fn done_future_into_py(
151151}
152152
153153#[ inline( always) ]
154- pub ( crate ) fn err_future_into_py ( py : Python , err : PyResult < ( ) > ) -> PyResult < Bound < PyAny > > {
154+ pub ( crate ) fn err_future_into_py ( py : Python , err : PyErr ) -> PyResult < Bound < PyAny > > {
155155 PyErrAwaitable :: new ( err) . into_bound_py_any ( py)
156156}
157157
@@ -247,32 +247,30 @@ where
247247 Ok ( py_fut. into_bound ( py) )
248248}
249249
250- // Wrapper function to replace pyo3_async_runtimes::tokio::future_into_py
251- // This function matches the signature and converts Result<T, E> to FutureResultToPy
250+ static SHARED_BLOCKING_RUNNER : OnceLock < Arc < BlockingRunner > > = OnceLock :: new ( ) ;
251+
252+ fn shared_blocking_runner ( ) -> Arc < BlockingRunner > {
253+ SHARED_BLOCKING_RUNNER
254+ . get_or_init ( || Arc :: new ( BlockingRunner :: new ( 1 , 30 ) ) )
255+ . clone ( )
256+ }
257+
252258pub fn future_into_py < F > ( py : Python , fut : F ) -> PyResult < Bound < PyAny > >
253259where
254260 F : Future < Output = Result < ( ) , anyhow:: Error > > + Send + ' static ,
255261{
256- // Try to get the tokio runtime handle
257- // When called from Python code, we might not be in a tokio async context
258- // So we try try_current() first, and if that fails, fall back to pyo3_async_runtimes
259262 match tokio:: runtime:: Handle :: try_current ( ) {
260263 Ok ( rt_handle) => {
261- // We have a handle, use our optimized implementation
262- // Get the event loop from Python
263264 let asyncio = py. import ( "asyncio" ) ?;
264265 let event_loop = asyncio
265- . call_method0 ( "get_event_loop " )
266+ . call_method0 ( "get_running_loop " )
266267 . or_else ( |_| asyncio. call_method0 ( "new_event_loop" ) ) ?;
267268 let event_loop: Py < PyAny > = event_loop. unbind ( ) ;
268269
269- // Create a simple blocking runner (1 thread, 30s timeout)
270- let blocking_runner = Arc :: new ( BlockingRunner :: new ( 1 , 30 ) ) ;
270+ let blocking_runner = shared_blocking_runner ( ) ;
271271
272- // Create RuntimeRef
273272 let rt_ref = RuntimeRef :: new ( rt_handle, blocking_runner, Arc :: new ( event_loop) ) ;
274273
275- // Convert the future to use FutureResultToPy
276274 let wrapped_fut = async move {
277275 match fut. await {
278276 Ok ( ( ) ) => FutureResultToPy :: None ,
@@ -286,13 +284,9 @@ where
286284 }
287285 } ;
288286
289- // Use the futlike implementation (better for most cases)
290287 future_into_py_futlike ( rt_ref, py, wrapped_fut)
291288 }
292289 Err ( _) => {
293- // Fall back to pyo3_async_runtimes when we can't get the handle
294- // This happens when called from Python code that's not in a tokio async context
295- // Convert Result<(), anyhow::Error> to PyResult<()> for pyo3_async_runtimes
296290 let py_fut = fut. map ( |result| {
297291 result. map_err ( |e| {
298292 PyErr :: new :: < pyo3:: exceptions:: PyRuntimeError , _ > ( format ! (
0 commit comments