Skip to content

Commit

Permalink
- [bug] Fixed some long-broken scoping behavior
Browse files Browse the repository at this point in the history
  involving variables declared in defs and such,
  which only became apparent when
  the strict_undefined flag was turned on.
  [ticket:192]
  • Loading branch information
zzzeek committed Jul 8, 2012
1 parent e9a1fd8 commit 91b7e5f
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 22 deletions.
6 changes: 6 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@
when no statements are otherwise present.
Courtesy Ben Trofatter [ticket:146]

- [bug] Fixed some long-broken scoping behavior
involving variables declared in defs and such,
which only became apparent when
the strict_undefined flag was turned on.
[ticket:192]

- [bug] Can now use strict_undefined at the
same time args passed to def() are used
by other elements of the <%def> tag.
Expand Down
44 changes: 22 additions & 22 deletions mako/pyparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,15 @@ class FindIdentifiers(_ast_util.NodeVisitor):
def __init__(self, listener, **exception_kwargs):
self.in_function = False
self.in_assign_targets = False
self.local_ident_stack = {}
self.local_ident_stack = set()
self.listener = listener
self.exception_kwargs = exception_kwargs

def _add_declared(self, name):
if not self.in_function:
self.listener.declared_identifiers.add(name)
else:
self.local_ident_stack.add(name)

def visit_ClassDef(self, node):
self._add_declared(node.name)
Expand Down Expand Up @@ -118,23 +120,20 @@ def _visit_function(self, node, islambda):
# argument names in each function header so they arent
# counted as "undeclared"

saved = {}
inf = self.in_function
self.in_function = True
for arg in node.args.args:
if arg_id(arg) in self.local_ident_stack:
saved[arg_id(arg)] = True
else:
self.local_ident_stack[arg_id(arg)] = True

local_ident_stack = self.local_ident_stack
self.local_ident_stack = local_ident_stack.union([
arg_id(arg) for arg in node.args.args
])
if islambda:
self.visit(node.body)
else:
for n in node.body:
self.visit(n)
self.in_function = inf
for arg in node.args.args:
if arg_id(arg) not in saved:
del self.local_ident_stack[arg_id(arg)]
self.local_ident_stack = local_ident_stack

def visit_For(self, node):

Expand All @@ -149,8 +148,10 @@ def visit_For(self, node):

def visit_Name(self, node):
if isinstance(node.ctx, _ast.Store):
# this is eqiuvalent to visit_AssName in
# compiler
self._add_declared(node.id)
if node.id not in reserved and node.id \
elif node.id not in reserved and node.id \
not in self.listener.declared_identifiers and node.id \
not in self.local_ident_stack:
self.listener.undeclared_identifiers.add(node.id)
Expand Down Expand Up @@ -228,13 +229,15 @@ class FindIdentifiers(object):

def __init__(self, listener, **exception_kwargs):
self.in_function = False
self.local_ident_stack = {}
self.local_ident_stack = set()
self.listener = listener
self.exception_kwargs = exception_kwargs

def _add_declared(self, name):
if not self.in_function:
self.listener.declared_identifiers.add(name)
else:
self.local_ident_stack.add(name)

def visitClass(self, node, *args):
self._add_declared(node.name)
Expand All @@ -247,7 +250,6 @@ def visitAssign(self, node, *args):
# flip around the visiting of Assign so the expression gets
# evaluated first, in the case of a clause like "x=x+5" (x
# is undeclared)

self.visit(node.expr, *args)
for n in node.nodes:
self.visit(n, *args)
Expand All @@ -267,20 +269,18 @@ def _visit_function(self, node, args):
# argument names in each function header so they arent
# counted as "undeclared"

saved = {}
inf = self.in_function
self.in_function = True
for arg in node.argnames:
if arg in self.local_ident_stack:
saved[arg] = True
else:
self.local_ident_stack[arg] = True

local_ident_stack = self.local_ident_stack
self.local_ident_stack = local_ident_stack.union([
arg_id(arg) for arg in node.argnames
])

for n in node.getChildNodes():
self.visit(n, *args)
self.in_function = inf
for arg in node.argnames:
if arg not in saved:
del self.local_ident_stack[arg]
self.local_ident_stack = local_ident_stack

def visitFor(self, node, *args):

Expand Down
34 changes: 34 additions & 0 deletions test/test_ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,40 @@ def x(q):
eq_(parsed.undeclared_identifiers, set())


def test_locate_identifiers_12(self):
code = """
def foo():
s = 1
def bar():
t = s
"""
parsed = ast.PythonCode(code, **exception_kwargs)
eq_(parsed.declared_identifiers, set(['foo']))
eq_(parsed.undeclared_identifiers, set())

def test_locate_identifiers_13(self):
code = """
def foo():
class Bat(object):
pass
Bat
"""
parsed = ast.PythonCode(code, **exception_kwargs)
eq_(parsed.declared_identifiers, set(['foo']))
eq_(parsed.undeclared_identifiers, set())

def test_locate_identifiers_14(self):
code = """
def foo():
class Bat(object):
pass
Bat
print(Bat)
"""
parsed = ast.PythonCode(code, **exception_kwargs)
eq_(parsed.declared_identifiers, set(['foo']))
eq_(parsed.undeclared_identifiers, set(['Bat']))

def test_no_global_imports(self):
code = """
Expand Down

0 comments on commit 91b7e5f

Please sign in to comment.