@@ -9,7 +9,7 @@ use crate::{
99 } ,
1010 class:: { PyClassImpl , StaticType } ,
1111 convert:: { ToPyException , ToPyObject } ,
12- function:: { ArgIterable , FuncArgs , IntoFuncArgs } ,
12+ function:: { ArgIterable , FuncArgs , IntoFuncArgs , PySetterValue } ,
1313 py_io:: { self , Write } ,
1414 stdlib:: sys,
1515 suggestion:: offer_suggestions,
@@ -994,7 +994,12 @@ impl ExceptionZoo {
994994 extend_exception ! ( PyRecursionError , ctx, excs. recursion_error) ;
995995
996996 extend_exception ! ( PySyntaxError , ctx, excs. syntax_error, {
997- "msg" => ctx. new_readonly_getset( "msg" , excs. syntax_error, make_arg_getter( 0 ) ) ,
997+ "msg" => ctx. new_static_getset(
998+ "msg" ,
999+ excs. syntax_error,
1000+ make_arg_getter( 0 ) ,
1001+ syntax_error_set_msg,
1002+ ) ,
9981003 // TODO: members
9991004 "filename" => ctx. none( ) ,
10001005 "lineno" => ctx. none( ) ,
@@ -1041,6 +1046,25 @@ fn make_arg_getter(idx: usize) -> impl Fn(PyBaseExceptionRef) -> Option<PyObject
10411046 move |exc| exc. get_arg ( idx)
10421047}
10431048
1049+ fn syntax_error_set_msg (
1050+ exc : PyBaseExceptionRef ,
1051+ value : PySetterValue ,
1052+ vm : & VirtualMachine ,
1053+ ) -> PyResult < ( ) > {
1054+ let mut args = exc. args . write ( ) ;
1055+ let mut new_args = args. as_slice ( ) . to_vec ( ) ;
1056+ // Ensure the message slot at index 0 always exists for SyntaxError.args.
1057+ if new_args. is_empty ( ) {
1058+ new_args. push ( vm. ctx . none ( ) ) ;
1059+ }
1060+ match value {
1061+ PySetterValue :: Assign ( value) => new_args[ 0 ] = value,
1062+ PySetterValue :: Delete => new_args[ 0 ] = vm. ctx . none ( ) ,
1063+ }
1064+ * args = PyTuple :: new_ref ( new_args, & vm. ctx ) ;
1065+ Ok ( ( ) )
1066+ }
1067+
10441068fn system_exit_code ( exc : PyBaseExceptionRef ) -> Option < PyObjectRef > {
10451069 exc. args . read ( ) . first ( ) . map ( |code| {
10461070 match_class ! ( match code {
@@ -2048,15 +2072,14 @@ pub(super) mod types {
20482072 . unwrap_or_else ( |_| vm. ctx . new_str ( "<filename str() failed>" ) )
20492073 } ) ;
20502074
2051- let args = zelf. args ( ) ;
2052-
2053- let msg = if args. len ( ) == 1 {
2054- vm. exception_args_as_string ( args, false )
2055- . into_iter ( )
2056- . exactly_one ( )
2057- . unwrap ( )
2058- } else {
2059- return zelf. __str__ ( vm) ;
2075+ let msg = match zelf. as_object ( ) . get_attr ( "msg" , vm) {
2076+ Ok ( obj) => obj
2077+ . str ( vm)
2078+ . unwrap_or_else ( |_| vm. ctx . new_str ( "<msg str() failed>" ) ) ,
2079+ Err ( _) => {
2080+ // Fallback to the base formatting if the msg attribute was deleted or attribute lookup fails for any reason.
2081+ return Py :: < PyBaseException > :: __str__ ( zelf, vm) ;
2082+ }
20602083 } ;
20612084
20622085 let msg_with_location_info: String = match ( maybe_lineno, maybe_filename) {
0 commit comments