Skip to content

Commit 0c904df

Browse files
committed
vm: Save current active exception on opening new try block.
Required to reraise correct exceptions in except block, regardless if more try blocks with active exceptions happen in the same except block. P.S. This "automagic reraise" appears to be quite wasteful feature of Python - we need to save pending exception just in case it *might* be reraised. Instead, programmer could explcitly capture exception to a variable using "except ... as var", and reraise that. So, consider disabling argless raise support as an optimization.
1 parent 69975df commit 0c904df

3 files changed

Lines changed: 27 additions & 0 deletions

File tree

py/bc.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ typedef struct _mp_exc_stack {
99
const byte *handler;
1010
// bit 0 is saved currently_in_except_block value
1111
mp_obj_t *val_sp;
12+
// Saved exception, valid if currently_in_except_block bit is 1
13+
mp_obj_t prev_exc;
1214
// We might only have 2 interesting cases here: SETUP_EXCEPT & SETUP_FINALLY,
1315
// consider storing it in bit 1 of val_sp. TODO: SETUP_WITH?
1416
byte opcode;

py/vm.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,12 @@ typedef enum {
5151
exc_sp->opcode = op; \
5252
exc_sp->handler = ip + unum; \
5353
exc_sp->val_sp = MP_TAGPTR_MAKE(sp, currently_in_except_block); \
54+
exc_sp->prev_exc = nlr.ret_val; \
5455
currently_in_except_block = 0; /* in a try block now */
5556

5657
#define POP_EXC_BLOCK() \
5758
currently_in_except_block = MP_TAGPTR_TAG(exc_sp->val_sp); /* restore previous state */ \
59+
if (currently_in_except_block) { nlr.ret_val = exc_sp->prev_exc; } \
5860
exc_sp--; /* pop back to previous exception handler */
5961

6062
mp_vm_return_kind_t mp_execute_byte_code(const byte *code, const mp_obj_t *args, uint n_args, const mp_obj_t *args2, uint n_args2, mp_obj_t *ret) {

tests/basics/try-reraise2.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Reraise not the latest occured exception
2+
def f():
3+
try:
4+
raise ValueError("val", 3)
5+
except:
6+
try:
7+
raise TypeError
8+
except:
9+
try:
10+
try:
11+
raise AttributeError
12+
except:
13+
pass
14+
raise
15+
except TypeError:
16+
pass
17+
# This should raise original ValueError, not the most recently occurred AttributeError
18+
raise
19+
20+
try:
21+
f()
22+
except ValueError as e:
23+
print(repr(e))

0 commit comments

Comments
 (0)