Skip to content

Commit cd726a2

Browse files
committed
Cleanup exceptions a bit
1 parent f22a12d commit cd726a2

7 files changed

Lines changed: 65 additions & 106 deletions

File tree

Cargo.lock

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/main.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ extern crate log;
77
use clap::{App, AppSettings, Arg, ArgMatches};
88
use rustpython_compiler::compile;
99
use rustpython_vm::{
10+
exceptions::{print_exception, PyBaseExceptionRef},
1011
match_class,
11-
obj::{objint::PyInt, objtuple::PyTuple, objtype},
12-
print_exception,
12+
obj::{objint::PyInt, objtype},
1313
pyobject::{ItemProtocol, PyResult},
1414
scope::Scope,
1515
util, InitParameter, PySettings, VirtualMachine,
@@ -51,8 +51,8 @@ fn main() {
5151
// See if any exception leaked out:
5252
if let Err(err) = res {
5353
if objtype::isinstance(&err, &vm.ctx.exceptions.system_exit) {
54-
let args = vm.get_attribute(err.clone(), "args").unwrap();
55-
let args = args.downcast::<PyTuple>().expect("'args' must be a tuple");
54+
let err: PyBaseExceptionRef = err.downcast().unwrap();
55+
let args = err.args();
5656
match args.elements.len() {
5757
0 => return,
5858
1 => match_class!(match args.elements[0].clone() {

src/shell.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ mod rustyline_helper;
55
use rustpython_compiler::{compile, error::CompileError, error::CompileErrorType};
66
use rustpython_parser::error::ParseErrorType;
77
use rustpython_vm::{
8+
exceptions::print_exception,
89
obj::objtype,
9-
print_exception,
1010
pyobject::{ItemProtocol, PyObjectRef, PyResult},
1111
scope::Scope,
1212
VirtualMachine,

vm/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ chrono = { version = "=0.4.9", features = ["wasmbind"] }
4949
unicode-xid = "0.2.0"
5050
lazy_static = "^1.0.1"
5151
lexical = "4"
52-
itertools = "^0.8.0"
52+
itertools = "0.8"
5353
hex = "0.4.0"
5454
hexf-parse = "0.1.0"
5555
indexmap = "1.0.2"

vm/src/exceptions.rs

Lines changed: 50 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
use crate::function::PyFuncArgs;
2-
use crate::obj::objiter;
2+
use crate::obj::objstr::{PyString, PyStringRef};
33
use crate::obj::objtraceback::PyTracebackRef;
44
use crate::obj::objtuple::{PyTuple, PyTupleRef};
5-
use crate::obj::objtype;
65
use crate::obj::objtype::PyClassRef;
76
use crate::pyobject::{
87
IdProtocol, PyClassImpl, PyContext, PyIterable, PyObjectRef, PyRef, PyResult, PyValue,
@@ -47,15 +46,15 @@ impl PyBaseException {
4746
#[pyslot(new)]
4847
fn tp_new(
4948
cls: PyClassRef,
50-
_args: PyFuncArgs,
49+
args: PyFuncArgs,
5150
vm: &VirtualMachine,
5251
) -> PyResult<PyBaseExceptionRef> {
5352
PyBaseException {
5453
traceback: RefCell::new(None),
5554
cause: RefCell::new(None),
5655
context: RefCell::new(None),
5756
suppress_context: Cell::new(false),
58-
args: RefCell::new(PyTuple::from(vec![]).into_ref(vm)),
57+
args: RefCell::new(PyTuple::from(args.args).into_ref(vm)),
5958
}
6059
.into_ref_with_type(vm, cls)
6160
}
@@ -66,8 +65,8 @@ impl PyBaseException {
6665
Ok(())
6766
}
6867

69-
#[pyproperty]
70-
fn args(&self, _vm: &VirtualMachine) -> PyTupleRef {
68+
#[pyproperty(name = "args")]
69+
fn get_args(&self, _vm: &VirtualMachine) -> PyTupleRef {
7170
self.args.borrow().clone()
7271
}
7372

@@ -127,6 +126,30 @@ impl PyBaseException {
127126
zelf.traceback.replace(tb);
128127
Ok(zelf.as_object().clone())
129128
}
129+
130+
#[pymethod(name = "__str__")]
131+
fn str(&self, vm: &VirtualMachine) -> PyStringRef {
132+
let str_args = exception_args_as_string(vm, self.args(), false);
133+
match str_args.into_iter().exactly_one() {
134+
Err(i) if i.len() == 0 => PyString::from("").into_ref(vm),
135+
Ok(s) => s,
136+
Err(i) => PyString::from(format!("({})", i.format(", "))).into_ref(vm),
137+
}
138+
}
139+
140+
#[pymethod(name = "__repr__")]
141+
fn repr(zelf: PyRef<Self>, vm: &VirtualMachine) -> String {
142+
let repr_args = exception_args_as_string(vm, zelf.args(), false);
143+
let cls = zelf.class();
144+
match repr_args.into_iter().exactly_one() {
145+
Ok(one) => format!("{}({},)", cls.name, one),
146+
Err(i) => format!("{}({})", cls.name, i.format(", ")),
147+
}
148+
}
149+
150+
pub fn args(&self) -> PyTupleRef {
151+
self.args.borrow().clone()
152+
}
130153
}
131154

132155
/// Print exception chain
@@ -207,27 +230,20 @@ pub fn print_exception_inner<W: Write>(
207230
vm: &VirtualMachine,
208231
exc: &PyObjectRef,
209232
) -> io::Result<()> {
210-
if let Ok(tb) = vm.get_attribute(exc.clone(), "__traceback__") {
211-
if objtype::isinstance(&tb, &vm.ctx.traceback_type()) {
212-
writeln!(output, "Traceback (most recent call last):")?;
213-
let mut tb: PyTracebackRef = tb.downcast().expect(" must be a traceback object");
214-
loop {
215-
print_traceback_entry(&mut output, &tb)?;
216-
tb = match &tb.next {
217-
Some(tb) => tb.clone(),
218-
None => break,
219-
};
220-
}
233+
let exc: PyBaseExceptionRef = exc.clone().downcast().unwrap();
234+
235+
if let Some(tb) = exc.traceback.borrow().clone() {
236+
writeln!(output, "Traceback (most recent call last):")?;
237+
let mut tb = &Some(tb);
238+
while let Some(traceback) = tb {
239+
print_traceback_entry(&mut output, traceback)?;
240+
tb = &traceback.next;
221241
}
222242
} else {
223243
writeln!(output, "No traceback set on exception")?;
224244
}
225245

226-
let varargs = vm
227-
.get_attribute(exc.clone(), "args")
228-
.unwrap()
229-
.downcast::<PyTuple>()
230-
.expect("'args' must be a tuple");
246+
let varargs = exc.args();
231247
let args_repr = exception_args_as_string(vm, varargs, true);
232248

233249
let exc_name = exc.class().name.clone();
@@ -247,73 +263,30 @@ fn exception_args_as_string(
247263
vm: &VirtualMachine,
248264
varargs: PyTupleRef,
249265
str_single: bool,
250-
) -> Vec<String> {
266+
) -> Vec<PyStringRef> {
251267
match varargs.elements.len() {
252268
0 => vec![],
253269
1 => {
254270
let args0_repr = if str_single {
255-
vm.to_pystr(&varargs.elements[0])
256-
.unwrap_or_else(|_| "<element str() failed>".to_string())
271+
vm.to_str(&varargs.elements[0])
272+
.unwrap_or_else(|_| PyString::from("<element str() failed>").into_ref(vm))
257273
} else {
258274
vm.to_repr(&varargs.elements[0])
259-
.map(|s| s.as_str().to_owned())
260-
.unwrap_or_else(|_| "<element repr() failed>".to_string())
275+
.unwrap_or_else(|_| PyString::from("<element repr() failed>").into_ref(vm))
261276
};
262277
vec![args0_repr]
263278
}
264279
_ => varargs
265280
.elements
266281
.iter()
267-
.map(|vararg| match vm.to_repr(vararg) {
268-
Ok(arg_repr) => arg_repr.as_str().to_string(),
269-
Err(_) => "<element repr() failed>".to_string(),
282+
.map(|vararg| {
283+
vm.to_repr(vararg)
284+
.unwrap_or_else(|_| PyString::from("<element repr() failed>").into_ref(vm))
270285
})
271286
.collect(),
272287
}
273288
}
274289

275-
fn exception_str(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
276-
arg_check!(
277-
vm,
278-
args,
279-
required = [(exc, Some(vm.ctx.exceptions.exception_type.clone()))]
280-
);
281-
let args = vm
282-
.get_attribute(exc.clone(), "args")
283-
.unwrap()
284-
.downcast::<PyTuple>()
285-
.expect("'args' must be a tuple");
286-
let args_str = exception_args_as_string(vm, args, false);
287-
let joined_str = match args_str.len() {
288-
0 => "".to_string(),
289-
1 => args_str.into_iter().next().unwrap(),
290-
_ => format!("({})", args_str.into_iter().format(", ")),
291-
};
292-
Ok(vm.new_str(joined_str))
293-
}
294-
295-
fn exception_repr(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
296-
arg_check!(
297-
vm,
298-
args,
299-
required = [(exc, Some(vm.ctx.exceptions.exception_type.clone()))]
300-
);
301-
let args = vm
302-
.get_attribute(exc.clone(), "args")
303-
.unwrap()
304-
.downcast::<PyTuple>()
305-
.expect("'args' must be a tuple");
306-
let args_repr = exception_args_as_string(vm, args, false);
307-
308-
let exc_name = exc.class().name.clone();
309-
let joined_str = match args_repr.len() {
310-
0 => format!("{}()", exc_name),
311-
1 => format!("{}({},)", exc_name, args_repr[0]),
312-
_ => format!("{}({})", exc_name, args_repr.join(", ")),
313-
};
314-
Ok(vm.new_str(joined_str))
315-
}
316-
317290
#[derive(Debug)]
318291
pub struct ExceptionZoo {
319292
pub arithmetic_error: PyClassRef,
@@ -508,10 +481,7 @@ impl ExceptionZoo {
508481
}
509482
}
510483

511-
fn import_error_init(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
512-
let exc_self = args.args[0].clone();
513-
514-
vm.set_attr(&exc_self, "args", vm.ctx.new_tuple(args.args[1..].to_vec()))?;
484+
fn import_error_init(exc_self: PyObjectRef, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult<()> {
515485
vm.set_attr(
516486
&exc_self,
517487
"name",
@@ -528,12 +498,7 @@ fn import_error_init(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
528498
.cloned()
529499
.unwrap_or_else(|| vm.get_none()),
530500
)?;
531-
vm.set_attr(
532-
&exc_self,
533-
"msg",
534-
args.args.get(1).cloned().unwrap_or_else(|| vm.get_none()),
535-
)?;
536-
Ok(vm.get_none())
501+
Ok(())
537502
}
538503

539504
fn none_getter(_obj: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
@@ -556,11 +521,6 @@ pub fn init(ctx: &PyContext) {
556521

557522
PyBaseException::extend_class(ctx, &excs.base_exception_type);
558523

559-
extend_class!(ctx, &excs.exception_type, {
560-
"__str__" => ctx.new_rustfunc(exception_str),
561-
"__repr__" => ctx.new_rustfunc(exception_repr),
562-
});
563-
564524
extend_class!(ctx, &excs.syntax_error, {
565525
"msg" => ctx.new_property(make_arg_getter(0)),
566526
"filename" => ctx.new_property(make_arg_getter(1)),
@@ -570,13 +530,12 @@ pub fn init(ctx: &PyContext) {
570530
});
571531

572532
extend_class!(ctx, &excs.import_error, {
573-
"__init__" => ctx.new_rustfunc(import_error_init)
533+
"__init__" => ctx.new_rustfunc(import_error_init),
534+
"msg" => ctx.new_property(make_arg_getter(0)),
574535
});
575536

576537
extend_class!(ctx, &excs.stop_iteration, {
577-
"value" => ctx.new_rustfunc(|obj: PyObjectRef, vm: &VirtualMachine| {
578-
objiter::stop_iter_value(vm, &obj)
579-
}),
538+
"value" => ctx.new_property(make_arg_getter(0)),
580539
});
581540

582541
extend_class!(ctx, &excs.unicode_decode_error, {

vm/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ pub mod cformat;
5353
mod dictdatatype;
5454
#[cfg(feature = "rustpython-compiler")]
5555
pub mod eval;
56-
mod exceptions;
56+
pub mod exceptions;
5757
pub mod format;
5858
mod frame;
5959
mod frozen;

vm/src/obj/objiter.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
use std::cell::Cell;
66

7-
use super::objtuple::PyTuple;
87
use super::objtype::{self, PyClassRef};
8+
use crate::exceptions::PyBaseExceptionRef;
99
use crate::pyobject::{
1010
PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, TypeProtocol,
1111
};
@@ -74,8 +74,8 @@ pub fn new_stop_iteration(vm: &VirtualMachine) -> PyObjectRef {
7474
}
7575

7676
pub fn stop_iter_value(vm: &VirtualMachine, exc: &PyObjectRef) -> PyResult {
77-
let args = vm.get_attribute(exc.clone(), "args")?;
78-
let args: &PyTuple = args.payload().unwrap();
77+
let exc = PyBaseExceptionRef::try_from_object(vm, exc.clone())?;
78+
let args = exc.args();
7979
let val = args
8080
.elements
8181
.first()

0 commit comments

Comments
 (0)