Skip to content

Commit 97c8d11

Browse files
committed
Make PyResult<T> = Result<T, PyBaseExceptionRef>
1 parent cd726a2 commit 97c8d11

22 files changed

+198
-161
lines changed

src/main.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ extern crate log;
77
use clap::{App, AppSettings, Arg, ArgMatches};
88
use rustpython_compiler::compile;
99
use rustpython_vm::{
10-
exceptions::{print_exception, PyBaseExceptionRef},
10+
exceptions::print_exception,
1111
match_class,
1212
obj::{objint::PyInt, objtype},
1313
pyobject::{ItemProtocol, PyResult},
@@ -51,7 +51,6 @@ 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 err: PyBaseExceptionRef = err.downcast().unwrap();
5554
let args = err.args();
5655
match args.elements.len() {
5756
0 => return,

src/shell.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ 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,
8+
exceptions::{print_exception, PyBaseExceptionRef},
99
obj::objtype,
10-
pyobject::{ItemProtocol, PyObjectRef, PyResult},
10+
pyobject::{ItemProtocol, PyResult},
1111
scope::Scope,
1212
VirtualMachine,
1313
};
@@ -16,7 +16,7 @@ use readline::{Readline, ReadlineResult};
1616

1717
enum ShellExecResult {
1818
Ok,
19-
PyErr(PyObjectRef),
19+
PyErr(PyBaseExceptionRef),
2020
Continue,
2121
}
2222

vm/src/builtins.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use num_traits::{Signed, ToPrimitive, Zero};
1212
#[cfg(feature = "rustpython-compiler")]
1313
use rustpython_compiler::compile;
1414

15+
use crate::exceptions::PyBaseExceptionRef;
1516
use crate::function::{single_or_tuple_any, Args, KwArgs, OptionalArg, PyFuncArgs};
1617
use crate::obj::objbool::{self, IntoPyBool};
1718
use crate::obj::objbyteinner::PyByteInner;
@@ -290,7 +291,7 @@ fn builtin_format(
290291
})
291292
}
292293

