# HG changeset patch # User cmlenz # Date 1155737070 0 # Node ID 8bd5c8cd33e0e6dc4f8f9f7c1573484d3b4dfceb # Parent fc2ff46d1ae38ec6e54ee75c60291c844e851946 * Make sure `py:def` macros don't go out of scope if they are defined inside another directive. * Cleaned up the `DefDirective` implementation a bit. diff --git a/markup/template.py b/markup/template.py --- a/markup/template.py +++ b/markup/template.py @@ -290,7 +290,7 @@

""" - __slots__ = ['name', 'args', 'defaults', 'stream', 'directives'] + __slots__ = ['name', 'args', 'defaults'] ATTRIBUTE = 'function' @@ -309,27 +309,33 @@ self.args.append(arg.name) else: self.name = ast.name - self.stream, self.directives = [], [] def __call__(self, stream, ctxt, directives): - self.stream = list(stream) - self.directives = directives - ctxt[self.name] = lambda *args, **kwargs: self._exec(ctxt, *args, - **kwargs) - return [] + stream = list(stream) - def _exec(self, ctxt, *args, **kwargs): - scope = {} - args = list(args) # make mutable - for name in self.args: - if args: - scope[name] = args.pop(0) - else: - scope[name] = kwargs.pop(name, self.defaults.get(name)) - ctxt.push(scope) - for event in _apply_directives(self.stream, ctxt, self.directives): - yield event - ctxt.pop() + def function(*args, **kwargs): + scope = {} + args = list(args) # make mutable + for name in self.args: + if args: + scope[name] = args.pop(0) + else: + scope[name] = kwargs.pop(name, self.defaults.get(name)) + ctxt.push(scope) + for event in _apply_directives(stream, ctxt, directives): + yield event + ctxt.pop() + try: + function.__name__ = self.name + except TypeError: + # Function name can't be set in Python 2.3 + pass + + # Store the function reference in the bottom context frame so that it + # doesn't get popped off before processing the template has finished + ctxt.frames[-1][self.name] = function + + return [] class ForDirective(Directive): diff --git a/markup/tests/template.py b/markup/tests/template.py --- a/markup/tests/template.py +++ b/markup/tests/template.py @@ -185,6 +185,24 @@ foo """, str(tmpl.generate())) + def test_nested_defs(self): + """ + Verify that a template function defined inside a conditional block can + be called from outside that block. + """ + tmpl = Template(""" + + ${what} + + + ${what} + + ${echo('foo')} + """) + self.assertEqual(""" + foo + """, str(tmpl.generate(semantic=True))) + class ForDirectiveTestCase(unittest.TestCase): """Tests for the `py:for` template directive."""