Skip to content

Commit 143e329

Browse files
authored
Merge pull request RustPython#831 from palaviv/exception-context
Exception reraise and context
2 parents 51aa817 + 2ffd1c9 commit 143e329

File tree

5 files changed

+107
-17
lines changed

5 files changed

+107
-17
lines changed

tests/snippets/try_exceptions.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ def __init__(self):
106106
raise NameError from ex
107107
except NameError as ex2:
108108
assert ex2.__cause__ == cause
109+
assert ex2.__context__ == cause
109110

110111
try:
111112
raise ZeroDivisionError from None
@@ -125,3 +126,44 @@ def __init__(self):
125126
except ZeroDivisionError as ex:
126127
assert type(ex.__cause__) == NameError
127128

129+
with assertRaises(NameError):
130+
try:
131+
raise NameError
132+
except:
133+
raise
134+
135+
with assertRaises(RuntimeError):
136+
raise
137+
138+
context = None
139+
try:
140+
try:
141+
raise ZeroDivisionError
142+
except ZeroDivisionError as ex:
143+
assert ex.__context__ == None
144+
context = ex
145+
raise NameError
146+
except NameError as ex2:
147+
assert ex2.__context__ == context
148+
assert type(ex2.__context__) == ZeroDivisionError
149+
150+
try:
151+
raise ZeroDivisionError
152+
except ZeroDivisionError as ex:
153+
assert ex.__context__ == None
154+
155+
try:
156+
raise ZeroDivisionError from NameError
157+
except ZeroDivisionError as ex:
158+
assert type(ex.__cause__) == NameError
159+
assert ex.__context__ == None
160+
161+
try:
162+
try:
163+
raise ZeroDivisionError
164+
except ZeroDivisionError as ex:
165+
pass
166+
finally:
167+
raise NameError
168+
except NameError as ex2:
169+
assert ex2.__context__ == None

