frompyobject: improve error message for tuple case
This commit is contained in:
parent
2769963536
commit
cfb91057af
|
@ -3,7 +3,7 @@ use crate::{
|
||||||
utils::get_pyo3_crate,
|
utils::get_pyo3_crate,
|
||||||
};
|
};
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::quote;
|
use quote::{quote, format_ident};
|
||||||
use syn::{
|
use syn::{
|
||||||
parenthesized,
|
parenthesized,
|
||||||
parse::{Parse, ParseStream},
|
parse::{Parse, ParseStream},
|
||||||
|
@ -244,48 +244,33 @@ impl<'a> Container<'a> {
|
||||||
|
|
||||||
fn build_tuple_struct(&self, tups: &[FieldPyO3Attributes]) -> TokenStream {
|
fn build_tuple_struct(&self, tups: &[FieldPyO3Attributes]) -> TokenStream {
|
||||||
let self_ty = &self.path;
|
let self_ty = &self.path;
|
||||||
let mut fields: Punctuated<TokenStream, syn::Token![,]> = Punctuated::new();
|
let field_idents: Vec<_> = (0..tups.len()).into_iter().map(|i| format_ident!("arg{}", i)).collect();
|
||||||
for (index, attrs) in tups.iter().enumerate() {
|
let fields = tups.iter().zip(&field_idents).enumerate().map(|(index, (attrs, ident))| {
|
||||||
let error_msg = format!("failed to extract field {}.{}", quote!(#self_ty), index);
|
let error_msg = format!("failed to extract field {}.{}", quote!(#self_ty), index);
|
||||||
|
|
||||||
let parsed_item = match &attrs.from_py_with {
|
let parsed_item = match &attrs.from_py_with {
|
||||||
None => quote!(
|
None => quote!(
|
||||||
obj.get_item(#index)?.extract()
|
_pyo3::PyAny::extract(#ident)
|
||||||
),
|
),
|
||||||
Some(FromPyWithAttribute {
|
Some(FromPyWithAttribute {
|
||||||
value: expr_path, ..
|
value: expr_path, ..
|
||||||
}) => quote! (
|
}) => quote! (
|
||||||
#expr_path(obj.get_item(#index)?)
|
#expr_path(#ident)
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
let extractor = quote!(
|
quote!(
|
||||||
#parsed_item.map_err(|inner| {
|
#parsed_item.map_err(|inner| {
|
||||||
let py = _pyo3::PyNativeType::py(obj);
|
let py = _pyo3::PyNativeType::py(obj);
|
||||||
let new_err = _pyo3::exceptions::PyTypeError::new_err(#error_msg);
|
let new_err = _pyo3::exceptions::PyTypeError::new_err(#error_msg);
|
||||||
new_err.set_cause(py, ::std::option::Option::Some(inner));
|
new_err.set_cause(py, ::std::option::Option::Some(inner));
|
||||||
new_err
|
new_err
|
||||||
})?
|
})?
|
||||||
);
|
)
|
||||||
|
});
|
||||||
fields.push(quote!(#extractor));
|
|
||||||
}
|
|
||||||
let len = tups.len();
|
|
||||||
let msg = if self.is_enum_variant {
|
|
||||||
quote!(::std::format!(
|
|
||||||
"expected tuple of length {}, but got length {}",
|
|
||||||
#len,
|
|
||||||
s.len()
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
quote!("")
|
|
||||||
};
|
|
||||||
quote!(
|
quote!(
|
||||||
let s = <_pyo3::types::PyTuple as _pyo3::conversion::PyTryFrom>::try_from(obj)?;
|
let (#(#field_idents),*) = obj.extract()?;
|
||||||
if s.len() != #len {
|
::std::result::Result::Ok(#self_ty(#(#fields),*))
|
||||||
return ::std::result::Result::Err(_pyo3::exceptions::PyValueError::new_err(#msg))
|
|
||||||
}
|
|
||||||
::std::result::Result::Ok(#self_ty(#fields))
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -291,9 +291,10 @@ impl<'a> IntoIterator for &'a PyTuple {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cold]
|
||||||
fn wrong_tuple_length(t: &PyTuple, expected_length: usize) -> PyErr {
|
fn wrong_tuple_length(t: &PyTuple, expected_length: usize) -> PyErr {
|
||||||
let msg = format!(
|
let msg = format!(
|
||||||
"Expected tuple of length {}, but got tuple of length {}.",
|
"expected tuple of length {}, but got tuple of length {}",
|
||||||
expected_length,
|
expected_length,
|
||||||
t.len()
|
t.len()
|
||||||
);
|
);
|
||||||
|
|
|
@ -399,6 +399,21 @@ TypeError: failed to extract enum Foo ('TupleVar | StructVar | TransparentTuple
|
||||||
- variant StructWithGetItem (StructWithGetItem): 'a'
|
- variant StructWithGetItem (StructWithGetItem): 'a'
|
||||||
- variant StructWithGetItemArg (StructWithGetItemArg): 'foo'"
|
- variant StructWithGetItemArg (StructWithGetItemArg): 'foo'"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let tup = PyTuple::empty(py);
|
||||||
|
let err = Foo::extract(tup.as_ref()).unwrap_err();
|
||||||
|
assert_eq!(
|
||||||
|
err.to_string(),
|
||||||
|
"\
|
||||||
|
TypeError: failed to extract enum Foo ('TupleVar | StructVar | TransparentTuple | TransparentStructVar | StructVarGetAttrArg | StructWithGetItem | StructWithGetItemArg')
|
||||||
|
- variant TupleVar (TupleVar): expected tuple of length 2, but got tuple of length 0
|
||||||
|
- variant StructVar (StructVar): 'tuple' object has no attribute 'test'
|
||||||
|
- variant TransparentTuple (TransparentTuple): 'tuple' object cannot be interpreted as an integer
|
||||||
|
- variant TransparentStructVar (TransparentStructVar): failed to extract field Foo :: TransparentStructVar.a
|
||||||
|
- variant StructVarGetAttrArg (StructVarGetAttrArg): 'tuple' object has no attribute 'bla'
|
||||||
|
- variant StructWithGetItem (StructWithGetItem): tuple indices must be integers or slices, not str
|
||||||
|
- variant StructWithGetItemArg (StructWithGetItemArg): tuple indices must be integers or slices, not str"
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -500,6 +515,23 @@ fn test_from_py_with_tuple_struct() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_from_py_with_tuple_struct_error() {
|
||||||
|
Python::with_gil(|py| {
|
||||||
|
let py_zap = py
|
||||||
|
.eval(r#"("whatever", [1, 2, 3], "third")"#, None, None)
|
||||||
|
.expect("failed to create tuple");
|
||||||
|
|
||||||
|
let f = ZapTuple::extract(py_zap);
|
||||||
|
|
||||||
|
assert!(f.is_err());
|
||||||
|
assert_eq!(
|
||||||
|
f.unwrap_err().to_string(),
|
||||||
|
"ValueError: expected tuple of length 2, but got tuple of length 3"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, FromPyObject, PartialEq)]
|
#[derive(Debug, FromPyObject, PartialEq)]
|
||||||
pub enum ZapEnum {
|
pub enum ZapEnum {
|
||||||
Zip(#[pyo3(from_py_with = "PyAny::len")] usize),
|
Zip(#[pyo3(from_py_with = "PyAny::len")] usize),
|
||||||
|
|
Loading…
Reference in a new issue