namespace model moved around to be more module-level, called from a variety of codepaths into the module (main render, inherit only, def call only)
diff --git a/lib/mako/codegen.py b/lib/mako/codegen.py index 7095627..d373545 100644 --- a/lib/mako/codegen.py +++ b/lib/mako/codegen.py
@@ -21,53 +21,18 @@ buf = util.FastEncodingBuffer() printer = PythonPrinter(buf) - module_code = [] - class FindPyDecls(object): - def visitCode(self, node): - if node.ismodule: - module_code.append(node) - f = FindPyDecls() - self.node.accept_visitor(f) - - module_identifiers = _Identifiers() - for n in module_code: - module_identifiers = module_identifiers.branch(n) + _GenerateRenderMethod(printer, self, self.node) - main_identifiers = module_identifiers.branch(self.node) - module_identifiers.topleveldefs = module_identifiers.topleveldefs.union(main_identifiers.topleveldefs) - - # module-level names, python code - printer.writeline("from mako import runtime") - printer.writeline("_magic_number = %s" % repr(MAGIC_NUMBER)) - printer.writeline("_modified_time = %s" % repr(time.time())) - printer.writeline("_template_filename=%s" % repr(self.filename)) - printer.writeline("UNDEFINED = runtime.UNDEFINED") - printer.writeline("from mako import filters") - [module_identifiers.declared.add(x) for x in ["UNDEFINED"]] - printer.writeline("_exports = %s" % repr([n.name for n in main_identifiers.topleveldefs])) - printer.write("\n\n") - - - for n in module_code: - printer.writeline("# SOURCE LINE %d" % n.lineno, is_comment=True) - printer.write_indented_block(n.text) - - # print main render() method - _GenerateRenderMethod(printer, module_identifiers, self.node) - - # print render() for each top-level def - for node in main_identifiers.topleveldefs: - _GenerateRenderMethod(printer, module_identifiers, node) - return buf.getvalue() class _GenerateRenderMethod(object): - def __init__(self, printer, identifiers, node): + def __init__(self, printer, compiler, node): self.printer = printer self.last_source_line = -1 - + self.compiler = compiler self.node = node + if isinstance(node, parsetree.DefTag): name = "render_" + node.name args = node.function_decl.get_argument_expressions() @@ -86,36 +51,132 @@ args = [a for a in ['context'] + args + ['**kwargs']] if not self.in_def: - self._inherit() - - printer.writeline("def %s(%s):" % (name, ','.join(args))) - if buffered or filtered: - printer.writeline("context.push_buffer()") - printer.writeline("try:") + defs = self.write_toplevel() + else: + defs = None - self.identifiers = identifiers.branch(node) - if len(self.identifiers.locally_assigned) > 0: - printer.writeline("__locals = {}") + self.write_render_callable(name, args, buffered, filtered) + + if defs is not None: + for node in defs: + _GenerateRenderMethod(printer, compiler, node) + + def write_toplevel(self): + inherit = [] + namespaces = {} + module_code = [] + class FindTopLevel(object): + def visitInheritTag(s, node): + inherit.append(node) + def visitNamespaceTag(self, node): + namespaces[node.name] = node + def visitCode(self, node): + if node.ismodule: + module_code.append(node) + f = FindTopLevel() + for n in self.node.nodes: + n.accept_visitor(f) + if len(module_code): + self.write_module_code(module_code) + + self.compiler.namespaces = namespaces + + module_identifiers = _Identifiers() + for n in module_code: + module_identifiers = module_identifiers.branch(n) + + # module-level names, python code + self.printer.writeline("from mako import runtime") + self.printer.writeline("_magic_number = %s" % repr(MAGIC_NUMBER)) + self.printer.writeline("_modified_time = %s" % repr(time.time())) + self.printer.writeline("_template_filename=%s" % repr(self.compiler.filename)) + self.printer.writeline("UNDEFINED = runtime.UNDEFINED") + self.printer.writeline("from mako import filters") + + main_identifiers = module_identifiers.branch(self.node) + 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.write("\n\n") + + if len(inherit): + self.write_namespaces(namespaces) + self.write_inherit(inherit[-1]) + elif len(namespaces): + self.write_namespaces(namespaces) + + return main_identifiers.topleveldefs + + def write_render_callable(self, name, args, buffered, filtered): + self.printer.writeline("def %s(%s):" % (name, ','.join(args))) + if buffered or filtered: + self.printer.writeline("context.push_buffer()") + self.printer.writeline("try:") + + self.identifiers = self.compiler.identifiers.branch(self.node) + if not self.in_def and len(self.identifiers.locally_assigned) > 0: + self.printer.writeline("__locals = {}") self.write_variable_declares(self.identifiers, first="kwargs") - for n in node.nodes: + for n in self.node.nodes: n.accept_visitor(self) - self.write_def_finish(node, buffered, filtered) - printer.writeline(None) - printer.write("\n\n") + self.write_def_finish(self.node, buffered, filtered) + self.printer.writeline(None) + self.printer.write("\n\n") - def _inherit(self): - class FindInherit(object): - def visitInheritTag(s, node): - self.printer.writeline("def _inherit(context):") - self.printer.writeline("return runtime.inherit_from(context, %s)" % (repr(node.attributes['file']))) + + def write_module_code(self, module_code): + for n in module_code: + self.write_source_comment(n) + self.printer.write_indented_block(n.text) + + def write_inherit(self, node): + self.printer.writeline("def _inherit(context):") + self.printer.writeline("generate_namespaces(context)") + self.printer.writeline("return runtime.inherit_from(context, %s)" % (repr(node.attributes['file']))) + self.printer.writeline(None) + + def write_namespaces(self, namespaces): + self.printer.writelines( + "def get_namespace(context, name):", + "try:", + "return context.namespaces[(render, name)]", + "except KeyError:", + "generate_namespaces(context)", + "return context.namespaces[(render, name)]", + None,None + ) + self.printer.writeline("def generate_namespaces(context):") + for node in namespaces.values(): + self.write_source_comment(node) + if len(node.nodes): + self.printer.writeline("def make_namespace():") + export = [] + identifiers = self.compiler.identifiers.branch(node) + class NSDefVisitor(object): + def visitDefTag(s, node): + self.write_inline_def(node, identifiers) + export.append(node.name) + vis = NSDefVisitor() + for n in node.nodes: + n.accept_visitor(vis) + self.printer.writeline("return [%s]" % (','.join(export))) self.printer.writeline(None) - f = FindInherit() - for n in self.node.nodes: - n.accept_visitor(f) - + callable_name = "make_namespace()" + else: + callable_name = "None" + self.printer.writeline("ns = runtime.Namespace(%s, context.clean_inheritance_tokens(), templateuri=%s, callables=%s)" % (repr(node.name), node.parsed_attributes.get('file', 'None'), callable_name)) + if eval(node.attributes.get('inheritable', "False")): + self.printer.writeline("context['self'].%s = ns" % (node.name)) + self.printer.writeline("context.namespaces[(render, %s)] = ns" % repr(node.name)) + self.printer.write("\n") + if not len(namespaces): + self.printer.writeline("pass") + self.printer.writeline(None) + def write_variable_declares(self, identifiers, first=None): """write variable declarations at the top of a function. @@ -155,6 +216,8 @@ self.write_def_decl(comp, identifiers) else: self.write_inline_def(comp, identifiers) + elif ident in self.compiler.namespaces: + self.printer.writeline("%s = get_namespace(context, %s)" % (ident, repr(ident))) else: if first is not None: self.printer.writeline("%s = %s.get(%s, context.get(%s, UNDEFINED))" % (ident, first, repr(ident), repr(ident))) @@ -171,7 +234,7 @@ funcname = node.function_decl.funcname namedecls = node.function_decl.get_argument_expressions() nameargs = node.function_decl.get_argument_expressions(include_defaults=False) - if len(self.identifiers.locally_assigned) > 0: + if not self.in_def and len(self.identifiers.locally_assigned) > 0: nameargs.insert(0, 'context.locals_(__locals)') else: nameargs.insert(0, 'context') @@ -253,28 +316,7 @@ self.printer.writeline("runtime.include_file(context, %s, import_symbols=%s)" % (node.parsed_attributes['file'], repr(node.attributes.get('import', False)))) def visitNamespaceTag(self, node): - self.write_source_comment(node) - if len(node.nodes): - self.printer.writeline("def make_namespace():") - export = [] - identifiers = self.identifiers.branch(node) - class NSDefVisitor(object): - def visitDefTag(s, node): - self.write_inline_def(node, identifiers) - export.append(node.name) - vis = NSDefVisitor() - for n in node.nodes: - n.accept_visitor(vis) - self.printer.writeline("return [%s]" % (','.join(export))) - self.printer.writeline(None) - callable_name = "make_namespace()" - else: - callable_name = "None" - self.printer.writeline("%s = runtime.Namespace(%s, context.clean_inheritance_tokens(), templateuri=%s, callables=%s)" % (node.name, repr(node.name), node.parsed_attributes.get('file', 'None'), callable_name)) - if eval(node.attributes.get('inheritable', "False")): - self.printer.writeline("self.%s = %s" % (node.name, node.name)) - if not self.in_def: - self.printer.writeline("__locals[%s] = %s" % (repr(node.name), node.name)) + pass def visitDefTag(self, node): pass @@ -395,13 +437,6 @@ n.accept_visitor(self) def visitIncludeTag(self, node): self.check_declared(node) - def visitNamespaceTag(self, node): - self.check_declared(node) - self.locally_declared.add(node.name) - self.locally_assigned.add(node.name) - if node is self.node: - for n in node.nodes: - n.accept_visitor(self) def visitCallTag(self, node): self.check_declared(node)
diff --git a/lib/mako/parsetree.py b/lib/mako/parsetree.py index f3dfded..572202d 100644 --- a/lib/mako/parsetree.py +++ b/lib/mako/parsetree.py
@@ -223,7 +223,7 @@ class NamespaceTag(Tag): __keyword__ = 'namespace' def __init__(self, keyword, attributes, **kwargs): - super(NamespaceTag, self).__init__(keyword, attributes, ('file',), ('name','inheritable'), ('name',), **kwargs) + super(NamespaceTag, self).__init__(keyword, attributes, (), ('name','inheritable','file'), ('name',), **kwargs) self.name = attributes['name'] def declared_identifiers(self): return []
diff --git a/lib/mako/pygen.py b/lib/mako/pygen.py index fc7ba3b..310d1f3 100644 --- a/lib/mako/pygen.py +++ b/lib/mako/pygen.py
@@ -45,7 +45,11 @@ self.in_indent_lines = False for l in re.split(r'\r?\n', block): self.line_buffer.append(l) - + + def writelines(self, *lines): + for line in lines: + self.writeline(line) + def writeline(self, line, is_comment=False): """print a line of python, indenting it according to the current indent level.
diff --git a/lib/mako/runtime.py b/lib/mako/runtime.py index 34975fb..dbe50ff 100644 --- a/lib/mako/runtime.py +++ b/lib/mako/runtime.py
@@ -14,6 +14,7 @@ self._buffer_stack = [buffer] self._argstack = [data] self._with_template = None + self.namespaces = {} #data['args'] = _AttrFacade(self) data['capture'] = lambda x, *args, **kwargs: capture(self, x, *args, **kwargs) def keys(self): @@ -45,6 +46,7 @@ x = self._argstack[-1].copy() c._argstack = [x] c._with_template = self._with_template + c.namespaces = self.namespaces #x['args'] = _AttrFacade(c) return c def locals_(self, d): @@ -150,6 +152,7 @@ if callable_ is not None: return callable_(lclcontext) else: + template.module.generate_namespaces(context) return (template.callable_, lclcontext) def _lookup_template(context, uri):
diff --git a/test/namespace.py b/test/namespace.py index 282212b..3ce482b 100644 --- a/test/namespace.py +++ b/test/namespace.py
@@ -87,6 +87,54 @@ """) print collection.get_template("main.html").render() + + def test_in_remote_def(self): + collection = lookup.TemplateLookup() + collection.put_string("main.html", """ + <%namespace name="foo" file="ns.html"/> + + this is main. ${bar()} + <%def name="bar"> + this is bar, foo is ${foo.bar()} + </%def> + """) + + collection.put_string("ns.html", """ + <%def name="bar"> + this is ns.html->bar + </%def> + """) + collection.put_string("index.html", """ + <%namespace name="main" file="main.html"/> + + this is index + ${main.bar()} + """) + + print collection.get_template("index.html").render() + + def test_inheritance(self): + """test namespace initialization in a base inherited template that doesnt otherwise access the namespace""" + collection = lookup.TemplateLookup() + collection.put_string("base.html", """ + <%namespace name="foo" file="ns.html" inheritable="True"/> + + ${next.body()} +""") + collection.put_string("ns.html", """ + <%def name="bar"> + this is ns.html->bar + </%def> + """) + + collection.put_string("index.html", """ + <%inherit file="base.html"/> + + this is index + ${self.foo.bar()} + """) + + print collection.get_template("index.html").render() if __name__ == '__main__': unittest.main()