Skip to content
Snippets Groups Projects
Commit 1c37cc91 authored by Mike Bayer's avatar Mike Bayer
Browse files

more pythonlike about scope: variable assignment now follows python...

more pythonlike about scope:  variable assignment now follows python conventions (i.e., you lose access to the enclosing scope version when you assign locally)
parent fa0f0380
No related branches found
No related tags found
No related merge requests found
...@@ -100,9 +100,7 @@ class _GenerateRenderMethod(object): ...@@ -100,9 +100,7 @@ class _GenerateRenderMethod(object):
# collection of all components available to us in this scope # collection of all components available to us in this scope
comp_idents = dict([(c.name, c) for c in identifiers.components]) comp_idents = dict([(c.name, c) for c in identifiers.components])
# write explicit "context.get()" statements for variables that are already in the to_write = util.Set()
# local namespace, that we are going to re-assign
to_write = identifiers.declared.intersection(identifiers.locally_declared)
# write "context.get()" for all variables we are going to need that arent in the namespace yet # write "context.get()" for all variables we are going to need that arent in the namespace yet
to_write = to_write.union(identifiers.undeclared) to_write = to_write.union(identifiers.undeclared)
...@@ -113,6 +111,11 @@ class _GenerateRenderMethod(object): ...@@ -113,6 +111,11 @@ class _GenerateRenderMethod(object):
# remove identifiers that are declared in the argument signature of the callable # remove identifiers that are declared in the argument signature of the callable
to_write = to_write.difference(identifiers.argument_declared) to_write = to_write.difference(identifiers.argument_declared)
# remove identifiers that we are going to assign to. in this way we mimic Python's behavior,
# i.e. assignment to a variable within a block means that variable is now a "locally declared" var,
# which cannot be referenced beforehand.
to_write = to_write.difference(identifiers.locally_declared)
for ident in to_write: for ident in to_write:
if ident in comp_idents: if ident in comp_idents:
comp = comp_idents[ident] comp = comp_idents[ident]
...@@ -151,11 +154,11 @@ class _GenerateRenderMethod(object): ...@@ -151,11 +154,11 @@ class _GenerateRenderMethod(object):
# if we assign to variables in this closure, then we have to nest inside # if we assign to variables in this closure, then we have to nest inside
# of another callable so that the "context" variable is copied into the local scope # of another callable so that the "context" variable is copied into the local scope
make_closure = len(identifiers.locally_declared) > 0 #make_closure = len(identifiers.locally_declared) > 0
if make_closure: #if make_closure:
self.printer.writeline("try:") # self.printer.writeline("try:")
self.printer.writeline("context.push()") # self.printer.writeline("context.push()")
self.write_variable_declares(identifiers) self.write_variable_declares(identifiers)
for n in node.nodes: for n in node.nodes:
...@@ -163,11 +166,11 @@ class _GenerateRenderMethod(object): ...@@ -163,11 +166,11 @@ class _GenerateRenderMethod(object):
self.printer.writeline("return ''") self.printer.writeline("return ''")
self.printer.writeline(None) self.printer.writeline(None)
if make_closure: #if make_closure:
self.printer.writeline("finally:") # self.printer.writeline("finally:")
self.printer.writeline("context.pop()") # self.printer.writeline("context.pop()")
self.printer.writeline(None) # self.printer.writeline(None)
self.printer.writeline(None) # self.printer.writeline(None)
def visitExpression(self, node): def visitExpression(self, node):
self.write_source_comment(node) self.write_source_comment(node)
......
...@@ -14,7 +14,6 @@ class ComponentTest(unittest.TestCase): ...@@ -14,7 +14,6 @@ class ComponentTest(unittest.TestCase):
</%component> </%component>
""") """)
print template.code
assert template.render(variable='hi').strip() == """hello mycomp hi""" assert template.render(variable='hi').strip() == """hello mycomp hi"""
def test_component_blankargs(self): def test_component_blankargs(self):
...@@ -89,28 +88,11 @@ class ScopeTest(unittest.TestCase): ...@@ -89,28 +88,11 @@ class ScopeTest(unittest.TestCase):
y is ${y} y is ${y}
""") """)
assert flatten_result(t.render(y=None)) == "y is None y is 7" try:
t.render(y=None)
def test_scope_three(self): assert False
"""test in place assignment/undeclared variable combinations except UnboundLocalError:
assert True
i.e. things like 'x=x+1', x is declared and undeclared at the same time"""
template = Template("""
hi
<%component name="a">
y is ${y}
<%
x = x + y
a = 3
%>
x is ${x}
</%component>
${a()}
""")
assert flatten_result(template.render(x=5, y=10)) == "hi y is 10 x is 15"
def test_scope_four(self): def test_scope_four(self):
"""test that variables are pulled from 'enclosing' scope before context.""" """test that variables are pulled from 'enclosing' scope before context."""
...@@ -132,8 +114,6 @@ class ScopeTest(unittest.TestCase): ...@@ -132,8 +114,6 @@ class ScopeTest(unittest.TestCase):
${b()} ${b()}
""") """)
print t.code
print t.render()
assert flatten_result(t.render()) == "this is b. x is 9. calling a. this is a. x is 5." assert flatten_result(t.render()) == "this is b. x is 9. calling a. this is a. x is 5."
def test_scope_five(self): def test_scope_five(self):
...@@ -160,8 +140,6 @@ class ScopeTest(unittest.TestCase): ...@@ -160,8 +140,6 @@ class ScopeTest(unittest.TestCase):
</%component> </%component>
${enclosing()} ${enclosing()}
""") """)
print t.code
print t.render()
assert flatten_result(t.render()) == "this is b. x is 9. calling a. this is a. x is 5." assert flatten_result(t.render()) == "this is b. x is 9. calling a. this is a. x is 5."
def test_scope_six(self): def test_scope_six(self):
...@@ -248,6 +226,80 @@ class ScopeTest(unittest.TestCase): ...@@ -248,6 +226,80 @@ class ScopeTest(unittest.TestCase):
assert flatten_result(tmpl['main'].render(x=2)) == "this is main. this is secondary. x is 2" assert flatten_result(tmpl['main'].render(x=2)) == "this is main. this is secondary. x is 2"
def test_scope_ten(self):
t = Template("""
<%component name="a">
<%component name="b">
<%
y = 19
%>
b/c: ${c()}
b/y: ${y}
</%component>
<%component name="c">
c/y: ${y}
</%component>
<%
# we assign to "y". but the 'enclosing scope' of "b" and "c" is from the "y" on the outside
y = 10
%>
a/y: ${y}
a/b: ${b()}
</%component>
<%
y = 7
%>
main/a: ${a()}
main/y: ${y}
""")
assert flatten_result(t.render()) == "main/a: a/y: 10 a/b: b/c: c/y: 10 b/y: 19 main/y: 7"
def test_unbound_scope(self):
t = Template("""
<%
y = 10
%>
<%component name="a">
y is: ${y}
<%
# should raise error ?
y = 15
%>
y is ${y}
</%component>
${a()}
""")
try:
print t.render()
assert False
except UnboundLocalError:
assert True
def test_unbound_scope_two(self):
t = Template("""
<%component name="enclosing">
<%
y = 10
%>
<%component name="a">
y is: ${y}
<%
# should raise error ?
y = 15
%>
y is ${y}
</%component>
${a()}
</%component>
${enclosing()}
""")
try:
print t.render()
assert False
except UnboundLocalError:
assert True
class NestedComponentTest(unittest.TestCase): class NestedComponentTest(unittest.TestCase):
def test_nested_component(self): def test_nested_component(self):
...@@ -332,8 +384,7 @@ class NestedComponentTest(unittest.TestCase): ...@@ -332,8 +384,7 @@ class NestedComponentTest(unittest.TestCase):
${b1()} ${b2()} ${b3()} ${b1()} ${b2()} ${b3()}
</%component> </%component>
""") """)
print t.code assert flatten_result(t.render(x=5, y=None)) == "a a_b1 a_b2 a_b2_c1 a_b3 a_b3_c1 heres x: 5 y is 7 a_b3_c2 y is None c1 is a_b3_c1 heres x: 5 y is 7"
assert flatten_result(t.render(x=5)) == "a a_b1 a_b2 a_b2_c1 a_b3 a_b3_c1 heres x: 5 y is 7 a_b3_c2 y is None c1 is a_b3_c1 heres x: 5 y is 7"
def test_nested_nested_component_2(self): def test_nested_nested_component_2(self):
t = Template(""" t = Template("""
...@@ -352,85 +403,6 @@ class NestedComponentTest(unittest.TestCase): ...@@ -352,85 +403,6 @@ class NestedComponentTest(unittest.TestCase):
""" ) """ )
assert flatten_result(t.render()) == "this is a this is b this is c" assert flatten_result(t.render()) == "this is a this is b this is c"
def test_scope_ten(self):
t = Template("""
main/y: ${y}
<%component name="a">
<%component name="b">
b/y: ${y}
<%
y = 19
%>
b/c: ${c()}
b/y: ${y}
</%component>
a/y: ${y}
<%
y = 10
x = 12
%>
a/y: ${y}
a/b: ${b()}
<%component name="c">
c/y: ${y}
</%component>
</%component>
<%
y = 7
%>
main/y: ${y}
main/a: ${a}
main/y: ${y}
""")
def test_local_local_names(self):
"""test assignment of variables inside nested components, which requires extra scoping logic"""
t = Template("""
heres y: ${y}
<%component name="a">
<%component name="b">
b, heres y: ${y}
<%
y = 19
%>
b, heres c: ${c()}
b, heres y again: ${y}
</%component>
a, heres y: ${y}
<%
y = 10
x = 12
%>
a, now heres y: ${y}
a, heres b: ${b()}
<%component name="c">
this is c
</%component>
</%component>
<%
y = 7
%>
now heres y ${y}
${a()}
heres y again: ${y}
""")
print t.code
print flatten_result(t.render(y=5))
assert flatten_result(t.render(y=5)) == "heres y: 5 now heres y 7 a, heres y: 7 a, now heres y: 10 a, heres b: b, heres y: 10 b, heres c: this is c b, heres y again: 19 heres y again: 7"
def test_outer_scope(self): def test_outer_scope(self):
t = Template(""" t = Template("""
<%component name="a"> <%component name="a">
...@@ -452,7 +424,7 @@ class NestedComponentTest(unittest.TestCase): ...@@ -452,7 +424,7 @@ class NestedComponentTest(unittest.TestCase):
x is ${x} x is ${x}
""") """)
assert flatten_result(t.render(x=5)) == "b. c. x is 10. a: x is 10 x is 5" assert flatten_result(t.render(x=5)) == "b. c. x is 10. a: x is 5 x is 5"
class ExceptionTest(unittest.TestCase): class ExceptionTest(unittest.TestCase):
def test_raise(self): def test_raise(self):
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment