Skip to content

Commit 2220397

Browse files
authored
Merge pull request RustPython#821 from palaviv/excpetion-cause
Add exception cause
2 parents a91cfd1 + 256abe6 commit 2220397

File tree

3 files changed

+75
-25
lines changed

3 files changed

+75
-25
lines changed

tests/snippets/try_exceptions.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
1+
from testutils import assertRaises
22

33
try:
44
raise BaseException()
@@ -95,3 +95,33 @@ def __init__(self):
9595
l.append(3)
9696
print('boom', type(ex))
9797
assert l == [1, 3]
98+
99+
cause = None
100+
try:
101+
try:
102+
raise ZeroDivisionError
103+
except ZeroDivisionError as ex:
104+
assert ex.__cause__ == None
105+
cause = ex
106+
raise NameError from ex
107+
except NameError as ex2:
108+
assert ex2.__cause__ == cause
109+
110+
try:
111+
raise ZeroDivisionError from None
112+
except ZeroDivisionError as ex:
113+
assert ex.__cause__ == None
114+
115+
try:
116+
raise ZeroDivisionError
117+
except ZeroDivisionError as ex:
118+
assert ex.__cause__ == None
119+
120+
with assertRaises(TypeError):
121+
raise ZeroDivisionError from 5
122+
123+
try:
124+
raise ZeroDivisionError from NameError
125+
except ZeroDivisionError as ex:
126+
assert type(ex.__cause__) == NameError
127+

vm/src/exceptions.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::function::PyFuncArgs;
22
use crate::obj::objsequence;
33
use crate::obj::objtype;
44
use crate::obj::objtype::PyClassRef;
5-
use crate::pyobject::{create_type, PyContext, PyObjectRef, PyResult, TypeProtocol};
5+
use crate::pyobject::{create_type, IdProtocol, PyContext, PyObjectRef, PyResult, TypeProtocol};
66
use crate::vm::VirtualMachine;
77

88
fn exception_init(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
@@ -18,8 +18,19 @@ fn exception_init(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
1818
Ok(vm.get_none())
1919
}
2020

21-
// Print exception including traceback:
21+
// print excption chain
2222
pub fn print_exception(vm: &VirtualMachine, exc: &PyObjectRef) {
23+
if let Ok(cause) = vm.get_attribute(exc.clone(), "__cause__") {
24+
if !vm.get_none().is(&cause) {
25+
print_exception(vm, &cause);
26+
println!("\nThe above exception was the direct cause of the following exception:\n");
27+
}
28+
}
29+
print_exception_inner(vm, exc)
30+
}
31+
32+
// Print exception including traceback:
33+
pub fn print_exception_inner(vm: &VirtualMachine, exc: &PyObjectRef) {
2334
if let Ok(tb) = vm.get_attribute(exc.clone(), "__traceback__") {
2435
println!("Traceback (most recent call last):");
2536
if objtype::isinstance(&tb, &vm.ctx.list_type()) {

vm/src/frame.rs

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -688,31 +688,18 @@ impl Frame {
688688
}
689689

690690
bytecode::Instruction::Raise { argc } => {
691+
let cause = match argc {
692+
2 => self.get_exception(vm, true)?,
693+
_ => vm.get_none(),
694+
};
691695
let exception = match argc {
692-
1 => self.pop_value(),
693-
0 | 2 | 3 => panic!("Not implemented!"),
696+
1 | 2 => self.get_exception(vm, false)?,
697+
0 | 3 => panic!("Not implemented!"),
694698
_ => panic!("Invalid parameter for RAISE_VARARGS, must be between 0 to 3"),
695699
};
696-
if objtype::isinstance(&exception, &vm.ctx.exceptions.base_exception_type) {
697-
info!("Exception raised: {:?}", exception);
698-
Err(exception)
699-
} else if let Ok(exception) = PyClassRef::try_from_object(vm, exception) {
700-
if objtype::issubclass(&exception, &vm.ctx.exceptions.base_exception_type) {
701-
let exception = vm.new_empty_exception(exception)?;
702-
info!("Exception raised: {:?}", exception);
703-
Err(exception)
704-
} else {
705-
let msg = format!(
706-
"Can only raise BaseException derived types, not {}",
707-
exception
708-
);
709-
let type_error_type = vm.ctx.exceptions.type_error.clone();
710-
let type_error = vm.new_exception(type_error_type, msg);
711-
Err(type_error)
712-
}
713-
} else {
714-
Err(vm.new_type_error("exceptions must derive from BaseException".to_string()))
715-
}
700+
info!("Exception raised: {:?} with cause: {:?}", exception, cause);
701+
vm.set_attr(&exception, vm.new_str("__cause__".to_string()), cause)?;
702+
Err(exception)
716703
}
717704

718705
bytecode::Instruction::Break => {
@@ -1215,6 +1202,28 @@ impl Frame {
12151202
let stack = self.stack.borrow_mut();
12161203
stack[stack.len() - depth - 1].clone()
12171204
}
1205+
1206+
fn get_exception(&self, vm: &VirtualMachine, none_allowed: bool) -> PyResult {
1207+
let exception = self.pop_value();
1208+
if none_allowed && vm.get_none().is(&exception) {
1209+
Ok(exception)
1210+
} else if objtype::isinstance(&exception, &vm.ctx.exceptions.base_exception_type) {
1211+
Ok(exception)
1212+
} else if let Ok(exception) = PyClassRef::try_from_object(vm, exception) {
1213+
if objtype::issubclass(&exception, &vm.ctx.exceptions.base_exception_type) {
1214+
let exception = vm.new_empty_exception(exception)?;
1215+
Ok(exception)
1216+
} else {
1217+
let msg = format!(
1218+
"Can only raise BaseException derived types, not {}",
1219+
exception
1220+
);
1221+
Err(vm.new_type_error(msg))
1222+
}
1223+
} else {
1224+
Err(vm.new_type_error("exceptions must derive from BaseException".to_string()))
1225+
}
1226+
}
12181227
}
12191228

12201229
impl fmt::Debug for Frame {

0 commit comments

Comments
 (0)