Removed the custom error messages from the macro api

This commit is contained in:
R2D2 2021-06-07 10:18:38 +02:00
parent 416b1132b4
commit 973f0a5bae
3 changed files with 11 additions and 154 deletions

View File

@ -124,7 +124,6 @@ struct Container<'a> {
ty: ContainerType<'a>, ty: ContainerType<'a>,
err_name: String, err_name: String,
is_enum_variant: bool, is_enum_variant: bool,
error_msg: Option<String>,
} }
impl<'a> Container<'a> { impl<'a> Container<'a> {
@ -184,14 +183,11 @@ impl<'a> Container<'a> {
|lit_str| lit_str.value(), |lit_str| lit_str.value(),
); );
let error_msg = options.error_msg.map(|inner| inner.value());
let v = Container { let v = Container {
path, path,
ty: style, ty: style,
err_name, err_name,
is_enum_variant, is_enum_variant,
error_msg,
}; };
Ok(v) Ok(v)
} }
@ -209,14 +205,11 @@ impl<'a> Container<'a> {
fn build_newtype_struct(&self, field_ident: Option<&Ident>) -> TokenStream { fn build_newtype_struct(&self, field_ident: Option<&Ident>) -> TokenStream {
let self_ty = &self.path; let self_ty = &self.path;
if let Some(ident) = field_ident { if let Some(ident) = field_ident {
let error_msg = self.error_msg.as_ref().map_or( let error_msg = format!(
format!(
"failed to extract field {}.{}", "failed to extract field {}.{}",
quote!(#self_ty), quote!(#self_ty),
quote!(#ident) quote!(#ident)
), );
|msg| msg.clone(),
);
quote!( quote!(
Ok(#self_ty{#ident: obj.extract().map_err(|inner| { Ok(#self_ty{#ident: obj.extract().map_err(|inner| {
let err_msg = format!("{}\n\nCaused by:\n {}\n", let err_msg = format!("{}\n\nCaused by:\n {}\n",
@ -226,10 +219,7 @@ impl<'a> Container<'a> {
})?}) })?})
) )
} else { } else {
let error_msg = self.error_msg.as_ref().map_or( let error_msg = format!("failed to extract inner field of {}", quote!(#self_ty));
format!("failed to extract inner field of {}", quote!(#self_ty)),
|msg| msg.clone(),
);
quote!(Ok(#self_ty(obj.extract().map_err(|inner| { quote!(Ok(#self_ty(obj.extract().map_err(|inner| {
let err_msg = format!("{}\n\nCaused by:\n {}\n", let err_msg = format!("{}\n\nCaused by:\n {}\n",
#error_msg, #error_msg,
@ -242,12 +232,8 @@ impl<'a> Container<'a> {
fn build_tuple_struct(&self, len: usize) -> TokenStream { fn build_tuple_struct(&self, len: usize) -> TokenStream {
let self_ty = &self.path; let self_ty = &self.path;
let mut fields: Punctuated<TokenStream, syn::Token![,]> = Punctuated::new(); let mut fields: Punctuated<TokenStream, syn::Token![,]> = Punctuated::new();
for i in 0..len { for i in 0..len {
let error_msg = self.error_msg.as_ref().map_or( let error_msg = format!("failed to extract field {}.{}", quote!(#self_ty), i);
format!("failed to extract field {}.{}", quote!(#self_ty), i),
|msg| msg.clone(),
);
fields.push(quote!(s.get_item(#i).extract().map_err(|inner| { fields.push(quote!(s.get_item(#i).extract().map_err(|inner| {
let err_msg = format!("{}\n\nCaused by:\n {}\n", let err_msg = format!("{}\n\nCaused by:\n {}\n",
#error_msg, #error_msg,
@ -283,10 +269,7 @@ impl<'a> Container<'a> {
FieldGetter::GetItem(Some(key)) => quote!(get_item(#key)), FieldGetter::GetItem(Some(key)) => quote!(get_item(#key)),
FieldGetter::GetItem(None) => quote!(get_item(stringify!(#ident))), FieldGetter::GetItem(None) => quote!(get_item(stringify!(#ident))),
}; };
let conversion_error_msg = attrs.error_msg.as_ref().map_or( let conversion_error_msg = format!("failed to extract field {}.{}", quote!(#self_ty), ident);
format!("failed to extract field {}.{}", quote!(#self_ty), ident),
|msg| msg.value(),
);
let get_field = quote!(obj.#getter?); let get_field = quote!(obj.#getter?);
let extractor = match &attrs.from_py_with { let extractor = match &attrs.from_py_with {
None => quote!(#get_field.extract().map_err(|inner| { None => quote!(#get_field.extract().map_err(|inner| {
@ -315,8 +298,6 @@ struct ContainerOptions {
transparent: bool, transparent: bool,
/// Change the name of an enum variant in the generated error message. /// Change the name of an enum variant in the generated error message.
annotation: Option<syn::LitStr>, annotation: Option<syn::LitStr>,
/// Change the error message displayed when extract fails
error_msg: Option<syn::LitStr>,
} }
/// Attributes for deriving FromPyObject scoped on containers. /// Attributes for deriving FromPyObject scoped on containers.
@ -326,8 +307,6 @@ enum ContainerPyO3Attribute {
Transparent(attributes::kw::transparent), Transparent(attributes::kw::transparent),
/// Change the name of an enum variant in the generated error message. /// Change the name of an enum variant in the generated error message.
ErrorAnnotation(LitStr), ErrorAnnotation(LitStr),
/// Change the error message displayed when extract fails
ErrorMsg(LitStr),
} }
impl Parse for ContainerPyO3Attribute { impl Parse for ContainerPyO3Attribute {
@ -340,10 +319,6 @@ impl Parse for ContainerPyO3Attribute {
let _: attributes::kw::annotation = input.parse()?; let _: attributes::kw::annotation = input.parse()?;
let _: Token![=] = input.parse()?; let _: Token![=] = input.parse()?;
input.parse().map(ContainerPyO3Attribute::ErrorAnnotation) input.parse().map(ContainerPyO3Attribute::ErrorAnnotation)
} else if lookahead.peek(attributes::kw::error_message) {
let _: attributes::kw::error_message = input.parse()?;
let _: Token![=] = input.parse()?;
input.parse().map(ContainerPyO3Attribute::ErrorMsg)
} else { } else {
Err(lookahead.error()) Err(lookahead.error())
} }
@ -355,7 +330,6 @@ impl ContainerOptions {
let mut options = ContainerOptions { let mut options = ContainerOptions {
transparent: false, transparent: false,
annotation: None, annotation: None,
error_msg: None,
}; };
for attr in attrs { for attr in attrs {
if let Some(pyo3_attrs) = get_pyo3_attributes(attr)? { if let Some(pyo3_attrs) = get_pyo3_attributes(attr)? {
@ -375,13 +349,6 @@ impl ContainerOptions {
); );
options.annotation = Some(lit_str); options.annotation = Some(lit_str);
} }
ContainerPyO3Attribute::ErrorMsg(lit_str) => {
ensure_spanned!(
options.error_msg.is_none(),
lit_str.span() => "`error_message` may only be provided once"
);
options.error_msg = Some(lit_str);
}
} }
} }
} }
@ -395,7 +362,6 @@ impl ContainerOptions {
struct FieldPyO3Attributes { struct FieldPyO3Attributes {
getter: FieldGetter, getter: FieldGetter,
from_py_with: Option<FromPyWithAttribute>, from_py_with: Option<FromPyWithAttribute>,
error_msg: Option<LitStr>,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -407,7 +373,6 @@ enum FieldGetter {
enum FieldPyO3Attribute { enum FieldPyO3Attribute {
Getter(FieldGetter), Getter(FieldGetter),
FromPyWith(FromPyWithAttribute), FromPyWith(FromPyWithAttribute),
ErrorMsg(LitStr),
} }
impl Parse for FieldPyO3Attribute { impl Parse for FieldPyO3Attribute {
@ -451,10 +416,6 @@ impl Parse for FieldPyO3Attribute {
} }
} else if lookahead.peek(attributes::kw::from_py_with) { } else if lookahead.peek(attributes::kw::from_py_with) {
input.parse().map(FieldPyO3Attribute::FromPyWith) input.parse().map(FieldPyO3Attribute::FromPyWith)
} else if lookahead.peek(attributes::kw::error_message) {
let _: attributes::kw::error_message = input.parse()?;
let _: Token![=] = input.parse()?;
input.parse().map(FieldPyO3Attribute::ErrorMsg)
} else { } else {
Err(lookahead.error()) Err(lookahead.error())
} }
@ -467,7 +428,6 @@ impl FieldPyO3Attributes {
fn from_attrs(attrs: &[Attribute]) -> Result<Self> { fn from_attrs(attrs: &[Attribute]) -> Result<Self> {
let mut getter = None; let mut getter = None;
let mut from_py_with = None; let mut from_py_with = None;
let mut error_msg = None;
for attr in attrs { for attr in attrs {
if let Some(pyo3_attrs) = get_pyo3_attributes(attr)? { if let Some(pyo3_attrs) = get_pyo3_attributes(attr)? {
@ -487,13 +447,6 @@ impl FieldPyO3Attributes {
); );
from_py_with = Some(from_py_with_attr); from_py_with = Some(from_py_with_attr);
} }
FieldPyO3Attribute::ErrorMsg(err_msg) => {
ensure_spanned!(
error_msg.is_none(),
attr.span() => "`conversion_error` may only be provided once"
);
error_msg = Some(err_msg)
}
} }
} }
} }
@ -502,7 +455,6 @@ impl FieldPyO3Attributes {
Ok(FieldPyO3Attributes { Ok(FieldPyO3Attributes {
getter: getter.unwrap_or(FieldGetter::GetAttr(None)), getter: getter.unwrap_or(FieldGetter::GetAttr(None)),
from_py_with, from_py_with,
error_msg,
}) })
} }
} }

View File

@ -230,70 +230,7 @@ fn test_struct_nested_type_errors_with_generics() {
} }
#[derive(Debug, FromPyObject)] #[derive(Debug, FromPyObject)]
struct Custom<U, T> { #[pyo3(transparent)]
e: E<U, T>,
#[pyo3(error_message = "Type 'Tuple' should be of the form (str, integer)")]
tup: Tuple,
}
#[test]
fn test_custom_error_message_struct() {
let gil = Python::acquire_gil();
let py = gil.python();
let pybaz = PyBaz {
tup: ("test".into(), "test".into()),
e: PyE {
test: "foo".into(),
test2: 0,
},
}
.into_py(py);
let test: PyResult<Custom<String, usize>> = FromPyObject::extract(pybaz.as_ref(py));
assert!(test.is_err());
assert_eq!(
"TypeError: Type \'Tuple\' should be of the form (str, integer)\n\nCaused by:\n TypeError: failed to extract field Tuple.1\n\nCaused by:\n TypeError: \'str\' object cannot be interpreted as an integer\n\n",
test.unwrap_err().to_string()
);
}
#[derive(Debug, FromPyObject)]
#[pyo3(error_message = "Conversion failed: Tuple expects 'str', 'usize'")]
pub struct Tuple2(String, usize);
#[test]
fn test_custom_error_message_tuple_struct() {
let gil = Python::acquire_gil();
let py = gil.python();
let pytup = PyTuple::new(py, &["test".into_py(py), "test".into_py(py)]);
let test: PyResult<Tuple2> = FromPyObject::extract(pytup);
assert!(test.is_err());
assert_eq!(
"TypeError: Conversion failed: Tuple expects \'str\', \'usize\'\n\nCaused by:\n TypeError: \'str\' object cannot be interpreted as an integer\n",
test.unwrap_err().to_string()
);
}
#[derive(Debug, FromPyObject)]
#[pyo3(error_message = "Expected type str")]
pub struct TransparentTuple2(String);
#[test]
fn test_transparent_tuple_struct_error_message() {
let gil = Python::acquire_gil();
let py = gil.python();
let tup: PyObject = 1.into_py(py);
let tup = TransparentTuple2::extract(tup.as_ref(py));
assert!(tup.is_err());
assert_eq!(
"TypeError: Expected type str\n\nCaused by:\n TypeError: \'int\' object cannot be converted to \'PyString\'\n",
tup.unwrap_err().to_string()
);
}
#[derive(Debug, FromPyObject)]
#[pyo3(transparent, error_message = "Expected type str")]
pub struct TransparentStruct { pub struct TransparentStruct {
a: String, a: String,
} }
@ -306,39 +243,11 @@ fn test_transparent_struct_error_message() {
let tup = TransparentStruct::extract(tup.as_ref(py)); let tup = TransparentStruct::extract(tup.as_ref(py));
assert!(tup.is_err()); assert!(tup.is_err());
assert_eq!( assert_eq!(
"TypeError: Expected type str\n\nCaused by:\n TypeError: \'int\' object cannot be converted to \'PyString\'\n", "TypeError: failed to extract field TransparentStruct.a\n\nCaused by:\n TypeError: \'int\' object cannot be converted to \'PyString\'\n",
tup.unwrap_err().to_string() tup.unwrap_err().to_string()
); );
} }
#[derive(Debug, FromPyObject)]
struct Custom2<U, T> {
e: E<U, T>,
#[pyo3(error_message = "Type 'Tuple' should be of the form (str, integer)")]
tup: Tuple2,
}
#[test]
fn test_nested_custom_errors() {
let gil = Python::acquire_gil();
let py = gil.python();
let pybaz = PyBaz {
tup: ("test".into(), "test".into()),
e: PyE {
test: "foo".into(),
test2: 0,
},
}
.into_py(py);
let test: PyResult<Custom2<String, usize>> = FromPyObject::extract(pybaz.as_ref(py));
assert!(test.is_err());
assert_eq!(
"TypeError: Type \'Tuple\' should be of the form (str, integer)\n\nCaused by:\n TypeError: Conversion failed: Tuple expects \'str\', \'usize\'\n\nCaused by:\n TypeError: \'str\' object cannot be interpreted as an integer\n\n",
test.unwrap_err().to_string()
);
}
#[derive(Debug, FromPyObject)] #[derive(Debug, FromPyObject)]
pub enum Foo<'a> { pub enum Foo<'a> {
@ -453,11 +362,7 @@ pub enum Bar {
A(String), A(String),
#[pyo3(annotation = "uint")] #[pyo3(annotation = "uint")]
B(usize), B(usize),
#[pyo3( #[pyo3(annotation = "int", transparent)]
annotation = "int",
transparent,
error_message = "Bar::C expects an integer type"
)]
C(isize), C(isize),
} }
@ -470,7 +375,7 @@ fn test_err_rename() {
assert!(f.is_err()); assert!(f.is_err());
assert_eq!( assert_eq!(
f.unwrap_err().to_string(), f.unwrap_err().to_string(),
"TypeError: Failed to extract type Bar\n\nCaused by:\n TypeError: \'dict\' object cannot be converted to \'Union[str, uint, int]\'\n\nTypeError: failed to extract inner field of Bar :: A\n\nCaused by:\n TypeError: \'dict\' object cannot be converted to \'PyString\'\n\nTypeError: failed to extract inner field of Bar :: B\n\nCaused by:\n TypeError: \'dict\' object cannot be interpreted as an integer\n\nTypeError: Bar::C expects an integer type\n\nCaused by:\n TypeError: \'dict\' object cannot be interpreted as an integer\n\n" "TypeError: Failed to extract type Bar\n\nCaused by:\n TypeError: \'dict\' object cannot be converted to \'Union[str, uint, int]\'\n\nTypeError: failed to extract inner field of Bar :: A\n\nCaused by:\n TypeError: \'dict\' object cannot be converted to \'PyString\'\n\nTypeError: failed to extract inner field of Bar :: B\n\nCaused by:\n TypeError: \'dict\' object cannot be interpreted as an integer\n\nTypeError: failed to extract inner field of Bar :: C\n\nCaused by:\n TypeError: \'dict\' object cannot be interpreted as an integer\n\n"
); );
} }

View File

@ -84,7 +84,7 @@ error: transparent structs and variants can only have 1 field
70 | | }, 70 | | },
| |_____^ | |_____^
error: expected one of: `attribute`, `item`, `from_py_with`, `error_message` error: expected one of: `attribute`, `item`, `from_py_with`
--> $DIR/invalid_frompy_derive.rs:76:12 --> $DIR/invalid_frompy_derive.rs:76:12
| |
76 | #[pyo3(attr)] 76 | #[pyo3(attr)]
@ -132,7 +132,7 @@ error: only one of `attribute` or `item` can be provided
118 | #[pyo3(item, attribute)] 118 | #[pyo3(item, attribute)]
| ^ | ^
error: expected one of: `transparent`, `annotation`, `error_message` error: expected `transparent` or `annotation`
--> $DIR/invalid_frompy_derive.rs:123:8 --> $DIR/invalid_frompy_derive.rs:123:8
| |
123 | #[pyo3(unknown = "should not work")] 123 | #[pyo3(unknown = "should not work")]