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

- fix to code generation to correctly track multiple defs with the same name.

this is implemented by changing the "topleveldefs" and "closuredefs" collections
from a Set to a dictionary.  a unit test was added with alternate set-ordering
as the original issue only appeared on linux to start.
- "backslash" -> "slash" in syntax doc
parent b71033f5
No related branches found
No related tags found
No related merge requests found
......@@ -7,6 +7,7 @@ cant handle those files, setuptools not very good at "pruning" certain directori
occurs before filtering
- better error message when a lookup is attempted with a template that has no lookup
- implemented "module" attribute for namespace
- fix to code generation to correctly track multiple defs with the same name
0.1.0
......
......@@ -96,7 +96,7 @@ Any number of `<%! %>` blocks can be declared anywhere in a template; they will
### Tags
The rest of what Mako offers takes place in the form of tags. All tags use the same syntax, which is similar to an XML tag except that the first character of the tag name is a `%` character. The tag is closed either by a contained backslash character, or an explicit closing tag:
The rest of what Mako offers takes place in the form of tags. All tags use the same syntax, which is similar to an XML tag except that the first character of the tag name is a `%` character. The tag is closed either by a contained slash character, or an explicit closing tag:
<%include file="foo.txt"/>
......
......@@ -13,6 +13,7 @@ from mako import util, ast, parsetree, filters
MAGIC_NUMBER = 1
def compile(node, uri, filename=None):
"""generate module source code given a parsetree node, uri, and optional source filename"""
buf = util.FastEncodingBuffer()
......@@ -117,7 +118,7 @@ class _GenerateRenderMethod(object):
module_identifiers.topleveldefs = module_identifiers.topleveldefs.union(main_identifiers.topleveldefs)
[module_identifiers.declared.add(x) for x in ["UNDEFINED"]]
self.compiler.identifiers = module_identifiers
self.printer.writeline("_exports = %s" % repr([n.name for n in main_identifiers.topleveldefs]))
self.printer.writeline("_exports = %s" % repr([n.name for n in main_identifiers.topleveldefs.values()]))
self.printer.write("\n\n")
if len(module_code):
......@@ -129,7 +130,7 @@ class _GenerateRenderMethod(object):
elif len(namespaces):
self.write_namespaces(namespaces)
return main_identifiers.topleveldefs
return main_identifiers.topleveldefs.values()
def write_render_callable(self, node, name, args, buffered, filtered, cached):
"""write a top-level render callable.
......@@ -228,14 +229,13 @@ class _GenerateRenderMethod(object):
# collection of all defs available to us in this scope
comp_idents = dict([(c.name, c) for c in identifiers.defs])
to_write = util.Set()
# write "context.get()" for all variables we are going to need that arent in the namespace yet
to_write = to_write.union(identifiers.undeclared)
# write closure functions for closures that we define right here
to_write = to_write.union(util.Set([c.name for c in identifiers.closuredefs]))
to_write = to_write.union(util.Set([c.name for c in identifiers.closuredefs.values()]))
# remove identifiers that are declared in the argument signature of the callable
to_write = to_write.difference(identifiers.argument_declared)
......@@ -462,6 +462,9 @@ class _GenerateRenderMethod(object):
def visitDefTag(s, node):
self.write_inline_def(node, callable_identifiers, nested=False)
export.append(node.name)
# remove defs that are within the <%call> from the "closuredefs" defined
# in the body, so they dont render twice
del body_identifiers.closuredefs[node.name]
vis = DefVisitor()
for n in node.nodes:
n.accept_visitor(vis)
......@@ -507,7 +510,7 @@ class _Identifiers(object):
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 = util.Set(parent.declared).union([c.name for c in parent.closuredefs]).union(parent.locally_declared).union(parent.argument_declared)
self.declared = util.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,
......@@ -516,10 +519,10 @@ class _Identifiers(object):
self.declared = self.declared.union(parent.undeclared)
# top level defs that are available
self.topleveldefs = util.Set(parent.topleveldefs)
self.topleveldefs = util.SetLikeDict(**parent.topleveldefs)
else:
self.declared = util.Set()
self.topleveldefs = util.Set()
self.topleveldefs = util.SetLikeDict()
# things within this level that are referenced before they are declared (e.g. assigned to)
self.undeclared = util.Set()
......@@ -536,7 +539,7 @@ class _Identifiers(object):
self.argument_declared = util.Set()
# closure defs that are defined in this level
self.closuredefs = util.Set()
self.closuredefs = util.SetLikeDict()
self.node = node
......@@ -546,11 +549,11 @@ class _Identifiers(object):
def branch(self, node, **kwargs):
"""create a new Identifiers for a new Node, with this Identifiers as the parent."""
return _Identifiers(node, self, **kwargs)
defs = property(lambda s:s.topleveldefs.union(s.closuredefs))
defs = property(lambda self:util.Set(self.topleveldefs.union(self.closuredefs).values()))
def __repr__(self):
return "Identifiers(declared=%s, locally_declared=%s, undeclared=%s, topleveldefs=%s, closuredefs=%s, argumenetdeclared=%s)" % (repr(list(self.declared)), repr(list(self.locally_declared)), repr(list(self.undeclared)), repr([c.name for c in self.topleveldefs]), repr([c.name for c in self.closuredefs]), repr(self.argument_declared))
return "Identifiers(declared=%s, locally_declared=%s, undeclared=%s, topleveldefs=%s, closuredefs=%s, argumenetdeclared=%s)" % (repr(list(self.declared)), repr(list(self.locally_declared)), repr(list(self.undeclared)), repr([c.name for c in self.topleveldefs.values()]), repr([c.name for c in self.closuredefs.values()]), repr(self.argument_declared))
def check_declared(self, node):
"""update the state of this Identifiers with the undeclared and declared identifiers of the given node."""
......@@ -575,9 +578,9 @@ class _Identifiers(object):
self.locally_assigned = self.locally_assigned.union(node.declared_identifiers())
def visitDefTag(self, node):
if node.is_root():
self.topleveldefs.add(node)
self.topleveldefs[node.name] = node
elif node is not self.node:
self.closuredefs.add(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)
......
......@@ -34,7 +34,17 @@ def verify_directory(dir):
except:
if tries > 5:
raise
class SetLikeDict(dict):
"""a dictionary that has some setlike methods on it"""
def union(self, other):
"""produce a 'union' of this dict and another (at the key level).
values in the second dict take precedence over that of the first"""
x = SetLikeDict(**self)
x.update(other)
return x
class FastEncodingBuffer(object):
"""a very rudimentary buffer that is faster than StringIO, but doesnt crash on unicode data like cStringIO."""
def __init__(self, encoding=None):
......
from mako.template import Template
from mako import util
import unittest
from util import result_lines
......@@ -41,9 +42,23 @@ class CallTest(unittest.TestCase):
${bar()}
""")
assert result_lines(t.render()) == ['foo calling comp1:', 'this is comp1, 5', 'foo calling body:', 'this is the body,', 'this is comp1, 6', 'this is bar']
def test_compound_call_revset(self):
# monkeypatch Set to return items in reverse
oldset = util.Set
class goofyset(oldset):
def __iter__(self):
x = list(oldset.__iter__(self))
x.reverse()
return iter(x)
util.Set = goofyset
try:
self.test_compound_call()
finally:
util.Set = oldset
def test_chained_call(self):
"""test %calls that are chained through their targets"""
t = Template("""
......
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