# HG changeset patch # User cmlenz # Date 1153992137 0 # Node ID f12e7987d7f45302eaabc2a796f5a7a1347fd87b # Parent 0f246a30d3a7767baee6167b31242ed184bfa140 Added `py:with` directive based on Jonas' patch in #17. diff --git a/markup/template.py b/markup/template.py --- a/markup/template.py +++ b/markup/template.py @@ -619,6 +619,47 @@ return _apply_directives(stream, ctxt, directives) +class WithDirective(Directive): + """Implementation of the `py:with` template directive, which allows + shorthand access to variables and expressions. + + >>> tmpl = Template('''
+ ... $x $y $z + ...
''') + >>> print tmpl.generate(Context(x=42)) +
+ 42 7 52 +
+ """ + __slots__ = ['vars'] + + ATTRIBUTE = 'vars' + + def __init__(self, value, filename=None, lineno=-1, offset=-1): + Directive.__init__(self, None, filename, lineno, offset) + self.vars = [] + try: + for stmt in value.split(';'): + name, value = stmt.split('=', 1) + self.vars.append((name.strip(), + Expression(value.strip(), filename, lineno))) + except SyntaxError, err: + raise TemplateSyntaxError(err, filename, lineno, + offset + (err.offset or 0)) + + def __call__(self, stream, ctxt, directives): + ctxt.push(dict([(name, expr.evaluate(ctxt)) + for name, expr in self.vars])) + for event in _apply_directives(stream, ctxt, directives): + yield event + ctxt.pop() + + def __repr__(self): + return '<%s "%s">' % (self.__class__.__name__, + '; '.join(['%s = %s' % (name, expr.source) + for name, expr in self.vars])) + + class Template(object): """Can parse a template and transform it into the corresponding output based on context data. @@ -635,6 +676,7 @@ ('when', WhenDirective), ('otherwise', OtherwiseDirective), ('choose', ChooseDirective), + ('with', WithDirective), ('replace', ReplaceDirective), ('content', ContentDirective), ('attrs', AttrsDirective), diff --git a/markup/tests/template.py b/markup/tests/template.py --- a/markup/tests/template.py +++ b/markup/tests/template.py @@ -414,6 +414,30 @@ """, str(tmpl.generate())) +class WithDirectiveTestCase(unittest.TestCase): + """Tests for the `py:with` template directive.""" + + def test_shadowing(self): + tmpl = Template("""
+ ${x} + + ${x} +
""") + self.assertEqual("""
+ 42 + 84 + 42 +
""", str(tmpl.generate(Context(x=42)))) + + def test_as_element(self): + tmpl = Template("""
+ ${x} +
""") + self.assertEqual("""
+ 84 +
""", str(tmpl.generate(Context(x=42)))) + + class TemplateTestCase(unittest.TestCase): """Tests for basic template processing, expression evaluation and error reporting. @@ -578,6 +602,7 @@ suite.addTest(unittest.makeSuite(IfDirectiveTestCase, 'test')) suite.addTest(unittest.makeSuite(MatchDirectiveTestCase, 'test')) suite.addTest(unittest.makeSuite(StripDirectiveTestCase, 'test')) + suite.addTest(unittest.makeSuite(WithDirectiveTestCase, 'test')) return suite if __name__ == '__main__':