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

View File

@ -230,70 +230,7 @@ fn test_struct_nested_type_errors_with_generics() {
}
#[derive(Debug, FromPyObject)]
struct Custom<U, T> {
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")]
#[pyo3(transparent)]
pub struct TransparentStruct {
a: String,
}
@ -306,39 +243,11 @@ fn test_transparent_struct_error_message() {
let tup = TransparentStruct::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",
"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()
);
}
#[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)]
pub enum Foo<'a> {
@ -453,11 +362,7 @@ pub enum Bar {
A(String),
#[pyo3(annotation = "uint")]
B(usize),
#[pyo3(
annotation = "int",
transparent,
error_message = "Bar::C expects an integer type"
)]
#[pyo3(annotation = "int", transparent)]
C(isize),
}
@ -470,7 +375,7 @@ fn test_err_rename() {
assert!(f.is_err());
assert_eq!(
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 | | },
| |_____^
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
|
76 | #[pyo3(attr)]
@ -132,7 +132,7 @@ error: only one of `attribute` or `item` can be provided
118 | #[pyo3(item, attribute)]
| ^
error: expected one of: `transparent`, `annotation`, `error_message`
error: expected `transparent` or `annotation`
--> $DIR/invalid_frompy_derive.rs:123:8
|
123 | #[pyo3(unknown = "should not work")]