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
In Python, we can call this pyo3 function just like any other async function:
296
+
297
+
```python
298
+
from example import call_rust_sleep
299
+
300
+
asyncdefrust_sleep():
301
+
await call_rust_sleep()
302
+
```
303
+
304
+
## Managing Event Loops
305
+
306
+
Python's event loop requires some special treatment, especially regarding the main thread. Some of
307
+
Python's `asyncio` features, like proper signal handling, require control over the main thread, which
308
+
doesn't always play well with Rust.
309
+
310
+
Luckily, Rust's event loops are pretty flexible and don't _need_ control over the main thread, so in
311
+
`pyo3-asyncio`, we decided the best way to handle Rust/Python interop was to just surrender the main
312
+
thread to Python and run Rust's event loops in the background. Unfortunately, since most event loop
313
+
implementations _prefer_ control over the main thread, this can still make some things awkward.
314
+
315
+
### PyO3 Asyncio Initialization
316
+
317
+
Because Python needs to control the main thread, we can't use the convenient proc macros from Rust
318
+
runtimes to handle the `main` function or `#[test]` functions. Instead, the initialization for PyO3 has to be done from the `main` function and the main
319
+
thread must block on [`pyo3_asyncio::run_forever`](https://docs.rs/pyo3-asyncio/latest/pyo3_asyncio/fn.run_forever.html) or [`pyo3_asyncio::async_std::run_until_complete`](https://docs.rs/pyo3-asyncio/latest/pyo3_asyncio/async_std/fn.run_until_complete.html).
320
+
321
+
Because we have to block on one of those functions, we can't use [`#[async_std::main]`](https://docs.rs/async-std/latest/async_std/attr.main.html) or [`#[tokio::main]`](https://docs.rs/tokio/1.1.0/tokio/attr.main.html)
322
+
since it's not a good idea to make long blocking calls during an async function.
323
+
324
+
> Internally, these `#[main]` proc macros are expanded to something like this:
> thatcanavoidthisproblem, butagainthat'snotsomethingwecanuse here since we need it to
336
+
> block on the _main_ thread.
337
+
338
+
For this reason, `pyo3-asyncio` provides its own set of proc macros to provide you with this
339
+
initialization.These macros are intended to mirror the initialization of `async-std` and `tokio`
340
+
while also satisfying the Python runtime's needs.
341
+
342
+
Here's a full example of PyO3 initialization with the `async-std` runtime:
343
+
```rust no_run
344
+
usepyo3::prelude::*;
345
+
346
+
#[pyo3_asyncio::async_std::main]
347
+
asyncfnmain() ->PyResult<()> {
348
+
// PyO3 is initialized - Ready to go
349
+
350
+
letfut=Python::with_gil(|py|->PyResult<_> {
351
+
letasyncio=py.import("asyncio")?;
352
+
353
+
// convert asyncio.sleep into a Rust Future
354
+
pyo3_asyncio::async_std::into_future(
355
+
asyncio.call_method1("sleep", (1.into_py(py),))?
356
+
)
357
+
})?;
358
+
359
+
fut.await?;
360
+
361
+
Ok(())
362
+
}
363
+
```
364
+
365
+
## PyO3 Asyncio in Cargo Tests
366
+
367
+
The default Cargo Test harness does not currently allow test crates to provide their own `main`
368
+
function, so there doesn't seem to be a good way to allow Python to gain control over the main
369
+
thread.
370
+
371
+
We can, however, override the default test harness and provide our own. `pyo3-asyncio` provides some
372
+
utilities to help us do just that! In the following sections, we will provide an overview for
373
+
constructing a Cargo integration test with `pyo3-asyncio` and adding your tests to it.
374
+
375
+
### Main Test File
376
+
First, we need to create the test's main file. Although these tests are considered integration
377
+
tests, we cannot put them in the `tests` directory since that is a special directory owned by
378
+
Cargo. Instead, we put our tests in a `pytests` directory.
379
+
380
+
> The name `pytests` is just a convention. You can name this folder anything you want in your own
381
+
> projects.
382
+
383
+
We'll also want to provide the test's main function. Most of the functionality that the test harness needs is packed in the [`pyo3_asyncio::testing::main`](https://docs.rs/pyo3-asyncio/latest/pyo3_asyncio/testing/fn.main.html) function. This function will parse the test's CLI arguments, collect and pass the functions marked with [`#[pyo3_asyncio::async_std::test]`](https://docs.rs/pyo3-asyncio/latest/pyo3_asyncio/async_std/attr.test.html) or [`#[pyo3_asyncio::tokio::test]`](https://docs.rs/pyo3-asyncio/latest/pyo3_asyncio/tokio/attr.test.html) and pass them into the test harness for running and filtering.
384
+
385
+
`pytests/test_example.rs` for the `tokio` runtime:
386
+
```rust
387
+
#[pyo3_asyncio::tokio::main]
388
+
asyncfnmain() ->pyo3::PyResult<()> {
389
+
pyo3_asyncio::testing::main().await
390
+
}
391
+
```
392
+
393
+
`pytests/test_example.rs` for the `async-std` runtime:
394
+
```rust
395
+
#[pyo3_asyncio::async_std::main]
396
+
asyncfnmain() ->pyo3::PyResult<()> {
397
+
pyo3_asyncio::testing::main().await
398
+
}
399
+
```
400
+
401
+
### Cargo Configuration
402
+
Next, we need to add our test file to the Cargo manifest by adding the following section to the
403
+
`Cargo.toml`
404
+
405
+
```toml
406
+
[[test]]
407
+
name = "test_example"
408
+
path = "pytests/test_example.rs"
409
+
harness = false
410
+
```
411
+
412
+
Also add the `testing` and `attributes` features to the `pyo3-asyncio` dependency and select your preferred runtime:
413
+
414
+
```toml
415
+
pyo3-asyncio = { version = "0.13", features = ["testing", "attributes", "async-std-runtime"] }
416
+
```
417
+
418
+
At this point, you should be able to run the test via `cargo test`
419
+
420
+
### Adding Tests to the PyO3 Asyncio Test Harness
421
+
422
+
We can add tests anywhere in the test crate with the runtime's corresponding `#[test]` attribute:
423
+
424
+
For `async-std` use the [`pyo3_asyncio::async_std::test`](https://docs.rs/pyo3-asyncio/latest/pyo3_asyncio/async_std/attr.test.html) attribute:
0 commit comments