You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
One problem that arises when interacting with Python's asyncio library is that the functions we use to get a reference to the Python event loop can only be called in certain contexts. Since PyO3 Asyncio requires references to the event loop when performing conversions between Rust and Python, this is unfortunately something that you need to worry about.
366
+
One problem that arises when interacting with Python's asyncio library is that the functions we use to get a reference to the Python event loop can only be called in certain contexts. Since PyO3 Asyncio needs to interact with Python's event loop during conversions, the context of these conversions can matter a lot.
367
+
368
+
> The core conversions we've mentioned so far in this guide should insulate you from these concerns in most cases, but in the event that they don't, this section should provide you with the information you need to solve these problems.
367
369
368
370
#### The Main Dilemma
369
371
370
-
Python programs can have many independent event loop instances throughout the lifetime of the application (`asyncio.run` for example creates its own event loop each time it's called for instance), and they can even run concurrent with other event loops. The most correct method of obtaining a reference to the Python event loop is via `asyncio.get_running_loop`.
372
+
Python programs can have many independent event loop instances throughout the lifetime of the application (`asyncio.run` for example creates its own event loop each time it's called for instance), and they can even run concurrent with other event loops. For this reason, the most correct method of obtaining a reference to the Python event loop is via `asyncio.get_running_loop`.
371
373
372
-
`asyncio.get_running_loop` returns the event loop associated with the current OS thread. It can be used inside Python coroutines to spawn concurrent tasks, interact with timers, or in our case signal between Rust and Python. This is all well and good when we are operating on a Python thread, but what happens when we want to perform a PyO3 Asyncio conversion on a _Rust_ thread? Since the Rust thread is not associated with a Python event loop, `asyncio.get_running_loop` will fail.
374
+
`asyncio.get_running_loop` returns the event loop associated with the current OS thread. It can be used inside Python coroutines to spawn concurrent tasks, interact with timers, or in our case signal between Rust and Python. This is all well and good when we are operating on a Python thread, but since Rust threads are not associated with a Python event loop, `asyncio.get_running_loop` will fail when called on a Rust runtime.
373
375
374
376
#### The Solution
375
377
376
-
A really straightforward way of dealing with this problem is to pass a reference to the associated Python event loop for every conversion. That's why in PyO3 Asyncio, we introduced a new set of conversion functions that do just that:
378
+
A really straightforward way of dealing with this problem is to pass a reference to the associated Python event loop for every conversion. That's why in `v0.14`, we introduced a new set of conversion functions that do just that:
377
379
378
380
-`pyo3_asyncio::into_future_with_loop` - Convert a Python awaitable into a Rust future with the given asyncio event loop.
379
381
-`pyo3_asyncio::<runtime>::future_into_py_with_loop` - Convert a Rust future into a Python awaitable with the given asyncio event loop.
380
382
-`pyo3_asyncio::<runtime>::local_future_into_py_with_loop` - Convert a `!Send` Rust future into a Python awaitable with the given asyncio event loop.
381
383
382
-
One clear disadvantage to this approach (besides the verbose naming) is that the Rust application has to explicitly track its references to the Python event loop. In native libraries, we can't make any assumptions about the underlying event loop, so the only reliable way to make sure our conversions work properly is to store a reference to the current event loop to use later on.
384
+
One clear disadvantage to this approach (aside from the verbose naming) is that the Rust application has to explicitly track its references to the Python event loop. In native libraries, we can't make any assumptions about the underlying event loop, so the only reliable way to make sure our conversions work properly is to store a reference to the current event loop at the callsite to use later on.
383
385
384
386
```rust
385
387
usepyo3::prelude::*;
@@ -420,7 +422,7 @@ Another disadvantage to this explicit approach that is less obvious is that we c
420
422
421
423
In order to detect the Python event loop at the callsite, we need something like `asyncio.get_running_loop` that works for _both Python and Rust_. In Python, `asyncio.get_running_loop` uses thread-local data to retrieve the event loop associated with the current thread. What we need in Rust is something that can retrieve the Python event loop associated with the current _task_.
422
424
423
-
Enter `pyo3_asyncio::<runtime>::get_current_loop`. This function first checks task-local data for a Python event loop, then falls back on `asyncio.get_running_loop` if no task-local event loop is found. This way both bases are convered.
425
+
Enter `pyo3_asyncio::<runtime>::get_current_loop`. This function first checks task-local data for a Python event loop, then falls back on `asyncio.get_running_loop` if no task-local event loop is found. This way both bases are covered.
424
426
425
427
Now, all we need is a way to store the event loop in task-local data. Since this is a runtime-specific feature, you can find the following functions in each runtime module:
Even though this is more correct, it's clearly not more ergonomic. That's why we introduced a new set of functions with this functionality baked in:
493
497
494
-
-`pyo3_asyncio::<runtime>::into_future` - Convert a Python awaitable into a Rust future (using `pyo3_asyncio::<runtime>::get_current_loop`)
495
-
-`pyo3_asyncio::<runtime>::future_into_py` - Convert a Rust future into a Python awaitable (using `pyo3_asyncio::<runtime>::get_current_loop` and `pyo3_asyncio::<runtime>::scope` to set the task-local event loop for the given Rust future)
496
-
-`pyo3_asyncio::<runtime>::local_future_into_py` - Convert a `!Send` Rust future into a Python awaitable (using `pyo3_asyncio::<runtime>::get_current_loop` and `pyo3_asyncio::<runtime>::scope_local` to set the task-local event loop for the given Rust future).
498
+
-`pyo3_asyncio::<runtime>::into_future`
499
+
> Convert a Python awaitable into a Rust future (using `pyo3_asyncio::<runtime>::get_current_loop`)
500
+
-`pyo3_asyncio::<runtime>::future_into_py`
501
+
> Convert a Rust future into a Python awaitable (using `pyo3_asyncio::<runtime>::get_current_loop` and `pyo3_asyncio::<runtime>::scope` to set the task-local event loop for the given Rust future)
502
+
-`pyo3_asyncio::<runtime>::local_future_into_py`
503
+
> Convert a `!Send` Rust future into a Python awaitable (using `pyo3_asyncio::<runtime>::get_current_loop` and `pyo3_asyncio::<runtime>::scope_local` to set the task-local event loop for the given Rust future).
497
504
498
-
__These are the functions that we recommend using__. With these functions, the previous example can be written like so:
505
+
__These are the functions that we recommend using__. With these functions, the previous example can be rewritten to be more compact:
499
506
500
507
```rust
501
508
usepyo3::prelude::*;
@@ -545,10 +552,10 @@ Part of the reason why it's taken so long to push out a `v0.14` release is becau
545
552
546
553
This new release should address most the core issues that users have reported in the `v0.13` release, so I think we can expect more stability going forward.
547
554
548
-
Also, a special thanks to @ShadowJonathan for helping with the design and review
555
+
Also, a special thanks to [@ShadowJonathan](https://github.com/ShadowJonathan) for helping with the design and review
0 commit comments