vm/src/bytecode.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ pub enum Instruction {
135135
PopBlock,
136136
Raise {
137137
argc: usize,
138+
in_exc: bool,
138139
},
139140
BuildString {
140141
size: usize,
@@ -363,7 +364,7 @@ impl Instruction {
363364
SetupWith { end } => w!(SetupWith, end),
364365
CleanupWith { end } => w!(CleanupWith, end),
365366
PopBlock => w!(PopBlock),
366-
Raise { argc } => w!(Raise, argc),
367+
Raise { argc, in_exc } => w!(Raise, argc, in_exc),
367368
BuildString { size } => w!(BuildString, size),
368369
BuildTuple { size, unpack } => w!(BuildTuple, size, unpack),
369370
BuildList { size, unpack } => w!(BuildList, size, unpack),

vm/src/compile.rs

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ struct Compiler {
2424
current_qualified_path: Option<String>,
2525
in_loop: bool,
2626
in_function_def: bool,
27+
in_exc_handler: bool,
2728
}
2829

2930
/// Compile a given sourcecode into a bytecode object.
@@ -85,6 +86,7 @@ impl Compiler {
8586
current_qualified_path: None,
8687
in_loop: false,
8788
in_function_def: false,
89+
in_exc_handler: false,
8890
}
8991
}
9092

@@ -325,15 +327,24 @@ impl Compiler {
325327
match cause {
326328
Some(cause) => {
327329
self.compile_expression(cause)?;
328-
self.emit(Instruction::Raise { argc: 2 });
330+
self.emit(Instruction::Raise {
331+
argc: 2,
332+
in_exc: self.in_exc_handler,
333+
});
329334
}
330335
None => {
331-
self.emit(Instruction::Raise { argc: 1 });
336+
self.emit(Instruction::Raise {
337+
argc: 1,
338+
in_exc: self.in_exc_handler,
339+
});
332340
}
333341
}
334342
}
335343
None => {
336-
self.emit(Instruction::Raise { argc: 0 });
344+
self.emit(Instruction::Raise {
345+
argc: 0,
346+
in_exc: self.in_exc_handler,
347+
});
337348
}
338349
},
339350
ast::Statement::Try {
@@ -378,7 +389,10 @@ impl Compiler {
378389
});
379390
}
380391
}
381-
self.emit(Instruction::Raise { argc: 1 });
392+
self.emit(Instruction::Raise {
393+
argc: 1,
394+
in_exc: self.in_exc_handler,
395+
});
382396
self.set_label(end_label);
383397
}
384398
ast::Statement::Break => {
@@ -558,6 +572,8 @@ impl Compiler {
558572
self.emit(Instruction::Jump { target: else_label });
559573

560574
// except handlers:
575+
let was_in_exc_handler = self.in_exc_handler;
576+
self.in_exc_handler = true;
561577
self.set_label(handler_label);
562578
// Exception is on top of stack now
563579
handler_label = self.new_label();
@@ -586,19 +602,16 @@ impl Compiler {
586602

587603
// We have a match, store in name (except x as y)
588604
if let Some(alias) = &handler.name {
605+
// Duplicate exception for context:
606+
self.emit(Instruction::Duplicate);
589607
self.store_name(alias);
590-
} else {
591-
// Drop exception from top of stack:
592-
self.emit(Instruction::Pop);
593608
}
594-
} else {
595-
// Catch all!
596-
// Drop exception from top of stack:
597-
self.emit(Instruction::Pop);
598609
}
599610

600611
// Handler code:
601612
self.compile_statements(&handler.body)?;
613+
// Drop exception from top of stack:
614+
self.emit(Instruction::Pop);
602615
self.emit(Instruction::Jump {
603616
target: finally_label,
604617
});
@@ -619,7 +632,12 @@ impl Compiler {
619632
if let Some(statements) = finalbody {
620633
self.compile_statements(statements)?;
621634
}
622-
self.emit(Instruction::Raise { argc: 1 });
635+
self.emit(Instruction::Raise {
636+
argc: 0,
637+
in_exc: true,
638+
});
639+
640+
self.in_exc_handler = was_in_exc_handler;
623641

624642
// We successfully ran the try block:
625643
// else:
@@ -633,7 +651,6 @@ impl Compiler {
633651
if let Some(statements) = finalbody {
634652
self.compile_statements(statements)?;
635653
}
636-
637654
// unimplemented!();
638655
Ok(())
639656
}

vm/src/exceptions.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,22 @@ fn exception_init(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
2020

2121
// print excption chain
2222
pub fn print_exception(vm: &VirtualMachine, exc: &PyObjectRef) {
23+
let mut had_cause = false;
2324
if let Ok(cause) = vm.get_attribute(exc.clone(), "__cause__") {
2425
if !vm.get_none().is(&cause) {
26+
had_cause = true;
2527
print_exception(vm, &cause);
2628
println!("\nThe above exception was the direct cause of the following exception:\n");
2729
}
2830
}
31+
if !had_cause {
32+
if let Ok(context) = vm.get_attribute(exc.clone(), "__context__") {
33+
if !vm.get_none().is(&context) {
34+
print_exception(vm, &context);
35+
println!("\nDuring handling of the above exception, another exception occurred:\n");
36+
}
37+
}
38+
}
2939
print_exception_inner(vm, exc)
3040
}
3141

vm/src/frame.rs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -715,18 +715,38 @@ impl Frame {
715715
Ok(None)
716716
}
717717

718-
bytecode::Instruction::Raise { argc } => {
718+
bytecode::Instruction::Raise { argc, in_exc } => {
719719
let cause = match argc {
720720
2 => self.get_exception(vm, true)?,
721721
_ => vm.get_none(),
722722
};
723723
let exception = match argc {
724+
0 => match in_exc {
725+
true => self.get_exception(vm, false)?,
726+
false => {
727+
return Err(vm.new_exception(
728+
vm.ctx.exceptions.runtime_error.clone(),
729+
"No active exception to reraise".to_string(),
730+
));
731+
}
732+
},
724733
1 | 2 => self.get_exception(vm, false)?,
725-
0 | 3 => panic!("Not implemented!"),
734+
3 => panic!("Not implemented!"),
726735
_ => panic!("Invalid parameter for RAISE_VARARGS, must be between 0 to 3"),
727736
};
728-
info!("Exception raised: {:?} with cause: {:?}", exception, cause);
737+
let context = match argc {
738+
0 => vm.get_none(), // We have already got the exception,
739+
_ => match in_exc {
740+
true => self.get_exception(vm, false)?,
741+
false => vm.get_none(),
742+
},
743+
};
744+
info!(
745+
"Exception raised: {:?} with cause: {:?} and context: {:?}",
746+
exception, cause, context
747+
);
729748
vm.set_attr(&exception, vm.new_str("__cause__".to_string()), cause)?;
749+
vm.set_attr(&exception, vm.new_str("__context__".to_string()), context)?;
730750
Err(exception)
731751
}
732752

0 commit comments

Comments
 (0)