293-
fn catch_attr_exception<T>(ex: PyObjectRef, default: T, vm: &VirtualMachine) -> PyResult<T> {
294+
fn catch_attr_exception<T>(ex: PyBaseExceptionRef, default: T, vm: &VirtualMachine) -> PyResult<T> {
294295
if objtype::isinstance(&ex, &vm.ctx.exceptions.attribute_error) {
295296
Ok(default)
296297
} else {

vm/src/exceptions.rs

Lines changed: 57 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ use crate::obj::objtraceback::PyTracebackRef;
44
use crate::obj::objtuple::{PyTuple, PyTupleRef};
55
use crate::obj::objtype::PyClassRef;
66
use crate::pyobject::{
7-
IdProtocol, PyClassImpl, PyContext, PyIterable, PyObjectRef, PyRef, PyResult, PyValue,
8-
TypeProtocol,
7+
PyClassImpl, PyContext, PyIterable, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol,
98
};
109
use crate::types::create_type;
1110
use crate::vm::VirtualMachine;
@@ -18,8 +17,8 @@ use std::io::{self, BufRead, BufReader, Write};
1817
#[pyclass]
1918
pub struct PyBaseException {
2019
traceback: RefCell<Option<PyTracebackRef>>,
21-
cause: RefCell<Option<PyObjectRef>>,
22-
context: RefCell<Option<PyObjectRef>>,
20+
cause: RefCell<Option<PyBaseExceptionRef>>,
21+
context: RefCell<Option<PyBaseExceptionRef>>,
2322
suppress_context: Cell<bool>,
2423
args: RefCell<PyTupleRef>,
2524
}
@@ -83,12 +82,12 @@ impl PyBaseException {
8382
}
8483

8584
#[pyproperty(name = "__traceback__", setter)]
86-
fn set_traceback(&self, traceback: Option<PyTracebackRef>, _vm: &VirtualMachine) {
85+
fn setter_traceback(&self, traceback: Option<PyTracebackRef>, _vm: &VirtualMachine) {
8786
self.traceback.replace(traceback);
8887
}
8988

9089
#[pyproperty(name = "__cause__")]
91-
fn get_cause(&self, _vm: &VirtualMachine) -> Option<PyObjectRef> {
90+
fn get_cause(&self, _vm: &VirtualMachine) -> Option<PyBaseExceptionRef> {
9291
self.cause.borrow().clone()
9392
}
9493

@@ -98,7 +97,7 @@ impl PyBaseException {
9897
}
9998

10099
#[pyproperty(name = "__context__")]
101-
fn get_context(&self, _vm: &VirtualMachine) -> Option<PyObjectRef> {
100+
fn get_context(&self, _vm: &VirtualMachine) -> Option<PyBaseExceptionRef> {
102101
self.context.borrow().clone()
103102
}
104103

@@ -150,51 +149,67 @@ impl PyBaseException {
150149
pub fn args(&self) -> PyTupleRef {
151150
self.args.borrow().clone()
152151
}
152+
153+
pub fn traceback(&self) -> Option<PyTracebackRef> {
154+
self.traceback.borrow().clone()
155+
}
156+
pub fn set_traceback(&self, tb: Option<PyTracebackRef>) {
157+
self.traceback.replace(tb);
158+
}
159+
160+
pub fn cause(&self) -> Option<PyBaseExceptionRef> {
161+
self.cause.borrow().clone()
162+
}
163+
pub fn set_cause(&self, cause: Option<PyBaseExceptionRef>) {
164+
self.cause.replace(cause);
165+
}
166+
167+
pub fn context(&self) -> Option<PyBaseExceptionRef> {
168+
self.context.borrow().clone()
169+
}
170+
pub fn set_context(&self, context: Option<PyBaseExceptionRef>) {
171+
self.context.replace(context);
172+
}
153173
}
154174

155175
/// Print exception chain
156-
pub fn print_exception(vm: &VirtualMachine, exc: &PyObjectRef) {
157-
let _ = write_exception(io::stdout(), vm, exc);
176+
pub fn print_exception(vm: &VirtualMachine, exc: &PyBaseExceptionRef) {
177+
let stdout = io::stdout();
178+
let mut stdout = stdout.lock();
179+
let _ = write_exception(&mut stdout, vm, exc);
158180
}
159181

160182
pub fn write_exception<W: Write>(
161-
mut output: W,
183+
output: &mut W,
162184
vm: &VirtualMachine,
163-
exc: &PyObjectRef,
185+
exc: &PyBaseExceptionRef,
164186
) -> io::Result<()> {
165-
let mut had_cause = false;
166-
if let Ok(cause) = vm.get_attribute(exc.clone(), "__cause__") {
167-
if !vm.get_none().is(&cause) {
168-
had_cause = true;
169-
print_exception(vm, &cause);
187+
let cause = exc.cause();
188+
if let Some(cause) = &cause {
189+
write_exception(output, vm, cause)?;
190+
writeln!(
191+
output,
192+
"\nThe above exception was the direct cause of the following exception:\n"
193+
)?;
194+
}
195+
if cause.is_none() {
196+
if let Some(context) = exc.context() {
197+
write_exception(output, vm, &context)?;
170198
writeln!(
171199
output,
172-
"\nThe above exception was the direct cause of the following exception:\n"
200+
"\nDuring handling of the above exception, another exception occurred:\n"
173201
)?;
174202
}
175203
}
176-
if !had_cause {
177-
if let Ok(context) = vm.get_attribute(exc.clone(), "__context__") {
178-
if !vm.get_none().is(&context) {
179-
print_exception(vm, &context);
180-
writeln!(
181-
output,
182-
"\nDuring handling of the above exception, another exception occurred:\n"
183-
)?;
184-
}
185-
}
186-
}
187-
print_exception_inner(output, vm, exc)
204+
write_exception_inner(output, vm, exc)
188205
}
189206

190-
fn print_source_line<W: Write>(mut output: W, filename: &str, lineno: usize) -> io::Result<()> {
207+
fn print_source_line<W: Write>(output: &mut W, filename: &str, lineno: usize) -> io::Result<()> {
191208
// TODO: use io.open() method instead, when available, according to https://github.com/python/cpython/blob/master/Python/traceback.c#L393
192209
// TODO: support different encodings
193210
let file = match File::open(filename) {
194211
Ok(file) => file,
195-
Err(_) => {
196-
return Ok(());
197-
}
212+
Err(_) => return Ok(()),
198213
};
199214
let file = BufReader::new(file);
200215

@@ -212,7 +227,7 @@ fn print_source_line<W: Write>(mut output: W, filename: &str, lineno: usize) ->
212227
}
213228

214229
/// Print exception occurrence location from traceback element
215-
fn print_traceback_entry<W: Write>(mut output: W, tb_entry: &PyTracebackRef) -> io::Result<()> {
230+
fn write_traceback_entry<W: Write>(output: &mut W, tb_entry: &PyTracebackRef) -> io::Result<()> {
216231
let filename = tb_entry.frame.code.source_path.to_string();
217232
writeln!(
218233
output,
@@ -225,19 +240,17 @@ fn print_traceback_entry<W: Write>(mut output: W, tb_entry: &PyTracebackRef) ->
225240
}
226241

227242
/// Print exception with traceback
228-
pub fn print_exception_inner<W: Write>(
229-
mut output: W,
243+
pub fn write_exception_inner<W: Write>(
244+
output: &mut W,
230245
vm: &VirtualMachine,
231-
exc: &PyObjectRef,
246+
exc: &PyBaseExceptionRef,
232247
) -> io::Result<()> {
233-
let exc: PyBaseExceptionRef = exc.clone().downcast().unwrap();
234-
235248
if let Some(tb) = exc.traceback.borrow().clone() {
236249
writeln!(output, "Traceback (most recent call last):")?;
237-
let mut tb = &Some(tb);
250+
let mut tb = Some(&tb);
238251
while let Some(traceback) = tb {
239-
print_traceback_entry(&mut output, traceback)?;
240-
tb = &traceback.next;
252+
write_traceback_entry(output, traceback)?;
253+
tb = traceback.next.as_ref();
241254
}
242255
} else {
243256
writeln!(output, "No traceback set on exception")?;
@@ -505,14 +518,14 @@ fn none_getter(_obj: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
505518
vm.get_none()
506519
}
507520

508-
fn make_arg_getter(idx: usize) -> impl Fn(PyBaseExceptionRef, &VirtualMachine) -> PyResult {
521+
fn make_arg_getter(idx: usize) -> impl Fn(PyBaseExceptionRef, &VirtualMachine) -> PyObjectRef {
509522
move |exc, vm| {
510523
exc.args
511524
.borrow()
512525
.elements
513526
.get(idx)
514527
.cloned()
515-
.ok_or_else(|| vm.new_value_error(format!("couldn't get arg {} of exception", idx)))
528+
.unwrap_or_else(|| vm.get_none())
516529
}
517530
}
518531

vm/src/frame.rs

Lines changed: 21 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use indexmap::IndexMap;
55
use itertools::Itertools;
66

77
use crate::bytecode;
8+
use crate::exceptions::PyBaseExceptionRef;
89
use crate::function::{single_or_tuple_any, PyFuncArgs};
910
use crate::obj::objbool;
1011
use crate::obj::objcode::PyCodeRef;
@@ -15,7 +16,7 @@ use crate::obj::objiter;
1516
use crate::obj::objlist;
1617
use crate::obj::objslice::PySlice;
1718
use crate::obj::objstr::{self, PyString};
18-
use crate::obj::objtraceback::{PyTraceback, PyTracebackRef};
19+
use crate::obj::objtraceback::PyTraceback;
1920
use crate::obj::objtuple::PyTuple;
2021
use crate::obj::objtype::{self, PyClassRef};
2122
use crate::pyobject::{
@@ -63,7 +64,7 @@ enum UnwindReason {
6364
Returning { value: PyObjectRef },
6465

6566
/// We hit an exception, so unwind any try-except and finally blocks.
66-
Raising { exception: PyObjectRef },
67+
Raising { exception: PyBaseExceptionRef },
6768

6869
// NoWorries,
6970
/// We are unwinding blocks, since we hit break
@@ -176,32 +177,16 @@ impl Frame {
176177
// 1. Extract traceback from exception's '__traceback__' attr.
177178
// 2. Add new entry with current execution position (filename, lineno, code_object) to traceback.
178179
// 3. Unwind block stack till appropriate handler is found.
179-
assert!(objtype::isinstance(
180-
&exception,
181-
&vm.ctx.exceptions.base_exception_type
182-
));
183-
184-
let traceback = vm
185-
.get_attribute(exception.clone(), "__traceback__")
186-
.unwrap();
187180

188-
let next = if vm.is_none(&traceback) {
189-
None
190-
} else {
191-
let traceback: PyTracebackRef = traceback
192-
.downcast()
193-
.expect("next must be a traceback object");
194-
Some(traceback)
195-
};
181+
let next = exception.traceback();
196182

197183
let new_traceback = PyTraceback::new(
198184
next,
199185
self.clone().into_ref(vm),
200186
self.lasti.get(),
201187
lineno.row(),
202188
);
203-
vm.set_attr(&exception, "__traceback__", new_traceback.into_ref(vm))
204-
.unwrap();
189+
exception.set_traceback(Some(new_traceback.into_ref(vm)));
205190
vm_trace!("Adding to traceback: {:?} {:?}", new_traceback, lineno);
206191

207192
match self.unwind_blocks(vm, UnwindReason::Raising { exception }) {
@@ -464,8 +449,8 @@ impl Frame {
464449
let args = if let Some(exc) = exc {
465450
let exc_type = exc.class().into_object();
466451
let exc_val = exc.clone();
467-
let exc_tb = vm.ctx.none(); // TODO: retrieve traceback?
468-
vec![exc_type, exc_val, exc_tb]
452+
let exc_tb = exc.traceback().map_or(vm.get_none(), |tb| tb.into_object());
453+
vec![exc_type, exc_val.into_object(), exc_tb]
469454
} else {
470455
vec![vm.ctx.none(), vm.ctx.none(), vm.ctx.none()]
471456
};
@@ -745,7 +730,7 @@ impl Frame {
745730
self.pop_block();
746731
if let UnwindReason::Raising { exception } = &reason {
747732
self.push_block(BlockType::ExceptHandler {});
748-
self.push_value(exception.clone());
733+
self.push_value(exception.clone().into_object());
749734
vm.push_exception(exception.clone());
750735
self.jump(handler);
751736
return Ok(None);
@@ -978,8 +963,8 @@ impl Frame {
978963

979964
fn execute_raise(&self, vm: &VirtualMachine, argc: usize) -> FrameResult {
980965
let cause = match argc {
981-
2 => self.get_exception(vm, true)?,
982-
_ => vm.get_none(),
966+
2 => Some(self.get_exception(vm, true)?),
967+
_ => None,
983968
};
984969
let exception = match argc {
985970
0 => match vm.current_exception() {
@@ -996,18 +981,15 @@ impl Frame {
996981
_ => panic!("Invalid parameter for RAISE_VARARGS, must be between 0 to 3"),
997982
};
998983
let context = match argc {
999-
0 => vm.get_none(), // We have already got the exception,
1000-
_ => match vm.current_exception() {
1001-
Some(exc) => exc,
1002-
None => vm.get_none(),
1003-
},
984+
0 => None, // We have already got the exception,
985+
_ => vm.current_exception(),
1004986
};
1005987
info!(
1006988
"Exception raised: {:?} with cause: {:?} and context: {:?}",
1007989
exception, cause, context
1008990
);
1009-
vm.set_attr(&exception, vm.new_str("__cause__".to_string()), cause)?;
1010-
vm.set_attr(&exception, vm.new_str("__context__".to_string()), context)?;
991+
exception.set_cause(cause);
992+
exception.set_context(context);
1011993
Err(exception)
1012994
}
1013995

@@ -1385,12 +1367,16 @@ impl Frame {
13851367
}
13861368

13871369
#[cfg_attr(feature = "flame-it", flame("Frame"))]
1388-
fn get_exception(&self, vm: &VirtualMachine, none_allowed: bool) -> PyResult {
1370+
fn get_exception(
1371+
&self,
1372+
vm: &VirtualMachine,
1373+
none_allowed: bool,
1374+
) -> PyResult<PyBaseExceptionRef> {
13891375
let exception = self.pop_value();
1390-
if none_allowed && vm.get_none().is(&exception)
1376+
if none_allowed && vm.is_none(&exception)
13911377
|| objtype::isinstance(&exception, &vm.ctx.exceptions.base_exception_type)
13921378
{
1393-
Ok(exception)
1379+
Ok(exception.downcast().unwrap())
13941380
} else if let Ok(exc_type) = PyClassRef::try_from_object(vm, exception) {
13951381
if objtype::issubclass(&exc_type, &vm.ctx.exceptions.base_exception_type) {
13961382
let exception = vm.new_empty_exception(exc_type)?;

0 commit comments

Comments
 (0)