From ad31f973845ae63dcd05979fdafdb84c977ca087 Mon Sep 17 00:00:00 2001 From: "Jeong, YunWon" Date: Tue, 20 Jan 2026 00:54:59 +0900 Subject: [PATCH] FromArgs with error_msg --- crates/derive-impl/src/from_args.rs | 19 +++++++++++-- crates/vm/src/builtins/function.rs | 8 +++--- crates/vm/src/builtins/interpolation.rs | 36 +++++++++++-------------- crates/vm/src/builtins/super.rs | 9 +++---- 4 files changed, 39 insertions(+), 33 deletions(-) diff --git a/crates/derive-impl/src/from_args.rs b/crates/derive-impl/src/from_args.rs index 667f887e81c..9f2d0460fb0 100644 --- a/crates/derive-impl/src/from_args.rs +++ b/crates/derive-impl/src/from_args.rs @@ -37,6 +37,7 @@ struct ArgAttribute { name: Option, kind: ParameterKind, default: Option, + error_msg: Option, } impl ArgAttribute { @@ -63,6 +64,7 @@ impl ArgAttribute { name: None, kind, default: None, + error_msg: None, }); return Ok(()); }; @@ -94,6 +96,12 @@ impl ArgAttribute { } let val = meta.value()?.parse::()?; self.name = Some(val.value()) + } else if meta.path.is_ident("error_msg") { + if self.error_msg.is_some() { + return Err(meta.error("already have an error_msg")); + } + let val = meta.value()?.parse::()?; + self.error_msg = Some(val.value()) } else { return Err(meta.error("Unrecognized pyarg attribute")); } @@ -146,8 +154,15 @@ fn generate_field((i, field): (usize, &Field)) -> Result { .or(name_string) .ok_or_else(|| err_span!(field, "field in tuple struct must have name attribute"))?; - let middle = quote! { - .map(|x| ::rustpython_vm::convert::TryFromObject::try_from_object(vm, x)).transpose()? + let middle = if let Some(error_msg) = &attr.error_msg { + quote! { + .map(|x| ::rustpython_vm::convert::TryFromObject::try_from_object(vm, x) + .map_err(|_| vm.new_type_error(#error_msg))).transpose()? + } + } else { + quote! { + .map(|x| ::rustpython_vm::convert::TryFromObject::try_from_object(vm, x)).transpose()? + } }; let ending = if let Some(default) = attr.default { diff --git a/crates/vm/src/builtins/function.rs b/crates/vm/src/builtins/function.rs index 632fd867d2e..7eb74dec41e 100644 --- a/crates/vm/src/builtins/function.rs +++ b/crates/vm/src/builtins/function.rs @@ -855,13 +855,13 @@ pub struct PyFunctionNewArgs { code: PyRef, #[pyarg(positional)] globals: PyDictRef, - #[pyarg(any, optional)] + #[pyarg(any, optional, error_msg = "arg 3 (name) must be None or string")] name: OptionalArg, - #[pyarg(any, optional)] + #[pyarg(any, optional, error_msg = "arg 4 (defaults) must be None or tuple")] argdefs: Option, - #[pyarg(any, optional)] + #[pyarg(any, optional, error_msg = "arg 5 (closure) must be None or tuple")] closure: Option, - #[pyarg(any, optional)] + #[pyarg(any, optional, error_msg = "arg 6 (kwdefaults) must be None or dict")] kwdefaults: Option, } diff --git a/crates/vm/src/builtins/interpolation.rs b/crates/vm/src/builtins/interpolation.rs index 1d29ad7b49e..d8127c151e3 100644 --- a/crates/vm/src/builtins/interpolation.rs +++ b/crates/vm/src/builtins/interpolation.rs @@ -59,26 +59,16 @@ impl Constructor for PyInterpolation { type Args = InterpolationArgs; fn py_new(_cls: &Py, args: Self::Args, vm: &VirtualMachine) -> PyResult { - let conversion = match args.conversion { - OptionalArg::Present(c) => { - if vm.is_none(&c) { - vm.ctx.none() - } else { - let s = c.downcast::().map_err(|_| { - vm.new_type_error( - "Interpolation() argument 'conversion' must be str or None", - ) - })?; - let s_str = s.as_str(); - if s_str.len() != 1 || !matches!(s_str.chars().next(), Some('s' | 'r' | 'a')) { - return Err(vm.new_value_error( - "Interpolation() argument 'conversion' must be one of 's', 'a' or 'r'", - )); - } - s.into() - } + let conversion: PyObjectRef = if let Some(s) = args.conversion { + let s_str = s.as_str(); + if s_str.len() != 1 || !matches!(s_str.chars().next(), Some('s' | 'r' | 'a')) { + return Err(vm.new_value_error( + "Interpolation() argument 'conversion' must be one of 's', 'a' or 'r'", + )); } - OptionalArg::Missing => vm.ctx.none(), + s.into() + } else { + vm.ctx.none() }; let expression = args @@ -103,8 +93,12 @@ pub struct InterpolationArgs { value: PyObjectRef, #[pyarg(any, optional)] expression: OptionalArg, - #[pyarg(any, optional)] - conversion: OptionalArg, + #[pyarg( + any, + optional, + error_msg = "Interpolation() argument 'conversion' must be str or None" + )] + conversion: Option, #[pyarg(any, optional)] format_spec: OptionalArg, } diff --git a/crates/vm/src/builtins/super.rs b/crates/vm/src/builtins/super.rs index 893509bc6d3..b7bc3004332 100644 --- a/crates/vm/src/builtins/super.rs +++ b/crates/vm/src/builtins/super.rs @@ -60,8 +60,8 @@ impl Constructor for PySuper { #[derive(FromArgs)] pub struct InitArgs { - #[pyarg(positional, optional)] - py_type: OptionalArg, + #[pyarg(positional, optional, error_msg = "super() argument 1 must be a type")] + py_type: OptionalArg, #[pyarg(positional, optional)] py_obj: OptionalArg, } @@ -75,10 +75,7 @@ impl Initializer for PySuper { vm: &VirtualMachine, ) -> PyResult<()> { // Get the type: - let (typ, obj) = if let OptionalArg::Present(ty_obj) = py_type { - let ty = ty_obj - .downcast::() - .map_err(|_| vm.new_type_error("super() argument 1 must be a type"))?; + let (typ, obj) = if let OptionalArg::Present(ty) = py_type { (ty, py_obj.unwrap_or_none(vm)) } else { let frame = vm