diff options
Diffstat (limited to 'mako/codegen.py')
| -rw-r--r-- | mako/codegen.py | 126 |
1 files changed, 99 insertions, 27 deletions
diff --git a/mako/codegen.py b/mako/codegen.py index 2af1eac..5369180 100644 --- a/mako/codegen.py +++ b/mako/codegen.py @@ -9,7 +9,7 @@ import time import re from mako.pygen import PythonPrinter -from mako import util, ast, parsetree, filters +from mako import util, ast, parsetree, filters, exceptions MAGIC_NUMBER = 6 @@ -84,16 +84,18 @@ class _GenerateRenderMethod(object): self.node = node self.identifier_stack = [None] - self.in_def = isinstance(node, parsetree.DefTag) + self.in_def = isinstance(node, (parsetree.DefTag, parsetree.BlockTag)) if self.in_def: - name = "render_" + node.name - args = node.function_decl.get_argument_expressions() + name = "render_%s" % node.funcname + args = node.get_argument_expressions() filtered = len(node.filter_args.args) > 0 buffered = eval(node.attributes.get('buffered', 'False')) cached = eval(node.attributes.get('cached', 'False')) defs = None pagetag = None + if node.is_block and not node.is_anonymous: + args += ['**pageargs'] else: defs = self.write_toplevel() pagetag = self.compiler.pagetag @@ -238,7 +240,7 @@ class _GenerateRenderMethod(object): self.printer.writeline("context._push_buffer()") self.identifier_stack.append(self.compiler.identifiers.branch(self.node)) - if not self.in_def and '**pageargs' in args: + if (not self.in_def or self.node.is_block) and '**pageargs' in args: self.identifier_stack[-1].argument_declared.add('pageargs') if not self.in_def and ( @@ -308,8 +310,19 @@ class _GenerateRenderMethod(object): self.in_def = True class NSDefVisitor(object): def visitDefTag(s, node): + s.visitDefOrBase(node) + + def visitBlockTag(s, node): + s.visitDefOrBase(node) + + def visitDefOrBase(s, node): + if node.is_anonymous: + raise exceptions.CompileException( + "Can't put anonymous blocks inside <%namespace>", + **node.exception_kwargs + ) self.write_inline_def(node, identifiers, nested=False) - export.append(node.name) + export.append(node.funcname) vis = NSDefVisitor() for n in node.nodes: n.accept_visitor(vis) @@ -376,9 +389,8 @@ class _GenerateRenderMethod(object): top-level, it is fully rendered as a local closure. """ - # collection of all defs available to us in this scope - comp_idents = dict([(c.name, c) for c in identifiers.defs]) + comp_idents = dict([(c.funcname, c) for c in identifiers.defs]) to_write = set() # write "context.get()" for all variables we are going to @@ -387,7 +399,7 @@ class _GenerateRenderMethod(object): # write closure functions for closures that we define # right here - to_write = to_write.union([c.name for c in identifiers.closuredefs.values()]) + to_write = to_write.union([c.funcname for c in identifiers.closuredefs.values()]) # remove identifiers that are declared in the argument # signature of the callable @@ -420,10 +432,17 @@ class _GenerateRenderMethod(object): for ident in to_write: if ident in comp_idents: comp = comp_idents[ident] - if comp.is_root(): - self.write_def_decl(comp, identifiers) + if comp.is_block: + if not comp.is_anonymous: + self.write_def_decl(comp, identifiers) + else: + self.write_inline_def(comp, identifiers, nested=True) else: - self.write_inline_def(comp, identifiers, nested=True) + if comp.is_root(): + self.write_def_decl(comp, identifiers) + else: + self.write_inline_def(comp, identifiers, nested=True) + elif ident in self.compiler.namespaces: self.printer.writeline( "%s = _mako_get_namespace(context, %r)" % @@ -472,9 +491,9 @@ class _GenerateRenderMethod(object): def write_def_decl(self, node, identifiers): """write a locally-available callable referencing a top-level def""" - funcname = node.function_decl.funcname - namedecls = node.function_decl.get_argument_expressions() - nameargs = node.function_decl.get_argument_expressions(include_defaults=False) + funcname = node.funcname + namedecls = node.get_argument_expressions() + nameargs = node.get_argument_expressions(include_defaults=False) if not self.in_def and ( len(self.identifiers.locally_assigned) > 0 or @@ -488,13 +507,13 @@ class _GenerateRenderMethod(object): def write_inline_def(self, node, identifiers, nested): """write a locally-available def callable inside an enclosing def.""" - - namedecls = node.function_decl.get_argument_expressions() + + namedecls = node.get_argument_expressions() decorator = node.decorator if decorator: self.printer.writeline("@runtime._decorate_inline(context, %s)" % decorator) - self.printer.writeline("def %s(%s):" % (node.name, ",".join(namedecls))) + self.printer.writeline("def %s(%s):" % (node.funcname, ",".join(namedecls))) filtered = len(node.filter_args.args) > 0 buffered = eval(node.attributes.get('buffered', 'False')) cached = eval(node.attributes.get('cached', 'False')) @@ -519,7 +538,7 @@ class _GenerateRenderMethod(object): self.write_def_finish(node, buffered, filtered, cached) self.printer.writeline(None) if cached: - self.write_cache_decorator(node, node.name, + self.write_cache_decorator(node, node.funcname, namedecls, False, identifiers, inline=True, toplevel=False) @@ -750,6 +769,18 @@ class _GenerateRenderMethod(object): def visitDefTag(self, node): pass + def visitBlockTag(self, node): + if node.is_anonymous: + self.printer.writeline("%s()" % node.funcname) + else: + nameargs = node.get_argument_expressions(include_defaults=False) + nameargs += ['**pageargs'] + self.printer.writeline("if 'parent' not in context._data or " + "not hasattr(context._data['parent'], '%s'):" + % node.funcname) + self.printer.writeline("context['self'].%s(%s)" % (node.funcname, ",".join(nameargs))) + self.printer.writeline("\n") + def visitCallNamespaceTag(self, node): # TODO: we can put namespace-specific checks here, such # as ensure the given namespace will be imported, @@ -770,12 +801,19 @@ class _GenerateRenderMethod(object): self.identifier_stack.append(body_identifiers) class DefVisitor(object): def visitDefTag(s, node): + s.visitDefOrBase(node) + + def visitBlockTag(s, node): + s.visitDefOrBase(node) + + def visitDefOrBase(s, node): self.write_inline_def(node, callable_identifiers, nested=False) - export.append(node.name) + if not node.is_anonymous: + export.append(node.funcname) # remove defs that are within the <%call> from the "closuredefs" defined # in the body, so they dont render twice - if node.name in body_identifiers.closuredefs: - del body_identifiers.closuredefs[node.name] + if node.funcname in body_identifiers.closuredefs: + del body_identifiers.closuredefs[node.funcname] vis = DefVisitor() for n in node.nodes: @@ -934,12 +972,23 @@ class _Identifiers(object): if self.node is node: for n in node.nodes: n.accept_visitor(self) - + + def _check_name_exists(self, collection, node): + existing = collection.get(node.funcname) + collection[node.funcname] = node + if existing is not None and \ + existing is not node and \ + (node.is_block or existing.is_block): + raise exceptions.CompileException( + "%%def or %%block named '%s' already " + "exists in this template." % + node.funcname, **node.exception_kwargs) + def visitDefTag(self, node): - if node.is_root(): - self.topleveldefs[node.name] = node + if node.is_root() and not node.is_anonymous: + self._check_name_exists(self.topleveldefs, node) elif node is not self.node: - self.closuredefs[node.name] = node + self._check_name_exists(self.closuredefs, node) for ident in node.undeclared_identifiers(): if ident != 'context' and ident not in self.declared.union(self.locally_declared): @@ -951,7 +1000,30 @@ class _Identifiers(object): self.argument_declared.add(ident) for n in node.nodes: n.accept_visitor(self) - + + def visitBlockTag(self, node): + if node is not self.node and \ + not node.is_anonymous: + + if isinstance(self.node, parsetree.DefTag): + raise exceptions.CompileException( + "Named block '%s' not allowed inside of def '%s'" + % (node.name, self.node.name), **node.exception_kwargs) + elif isinstance(self.node, (parsetree.CallTag, parsetree.CallNamespaceTag)): + raise exceptions.CompileException( + "Named block '%s' not allowed inside of <%%call> tag" + % (node.name, ), **node.exception_kwargs) + + if not node.is_anonymous: + self._check_name_exists(self.topleveldefs, node) + self.undeclared.add(node.funcname) + elif node is not self.node: + self._check_name_exists(self.closuredefs, node) + for ident in node.declared_identifiers(): + self.argument_declared.add(ident) + for n in node.nodes: + n.accept_visitor(self) + def visitIncludeTag(self, node): self.check_declared(node) |
