Skip to content

Commit

Permalink
implement finally with exception groups
Browse files Browse the repository at this point in the history
  • Loading branch information
cfbolz committed Sep 25, 2024
1 parent a7402e8 commit 33b03a5
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 10 deletions.
30 changes: 21 additions & 9 deletions pypy/interpreter/astcompiler/codegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -963,7 +963,7 @@ def _visit_try_except(self, tr):
self._visit_body(tr.orelse)
self.use_next_block(end)

def _visit_try_finally(self, tr):
def _visit_try_finally(self, tr, has_handlers, body, finalbody):
body = self.new_block()
end = self.new_block()
exit = self.new_block()
Expand All @@ -972,22 +972,26 @@ def _visit_try_finally(self, tr):
self.emit_jump(ops.SETUP_FINALLY, end)
self.use_next_block(body)
self.push_frame_block(F_FINALLY_TRY, body, end, tr)
if tr.handlers:
self._visit_try_except(tr)
if has_handlers:
if isinstance(tr, ast.Try):
self._visit_try_except(tr)
else:
assert isinstance(tr, ast.TryStar)
self._visit_try_except_star(tr)
else:
self._visit_body(tr.body)
self._visit_body(body)
self.no_position_info()
self.emit_op(ops.POP_BLOCK)

# finally block, unexceptional case
self.pop_frame_block(F_FINALLY_TRY, body)
self._visit_body(tr.finalbody)
self._visit_body(finalbody)
self.emit_jump(ops.JUMP_FORWARD, exit)

# finally block, exceptional case
self.use_next_block(end)
self.push_frame_block(F_FINALLY_END, end)
self._visit_body(tr.finalbody)
self._visit_body(finalbody)
self.pop_frame_block(F_FINALLY_END, end)

# the RERAISE will be duplicated by duplicate_exits_without_lineno
Expand All @@ -998,11 +1002,19 @@ def _visit_try_finally(self, tr):

def visit_Try(self, tr):
if tr.finalbody:
return self._visit_try_finally(tr)
return self._visit_try_finally(
tr, tr.handlers is not None, tr.body, tr.finalbody)
else:
return self._visit_try_except(tr)

def visit_TryStar(self, tr):
if tr.finalbody:
return self._visit_try_finally(
tr, tr.handlers is not None, tr.body, tr.finalbody)
else:
return self._visit_try_except_star(tr)

def _visit_try_except_star(self, tr):
"""
Code generated for "try: S except* E1 as V1: S1 except* E2 as V2: S2 ...":
(The contents of the value stack is shown in [], with the top
Expand Down Expand Up @@ -3000,9 +3012,9 @@ def view(startblock):
str_instr = "%5s: %s" % (instr.position_info[0], ops.opname[instr.opcode])
if instr.opcode >= ops.HAVE_ARGUMENT and instr.jump is None:
str_instr += " %s" % (instr.arg, )
if hasattr(instr, '_stack_depth_after'):
if instr._stack_depth_after != -99:
str_instr += " stack depth after: %s" % instr._stack_depth_after
if instr._stack_depth_after < 0 and instr._stack_depth_after != -99:
if instr._stack_depth_after < 0:
fillcolor = "red"
label.append(str_instr)
if instr.jump is not None:
Expand Down
37 changes: 37 additions & 0 deletions pypy/interpreter/astcompiler/test/apptest_exceptiongroup.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,40 @@ def try_except_star_with_else_direct_return(x):
with raises(SyntaxError) as info:
exec(src)
assert str(info.value).startswith(f"'{kw}' cannot appear in an except* block")

def maybe_raise(err):
if err:
raise err


def with_finally(l, err):
try:
maybe_raise(err)
except* TypeError:
l.append(1)
except* ValueError:
l.append(2)
else:
l.append(3)
finally:
l.append(4)

def test_finally():
l = []
with_finally(l, None)
assert l == [3, 4]
l = []
with_finally(l, ValueError())
assert l == [2, 4]
l = []
with_finally(l, TypeError())
assert l == [1, 4]
l = []
with_finally(l, ExceptionGroup('abc', [ValueError(), TypeError()]))
assert l == [1, 2, 4]
with raises(ZeroDivisionError):
l = []
with_finally(l, ZeroDivisionError())
assert l == [4]


4 changes: 3 additions & 1 deletion pypy/interpreter/pyopcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -2167,10 +2167,12 @@ def exception_group_match(space, w_eg, w_typ):
w_list = space.newlist([w_eg])
w_wrapped = space.call_function(w_ExceptionGroup, space.newtext(''), w_list)
return w_wrapped, space.w_None
else:
elif space.isinstance_w(w_eg, w_BaseExceptionGroup):
w_tup = space.call_method(w_eg, 'split', w_typ)
w_match, w_rest = space.unpackiterable(w_tup, 2)
return w_match, w_rest
else:
return space.w_None, space.w_None

### helpers written at the application-level ###
# Some of these functions are expected to be generally useful if other
Expand Down

0 comments on commit 33b03a5

Please sign in to comment.