a little touch-and-go with def call with content....needs more work
diff --git a/lib/mako/codegen.py b/lib/mako/codegen.py index 480c620..66c72a1 100644 --- a/lib/mako/codegen.py +++ b/lib/mako/codegen.py
@@ -178,7 +178,7 @@ identifiers = self.compiler.identifiers.branch(node) class NSDefVisitor(object): def visitDefTag(s, node): - self.write_inline_def(node, identifiers) + self.write_inline_def(node, identifiers, nested=False) export.append(node.name) vis = NSDefVisitor() for n in node.nodes: @@ -247,7 +247,7 @@ if comp.is_root(): self.write_def_decl(comp, identifiers) else: - self.write_inline_def(comp, identifiers) + self.write_inline_def(comp, identifiers, nested=True) elif ident in self.compiler.namespaces: self.printer.writeline("%s = _mako_get_namespace(context, %s)" % (ident, repr(ident))) else: @@ -275,7 +275,7 @@ self.printer.writeline("return render_%s(%s)" % (funcname, ",".join(nameargs))) self.printer.writeline(None) - def write_inline_def(self, node, identifiers): + 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() self.printer.writeline("def %s(%s):" % (node.name, ",".join(namedecls))) @@ -288,7 +288,7 @@ "try:" ) - identifiers = identifiers.branch(node) + identifiers = identifiers.branch(node, nested=nested) self.write_variable_declares(identifiers) for n in node.nodes: @@ -405,16 +405,16 @@ def visitCallTag(self, node): self.printer.writeline("def ccall(context):") export = ['body'] - identifiers = self.identifiers.branch(node) + identifiers = self.identifiers.branch(node, includedefs=True, nested=True) + body_identifiers = identifiers.branch(node, includedefs=True, nested=False) class DefVisitor(object): def visitDefTag(s, node): - self.write_inline_def(node, identifiers) + self.write_inline_def(node, identifiers, nested=False) export.append(node.name) vis = DefVisitor() for n in node.nodes: n.accept_visitor(vis) self.printer.writeline("def body(**kwargs):") - body_identifiers = identifiers.branch(node, includedefs=False, includenode=False) # TODO: figure out best way to specify buffering/nonbuffering (at call time would be better) buffered = False if buffered: @@ -431,11 +431,18 @@ "return [%s]" % (','.join(export)), None ) + + self.printer.writeline( + # preserve local instance of current caller in local scope + "__cl = context.locals_({'caller':context.caller_stack[-1]})", + ) + self.printer.writelines( - # preserve local instance of current caller in local scope - "__cl = context.locals_({'caller':context.caller_stack[-1]})", # push on global "caller" to be picked up by the next ccall "context.caller_stack.append(runtime.Namespace('caller', __cl, callables=ccall(__cl)))", + # TODO: clean this up - insure proper caller is set + "context._data['caller'] = runtime._StackFacade(context.caller_stack)", + #"context.write('GOING TO CALL %s WITH CONTEXT ID '+ repr(id(context)) + ' CALLER ' + repr(context.get('caller')))" % node.attributes['expr'], "try:") self.write_source_comment(node) self.printer.writelines( @@ -448,11 +455,17 @@ class _Identifiers(object): """tracks the status of identifier names as template code is rendered.""" - def __init__(self, node=None, parent=None, includedefs=True, includenode=True): + def __init__(self, node=None, parent=None, includedefs=True, includenode=True, 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) + # 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.Set(parent.topleveldefs) else:
diff --git a/lib/mako/runtime.py b/lib/mako/runtime.py index 79c5868..16d7cee 100644 --- a/lib/mako/runtime.py +++ b/lib/mako/runtime.py
@@ -26,6 +26,8 @@ def keys(self): return self._data.keys() def __getitem__(self, key): + if key == 'caller': + return _StackFacade(self.caller_stack) return self._data[key] def _put(self, key, value): self._data[key] = value @@ -47,6 +49,8 @@ c._with_template = self._with_template c.namespaces = self.namespaces c.caller_stack = self.caller_stack + if not c._data.has_key('caller'): + raise "WTF" return c def locals_(self, d): """create a new Context with a copy of this Context's current state, updated with the given dictionary."""
diff --git a/test/call.py b/test/call.py index fce43f4..c821d9e 100644 --- a/test/call.py +++ b/test/call.py
@@ -13,6 +13,8 @@ this is the body, y is ${y} </%call> """) + print t.code + print t.render() assert result_lines(t.render()) == ['hi im foo', 'this is the body, y is 5'] @@ -41,6 +43,7 @@ ${bar()} """) + print t.code 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_multi_call(self): @@ -61,6 +64,7 @@ </%call> """) + print t.render() assert result_lines(t.render()) == [ 'this is a.', 'this is b. heres my body:', @@ -90,6 +94,7 @@ </%def> ${embedded()} """) + print t.render() assert result_lines(t.render()) == [ 'this is a.', 'this is b. heres my body:', @@ -117,6 +122,44 @@ """) assert result_lines(t.render()) == ['this is a', 'this is b', 'this is c:', "this is the body in b's call"] + def test_ccall_args(self): + t = Template(""" + <%def name="foo"> + foo context id: ${id(context)} + foo cstack: ${repr(context.caller_stack)} + foo, ccaller is ${context.get('caller')} + foo, context data is ${repr(context._data)} + ${caller.body(x=10)} + </%def> + + <%def name="bar"> + bar context id: ${id(context)} + bar cstack: ${repr(context.caller_stack)} + bar, cs is ${context.caller_stack[-1]} + bar, caller is ${caller} + bar, ccaller is ${context.get('caller')} + bar, body is ${context.caller_stack[-1].body()} + bar, context data is ${repr(context._data)} + </%def> + + x is: ${x} + + main context id: ${id(context)} + main cstack: ${repr(context.caller_stack)} + + <%call expr="foo()"> + this is foo body: ${x} + + foocall context id: ${id(context)} + foocall cstack: ${repr(context.caller_stack)} + <%call expr="bar()"> + this is bar body: ${x} + </%call> + </%call> +""") + print t.code + print t.render(x=5) + def test_call_in_nested_2(self): t = Template(""" <%def name="a">
diff --git a/test/def.py b/test/def.py index a0e409a..b7a7b86 100644 --- a/test/def.py +++ b/test/def.py
@@ -317,6 +317,21 @@ """) assert flatten_result(t.render()) == "hey, im hi. and heres this is foo , this is bar" + def test_nested_2(self): + t = Template(""" + x is ${x} + <%def name="a"> + this is a, x is ${x} + ${b()} + <%def name="b"> + this is b: ${x} + </%def> + </%def> + ${a()} +""") + print t.code + print t.render(x=10) + def test_nested_with_args(self): t = Template(""" ${a()}
diff --git a/test/namespace.py b/test/namespace.py index 6e9614b..c8d2e11 100644 --- a/test/namespace.py +++ b/test/namespace.py
@@ -344,6 +344,7 @@ </%call> """) print collection.get_template("index.html").code + print collection.get_template("functions.html").code print collection.get_template("index.html").render() if __name__ == '__main__':