Skip to content

Commit 79a3dea

Browse files
committed
Fix globals scanning for exception handlers
1 parent 8054149 commit 79a3dea

File tree

2 files changed

+39
-1
lines changed

2 files changed

+39
-1
lines changed

lib/pythonx/ast/scan_globals.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,25 @@ def __init__(self):
99
self.scopes = []
1010
# Add the global scope to the stack.
1111
self._push_scope()
12+
# Scope information specific to exception handlers.
13+
self.exception_handler_scopes = []
1214

1315
def _push_scope(self, is_comprehension=False):
1416
self.scopes.append({"defs": set(), "is_comprehension": is_comprehension})
1517

1618
def _pop_scope(self):
1719
self.scopes.pop()
1820

21+
def _push_exception_handler_scope(self):
22+
self.exception_handler_scopes.append({"defs": set()})
23+
24+
def _pop_exception_handler_scope(self):
25+
self.exception_handler_scopes.pop()
26+
1927
def _handle_ref(self, name):
20-
is_defined = any(name in scope["defs"] for scope in self.scopes)
28+
is_defined = any(name in scope["defs"] for scope in self.scopes) or any(
29+
name in scope["defs"] for scope in self.exception_handler_scopes
30+
)
2131

2232
if not is_defined:
2333
self.refs.add(name)
@@ -164,6 +174,15 @@ def visit_AnnAssign(self, node):
164174

165175
self.visit(node.target)
166176

177+
def visit_ExceptHandler(self, node):
178+
# Exception handlers define local variables available only within
179+
# the handler, however the handler body is still within the outer
180+
# scope, so we add a special ref-only scope.
181+
self._push_exception_handler_scope()
182+
self.exception_handler_scopes[-1]["defs"].add(node.name)
183+
self.generic_visit(node)
184+
self._pop_exception_handler_scope()
185+
167186
def visit_MatchAs(self, node):
168187
if node.name is not None:
169188
self.scopes[-1]["defs"].add(node.name)

test/pythonx/ast_test.exs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,25 @@ defmodule Pythonx.ASTTest do
200200
}
201201
end
202202

203+
test "try" do
204+
assert Pythonx.AST.scan_globals("""
205+
try:
206+
raise RuntimeError("error")
207+
except RuntimeError as e:
208+
x = e
209+
""") == %{referenced: MapSet.new([]), defined: MapSet.new(["x"])}
210+
211+
assert Pythonx.AST.scan_globals("""
212+
try:
213+
raise RuntimeError("error")
214+
except RuntimeError as e1:
215+
try:
216+
raise RuntimeError("error")
217+
except RuntimeError as e2:
218+
x = (e1, e2)
219+
""") == %{referenced: MapSet.new([]), defined: MapSet.new(["x"])}
220+
end
221+
203222
test "global in top-level expression" do
204223
assert Pythonx.AST.scan_globals("""
205224
x + 1

0 commit comments

Comments
 (0)