Skip to content

Commit 450ce88

Browse files
author
Andrew J Westlake
committed
Added pyo3_asyncio::async_std::main attribute
1 parent 86dcfaf commit 450ce88

File tree

6 files changed

+119
-33
lines changed

6 files changed

+119
-33
lines changed

Cargo.toml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,21 @@ license = "Apache-2.0"
1212
exclude = ["/.gitignore", "/codecov.yml", "/Makefile"]
1313
edition = "2018"
1414

15-
16-
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
15+
[workspace]
16+
members = ["pyo3-asyncio-macros"]
1717

1818
[features]
1919
async-std-runtime = ["async-std"]
20+
attributes = ["pyo3-asyncio-macros"]
2021
testing = ["clap"]
2122
tokio-runtime = ["tokio"]
2223
default = []
2324

25+
[[example]]
26+
name = "async_std"
27+
path = "examples/async_std.rs"
28+
required-features = ["async-std-runtime"]
29+
2430
[[test]]
2531
name = "test_async_std_asyncio"
2632
path = "pytests/test_async_std_asyncio.rs"
@@ -63,6 +69,7 @@ futures = "0.3"
6369
lazy_static = "1.4"
6470
once_cell = "1.5"
6571
pyo3 = "0.13"
72+
pyo3-asyncio-macros = { path = "pyo3-asyncio-macros", version = "=0.13.0", optional = true }
6673

6774
[dependencies.async-std]
6875
version = "1.9"

README.md

Lines changed: 12 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -31,36 +31,17 @@ More details on the usage of this library can be found in the [API docs](https:/
3131
```rust no_run
3232
use pyo3::prelude::*;
3333

34-
fn main() {
35-
Python::with_gil(|py| {
36-
// Initialize the runtime
37-
pyo3_asyncio::with_runtime(py, || {
38-
let asyncio: PyObject = py.import("asyncio")?.into();
39-
40-
// Run the event loop until the given future completes
41-
pyo3_asyncio::async_std::run_until_complete(py, async move {
42-
Python::with_gil(|py| {
43-
// convert asyncio.sleep into a Rust Future
44-
pyo3_asyncio::into_future(
45-
asyncio.call_method1(
46-
py,
47-
"sleep",
48-
(1.into_py(py),)
49-
)?
50-
.as_ref(py)
51-
)
52-
})?
53-
.await?;
54-
55-
Ok(())
56-
})?;
57-
58-
Ok(())
59-
})
60-
.map_err(|e| {
61-
e.print_and_set_sys_last_vars(py);
62-
})
63-
.unwrap();
64-
})
34+
#[pyo3_asyncio::async_std::main]
35+
async fn main() -> PyResult<()> {
36+
let fut = Python::with_gil(|py| {
37+
let asyncio = py.import("asyncio")?;
38+
39+
// convert asyncio.sleep into a Rust Future
40+
pyo3_asyncio::into_future(asyncio.call_method1("sleep", (1.into_py(py),))?)
41+
})?;
42+
43+
fut.await?;
44+
45+
Ok(())
6546
}
6647
```

examples/async_std.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#[pyo3_asyncio::async_std::main]
2+
async fn main() -> PyResult<()> {
3+
let fut = Python::with_gil(|py| {
4+
let asyncio = py.import("asyncio")?;
5+
6+
// convert asyncio.sleep into a Rust Future
7+
pyo3_asyncio::into_future(asyncio.call_method1("sleep", (1.into_py(py),))?)
8+
})?;
9+
10+
println!("sleeping for 1s");
11+
fut.await?;
12+
println!("done");
13+
14+
Ok(())
15+
}

pyo3-asyncio-macros/Cargo.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "pyo3-asyncio-macros"
3+
version = "0.13.0"
4+
authors = ["Andrew J Westlake <[email protected]>"]
5+
edition = "2018"
6+
7+
[lib]
8+
proc-macro = true
9+
10+
[dependencies]
11+
quote = "1"
12+
syn = { version = "1", features = ["full"] }

pyo3-asyncio-macros/src/lib.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#![forbid(unsafe_code, future_incompatible, rust_2018_idioms)]
2+
#![deny(missing_debug_implementations, nonstandard_style)]
3+
#![recursion_limit = "512"]
4+
5+
use proc_macro::TokenStream;
6+
use quote::{quote, quote_spanned};
7+
use syn::spanned::Spanned;
8+
9+
/// Enables an async main function.
10+
///
11+
/// # Examples
12+
///
13+
/// ```ignore
14+
/// #[pyo3_asyncio::async_std::main]
15+
/// async fn main() -> PyResult<()> {
16+
/// Ok(())
17+
/// }
18+
/// ```
19+
#[cfg(not(test))] // NOTE: exporting main breaks tests, we should file an issue.
20+
#[proc_macro_attribute]
21+
pub fn async_std_main(_attr: TokenStream, item: TokenStream) -> TokenStream {
22+
let input = syn::parse_macro_input!(item as syn::ItemFn);
23+
24+
let ret = &input.sig.output;
25+
let inputs = &input.sig.inputs;
26+
let name = &input.sig.ident;
27+
let body = &input.block;
28+
let attrs = &input.attrs;
29+
let vis = &input.vis;
30+
31+
if name != "main" {
32+
return TokenStream::from(quote_spanned! { name.span() =>
33+
compile_error!("only the main function can be tagged with #[async_std::main]"),
34+
});
35+
}
36+
37+
if input.sig.asyncness.is_none() {
38+
return TokenStream::from(quote_spanned! { input.span() =>
39+
compile_error!("the async keyword is missing from the function declaration"),
40+
});
41+
}
42+
43+
let result = quote! {
44+
#vis fn main() {
45+
#(#attrs)*
46+
async fn main(#inputs) #ret {
47+
#body
48+
}
49+
50+
use pyo3::prelude::*;
51+
52+
Python::with_gil(|py| {
53+
pyo3_asyncio::with_runtime(py, || {
54+
pyo3_asyncio::async_std::run_until_complete(py, main())?;
55+
56+
Ok(())
57+
})
58+
.map_err(|e| {
59+
e.print_and_set_sys_last_vars(py);
60+
})
61+
.unwrap();
62+
});
63+
}
64+
65+
};
66+
67+
result.into()
68+
}

src/async_std.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ use pyo3::prelude::*;
55

66
use crate::generic::{self, JoinError, Runtime};
77

8+
#[cfg(feature = "attributes")]
9+
pub use pyo3_asyncio_macros::async_std_main as main;
10+
811
struct AsyncStdJoinError;
912

1013
impl JoinError for AsyncStdJoinError {

0 commit comments

Comments
 (0)