diff --git a/Cargo.toml b/Cargo.toml index 32fa5a006b6..32dc0ba75ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,6 +65,9 @@ pyo3-build-config = { path = "pyo3-build-config", version = "=0.21.0-dev", featu [features] default = ["macros"] +# Enables support for `async fn` for `#[pyfunction]` and `#[pymethods]`. +experimental-async = ["macros", "pyo3-macros/experimental-async"] + # Enables pyo3::inspect module and additional type information on FromPyObject # and IntoPy traits experimental-inspect = [] @@ -115,8 +118,9 @@ full = [ "chrono", "chrono-tz", "either", - "experimental-inspect", + "experimental-async", "experimental-declarative-modules", + "experimental-inspect", "eyre", "hashbrown", "indexmap", diff --git a/guide/src/async-await.md b/guide/src/async-await.md index c14b5d93d84..688f0a65bc4 100644 --- a/guide/src/async-await.md +++ b/guide/src/async-await.md @@ -6,6 +6,7 @@ ```rust # #![allow(dead_code)] +# #[cfg(feature = "experimental-async")] { use std::{thread, time::Duration}; use futures::channel::oneshot; use pyo3::prelude::*; @@ -20,6 +21,7 @@ async fn sleep(seconds: f64, result: Option) -> Option { rx.await.unwrap(); result } +# } ``` *Python awaitables instantiated with this method can only be awaited in *asyncio* context. Other Python async runtime may be supported in the future.* @@ -72,6 +74,7 @@ Cancellation on the Python side can be caught using [`CancelHandle`]({{#PYO3_DOC ```rust # #![allow(dead_code)] +# #[cfg(feature = "experimental-async")] { use futures::FutureExt; use pyo3::prelude::*; use pyo3::coroutine::CancelHandle; @@ -83,11 +86,12 @@ async fn cancellable(#[pyo3(cancel_handle)] mut cancel: CancelHandle) { _ = cancel.cancelled().fuse() => println!("cancelled"), } } +# } ``` ## The `Coroutine` type -To make a Rust future awaitable in Python, PyO3 defines a [`Coroutine`]({{#PYO3_DOCS_URL}}/pyo3/coroutine/struct.Coroutine.html) type, which implements the Python [coroutine protocol](https://docs.python.org/3/library/collections.abc.html#collections.abc.Coroutine). +To make a Rust future awaitable in Python, PyO3 defines a [`Coroutine`]({{#PYO3_DOCS_URL}}/pyo3/coroutine/struct.Coroutine.html) type, which implements the Python [coroutine protocol](https://docs.python.org/3/library/collections.abc.html#collections.abc.Coroutine). Each `coroutine.send` call is translated to a `Future::poll` call. If a [`CancelHandle`]({{#PYO3_DOCS_URL}}/pyo3/coroutine/struct.CancelHandle.html) parameter is declared, the exception passed to `coroutine.throw` call is stored in it and can be retrieved with [`CancelHandle::cancelled`]({{#PYO3_DOCS_URL}}/pyo3/coroutine/struct.CancelHandle.html#method.cancelled); otherwise, it cancels the Rust future, and the exception is reraised; diff --git a/guide/src/features.md b/guide/src/features.md index 43124e0076e..118284959d6 100644 --- a/guide/src/features.md +++ b/guide/src/features.md @@ -51,6 +51,12 @@ If you do not enable this feature, you should call `pyo3::prepare_freethreaded_p ## Advanced Features +### `experimental-async` + +This feature adds support for `async fn` in `#[pyfunction]` and `#[pymethods]`. + +The feature has some unfinished refinements and performance improvements. To help finish this off, see [issue #1632](https://github.com/PyO3/pyo3/issues/1632) and its associated draft PRs. + ### `experimental-inspect` This feature adds the `pyo3::inspect` module, as well as `IntoPy::type_output` and `FromPyObject::type_input` APIs to produce Python type "annotations" for Rust types. diff --git a/newsfragments/3931.added.md b/newsfragments/3931.added.md new file mode 100644 index 00000000000..b532adeeae5 --- /dev/null +++ b/newsfragments/3931.added.md @@ -0,0 +1 @@ +Add `experimental-async` feature. diff --git a/pyo3-macros-backend/Cargo.toml b/pyo3-macros-backend/Cargo.toml index 0e83cc29fd4..665c8c3510d 100644 --- a/pyo3-macros-backend/Cargo.toml +++ b/pyo3-macros-backend/Cargo.toml @@ -26,3 +26,6 @@ features = ["derive", "parsing", "printing", "clone-impls", "full", "extra-trait [lints] workspace = true + +[features] +experimental-async = [] diff --git a/pyo3-macros-backend/src/method.rs b/pyo3-macros-backend/src/method.rs index 1d2d22b236b..9a8cb828b22 100644 --- a/pyo3-macros-backend/src/method.rs +++ b/pyo3-macros-backend/src/method.rs @@ -512,6 +512,13 @@ impl<'a> FnSpec<'a> { } } + if self.asyncness.is_some() { + ensure_spanned!( + cfg!(feature = "experimental-async"), + self.asyncness.span() => "async functions are only supported with the `experimental-async` feature" + ); + } + let rust_call = |args: Vec, holders: &mut Vec| { let self_arg = self.tp.self_arg(cls, ExtractErrorMode::Raise, holders, ctx); diff --git a/pyo3-macros-backend/src/module.rs b/pyo3-macros-backend/src/module.rs index 7528ef8102f..7173fa8647d 100644 --- a/pyo3-macros-backend/src/module.rs +++ b/pyo3-macros-backend/src/module.rs @@ -115,19 +115,10 @@ pub fn pymodule_module_impl(mut module: syn::ItemMod) -> Result { for item in &mut *items { match item { Item::Use(item_use) => { - let mut is_pyo3 = false; - item_use.attrs.retain(|attr| { - let found = attr.path().is_ident("pymodule_export"); - is_pyo3 |= found; - !found - }); - if is_pyo3 { - let cfg_attrs = item_use - .attrs - .iter() - .filter(|attr| attr.path().is_ident("cfg")) - .cloned() - .collect::>(); + let is_pymodule_export = + find_and_remove_attribute(&mut item_use.attrs, "pymodule_export"); + if is_pymodule_export { + let cfg_attrs = get_cfg_attributes(&item_use.attrs); extract_use_items( &item_use.tree, &cfg_attrs, @@ -137,23 +128,116 @@ pub fn pymodule_module_impl(mut module: syn::ItemMod) -> Result { } } Item::Fn(item_fn) => { - let mut is_module_init = false; - item_fn.attrs.retain(|attr| { - let found = attr.path().is_ident("pymodule_init"); - is_module_init |= found; - !found - }); - if is_module_init { - ensure_spanned!(pymodule_init.is_none(), item_fn.span() => "only one pymodule_init may be specified"); - let ident = &item_fn.sig.ident; + ensure_spanned!( + !has_attribute(&item_fn.attrs, "pymodule_export"), + item.span() => "`#[pymodule_export]` may only be used on `use` statements" + ); + let is_pymodule_init = + find_and_remove_attribute(&mut item_fn.attrs, "pymodule_init"); + let ident = &item_fn.sig.ident; + if is_pymodule_init { + ensure_spanned!( + !has_attribute(&item_fn.attrs, "pyfunction"), + item_fn.span() => "`#[pyfunction]` cannot be used alongside `#[pymodule_init]`" + ); + ensure_spanned!(pymodule_init.is_none(), item_fn.span() => "only one `#[pymodule_init]` may be specified"); pymodule_init = Some(quote! { #ident(module)?; }); - } else { - bail_spanned!(item.span() => "only 'use' statements and and pymodule_init functions are allowed in #[pymodule]") + } else if has_attribute(&item_fn.attrs, "pyfunction") { + module_items.push(ident.clone()); + module_items_cfg_attrs.push(get_cfg_attributes(&item_fn.attrs)); } } - item => { - bail_spanned!(item.span() => "only 'use' statements and and pymodule_init functions are allowed in #[pymodule]") + Item::Struct(item_struct) => { + ensure_spanned!( + !has_attribute(&item_struct.attrs, "pymodule_export"), + item.span() => "`#[pymodule_export]` may only be used on `use` statements" + ); + if has_attribute(&item_struct.attrs, "pyclass") { + module_items.push(item_struct.ident.clone()); + module_items_cfg_attrs.push(get_cfg_attributes(&item_struct.attrs)); + } + } + Item::Enum(item_enum) => { + ensure_spanned!( + !has_attribute(&item_enum.attrs, "pymodule_export"), + item.span() => "`#[pymodule_export]` may only be used on `use` statements" + ); + if has_attribute(&item_enum.attrs, "pyclass") { + module_items.push(item_enum.ident.clone()); + module_items_cfg_attrs.push(get_cfg_attributes(&item_enum.attrs)); + } + } + Item::Mod(item_mod) => { + ensure_spanned!( + !has_attribute(&item_mod.attrs, "pymodule_export"), + item.span() => "`#[pymodule_export]` may only be used on `use` statements" + ); + if has_attribute(&item_mod.attrs, "pymodule") { + module_items.push(item_mod.ident.clone()); + module_items_cfg_attrs.push(get_cfg_attributes(&item_mod.attrs)); + } + } + Item::ForeignMod(item) => { + ensure_spanned!( + !has_attribute(&item.attrs, "pymodule_export"), + item.span() => "`#[pymodule_export]` may only be used on `use` statements" + ); + } + Item::Trait(item) => { + ensure_spanned!( + !has_attribute(&item.attrs, "pymodule_export"), + item.span() => "`#[pymodule_export]` may only be used on `use` statements" + ); + } + Item::Const(item) => { + ensure_spanned!( + !has_attribute(&item.attrs, "pymodule_export"), + item.span() => "`#[pymodule_export]` may only be used on `use` statements" + ); + } + Item::Static(item) => { + ensure_spanned!( + !has_attribute(&item.attrs, "pymodule_export"), + item.span() => "`#[pymodule_export]` may only be used on `use` statements" + ); + } + Item::Macro(item) => { + ensure_spanned!( + !has_attribute(&item.attrs, "pymodule_export"), + item.span() => "`#[pymodule_export]` may only be used on `use` statements" + ); + } + Item::ExternCrate(item) => { + ensure_spanned!( + !has_attribute(&item.attrs, "pymodule_export"), + item.span() => "`#[pymodule_export]` may only be used on `use` statements" + ); + } + Item::Impl(item) => { + ensure_spanned!( + !has_attribute(&item.attrs, "pymodule_export"), + item.span() => "`#[pymodule_export]` may only be used on `use` statements" + ); } + Item::TraitAlias(item) => { + ensure_spanned!( + !has_attribute(&item.attrs, "pymodule_export"), + item.span() => "`#[pymodule_export]` may only be used on `use` statements" + ); + } + Item::Type(item) => { + ensure_spanned!( + !has_attribute(&item.attrs, "pymodule_export"), + item.span() => "`#[pymodule_export]` may only be used on `use` statements" + ); + } + Item::Union(item) => { + ensure_spanned!( + !has_attribute(&item.attrs, "pymodule_export"), + item.span() => "`#[pymodule_export]` may only be used on `use` statements" + ); + } + _ => (), } } @@ -355,6 +439,31 @@ fn get_pyfn_attr(attrs: &mut Vec) -> syn::Result Vec { + attrs + .iter() + .filter(|attr| attr.path().is_ident("cfg")) + .cloned() + .collect() +} + +fn find_and_remove_attribute(attrs: &mut Vec, ident: &str) -> bool { + let mut found = false; + attrs.retain(|attr| { + if attr.path().is_ident(ident) { + found = true; + false + } else { + true + } + }); + found +} + +fn has_attribute(attrs: &[syn::Attribute], ident: &str) -> bool { + attrs.iter().any(|attr| attr.path().is_ident(ident)) +} + enum PyModulePyO3Option { Crate(CrateAttribute), Name(NameAttribute), diff --git a/pyo3-macros/Cargo.toml b/pyo3-macros/Cargo.toml index a0368a5f364..97d2de07cba 100644 --- a/pyo3-macros/Cargo.toml +++ b/pyo3-macros/Cargo.toml @@ -15,6 +15,7 @@ proc-macro = true [features] multiple-pymethods = [] +experimental-async = ["pyo3-macros-backend/experimental-async"] experimental-declarative-modules = [] [dependencies] diff --git a/src/impl_.rs b/src/impl_.rs index 77f9ff4ea1f..ea71b257c0e 100644 --- a/src/impl_.rs +++ b/src/impl_.rs @@ -6,7 +6,7 @@ //! APIs may may change at any time without documentation in the CHANGELOG and without //! breaking semver guarantees. -#[cfg(feature = "macros")] +#[cfg(feature = "experimental-async")] pub mod coroutine; pub mod deprecations; pub mod extract_argument; diff --git a/src/lib.rs b/src/lib.rs index 26d2ec55da1..c2c7d96a40a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -418,7 +418,7 @@ pub mod buffer; pub mod callback; pub mod conversion; mod conversions; -#[cfg(feature = "macros")] +#[cfg(feature = "experimental-async")] pub mod coroutine; #[macro_use] #[doc(hidden)] diff --git a/tests/test_compile_error.rs b/tests/test_compile_error.rs index 7da85efe4df..2c4ebabfb13 100644 --- a/tests/test_compile_error.rs +++ b/tests/test_compile_error.rs @@ -21,14 +21,14 @@ fn test_compile_errors() { t.compile_fail("tests/ui/deprecations.rs"); t.compile_fail("tests/ui/invalid_closure.rs"); t.compile_fail("tests/ui/pyclass_send.rs"); - #[cfg(any(not(Py_LIMITED_API), Py_3_10))] // to avoid PyFunctionArgument for &str t.compile_fail("tests/ui/invalid_argument_attributes.rs"); t.compile_fail("tests/ui/invalid_frompy_derive.rs"); t.compile_fail("tests/ui/static_ref.rs"); t.compile_fail("tests/ui/wrong_aspyref_lifetimes.rs"); t.compile_fail("tests/ui/invalid_pyfunctions.rs"); t.compile_fail("tests/ui/invalid_pymethods.rs"); - #[cfg(Py_LIMITED_API)] + // output changes with async feature + #[cfg(all(Py_LIMITED_API, feature = "experimental-async"))] t.compile_fail("tests/ui/abi3_nativetype_inheritance.rs"); t.compile_fail("tests/ui/invalid_intern_arg.rs"); t.compile_fail("tests/ui/invalid_frozen_pyclass_borrow.rs"); @@ -49,4 +49,7 @@ fn test_compile_errors() { t.compile_fail("tests/ui/invalid_pymodule_trait.rs"); #[cfg(feature = "experimental-declarative-modules")] t.compile_fail("tests/ui/invalid_pymodule_two_pymodule_init.rs"); + #[cfg(feature = "experimental-async")] + #[cfg(any(not(Py_LIMITED_API), Py_3_10))] // to avoid PyFunctionArgument for &str + t.compile_fail("tests/ui/invalid_cancel_handle.rs"); } diff --git a/tests/test_coroutine.rs b/tests/test_coroutine.rs index db79c72a233..17539fa113e 100644 --- a/tests/test_coroutine.rs +++ b/tests/test_coroutine.rs @@ -1,4 +1,4 @@ -#![cfg(feature = "macros")] +#![cfg(feature = "experimental-async")] #![cfg(not(target_arch = "wasm32"))] use std::{task::Poll, thread, time::Duration}; diff --git a/tests/test_declarative_module.rs b/tests/test_declarative_module.rs index 86913d9b800..3cb93765173 100644 --- a/tests/test_declarative_module.rs +++ b/tests/test_declarative_module.rs @@ -15,8 +15,8 @@ struct ValueClass { #[pymethods] impl ValueClass { #[new] - fn new(value: usize) -> ValueClass { - ValueClass { value } + fn new(value: usize) -> Self { + Self { value } } } @@ -48,6 +48,33 @@ mod declarative_module { #[pymodule_export] use super::{declarative_module2, double, MyError, ValueClass as Value}; + #[pymodule] + mod inner { + use super::*; + + #[pyfunction] + fn triple(x: usize) -> usize { + x * 3 + } + + #[pyclass] + struct Struct; + + #[pymethods] + impl Struct { + #[new] + fn new() -> Self { + Self + } + } + + #[pyclass] + enum Enum { + A, + B, + } + } + #[pymodule_init] fn init(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add("double2", m.getattr("double")?) @@ -65,7 +92,6 @@ mod declarative_submodule { use super::{double, double_value}; } -/// A module written using declarative syntax. #[pymodule] #[pyo3(name = "declarative_module_renamed")] mod declarative_module2 { @@ -84,7 +110,7 @@ fn test_declarative_module() { ); py_assert!(py, m, "m.double(2) == 4"); - py_assert!(py, m, "m.double2(3) == 6"); + py_assert!(py, m, "m.inner.triple(3) == 9"); py_assert!(py, m, "m.declarative_submodule.double(4) == 8"); py_assert!( py, @@ -97,5 +123,7 @@ fn test_declarative_module() { py_assert!(py, m, "not hasattr(m, 'LocatedClass')"); #[cfg(not(Py_LIMITED_API))] py_assert!(py, m, "hasattr(m, 'LocatedClass')"); + py_assert!(py, m, "isinstance(m.inner.Struct(), m.inner.Struct)"); + py_assert!(py, m, "isinstance(m.inner.Enum.A, m.inner.Enum)"); }) } diff --git a/tests/ui/invalid_argument_attributes.rs b/tests/ui/invalid_argument_attributes.rs index ed9d6ce6971..311c6c03e0e 100644 --- a/tests/ui/invalid_argument_attributes.rs +++ b/tests/ui/invalid_argument_attributes.rs @@ -15,29 +15,4 @@ fn from_py_with_value_not_a_string(#[pyo3(from_py_with = func)] param: String) { #[pyfunction] fn from_py_with_repeated(#[pyo3(from_py_with = "func", from_py_with = "func")] param: String) {} -#[pyfunction] -async fn from_py_with_value_and_cancel_handle( - #[pyo3(from_py_with = "func", cancel_handle)] _param: String, -) { -} - -#[pyfunction] -async fn cancel_handle_repeated(#[pyo3(cancel_handle, cancel_handle)] _param: String) {} - -#[pyfunction] -async fn cancel_handle_repeated2( - #[pyo3(cancel_handle)] _param: String, - #[pyo3(cancel_handle)] _param2: String, -) { -} - -#[pyfunction] -fn cancel_handle_synchronous(#[pyo3(cancel_handle)] _param: String) {} - -#[pyfunction] -async fn cancel_handle_wrong_type(#[pyo3(cancel_handle)] _param: String) {} - -#[pyfunction] -async fn missing_cancel_handle_attribute(_param: pyo3::coroutine::CancelHandle) {} - fn main() {} diff --git a/tests/ui/invalid_argument_attributes.stderr b/tests/ui/invalid_argument_attributes.stderr index 382aa7e3b0a..d27c25fd179 100644 --- a/tests/ui/invalid_argument_attributes.stderr +++ b/tests/ui/invalid_argument_attributes.stderr @@ -27,84 +27,3 @@ error: `from_py_with` may only be specified once per argument | 16 | fn from_py_with_repeated(#[pyo3(from_py_with = "func", from_py_with = "func")] param: String) {} | ^^^^^^^^^^^^ - -error: `from_py_with` and `cancel_handle` cannot be specified together - --> tests/ui/invalid_argument_attributes.rs:20:35 - | -20 | #[pyo3(from_py_with = "func", cancel_handle)] _param: String, - | ^^^^^^^^^^^^^ - -error: `cancel_handle` may only be specified once per argument - --> tests/ui/invalid_argument_attributes.rs:25:55 - | -25 | async fn cancel_handle_repeated(#[pyo3(cancel_handle, cancel_handle)] _param: String) {} - | ^^^^^^^^^^^^^ - -error: `cancel_handle` may only be specified once - --> tests/ui/invalid_argument_attributes.rs:30:28 - | -30 | #[pyo3(cancel_handle)] _param2: String, - | ^^^^^^^ - -error: `cancel_handle` attribute can only be used with `async fn` - --> tests/ui/invalid_argument_attributes.rs:35:53 - | -35 | fn cancel_handle_synchronous(#[pyo3(cancel_handle)] _param: String) {} - | ^^^^^^ - -error[E0308]: mismatched types - --> tests/ui/invalid_argument_attributes.rs:37:1 - | -37 | #[pyfunction] - | ^^^^^^^^^^^^^ - | | - | expected `String`, found `CancelHandle` - | arguments to this function are incorrect - | -note: function defined here - --> tests/ui/invalid_argument_attributes.rs:38:10 - | -38 | async fn cancel_handle_wrong_type(#[pyo3(cancel_handle)] _param: String) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^ -------------- - = note: this error originates in the attribute macro `pyfunction` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0277]: the trait bound `CancelHandle: PyClass` is not satisfied - --> tests/ui/invalid_argument_attributes.rs:41:50 - | -41 | async fn missing_cancel_handle_attribute(_param: pyo3::coroutine::CancelHandle) {} - | ^^^^ the trait `PyClass` is not implemented for `CancelHandle` - | - = help: the trait `PyClass` is implemented for `pyo3::coroutine::Coroutine` - = note: required for `CancelHandle` to implement `FromPyObject<'_>` - = note: required for `CancelHandle` to implement `FromPyObjectBound<'_, '_>` - = note: required for `CancelHandle` to implement `PyFunctionArgument<'_, '_>` -note: required by a bound in `extract_argument` - --> src/impl_/extract_argument.rs - | - | pub fn extract_argument<'a, 'py, T>( - | ---------------- required by a bound in this function -... - | T: PyFunctionArgument<'a, 'py>, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `extract_argument` - -error[E0277]: the trait bound `CancelHandle: Clone` is not satisfied - --> tests/ui/invalid_argument_attributes.rs:41:50 - | -41 | async fn missing_cancel_handle_attribute(_param: pyo3::coroutine::CancelHandle) {} - | ^^^^ the trait `Clone` is not implemented for `CancelHandle` - | - = help: the following other types implement trait `PyFunctionArgument<'a, 'py>`: - &'a pyo3::Bound<'py, T> - &'a pyo3::coroutine::Coroutine - &'a mut pyo3::coroutine::Coroutine - = note: required for `CancelHandle` to implement `FromPyObject<'_>` - = note: required for `CancelHandle` to implement `FromPyObjectBound<'_, '_>` - = note: required for `CancelHandle` to implement `PyFunctionArgument<'_, '_>` -note: required by a bound in `extract_argument` - --> src/impl_/extract_argument.rs - | - | pub fn extract_argument<'a, 'py, T>( - | ---------------- required by a bound in this function -... - | T: PyFunctionArgument<'a, 'py>, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `extract_argument` diff --git a/tests/ui/invalid_cancel_handle.rs b/tests/ui/invalid_cancel_handle.rs new file mode 100644 index 00000000000..59076b14418 --- /dev/null +++ b/tests/ui/invalid_cancel_handle.rs @@ -0,0 +1,22 @@ +use pyo3::prelude::*; + +#[pyfunction] +async fn cancel_handle_repeated(#[pyo3(cancel_handle, cancel_handle)] _param: String) {} + +#[pyfunction] +async fn cancel_handle_repeated2( + #[pyo3(cancel_handle)] _param: String, + #[pyo3(cancel_handle)] _param2: String, +) { +} + +#[pyfunction] +fn cancel_handle_synchronous(#[pyo3(cancel_handle)] _param: String) {} + +#[pyfunction] +async fn cancel_handle_wrong_type(#[pyo3(cancel_handle)] _param: String) {} + +#[pyfunction] +async fn missing_cancel_handle_attribute(_param: pyo3::coroutine::CancelHandle) {} + +fn main() {} diff --git a/tests/ui/invalid_cancel_handle.stderr b/tests/ui/invalid_cancel_handle.stderr new file mode 100644 index 00000000000..9e617bca988 --- /dev/null +++ b/tests/ui/invalid_cancel_handle.stderr @@ -0,0 +1,74 @@ +error: `cancel_handle` may only be specified once per argument + --> tests/ui/invalid_cancel_handle.rs:4:55 + | +4 | async fn cancel_handle_repeated(#[pyo3(cancel_handle, cancel_handle)] _param: String) {} + | ^^^^^^^^^^^^^ + +error: `cancel_handle` may only be specified once + --> tests/ui/invalid_cancel_handle.rs:9:28 + | +9 | #[pyo3(cancel_handle)] _param2: String, + | ^^^^^^^ + +error: `cancel_handle` attribute can only be used with `async fn` + --> tests/ui/invalid_cancel_handle.rs:14:53 + | +14 | fn cancel_handle_synchronous(#[pyo3(cancel_handle)] _param: String) {} + | ^^^^^^ + +error[E0308]: mismatched types + --> tests/ui/invalid_cancel_handle.rs:16:1 + | +16 | #[pyfunction] + | ^^^^^^^^^^^^^ + | | + | expected `String`, found `CancelHandle` + | arguments to this function are incorrect + | +note: function defined here + --> tests/ui/invalid_cancel_handle.rs:17:10 + | +17 | async fn cancel_handle_wrong_type(#[pyo3(cancel_handle)] _param: String) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ -------------- + = note: this error originates in the attribute macro `pyfunction` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `CancelHandle: PyClass` is not satisfied + --> tests/ui/invalid_cancel_handle.rs:20:50 + | +20 | async fn missing_cancel_handle_attribute(_param: pyo3::coroutine::CancelHandle) {} + | ^^^^ the trait `PyClass` is not implemented for `CancelHandle` + | + = help: the trait `PyClass` is implemented for `pyo3::coroutine::Coroutine` + = note: required for `CancelHandle` to implement `FromPyObject<'_>` + = note: required for `CancelHandle` to implement `FromPyObjectBound<'_, '_>` + = note: required for `CancelHandle` to implement `PyFunctionArgument<'_, '_>` +note: required by a bound in `extract_argument` + --> src/impl_/extract_argument.rs + | + | pub fn extract_argument<'a, 'py, T>( + | ---------------- required by a bound in this function +... + | T: PyFunctionArgument<'a, 'py>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `extract_argument` + +error[E0277]: the trait bound `CancelHandle: Clone` is not satisfied + --> tests/ui/invalid_cancel_handle.rs:20:50 + | +20 | async fn missing_cancel_handle_attribute(_param: pyo3::coroutine::CancelHandle) {} + | ^^^^ the trait `Clone` is not implemented for `CancelHandle` + | + = help: the following other types implement trait `PyFunctionArgument<'a, 'py>`: + &'a pyo3::Bound<'py, T> + &'a pyo3::coroutine::Coroutine + &'a mut pyo3::coroutine::Coroutine + = note: required for `CancelHandle` to implement `FromPyObject<'_>` + = note: required for `CancelHandle` to implement `FromPyObjectBound<'_, '_>` + = note: required for `CancelHandle` to implement `PyFunctionArgument<'_, '_>` +note: required by a bound in `extract_argument` + --> src/impl_/extract_argument.rs + | + | pub fn extract_argument<'a, 'py, T>( + | ---------------- required by a bound in this function +... + | T: PyFunctionArgument<'a, 'py>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `extract_argument` diff --git a/tests/ui/invalid_pymodule_trait.stderr b/tests/ui/invalid_pymodule_trait.stderr index 3ed128617f5..4b02f14a540 100644 --- a/tests/ui/invalid_pymodule_trait.stderr +++ b/tests/ui/invalid_pymodule_trait.stderr @@ -1,4 +1,4 @@ -error: only 'use' statements and and pymodule_init functions are allowed in #[pymodule] +error: `#[pymodule_export]` may only be used on `use` statements --> tests/ui/invalid_pymodule_trait.rs:5:5 | 5 | #[pymodule_export] diff --git a/tests/ui/invalid_pymodule_two_pymodule_init.stderr b/tests/ui/invalid_pymodule_two_pymodule_init.stderr index 9f0900f9348..c117ebd573f 100644 --- a/tests/ui/invalid_pymodule_two_pymodule_init.stderr +++ b/tests/ui/invalid_pymodule_two_pymodule_init.stderr @@ -1,4 +1,4 @@ -error: only one pymodule_init may be specified +error: only one `#[pymodule_init]` may be specified --> tests/ui/invalid_pymodule_two_pymodule_init.rs:11:5 | 11 | fn init2(m: &PyModule) -> PyResult<()> {