More FromPyObject derive suggestions by @davidhewitt
This commit is contained in:
parent
7a9f4a1633
commit
0f32f886b8
|
@ -183,17 +183,28 @@ use pyo3::prelude::*;
|
|||
struct RustyTuple(String, String);
|
||||
```
|
||||
|
||||
#### Deriving [`FromPyObject`] for wrapper types
|
||||
|
||||
The `pyo3(transparent)` attribute can be used on structs with exactly one field. This results
|
||||
in extracting directly from the input object, i.e. `obj.extract()`, rather than trying to access
|
||||
an item or attribute.
|
||||
Tuple structs with a single field are treated as wrapper types which are described in the
|
||||
following section. To override this behaviour and ensure that the input is in fact a tuple,
|
||||
specify the struct as
|
||||
```
|
||||
use pyo3::prelude::*;
|
||||
|
||||
#[derive(FromPyObject)]
|
||||
#[pyo3(transparent)]
|
||||
struct RustyTransparentTuple(String);
|
||||
struct RustyTuple((String,));
|
||||
```
|
||||
|
||||
#### Deriving [`FromPyObject`] for wrapper types
|
||||
|
||||
The `pyo3(transparent)` attribute can be used on structs with exactly one field. This results
|
||||
in extracting directly from the input object, i.e. `obj.extract()`, rather than trying to access
|
||||
an item or attribute. This behaviour is enabled per default for newtype structs and tuple-variants
|
||||
with a single field.
|
||||
|
||||
```
|
||||
use pyo3::prelude::*;
|
||||
|
||||
#[derive(FromPyObject)]
|
||||
struct RustyTransparentTupleStruct(String);
|
||||
|
||||
#[derive(FromPyObject)]
|
||||
#[pyo3(transparent)]
|
||||
|
@ -217,12 +228,10 @@ use pyo3::prelude::*;
|
|||
|
||||
#[derive(FromPyObject)]
|
||||
enum RustyEnum<'a> {
|
||||
#[pyo3(transparent)]
|
||||
Int(usize), // input is a positive int
|
||||
#[pyo3(transparent)]
|
||||
String(String), // input is a string
|
||||
IntTuple(usize, usize), // input is a 2-tuple with positive ints
|
||||
StringIntTuple(String, usize), // innput is a 2-tuple with String and int
|
||||
StringIntTuple(String, usize), // input is a 2-tuple with String and int
|
||||
Coordinates3d { // needs to be in front of 2d
|
||||
x: usize,
|
||||
y: usize,
|
||||
|
@ -257,9 +266,28 @@ enum RustyEnum {
|
|||
```
|
||||
|
||||
If the input is neither a string nor an integer, the error message will be:
|
||||
`"Can't convert <INPUT> to str, int"`, where `<INPUT>` is replaced by the type name and
|
||||
`"Can't convert <INPUT> to Union[str, int]"`, where `<INPUT>` is replaced by the type name and
|
||||
`repr()` of the input object.
|
||||
|
||||
#### `#[derive(FromPyObject)]` Container Attributes
|
||||
- `pyo3(transparent)`
|
||||
- extract the field directly from the object as `obj.extract()` instead of `get_item()` or
|
||||
`getattr()`
|
||||
- Newtype structs and tuple-variants are treated as transparent per default.
|
||||
- only supported for single-field structs and enum variants
|
||||
- `pyo3(annotation = "name")`
|
||||
- changes the name of the failed variant in the generated error message in case of failure.
|
||||
- e.g. `pyo3("int")` reports the variant's type as `int`.
|
||||
- only supported for enum variants
|
||||
|
||||
#### `#[derive(FromPyObject)]` Field Attributes
|
||||
- `pyo3(attribute)`, `pyo3(attribute("name"))`
|
||||
- retrieve the field from an attribute, possibly with a custom name specified as an argument
|
||||
- argument must be a string-literal.
|
||||
- `pyo3(item)`, `pyo3(item("key"))`
|
||||
- retrieve the field from a mapping, possibly with the custom key specified as an argument.
|
||||
- can be any literal that implements `ToBorrowedObject`
|
||||
|
||||
### `IntoPy<T>`
|
||||
|
||||
This trait defines the to-python conversion for a Rust type. It is usually implemented as
|
||||
|
|
|
@ -65,6 +65,11 @@ impl<'a> Enum<'a> {
|
|||
error_names.push_str(", ");
|
||||
}
|
||||
}
|
||||
let error_names = if self.variants.len() > 1 {
|
||||
format!("Union[{}]", error_names)
|
||||
} else {
|
||||
error_names
|
||||
};
|
||||
quote!(
|
||||
#(#var_extracts)*
|
||||
let type_name = obj.get_type().name();
|
||||
|
@ -134,7 +139,13 @@ impl<'a> Container<'a> {
|
|||
}
|
||||
let style = match (fields, transparent) {
|
||||
(Fields::Unnamed(_), true) => ContainerType::TupleNewtype,
|
||||
(Fields::Unnamed(unnamed), false) => ContainerType::Tuple(unnamed.unnamed.len()),
|
||||
(Fields::Unnamed(unnamed), false) => {
|
||||
if unnamed.unnamed.len() == 1 {
|
||||
ContainerType::TupleNewtype
|
||||
} else {
|
||||
ContainerType::Tuple(unnamed.unnamed.len())
|
||||
}
|
||||
}
|
||||
(Fields::Named(named), true) => {
|
||||
let field = named
|
||||
.named
|
||||
|
@ -321,7 +332,8 @@ impl ContainerAttribute {
|
|||
if let syn::NestedMeta::Meta(metaitem) = &meta {
|
||||
match metaitem {
|
||||
Meta::Path(p) if p.is_ident("transparent") => {
|
||||
attrs.push(ContainerAttribute::Transparent)
|
||||
attrs.push(ContainerAttribute::Transparent);
|
||||
continue;
|
||||
}
|
||||
Meta::NameValue(nv) if nv.path.is_ident("annotation") => {
|
||||
if let syn::Lit::Str(s) = &nv.lit {
|
||||
|
@ -329,21 +341,13 @@ impl ContainerAttribute {
|
|||
} else {
|
||||
return Err(spanned_err(&nv.lit, "Expected string literal."));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
other => {
|
||||
return Err(spanned_err(
|
||||
other,
|
||||
"Expected `transparent` or `annotation = \"name\"`",
|
||||
))
|
||||
}
|
||||
_ => {} // return Err below
|
||||
}
|
||||
} else {
|
||||
return Err(spanned_err(
|
||||
meta,
|
||||
"Unknown container attribute, expected `transparent` or \
|
||||
`annotation(\"err_name\")`",
|
||||
));
|
||||
}
|
||||
|
||||
return Err(spanned_err(meta, "Unrecognized `pyo3` container attribute"));
|
||||
}
|
||||
Ok(attrs)
|
||||
}
|
||||
|
@ -514,7 +518,7 @@ pub fn build_derive_from_pyobject(tokens: &DeriveInput) -> Result<TokenStream> {
|
|||
syn::Data::Union(_) => {
|
||||
return Err(spanned_err(
|
||||
tokens,
|
||||
"FromPyObject can not be derived for unions.",
|
||||
"#[derive(FromPyObject)] is not supported for unions.",
|
||||
))
|
||||
}
|
||||
};
|
||||
|
|
|
@ -155,7 +155,6 @@ fn test_tuple_struct() {
|
|||
}
|
||||
|
||||
#[derive(FromPyObject)]
|
||||
#[pyo3(transparent)]
|
||||
pub struct TransparentTuple(String);
|
||||
|
||||
#[test]
|
||||
|
@ -301,7 +300,7 @@ fn test_err_rename() {
|
|||
PyErrValue::ToObject(to) => {
|
||||
let o = to.to_object(py);
|
||||
let s = String::extract(o.as_ref(py)).expect("Err val is not a string");
|
||||
assert_eq!(s, "Can't convert {} (dict) to str, uint, int")
|
||||
assert_eq!(s, "Can't convert {} (dict) to Union[str, uint, int]")
|
||||
}
|
||||
_ => panic!("Expected PyErrValue::ToObject"),
|
||||
},
|
||||
|
|
|
@ -132,7 +132,7 @@ error: Only one of `item`, `attribute` can be provided, possibly with an additio
|
|||
118 | #[pyo3(item, attribute)]
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: Expected `transparent` or `annotation = "name"`
|
||||
error: Unrecognized `pyo3` container attribute
|
||||
--> $DIR/invalid_frompy_derive.rs:123:8
|
||||
|
|
||||
123 | #[pyo3(unknown = "should not work")]
|
||||
|
@ -156,7 +156,7 @@ error: FromPyObject can be derived with at most one lifetime parameter.
|
|||
141 | enum TooManyLifetimes<'a, 'b> {
|
||||
| ^^^^^^^^
|
||||
|
||||
error: FromPyObject can not be derived for unions.
|
||||
error: #[derive(FromPyObject)] is not supported for unions.
|
||||
--> $DIR/invalid_frompy_derive.rs:147:1
|
||||
|
|
||||
147 | / union Union {
|
||||
|
|
Loading…
Reference in New Issue