summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2010-03-05 18:13:47 +0000
committerMike Bayer <mike_mp@zzzcomputing.com>2010-03-05 18:13:47 +0000
commit876c371f4e12abf9f1126420ae261c37f4b09c57 (patch)
treede7eaf944ad198757a90626dc5fb55c7aa78fabe
parenta1d018c13bd498b679e091b3a29c479727a6d937 (diff)
downloadmako-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--CHANGES6
-rw-r--r--mako/codegen.py51
-rw-r--r--test/test_namespace.py94
3 files changed, 119 insertions, 32 deletions
diff --git a/CHANGES b/CHANGES
index 2de917e..06d7643 100644
--- a/CHANGES
+++ b/CHANGES
@@ -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()