inheritance/ccall with content coming together now
diff --git a/examples/bench/mako_inheritance/base.html b/examples/bench/mako_inheritance/base.html index 618dde7..f076e78 100644 --- a/examples/bench/mako_inheritance/base.html +++ b/examples/bench/mako_inheritance/base.html
@@ -11,10 +11,14 @@ <p>hello ${name}!</p> </%component> - <%include file="header.html"/> +<div id="header"> + <h1>${title}</h1> +</div> ${self.body()} - <%include file="footer.html"/> + <div id="footer"> + </div> + </body> </html>
diff --git a/examples/bench/mako_inheritance/footer.html b/examples/bench/mako_inheritance/footer.html deleted file mode 100644 index 1b00330..0000000 --- a/examples/bench/mako_inheritance/footer.html +++ /dev/null
@@ -1,2 +0,0 @@ -<div id="footer"> -</div>
diff --git a/examples/bench/mako_inheritance/header.html b/examples/bench/mako_inheritance/header.html deleted file mode 100644 index e4f3382..0000000 --- a/examples/bench/mako_inheritance/header.html +++ /dev/null
@@ -1,5 +0,0 @@ -<div id="header"> - <h1>${title}</h1> -</div> - -
diff --git a/lib/mako/codegen.py b/lib/mako/codegen.py index 092bf05..94572f9 100644 --- a/lib/mako/codegen.py +++ b/lib/mako/codegen.py
@@ -104,7 +104,7 @@ for n in self.node.nodes: n.accept_visitor(f) - def write_variable_declares(self, identifiers): + def write_variable_declares(self, identifiers, first=None): """write variable declarations at the top of a function. the variable declarations are generated based on the names that are referenced @@ -142,7 +142,10 @@ else: self.write_inline_component(comp, identifiers) else: - self.printer.writeline("%s = context.get(%s, UNDEFINED)" % (ident, repr(ident))) + if first is not None: + self.printer.writeline("%s = %s.get(%s, context.get(%s, UNDEFINED))" % (ident, first, repr(ident), repr(ident))) + else: + self.printer.writeline("%s = context.get(%s, UNDEFINED)" % (ident, repr(ident))) def write_source_comment(self, node): if self.last_source_line != node.lineno: @@ -216,38 +219,42 @@ n.accept_visitor(vis) self.printer.writeline("return [%s]" % (','.join(export))) self.printer.writeline(None) - self.printer.writeline("%s = runtime.ContextualNamespace(%s, context, callables=make_namespace())" % (node.name, repr(node.name))) + self.printer.writeline("%s = runtime.Namespace(%s, context, callables=make_namespace())" % (node.name, repr(node.name))) def visitComponentTag(self, node): pass def visitCallTag(self, node): self.write_source_comment(node) - self.printer.writeline("def ccall():") + self.printer.writeline("def ccall(context):") export = ['body'] identifiers = self.identifiers.branch(node) - self.write_variable_declares(identifiers) class ComponentVisitor(object): def visitComponentTag(s, node): + self.write_inline_component(node, identifiers) export.append(node.name) vis = ComponentVisitor() for n in node.nodes: n.accept_visitor(vis) - self.printer.writeline("def body():") + self.printer.writeline("def body(**kwargs):") + body_identifiers = identifiers.branch(node, includecomponents=False, includenode=False) + self.write_variable_declares(body_identifiers, first="kwargs") for n in node.nodes: n.accept_visitor(self) self.printer.writeline("return ''") self.printer.writeline(None) - self.printer.writeline("context.push({%s})" % - (','.join(["%s:%s" % (repr(x), x) for x in export]) ) - ) + self.printer.writeline("return [%s]" % (','.join(export))) + self.printer.writeline(None) + self.printer.writeline("__cl = context.locals_({})") + self.printer.writeline("context.push({'caller':runtime.Namespace('caller', __cl, callables=ccall(__cl))})") + self.printer.writeline("try:") self.printer.writeline("context.write(unicode(%s))" % node.attributes['expr']) + self.printer.writeline("finally:") self.printer.writeline("context.pop()") self.printer.writeline(None) - self.printer.writeline("ccall()") class _Identifiers(object): """tracks the status of identifier names as template code is rendered.""" - def __init__(self, node=None, parent=None): + def __init__(self, node=None, parent=None, includecomponents=True, includenode=True): 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.closurecomponents]).union(parent.locally_declared) @@ -276,12 +283,17 @@ self.closurecomponents = util.Set() self.node = node + self.includecomponents = includecomponents if node is not None: - node.accept_visitor(self) + if includenode: + node.accept_visitor(self) + else: + for n in node.nodes: + n.accept_visitor(self) - def branch(self, node): + def branch(self, node, **kwargs): """create a new Identifiers for a new Node, with this Identifiers as the parent.""" - return _Identifiers(node, self) + return _Identifiers(node, self, **kwargs) components = property(lambda s:s.toplevelcomponents.union(s.closurecomponents)) @@ -305,6 +317,8 @@ self.check_declared(node) self.locally_assigned = self.locally_assigned.union(node.declared_identifiers()) def visitComponentTag(self, node): + if not self.includecomponents: + return if node.is_root(): self.toplevelcomponents.add(node) elif node is not self.node:
diff --git a/lib/mako/lookup.py b/lib/mako/lookup.py index 608d0ca..53d580a 100644 --- a/lib/mako/lookup.py +++ b/lib/mako/lookup.py
@@ -20,11 +20,12 @@ raise NotImplementedError() class TemplateLookup(TemplateCollection): - def __init__(self, directories=None, module_directory=None, filesystem_checks=True, collection_size=-1): + def __init__(self, directories=None, module_directory=None, filesystem_checks=True, collection_size=-1, format_exceptions=False, error_handler=None, output_encoding=None): self.directories = directories or [] self.module_directory = module_directory self.filesystem_checks = filesystem_checks self.collection_size = collection_size + self.template_args = {'format_exceptions':format_exceptions, 'error_handler':error_handler, 'output_encoding':output_encoding} self._collection = {} def get_template(self, uri): try: @@ -33,8 +34,11 @@ for dir in self.directories: srcfile = posixpath.join(dir, uri) if os.access(srcfile, os.F_OK): - self._collection[uri] = Template(file(srcfile).read(), lookup=self) + self._collection[uri] = Template(file(srcfile).read(), description=uri, filename=srcfile, lookup=self, **self.template_args) return self._collection[uri] else: raise exceptions.TemplateLookupException("Cant locate template for uri '%s'" % uri) - + def put_string(self, uri, text): + self._collection[uri] = Template(text, lookup=self, description=uri, **self.template_args) + def put_template(self, uri, template): + self._collection[uri] = template \ No newline at end of file
diff --git a/lib/mako/runtime.py b/lib/mako/runtime.py index 123cb53..21ff882 100644 --- a/lib/mako/runtime.py +++ b/lib/mako/runtime.py
@@ -15,26 +15,34 @@ self._argstack = [data] self.with_template = None data['args'] = _AttrFacade(self) + def keys(self): + return self._argstack[-1].keys() def __getitem__(self, key): return self._argstack[-1][key] + def _put(self, key, value): + self._argstack[-1][key] = value def get(self, key, default=None): return self._argstack[-1].get(key, default) def write(self, string): + """write a string to this Context's underlying output buffer.""" self.buffer.write(string) def push(self, args): + """push a dictionary of values onto this Context's stack of data.""" x = self._argstack[-1].copy() x.update(args) self._argstack.append(x) def pop(self): + """pop a dictionary off this Context's stack of data.""" self._argstack.pop() def locals_(self, d): + """create a new Context with a copy of this Context's current state, updated with the given dictionary.""" c = Context.__new__(Context) c.buffer = self.buffer - c._argstack = [x for x in self._argstack] + x = self._argstack[-1].copy() + x.update(d) + c._argstack = [x] c.with_template = self.with_template - if c.with_template is None: - raise "hi" - c.push(d) + x['args'] = _AttrFacade(c) return c class _AttrFacade(object): @@ -52,8 +60,9 @@ class Namespace(object): """provides access to collections of rendering methods, which can be local, from other templates, or from imported modules""" - def __init__(self, name, module=None, template=None, callables=None, inherits=None): + def __init__(self, name, context, module=None, template=None, callables=None, inherits=None): self.name = name + self.context = context self.module = module self.template = template self.inherits = inherits @@ -62,7 +71,7 @@ else: self.callables = {} - def contextual_callable(self, context, key): + def __getattr__(self, key): if self.callables is not None: try: return self.callables[key] @@ -77,24 +86,17 @@ except AttributeError: callable_ = None if callable_ is not None: - return lambda *args, **kwargs:callable_(context, *args, **kwargs) + return lambda *args, **kwargs:callable_(self.context, *args, **kwargs) if self.module is not None: try: callable_ = getattr(self.module, key) - return lambda *args, **kwargs:callable_(context, *args, **kwargs) + return lambda *args, **kwargs:callable_(self.context, *args, **kwargs) except AttributeError: pass if self.inherits is not None: - return self.inherits.contextual_callable(context, key) + return getattr(self.inherits, key) raise exceptions.RuntimeException("Namespace '%s' has no member '%s'" % (self.name, key)) -class ContextualNamespace(Namespace): - def __init__(self, name, context, **kwargs): - super(ContextualNamespace, self).__init__(name, **kwargs) - self.context = context - def __getattr__(self, key): - return self.contextual_callable(self.context, key) - def _lookup_template(context, uri): lookup = context.with_template.lookup return lookup.get_template(uri) @@ -109,13 +111,13 @@ self_ns = context.get('self', None) if self_ns is None: fromtempl = context.with_template - self_ns = ContextualNamespace('self', context, template=fromtempl) + self_ns = Namespace('self:%s' % fromtempl.description, context, template=fromtempl) context._argstack[-1]['self'] = self_ns ih = self_ns while ih.inherits is not None: ih = ih.inherits lclcontext = context.locals_({'next':ih}) - ih.inherits = ContextualNamespace('self', lclcontext, template = template) + ih.inherits = Namespace("self:%s" % template.description, lclcontext, template = template) context._argstack[-1]['parent'] = ih.inherits callable_ = getattr(template.module, '_inherit', getattr(template.module, 'render')) callable_(lclcontext)
diff --git a/lib/mako/template.py b/lib/mako/template.py index 470d5d0..1aacf4e 100644 --- a/lib/mako/template.py +++ b/lib/mako/template.py
@@ -23,7 +23,7 @@ class Template(object): """a compiled template""" - def __init__(self, text=None, module=None, identifier=None, filename=None, format_exceptions=False, error_handler=None, lookup=None, output_encoding=None): + def __init__(self, text=None, module=None, identifier=None, description=None, filename=None, format_exceptions=False, error_handler=None, lookup=None, output_encoding=None): """construct a new Template instance using either literal template text, or a previously loaded template module text - textual template source, or None if a module is to be provided @@ -49,6 +49,7 @@ self._source = None self._code = None self.module = module + self.description = description self.callable_ = self.module.render self.format_exceptions = format_exceptions self.error_handler = error_handler
diff --git a/test/call.py b/test/call.py index 0354576..efc0a3a 100644 --- a/test/call.py +++ b/test/call.py
@@ -7,16 +7,16 @@ <%component name="foo"> - hi im foo ${body()} + hi im foo ${caller.body(y=5)} </%component> <%call expr="foo()"> - this is the body + this is the body, y is ${y} </%call> """) print t.code -# print t.render() + print t.render() def test_compound_call(self): @@ -31,15 +31,15 @@ </%component> <%component name="foo"> - foo calling comp1: ${args.comp1()} - foo calling body: ${body()} + foo calling comp1: ${caller.comp1(x=5)} + foo calling body: ${caller.body()} </%component> <%call expr="foo()"> - <%component name="comp1"> - this is comp1 + <%component name="comp1(x)"> + this is comp1, ${x} </%component> - this is the body, ${comp1()} + this is the body, ${comp1(6)} </%call> ${bar()} @@ -47,6 +47,49 @@ print t.code print t.render() + def test_multi_call(self): + t = Template(""" + <%component name="a"> + this is a. + <%call expr="b()"> + this is a's ccall. heres my body: ${caller.body()} + </%call> + </%component> + <%component name="b"> + this is b. heres my body: ${caller.body()} + whats in the body's caller's body ? ${caller.context['caller'].body()} + </%component> + + <%call expr="a()"> + heres the main templ call + </%call> + +""") + print t.code + print t.render() + + def test_multi_call_in_nested(self): + t = Template(""" + <%component name="embedded"> + <%component name="a"> + this is a. + <%call expr="b()"> + this is a's ccall. heres my body: ${caller.body()} + </%call> + </%component> + <%component name="b"> + this is b. heres my body: ${caller.body()} + </%component> + + <%call expr="a()"> + heres the main templ call + </%call> + </%component> + ${embedded()} +""") + print t.code + print t.render() + def test_call_in_nested(self): t = Template(""" <%component name="a"> @@ -58,7 +101,7 @@ </%call> </%component> <%component name="c"> - this is c: ${body()} + this is c: ${caller.body()} </%component> </%component> ${a()} @@ -86,8 +129,8 @@ </%call> </%component> <%component name="c"> - this is c: ${body()} - the embedded "d" is: ${d()} + this is c: ${caller.body()} + the embedded "d" is: ${caller.d()} </%component> </%component> ${a()}
diff --git a/test/inheritance.py b/test/inheritance.py index 2289104..f4c810c 100644 --- a/test/inheritance.py +++ b/test/inheritance.py
@@ -31,44 +31,50 @@ footer: ${self.footer()} <%component name="footer"> - this is the footer + this is the footer. header again ${next.header()} </%component> """, lookup=collection) print tmpl['main'].render() def test_multilevel_nesting(self): - tmpl = {} - class LocalTmplCollection(lookup.TemplateCollection): - def get_template(self, uri): - return tmpl[uri] - collection = LocalTmplCollection() + collection = lookup.TemplateLookup() - tmpl['main'] = Template(""" + collection.put_string('main', """ <%inherit file="layout"/> -main_body ${parent.footer()} -""", lookup=collection) +<%component name="d">main_d</%component> +main_body ${parent.d()} +full stack from the top: + ${self.name} ${parent.name} ${parent.context['parent'].name} ${parent.context['parent'].context['parent'].name} +""") - tmpl['layout'] = Template(""" + collection.put_string('layout', """ <%inherit file="general"/> +<%component name="d">layout_d</%component> layout_body +parent name: ${parent.name} +${parent.d()} +${parent.context['parent'].d()} ${next.body()} """) - tmpl['general'] = Template(""" + collection.put_string('general', """ <%inherit file="base"/> +<%component name="d">general_d</%component> general_body +${next.d()} +${next.context['next'].d()} ${next.body()} """) - tmpl['base'] = Template(""" + collection.put_string('base', """ base_body +full stack from the base: + ${self.name} ${self.context['parent'].name} ${self.context['parent'].context['parent'].name} ${self.context['parent'].context['parent'].context['parent'].name} ${next.body()} -<%component name="footer"> - base footer -</%component> -""", lookup=collection) +<%component name="d">base_d</%component> +""") - print tmpl['main'].render() + print collection.get_template('main').render() if __name__ == '__main__':