diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2010-03-05 18:13:47 +0000 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2010-03-05 18:13:47 +0000 |
commit | 876c371f4e12abf9f1126420ae261c37f4b09c57 (patch) | |
tree | de7eaf944ad198757a90626dc5fb55c7aa78fabe | |
parent | a1d018c13bd498b679e091b3a29c479727a6d937 (diff) | |
download | mako-876c371f4e12abf9f1126420ae261c37f4b09c57.tar.gz |
- defs declared within a <%namespace> section, an
uncommon feature, have been improved. The defs
no longer get doubly-rendered in the body() scope,
and now allow local variable assignment without
breakage. [ticket:109]
-rw-r--r-- | CHANGES | 6 | ||||
-rw-r--r-- | mako/codegen.py | 51 | ||||
-rw-r--r-- | test/test_namespace.py | 94 |
3 files changed, 119 insertions, 32 deletions
@@ -38,6 +38,12 @@ arguments are passed down through the inherits chain. Undeclared arguments go into **pageargs as usual. [ticket:116] + +- defs declared within a <%namespace> section, an + uncommon feature, have been improved. The defs + no longer get doubly-rendered in the body() scope, + and now allow local variable assignment without + breakage. [ticket:109] 0.2.6 diff --git a/mako/codegen.py b/mako/codegen.py index 71a608f..54e6015 100644 --- a/mako/codegen.py +++ b/mako/codegen.py @@ -287,6 +287,8 @@ class _GenerateRenderMethod(object): None,None ) self.printer.writeline("def _mako_generate_namespaces(context):") + + for node in namespaces.values(): if node.attributes.has_key('import'): self.compiler.has_ns_imports = True @@ -295,6 +297,7 @@ class _GenerateRenderMethod(object): self.printer.writeline("def make_namespace():") export = [] identifiers = self.compiler.identifiers.branch(node) + self.in_def = True class NSDefVisitor(object): def visitDefTag(s, node): self.write_inline_def(node, identifiers, nested=False) @@ -304,6 +307,7 @@ class _GenerateRenderMethod(object): n.accept_visitor(vis) self.printer.writeline("return [%s]" % (','.join(export))) self.printer.writeline(None) + self.in_def = False callable_name = "make_namespace()" else: callable_name = "None" @@ -766,23 +770,30 @@ class _Identifiers(object): """tracks the status of identifier names as template code is rendered.""" def __init__(self, node=None, parent=None, nested=False): + if parent is not None: - # things that have already been declared - # in an enclosing namespace (i.e. names we can just use) - self.declared = set(parent.declared).\ - union([c.name for c in parent.closuredefs.values()]).\ - union(parent.locally_declared).\ - union(parent.argument_declared) + # if we are the branch created in write_namespaces(), + # we don't share any context from the main body(). + if isinstance(node, parsetree.NamespaceTag): + self.declared = set() + self.topleveldefs = util.SetLikeDict() + else: + # things that have already been declared + # in an enclosing namespace (i.e. names we can just use) + self.declared = set(parent.declared).\ + union([c.name for c in parent.closuredefs.values()]).\ + union(parent.locally_declared).\ + union(parent.argument_declared) - # if these identifiers correspond to a "nested" - # scope, it means whatever the parent identifiers - # had as undeclared will have been declared by that parent, - # and therefore we have them in our scope. - if nested: - self.declared = self.declared.union(parent.undeclared) + # if these identifiers correspond to a "nested" + # scope, it means whatever the parent identifiers + # had as undeclared will have been declared by that parent, + # and therefore we have them in our scope. + if nested: + self.declared = self.declared.union(parent.undeclared) - # top level defs that are available - self.topleveldefs = util.SetLikeDict(**parent.topleveldefs) + # top level defs that are available + self.topleveldefs = util.SetLikeDict(**parent.topleveldefs) else: self.declared = set() self.topleveldefs = util.SetLikeDict() @@ -837,7 +848,7 @@ class _Identifiers(object): def check_declared(self, node): """update the state of this Identifiers with the undeclared and declared identifiers of the given node.""" - + for ident in node.undeclared_identifiers(): if ident != 'context' and ident not in self.declared.union(self.locally_declared): self.undeclared.add(ident) @@ -859,15 +870,25 @@ class _Identifiers(object): if not node.ismodule: self.check_declared(node) self.locally_assigned = self.locally_assigned.union(node.declared_identifiers()) + + def visitNamespaceTag(self, node): + # only traverse into the sub-elements of a + # <%namespace> tag if we are the branch created in + # write_namespaces() + if self.node is node: + for n in node.nodes: + n.accept_visitor(self) def visitDefTag(self, node): if node.is_root(): self.topleveldefs[node.name] = node elif node is not self.node: self.closuredefs[node.name] = node + for ident in node.undeclared_identifiers(): if ident != 'context' and ident not in self.declared.union(self.locally_declared): self.undeclared.add(ident) + # visit defs only one level deep if node is self.node: for ident in node.declared_identifiers(): diff --git a/test/test_namespace.py b/test/test_namespace.py index 6351caa..1600ca9 100644 --- a/test/test_namespace.py +++ b/test/test_namespace.py @@ -1,25 +1,85 @@ from mako.template import Template from mako import lookup from util import flatten_result, result_lines -import unittest - -class NamespaceTest(unittest.TestCase): - def test_inline(self): - t = Template(""" - <%namespace name="x"> - <%def name="a()"> - this is x a - </%def> - <%def name="b()"> - this is x b, and heres ${a()} - </%def> - </%namespace> +from test import TemplateTest + +class NamespaceTest(TemplateTest): + def test_inline_crossreference(self): + self._do_memory_test( + """ + <%namespace name="x"> + <%def name="a()"> + this is x a + </%def> + <%def name="b()"> + this is x b, and heres ${a()} + </%def> + </%namespace> - ${x.a()} + ${x.a()} - ${x.b()} -""") - assert flatten_result(t.render()) == "this is x a this is x b, and heres this is x a" + ${x.b()} + """, + "this is x a this is x b, and heres this is x a", + filters=flatten_result + ) + + def test_inline_assignment(self): + self._do_memory_test( + """ + <%namespace name="x"> + <%def name="a()"> + <% + x = 5 + %> + this is x: ${x} + </%def> + </%namespace> + + ${x.a()} + + """, + "this is x: 5", + filters=flatten_result + ) + + def test_inline_arguments(self): + self._do_memory_test( + """ + <%namespace name="x"> + <%def name="a(x, y)"> + <% + result = x * y + %> + result: ${result} + </%def> + </%namespace> + + ${x.a(5, 10)} + + """, + "result: 50", + filters=flatten_result + ) + + def test_inline_not_duped(self): + self._do_memory_test( + """ + <%namespace name="x"> + <%def name="a()"> + foo + </%def> + </%namespace> + + <% + assert x.a is not UNDEFINED, "namespace x.a wasn't defined" + assert a is UNDEFINED, "name 'a' is in the body locals" + %> + + """, + "", + filters=flatten_result + ) def test_template(self): collection = lookup.TemplateLookup() |