Skip to content

Commit be9e44a

Browse files
Copilotyouknowone
andauthored
Allow SyntaxError.msg to be writable and reflected in string formatting (RustPython#6493)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>
1 parent cbde5ce commit be9e44a

3 files changed

Lines changed: 43 additions & 12 deletions

File tree

DEVELOPMENT.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,8 @@ repository's structure:
130130
- `stdlib`: Standard library parts implemented in rust.
131131
- `src`: using the other subcrates to bring rustpython to life.
132132
- `wasm`: Binary crate and resources for WebAssembly build
133-
- `extra_tests`: extra integration test snippets as a supplement to `Lib/test`
133+
- `extra_tests`: extra integration test snippets as a supplement to `Lib/test`.
134+
Add new RustPython-only regression tests here; do not place new tests under `Lib/test`.
134135

135136
## Understanding Internals
136137

crates/vm/src/exceptions.rs

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
10441068
fn 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) {

extra_tests/snippets/builtin_exceptions.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,13 @@ def __init__(self, value):
8585
assert exc.offset is None
8686
assert exc.text is None
8787

88+
err = SyntaxError("bad bad", ("bad.py", 1, 2, "abcdefg"))
89+
err.msg = "changed"
90+
assert err.msg == "changed"
91+
assert str(err) == "changed (bad.py, line 1)"
92+
del err.msg
93+
assert err.msg is None
94+
8895
# Regression to:
8996
# https://github.com/RustPython/RustPython/issues/2779
9097

0 commit comments

Comments
 (0)