Skip to content

Commit 1110bde

Browse files
committed
Add prototype of context manager handling during exceptions.
1 parent e561559 commit 1110bde

File tree

3 files changed

+120
-23
lines changed

3 files changed

+120
-23
lines changed

tests/snippets/with.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,35 @@ def __exit__(self, exc_type, exc_val, exc_tb):
3535
assert c == [4]
3636
assert ls == [4, 5]
3737

38+
# Double context manager:
3839
ls = []
3940
with ContextManager() as c1, ContextManager2() as c2:
4041
print(c1)
4142
assert c2 == [1, 4, 3]
4243
assert ls == [1, 4, 3, 5, 2]
44+
45+
# Context manager in return:
46+
47+
ls = []
48+
def foo():
49+
with ContextManager():
50+
return
51+
foo()
52+
assert ls == [1, 2]
53+
54+
# Exception handling context manager:
55+
class assertRaises:
56+
def __init__(self, tp):
57+
self.tp = tp
58+
59+
def __enter__(self):
60+
print('Entering danger zone, but handling RuntimeError')
61+
62+
def __exit__(self, exc_type, exc_val, exc_tb):
63+
if exc_type is self.tp:
64+
print("Exception captured!")
65+
return True
66+
67+
68+
with assertRaises(RuntimeError):
69+
raise RuntimeError('w00t')

vm/src/compile.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,9 @@ impl Compiler {
517517
}
518518
}
519519
None => {
520-
// TODO: Put none on stack
520+
self.emit(Instruction::LoadConst {
521+
value: bytecode::Constant::None,
522+
});
521523
}
522524
}
523525

vm/src/vm.rs

Lines changed: 90 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use super::obj::objtype;
2222
use super::objbool;
2323
use super::pyobject::{
2424
AttributeProtocol, DictProtocol, IdProtocol, ParentProtocol, PyContext, PyFuncArgs, PyObject,
25-
PyObjectKind, PyObjectRef, PyResult, ToRust,
25+
PyObjectKind, PyObjectRef, PyResult, ToRust, TypeProtocol,
2626
};
2727
use super::stdlib;
2828
use super::sysmodule;
@@ -175,24 +175,69 @@ impl VirtualMachine {
175175
self.current_frame().last_block()
176176
}
177177

178-
fn with_exit(&mut self) {
178+
fn with_exit(&mut self, context_manager: &PyObjectRef, exc: Option<PyObjectRef>) -> PyResult {
179179
// Assume top of stack is __exit__ method:
180180
// TODO: do we want to put the exit call on the stack?
181-
let exit_method = self.pop_value();
182-
let args = PyFuncArgs::default();
181+
// let exit_method = self.pop_value();
182+
// let args = PyFuncArgs::default();
183183
// TODO: what happens when we got an error during handling exception?
184-
self.invoke(exit_method, args).unwrap();
184+
let args = if let Some(exc) = exc {
185+
let exc_type = exc.typ();
186+
let exc_val = exc.clone();
187+
let exc_tb = self.ctx.none(); // TODO: retrieve traceback?
188+
vec![exc_type, exc_val, exc_tb]
189+
} else {
190+
let exc_type = self.ctx.none();
191+
let exc_val = self.ctx.none();
192+
let exc_tb = self.ctx.none();
193+
vec![exc_type, exc_val, exc_tb]
194+
};
195+
self.call_method(context_manager.clone(), "__exit__", args)
196+
}
197+
198+
// Unwind all blocks:
199+
fn unwind_blocks(&mut self) -> Option<PyObjectRef> {
200+
loop {
201+
let block = self.pop_block();
202+
match block {
203+
Some(Block::Loop { .. }) => {}
204+
Some(Block::TryExcept { .. }) => {
205+
// TODO: execute finally handler
206+
}
207+
Some(Block::With {
208+
end: _,
209+
context_manager,
210+
}) => {
211+
match self.with_exit(&context_manager, None) {
212+
Ok(..) => {}
213+
Err(exc) => {
214+
// __exit__ went wrong,
215+
return Some(exc);
216+
}
217+
}
218+
}
219+
None => break None,
220+
}
221+
}
185222
}
186223

187224
fn unwind_loop(&mut self) -> Block {
188225
loop {
189226
let block = self.pop_block();
190227
match block {
191228
Some(Block::Loop { start: _, end: __ }) => break block.unwrap(),
192-
Some(Block::TryExcept { .. }) => {}
193-
Some(Block::With { .. }) => {
194-
self.with_exit();
229+
Some(Block::TryExcept { .. }) => {
230+
// TODO: execute finally handler
195231
}
232+
Some(Block::With {
233+
end: _,
234+
context_manager,
235+
}) => match self.with_exit(&context_manager, None) {
236+
Ok(..) => {}
237+
Err(exc) => {
238+
panic!("Exception in with __exit__ {:?}", exc);
239+
}
240+
},
196241
None => panic!("No block to break / continue"),
197242
}
198243
}
@@ -208,8 +253,33 @@ impl VirtualMachine {
208253
self.jump(handler);
209254
return None;
210255
}
211-
Some(Block::With { .. }) => {
212-
self.with_exit();
256+
Some(Block::With {
257+
end,
258+
context_manager,
259+
}) => {
260+
match self.with_exit(&context_manager, Some(exc.clone())) {
261+
Ok(exit_action) => {
262+
match objbool::boolval(self, exit_action) {
263+
Ok(handle_exception) => {
264+
if handle_exception {
265+
// We handle the exception, so return!
266+
self.jump(end);
267+
return None;
268+
} else {
269+
// go on with the stack unwinding.
270+
}
271+
}
272+
Err(exit_exc) => {
273+
return Some(exit_exc);
274+
}
275+
}
276+
// if objtype::isinstance
277+
}
278+
Err(exit_exc) => {
279+
// TODO: what about original exception?
280+
return Some(exit_exc);
281+
}
282+
}
213283
}
214284
Some(Block::Loop { .. }) => {}
215285
None => break,
@@ -825,7 +895,11 @@ impl VirtualMachine {
825895
bytecode::Instruction::CompareOperation { ref op } => self.execute_compare(op),
826896
bytecode::Instruction::ReturnValue => {
827897
let value = self.pop_value();
828-
Some(Ok(value))
898+
if let Some(exc) = self.unwind_blocks() {
899+
Some(Err(exc))
900+
} else {
901+
Some(Ok(value))
902+
}
829903
}
830904
bytecode::Instruction::SetupLoop { start, end } => {
831905
self.push_block(Block::Loop {
@@ -862,17 +936,11 @@ impl VirtualMachine {
862936
{
863937
assert!(end1 == end2);
864938

865-
// call exit now:
866-
// TODO: improve exception handling in context manager.
867-
let exc_type = self.ctx.none();
868-
let exc_val = self.ctx.none();
869-
let exc_tb = self.ctx.none();
870-
self.call_method(
871-
context_manager.clone(),
872-
"__exit__",
873-
vec![exc_type, exc_val, exc_tb],
874-
).unwrap();
875-
None
939+
// call exit now with no exception:
940+
match self.with_exit(context_manager, None) {
941+
Ok(..) => None,
942+
Err(exc) => Some(Err(exc)),
943+
}
876944
} else {
877945
panic!("Block stack is incorrect, expected a with block");
878946
}

0 commit comments

Comments
 (0)