@@ -141,12 +141,17 @@ pub mod doc_test {
141
141
const EXPECT_INIT : & str = "PyO3 Asyncio has not been initialized" ;
142
142
143
143
static ASYNCIO : OnceCell < PyObject > = OnceCell :: new ( ) ;
144
+ static ENSURE_FUTURE : OnceCell < PyObject > = OnceCell :: new ( ) ;
144
145
static EVENT_LOOP : OnceCell < PyObject > = OnceCell :: new ( ) ;
145
146
static EXECUTOR : OnceCell < PyObject > = OnceCell :: new ( ) ;
146
147
static CALL_SOON : OnceCell < PyObject > = OnceCell :: new ( ) ;
147
148
static CREATE_TASK : OnceCell < PyObject > = OnceCell :: new ( ) ;
148
149
static CREATE_FUTURE : OnceCell < PyObject > = OnceCell :: new ( ) ;
149
150
151
+ fn ensure_future ( py : Python ) -> & PyAny {
152
+ ENSURE_FUTURE . get ( ) . expect ( EXPECT_INIT ) . as_ref ( py)
153
+ }
154
+
150
155
#[ allow( clippy:: needless_doctest_main) ]
151
156
/// Wraps the provided function with the initialization and finalization for PyO3 Asyncio
152
157
///
@@ -192,6 +197,9 @@ where
192
197
/// Must be called at the start of your program
193
198
fn try_init ( py : Python ) -> PyResult < ( ) > {
194
199
let asyncio = py. import ( "asyncio" ) ?;
200
+
201
+ let ensure_future = asyncio. getattr ( "ensure_future" ) ?;
202
+
195
203
let event_loop = asyncio. call_method0 ( "get_event_loop" ) ?;
196
204
let executor = py
197
205
. import ( "concurrent.futures.thread" ) ?
@@ -205,6 +213,7 @@ fn try_init(py: Python) -> PyResult<()> {
205
213
let create_future = event_loop. getattr ( "create_future" ) ?;
206
214
207
215
ASYNCIO . get_or_init ( || asyncio. into ( ) ) ;
216
+ ENSURE_FUTURE . get_or_init ( || ensure_future. into ( ) ) ;
208
217
EVENT_LOOP . get_or_init ( || event_loop. into ( ) ) ;
209
218
EXECUTOR . get_or_init ( || executor. into ( ) ) ;
210
219
CALL_SOON . get_or_init ( || call_soon. into ( ) ) ;
@@ -321,6 +330,26 @@ impl PyTaskCompleter {
321
330
}
322
331
}
323
332
333
+ #[ pyclass]
334
+ struct PyEnsureFuture {
335
+ awaitable : PyObject ,
336
+ tx : Option < oneshot:: Sender < PyResult < PyObject > > > ,
337
+ }
338
+
339
+ #[ pymethods]
340
+ impl PyEnsureFuture {
341
+ #[ call]
342
+ pub fn __call__ ( & mut self ) -> PyResult < ( ) > {
343
+ Python :: with_gil ( |py| {
344
+ let task = ensure_future ( py) . call1 ( ( self . awaitable . as_ref ( py) , ) ) ?;
345
+ let on_complete = PyTaskCompleter { tx : self . tx . take ( ) } ;
346
+ task. call_method1 ( "add_done_callback" , ( on_complete, ) ) ?;
347
+
348
+ Ok ( ( ) )
349
+ } )
350
+ }
351
+ }
352
+
324
353
/// Convert a Python `awaitable` into a Rust Future
325
354
///
326
355
/// This function converts the `awaitable` into a Python Task using `run_coroutine_threadsafe`. A
@@ -373,13 +402,13 @@ pub fn into_future(awaitable: &PyAny) -> PyResult<impl Future<Output = PyResult<
373
402
let py = awaitable. py ( ) ;
374
403
let ( tx, rx) = oneshot:: channel ( ) ;
375
404
376
- let task = CREATE_TASK
377
- . get ( )
378
- . expect ( EXPECT_INIT )
379
- . call1 ( py , ( awaitable, get_event_loop ( py ) ) ) ? ;
380
- let on_complete = PyTaskCompleter { tx : Some ( tx) } ;
381
-
382
- task . call_method1 ( py , "add_done_callback" , ( on_complete , ) ) ?;
405
+ CALL_SOON . get ( ) . expect ( EXPECT_INIT ) . call1 (
406
+ py ,
407
+ ( PyEnsureFuture {
408
+ awaitable : awaitable . into ( ) ,
409
+ tx : Some ( tx) ,
410
+ } , ) ,
411
+ ) ?;
383
412
384
413
Ok ( async move {
385
414
match rx. await {
0 commit comments