diff --git a/Cargo.toml b/Cargo.toml index e1dea34f6b7..8234c374ca9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -69,8 +69,7 @@ portable-atomic = "1.0" assert_approx_eq = "1.1.0" chrono = "0.4.25" chrono-tz = ">= 0.10, < 0.11" -# Required for "and $N others" normalization -trybuild = ">=1.0.70" +trybuild = ">=1.0.115" proptest = { version = "1.0", default-features = false, features = ["std"] } send_wrapper = "0.6" serde = { version = "1.0", features = ["derive"] } diff --git a/guide/pyclass-parameters.md b/guide/pyclass-parameters.md index 5aefa4c69a0..c96082dd035 100644 --- a/guide/pyclass-parameters.md +++ b/guide/pyclass-parameters.md @@ -22,6 +22,7 @@ | `rename_all = "renaming_rule"` | Applies renaming rules to every getters and setters of a struct, or every variants of an enum. Possible values are: "camelCase", "kebab-case", "lowercase", "PascalCase", "SCREAMING-KEBAB-CASE", "SCREAMING_SNAKE_CASE", "snake_case", "UPPERCASE". | | `sequence` | Inform PyO3 that this class is a [`Sequence`][params-sequence], and so leave its C-API mapping length slot empty. | | `set_all` | Generates setters for all fields of the pyclass. | +| `new = "from_fields"` | Generates a default `__new__` constructor with all fields as parameters in the `new()` method. | | `skip_from_py_object` | Prevents this PyClass from participating in the `FromPyObject: PyClass + Clone` blanket implementation. This allows a custom `FromPyObject` impl, even if `self` is `Clone`. | | `str` | Implements `__str__` using the `Display` implementation of the underlying Rust datatype or by passing an optional format string `str=""`. *Note: The optional format string is only allowed for structs. `name` and `rename_all` are incompatible with the optional format string. Additional details can be found in the discussion on this [PR](https://github.com/PyO3/pyo3/pull/4233).* | | `subclass` | Allows other Python classes and `#[pyclass]` to inherit from this class. Enums cannot be subclassed. | diff --git a/newsfragments/5421.added.md b/newsfragments/5421.added.md new file mode 100644 index 00000000000..f4b6dd5ae18 --- /dev/null +++ b/newsfragments/5421.added.md @@ -0,0 +1 @@ +Implement `new = "from_fields"` attribute for `#[pyclass]` \ No newline at end of file diff --git a/pyo3-macros-backend/src/attributes.rs b/pyo3-macros-backend/src/attributes.rs index 6e7de98e318..9894c463628 100644 --- a/pyo3-macros-backend/src/attributes.rs +++ b/pyo3-macros-backend/src/attributes.rs @@ -40,6 +40,7 @@ pub mod kw { syn::custom_keyword!(sequence); syn::custom_keyword!(set); syn::custom_keyword!(set_all); + syn::custom_keyword!(new); syn::custom_keyword!(signature); syn::custom_keyword!(str); syn::custom_keyword!(subclass); @@ -311,6 +312,33 @@ impl ToTokens for TextSignatureAttributeValue { } } +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum NewImplTypeAttributeValue { + FromFields, + // Future variant for 'default' should go here +} + +impl Parse for NewImplTypeAttributeValue { + fn parse(input: ParseStream<'_>) -> Result { + let string_literal: LitStr = input.parse()?; + if string_literal.value().as_str() == "from_fields" { + Ok(NewImplTypeAttributeValue::FromFields) + } else { + bail_spanned!(string_literal.span() => "expected \"from_fields\"") + } + } +} + +impl ToTokens for NewImplTypeAttributeValue { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + NewImplTypeAttributeValue::FromFields => { + tokens.extend(quote! { "from_fields" }); + } + } + } +} + pub type ExtendsAttribute = KeywordAttribute; pub type FreelistAttribute = KeywordAttribute>; pub type ModuleAttribute = KeywordAttribute; @@ -318,6 +346,7 @@ pub type NameAttribute = KeywordAttribute; pub type RenameAllAttribute = KeywordAttribute; pub type StrFormatterAttribute = OptionalKeywordAttribute; pub type TextSignatureAttribute = KeywordAttribute; +pub type NewImplTypeAttribute = KeywordAttribute; pub type SubmoduleAttribute = kw::submodule; pub type GILUsedAttribute = KeywordAttribute; diff --git a/pyo3-macros-backend/src/pyclass.rs b/pyo3-macros-backend/src/pyclass.rs index d4939655b95..99d19509e9c 100644 --- a/pyo3-macros-backend/src/pyclass.rs +++ b/pyo3-macros-backend/src/pyclass.rs @@ -11,7 +11,8 @@ use syn::{parse_quote, parse_quote_spanned, spanned::Spanned, ImplItemFn, Result use crate::attributes::kw::frozen; use crate::attributes::{ self, kw, take_pyo3_options, CrateAttribute, ExtendsAttribute, FreelistAttribute, - ModuleAttribute, NameAttribute, NameLitStr, RenameAllAttribute, StrFormatterAttribute, + ModuleAttribute, NameAttribute, NameLitStr, NewImplTypeAttribute, NewImplTypeAttributeValue, + RenameAllAttribute, StrFormatterAttribute, }; use crate::combine_errors::CombineErrors; #[cfg(feature = "experimental-inspect")] @@ -88,6 +89,7 @@ pub struct PyClassPyO3Options { pub rename_all: Option, pub sequence: Option, pub set_all: Option, + pub new: Option, pub str: Option, pub subclass: Option, pub unsendable: Option, @@ -115,6 +117,7 @@ pub enum PyClassPyO3Option { RenameAll(RenameAllAttribute), Sequence(kw::sequence), SetAll(kw::set_all), + New(NewImplTypeAttribute), Str(StrFormatterAttribute), Subclass(kw::subclass), Unsendable(kw::unsendable), @@ -161,6 +164,8 @@ impl Parse for PyClassPyO3Option { input.parse().map(PyClassPyO3Option::Sequence) } else if lookahead.peek(attributes::kw::set_all) { input.parse().map(PyClassPyO3Option::SetAll) + } else if lookahead.peek(attributes::kw::new) { + input.parse().map(PyClassPyO3Option::New) } else if lookahead.peek(attributes::kw::str) { input.parse().map(PyClassPyO3Option::Str) } else if lookahead.peek(attributes::kw::subclass) { @@ -243,6 +248,7 @@ impl PyClassPyO3Options { PyClassPyO3Option::RenameAll(rename_all) => set_option!(rename_all), PyClassPyO3Option::Sequence(sequence) => set_option!(sequence), PyClassPyO3Option::SetAll(set_all) => set_option!(set_all), + PyClassPyO3Option::New(new) => set_option!(new), PyClassPyO3Option::Str(str) => set_option!(str), PyClassPyO3Option::Subclass(subclass) => set_option!(subclass), PyClassPyO3Option::Unsendable(unsendable) => set_option!(unsendable), @@ -488,6 +494,13 @@ fn impl_class( } } + let (default_new, default_new_slot) = pyclass_new_impl( + &args.options, + &syn::parse_quote!(#cls), + field_options.iter().map(|(f, _)| f), + ctx, + )?; + let mut default_methods = descriptors_to_items( cls, args.options.rename_all.as_ref(), @@ -516,6 +529,7 @@ fn impl_class( slots.extend(default_richcmp_slot); slots.extend(default_hash_slot); slots.extend(default_str_slot); + slots.extend(default_new_slot); let impl_builder = PyClassImplsBuilder::new(cls, cls, args, methods_type, default_methods, slots).doc(doc); @@ -543,6 +557,7 @@ fn impl_class( #default_richcmp #default_hash #default_str + #default_new #default_class_getitem } }) @@ -1711,11 +1726,11 @@ fn generate_protocol_slot( ) -> syn::Result { let spec = FnSpec::parse( &mut method.sig, - &mut Vec::new(), + &mut method.attrs, PyFunctionOptions::default(), )?; #[cfg_attr(not(feature = "experimental-inspect"), allow(unused_mut))] - let mut def = slot.generate_type_slot(&syn::parse_quote!(#cls), &spec, name, ctx)?; + let mut def = slot.generate_type_slot(cls, &spec, name, ctx)?; #[cfg(feature = "experimental-inspect")] def.add_introspection(introspection_data.generate(ctx, cls)); Ok(def) @@ -2431,6 +2446,94 @@ fn pyclass_hash( } } +fn pyclass_new_impl<'a>( + options: &PyClassPyO3Options, + ty: &syn::Type, + fields: impl Iterator, + ctx: &Ctx, +) -> Result<(Option, Option)> { + if options + .new + .as_ref() + .is_some_and(|o| matches!(o.value, NewImplTypeAttributeValue::FromFields)) + { + ensure_spanned!( + options.extends.is_none(), options.new.span() => "The `new=\"from_fields\"` option cannot be used with `extends`."; + ); + } + + let mut tuple_struct: bool = false; + + match &options.new { + Some(opt) => { + let mut field_idents = vec![]; + let mut field_types = vec![]; + for (idx, field) in fields.enumerate() { + tuple_struct = field.ident.is_none(); + + field_idents.push( + field + .ident + .clone() + .unwrap_or_else(|| format_ident!("_{}", idx)), + ); + field_types.push(&field.ty); + } + + let mut new_impl = if tuple_struct { + parse_quote_spanned! { opt.span() => + #[new] + fn __pyo3_generated____new__( #( #field_idents : #field_types ),* ) -> Self { + Self ( + #( #field_idents, )* + ) + } + } + } else { + parse_quote_spanned! { opt.span() => + #[new] + fn __pyo3_generated____new__( #( #field_idents : #field_types ),* ) -> Self { + Self { + #( #field_idents, )* + } + } + } + }; + + let new_slot = generate_protocol_slot( + ty, + &mut new_impl, + &__NEW__, + "__new__", + #[cfg(feature = "experimental-inspect")] + FunctionIntrospectionData { + names: &["__new__"], + arguments: field_idents + .iter() + .zip(field_types.iter()) + .map(|(ident, ty)| { + FnArg::Regular(RegularArg { + name: Cow::Owned(ident.clone()), + ty, + from_py_with: None, + default_value: None, + option_wrapped_type: None, + annotation: None, + }) + }) + .collect(), + returns: ty.clone(), + }, + ctx, + ) + .unwrap(); + + Ok((Some(new_impl), Some(new_slot))) + } + None => Ok((None, None)), + } +} + fn pyclass_class_getitem( options: &PyClassPyO3Options, cls: &syn::Type, diff --git a/tests/test_class_attributes.rs b/tests/test_class_attributes.rs index 7ef8ac63267..40b848cf7e0 100644 --- a/tests/test_class_attributes.rs +++ b/tests/test_class_attributes.rs @@ -235,6 +235,42 @@ fn test_renaming_all_struct_fields() { }); } +#[pyclass(get_all, set_all, new = "from_fields")] +struct AutoNewCls { + a: i32, + b: String, + c: Option, +} + +#[test] +fn new_impl() { + Python::attach(|py| { + // python should be able to do AutoNewCls(1, "two", 3.0) + let cls = py.get_type::(); + pyo3::py_run!( + py, + cls, + "inst = cls(1, 'two', 3.0); assert inst.a == 1; assert inst.b == 'two'; assert inst.c == 3.0" + ); + }); +} + +#[pyclass(new = "from_fields", get_all)] +struct Point2d(#[pyo3(name = "first")] f64, #[pyo3(name = "second")] f64); + +#[test] +fn new_impl_tuple_struct() { + Python::attach(|py| { + // python should be able to do AutoNewCls(1, "two", 3.0) + let cls = py.get_type::(); + pyo3::py_run!( + py, + cls, + "inst = cls(0.2, 0.3); assert inst.first == 0.2; assert inst.second == 0.3" + ); + }); +} + macro_rules! test_case { ($struct_name: ident, $rule: literal, $field_name: ident, $renamed_field_name: literal, $test_name: ident) => { #[pyclass(get_all, set_all, rename_all = $rule)] diff --git a/tests/test_compile_error.rs b/tests/test_compile_error.rs index 2bba54a86e4..423f0dd5cea 100644 --- a/tests/test_compile_error.rs +++ b/tests/test_compile_error.rs @@ -9,6 +9,7 @@ fn test_compile_errors() { #[cfg(not(feature = "experimental-inspect"))] t.compile_fail("tests/ui/invalid_property_args.rs"); t.compile_fail("tests/ui/invalid_proto_pymethods.rs"); + #[cfg(not(all(Py_LIMITED_API, not(Py_3_10))))] // to avoid PyFunctionArgument for &str t.compile_fail("tests/ui/invalid_pyclass_args.rs"); t.compile_fail("tests/ui/invalid_pyclass_doc.rs"); t.compile_fail("tests/ui/invalid_pyclass_enum.rs"); @@ -19,6 +20,7 @@ fn test_compile_errors() { #[cfg(Py_3_9)] t.compile_fail("tests/ui/pyclass_generic_enum.rs"); #[cfg(not(feature = "experimental-inspect"))] + #[cfg(not(all(Py_LIMITED_API, not(Py_3_10))))] // to avoid PyFunctionArgument for &str t.compile_fail("tests/ui/invalid_pyfunction_argument.rs"); t.compile_fail("tests/ui/invalid_pyfunction_definition.rs"); t.compile_fail("tests/ui/invalid_pyfunction_signatures.rs"); diff --git a/tests/ui/invalid_pyclass_args.rs b/tests/ui/invalid_pyclass_args.rs index 4c3c9fe02dc..49af59b070d 100644 --- a/tests/ui/invalid_pyclass_args.rs +++ b/tests/ui/invalid_pyclass_args.rs @@ -200,4 +200,22 @@ struct StructImplicitFromPyObjectDeprecated { b: String, } +#[pyclass(new = "from_fields")] +struct NonPythonField { + field: Box, +} + +#[pyclass(new = "from_fields")] +struct NewFromFieldsWithManualNew { + field: i32, +} + +#[pymethods] +impl NewFromFieldsWithManualNew { + #[new] + fn new(field: i32) -> Self { + Self { field } + } +} + fn main() {} diff --git a/tests/ui/invalid_pyclass_args.stderr b/tests/ui/invalid_pyclass_args.stderr index 8402451ec44..e9b217abe37 100644 --- a/tests/ui/invalid_pyclass_args.stderr +++ b/tests/ui/invalid_pyclass_args.stderr @@ -1,4 +1,4 @@ -error: expected one of: `crate`, `dict`, `eq`, `eq_int`, `extends`, `freelist`, `frozen`, `get_all`, `hash`, `immutable_type`, `mapping`, `module`, `name`, `ord`, `rename_all`, `sequence`, `set_all`, `str`, `subclass`, `unsendable`, `weakref`, `generic`, `from_py_object`, `skip_from_py_object` +error: expected one of: `crate`, `dict`, `eq`, `eq_int`, `extends`, `freelist`, `frozen`, `get_all`, `hash`, `immutable_type`, `mapping`, `module`, `name`, `ord`, `rename_all`, `sequence`, `set_all`, `new`, `str`, `subclass`, `unsendable`, `weakref`, `generic`, `from_py_object`, `skip_from_py_object` --> tests/ui/invalid_pyclass_args.rs:4:11 | 4 | #[pyclass(extend=pyo3::types::PyDict)] @@ -46,7 +46,7 @@ error: expected string literal 25 | #[pyclass(module = my_module)] | ^^^^^^^^^ -error: expected one of: `crate`, `dict`, `eq`, `eq_int`, `extends`, `freelist`, `frozen`, `get_all`, `hash`, `immutable_type`, `mapping`, `module`, `name`, `ord`, `rename_all`, `sequence`, `set_all`, `str`, `subclass`, `unsendable`, `weakref`, `generic`, `from_py_object`, `skip_from_py_object` +error: expected one of: `crate`, `dict`, `eq`, `eq_int`, `extends`, `freelist`, `frozen`, `get_all`, `hash`, `immutable_type`, `mapping`, `module`, `name`, `ord`, `rename_all`, `sequence`, `set_all`, `new`, `str`, `subclass`, `unsendable`, `weakref`, `generic`, `from_py_object`, `skip_from_py_object` --> tests/ui/invalid_pyclass_args.rs:28:11 | 28 | #[pyclass(weakrev)] @@ -181,6 +181,46 @@ help: consider annotating `StructFromPyObjectNoClone` with `#[derive(Clone)]` 192 | struct StructFromPyObjectNoClone { | +error[E0119]: conflicting implementations of trait `PyClassNewTextSignature` for type `NewFromFieldsWithManualNew` + --> tests/ui/invalid_pyclass_args.rs:213:1 + | +208 | #[pyclass(new = "from_fields")] + | ------------------------------- first implementation here +... +213 | #[pymethods] + | ^^^^^^^^^^^^ conflicting implementation for `NewFromFieldsWithManualNew` + | + = note: this error originates in the attribute macro `pymethods` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: `Box` cannot be used as a Python function argument + --> tests/ui/invalid_pyclass_args.rs:205:12 + | +205 | field: Box, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `PyFunctionArgument<'_, '_, '_, false>` is not implemented for `Box` + | + = note: implement `FromPyObject` to enable using `Box` as a function argument + = note: `Python<'py>` is also a valid argument type to pass the Python token into `#[pyfunction]`s and `#[pymethods]` +help: the following other types implement trait `PyFunctionArgument<'a, 'holder, 'py, IMPLEMENTS_FROMPYOBJECT>` + --> src/impl_/extract_argument.rs + | + | / impl<'a, 'holder, 'py, T: 'a + 'py> PyFunctionArgument<'a, 'holder, 'py, false> + | | for &'holder Bound<'py, T> + | | where + | | T: PyTypeCheck, + | |___________________^ `&'holder pyo3::Bound<'py, T>` implements `PyFunctionArgument<'a, 'holder, 'py, false>` +... + | / impl<'a, 'holder, 'py, T> PyFunctionArgument<'a, 'holder, 'py, false> for Option + | | where + | | T: PyFunctionArgument<'a, 'holder, 'py, false>, + | |___________________________________________________^ `Option` implements `PyFunctionArgument<'a, 'holder, 'py, false>` +... + | impl<'a, 'holder, T: PyClass> PyFunctionArgument<'a, 'holder, '_, false> for &'holder T { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `&'holder T` implements `PyFunctionArgument<'a, 'holder, '_, false>` +... + | / impl<'a, 'holder, T: PyClass> PyFunctionArgument<'a, 'holder, '_, false> + | | for &'holder mut T + | |______________________^ `&'holder mut T` implements `PyFunctionArgument<'a, 'holder, '_, false>` + error[E0592]: duplicate definitions with name `__pymethod___richcmp____` --> tests/ui/invalid_pyclass_args.rs:37:1 | @@ -214,6 +254,17 @@ error[E0592]: duplicate definitions with name `__pymethod___str____` | = note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info) +error[E0592]: duplicate definitions with name `__pymethod___new____` + --> tests/ui/invalid_pyclass_args.rs:208:1 + | +208 | #[pyclass(new = "from_fields")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ duplicate definitions for `__pymethod___new____` +... +213 | #[pymethods] + | ------------ other definition for `__pymethod___new____` + | + = note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info) + error[E0369]: binary operation `==` cannot be applied to type `&EqOptRequiresEq` --> tests/ui/invalid_pyclass_args.rs:34:11 | @@ -400,3 +451,135 @@ warning: use of deprecated associated constant `pyo3::impl_::deprecated::HasAuto | = note: `#[warn(deprecated)]` on by default = note: this warning originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: `Box` cannot be used as a Python function argument + --> tests/ui/invalid_pyclass_args.rs:205:12 + | +205 | field: Box, + | ^^^ the trait `pyo3::PyClass` is not implemented for `Box` + | + = note: implement `FromPyObject` to enable using `Box` as a function argument + = note: `Python<'py>` is also a valid argument type to pass the Python token into `#[pyfunction]`s and `#[pymethods]` + = help: the following other types implement trait `pyo3::PyClass`: + Coord + Coord2 + Coord3 + EqOptAndManualRichCmp + EqOptRequiresEq + HashOptAndManualHash + HashOptRequiresHash + NewFromFieldsWithManualNew + and $N others + = note: required for `Box` to implement `pyo3::FromPyObject<'_, '_>` + = note: required for `Box` to implement `PyFunctionArgument<'_, '_, '_, true>` +note: required by a bound in `extract_argument` + --> src/impl_/extract_argument.rs + | + | pub fn extract_argument<'a, 'holder, 'py, T, const IMPLEMENTS_FROMPYOBJECT: bool>( + | ---------------- required by a bound in this function +... + | T: PyFunctionArgument<'a, 'holder, 'py, IMPLEMENTS_FROMPYOBJECT>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `extract_argument` + +error[E0277]: `Box` cannot be used as a Python function argument + --> tests/ui/invalid_pyclass_args.rs:205:12 + | +205 | field: Box, + | ^^^ the trait `ExtractPyClassWithClone` is not implemented for `Box` + | + = note: implement `FromPyObject` to enable using `Box` as a function argument + = note: `Python<'py>` is also a valid argument type to pass the Python token into `#[pyfunction]`s and `#[pymethods]` + = help: the following other types implement trait `ExtractPyClassWithClone`: + Coord + Coord2 + Coord3 + EqOptAndManualRichCmp + EqOptRequiresEq + HashOptAndManualHash + HashOptRequiresHash + NewFromFieldsWithManualNew + and $N others + = note: required for `Box` to implement `pyo3::FromPyObject<'_, '_>` + = note: required for `Box` to implement `PyFunctionArgument<'_, '_, '_, true>` +note: required by a bound in `extract_argument` + --> src/impl_/extract_argument.rs + | + | pub fn extract_argument<'a, 'holder, 'py, T, const IMPLEMENTS_FROMPYOBJECT: bool>( + | ---------------- required by a bound in this function +... + | T: PyFunctionArgument<'a, 'holder, 'py, IMPLEMENTS_FROMPYOBJECT>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `extract_argument` + +error[E0277]: the trait bound `dyn std::error::Error + Send + Sync: Clone` is not satisfied + --> tests/ui/invalid_pyclass_args.rs:205:12 + | +205 | field: Box, + | ^^^ the trait `Clone` is not implemented for `dyn std::error::Error + Send + Sync` + | +help: the following other types implement trait `PyFunctionArgument<'a, 'holder, 'py, IMPLEMENTS_FROMPYOBJECT>` + --> src/impl_/extract_argument.rs + | + | / impl<'a, 'holder, 'py, T: 'a + 'py> PyFunctionArgument<'a, 'holder, 'py, false> + | | for &'holder Bound<'py, T> + | | where + | | T: PyTypeCheck, + | |___________________^ `&'holder pyo3::Bound<'py, T>` implements `PyFunctionArgument<'a, 'holder, 'py, false>` +... + | / impl<'a, 'holder, 'py, T> PyFunctionArgument<'a, 'holder, 'py, false> for Option + | | where + | | T: PyFunctionArgument<'a, 'holder, 'py, false>, + | |___________________________________________________^ `Option` implements `PyFunctionArgument<'a, 'holder, 'py, false>` +... + | impl<'a, 'holder, T: PyClass> PyFunctionArgument<'a, 'holder, '_, false> for &'holder T { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `&'holder T` implements `PyFunctionArgument<'a, 'holder, '_, false>` +... + | / impl<'a, 'holder, T: PyClass> PyFunctionArgument<'a, 'holder, '_, false> + | | for &'holder mut T + | |______________________^ `&'holder mut T` implements `PyFunctionArgument<'a, 'holder, '_, false>` + = note: required for `Box` to implement `Clone` + = note: required for `Box` to implement `pyo3::FromPyObject<'_, '_>` + = note: required for `Box` to implement `PyFunctionArgument<'_, '_, '_, true>` +note: required by a bound in `extract_argument` + --> src/impl_/extract_argument.rs + | + | pub fn extract_argument<'a, 'holder, 'py, T, const IMPLEMENTS_FROMPYOBJECT: bool>( + | ---------------- required by a bound in this function +... + | T: PyFunctionArgument<'a, 'holder, 'py, IMPLEMENTS_FROMPYOBJECT>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `extract_argument` + +error[E0034]: multiple applicable items in scope + --> tests/ui/invalid_pyclass_args.rs:208:1 + | +208 | #[pyclass(new = "from_fields")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ multiple `__pymethod___new____` found + | +note: candidate #1 is defined in an impl for the type `NewFromFieldsWithManualNew` + --> tests/ui/invalid_pyclass_args.rs:208:1 + | +208 | #[pyclass(new = "from_fields")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: candidate #2 is defined in an impl for the type `NewFromFieldsWithManualNew` + --> tests/ui/invalid_pyclass_args.rs:213:1 + | +213 | #[pymethods] + | ^^^^^^^^^^^^ + = note: this error originates in the attribute macro `pyclass` which comes from the expansion of the attribute macro `pymethods` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0034]: multiple applicable items in scope + --> tests/ui/invalid_pyclass_args.rs:213:1 + | +213 | #[pymethods] + | ^^^^^^^^^^^^ multiple `__pymethod___new____` found + | +note: candidate #1 is defined in an impl for the type `NewFromFieldsWithManualNew` + --> tests/ui/invalid_pyclass_args.rs:208:1 + | +208 | #[pyclass(new = "from_fields")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: candidate #2 is defined in an impl for the type `NewFromFieldsWithManualNew` + --> tests/ui/invalid_pyclass_args.rs:213:1 + | +213 | #[pymethods] + | ^^^^^^^^^^^^ + = note: this error originates in the attribute macro `pymethods` (in Nightly builds, run with -Z macro-backtrace for more info)