# HG changeset patch # User mgood # Date 1151970903 0 # Node ID 436e30c8420b27f4703e245464923c1de57c18f7 # Parent e03b77726756cf41fcfa03a2800c70bf6b4a8f54 implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks diff --git a/markup/template.py b/markup/template.py --- a/markup/template.py +++ b/markup/template.py @@ -503,6 +503,123 @@ yield event +class ChooseDirective(Directive): + """Implementation of the `py:choose` directive for conditionally selecting + one of several body elements to display. + + If the `py:choose` expression is empty the expressions of nested `py:when` + directives are tested for truth. The first true `py:when` body is output. + + >>> ctxt = Context() + >>> tmpl = Template('''
+ ... 0 + ... 1 + ...
''') + >>> print tmpl.generate(ctxt) +
+ 1 +
+ + If multiple `py:when` bodies match only the first is output. + >>> tmpl = Template('''
+ ... 1 + ... 2 + ...
''') + >>> print tmpl.generate(ctxt) +
+ 1 +
+ + If the `py:choose` directive contains an expression, the nested `py:when` + directives are tested for equality to the `py:choose` expression. + >>> tmpl = Template('''
+ ... 1 + ... 2 + ...
''') + >>> print tmpl.generate(ctxt) +
+ 2 +
+ + If no `py:when` directive is matched then the fallback directive + `py:otherwise` will be used. + >>> tmpl = Template('''
+ ... hidden + ... hello + ...
''') + >>> print tmpl.generate(ctxt) +
+ hello +
+ + `py:choose` blocks can be nested: + >>> tmpl = Template('''
+ ...
+ ... 2 + ... 3 + ...
+ ...
''') + >>> print tmpl.generate(ctxt) +
+
+ 3 +
+
+ + Behavior is undefined if a `py:choose` block contains content outside a + `py:when` or `py:otherwise` block. Behavior is also undefined if a + `py:otherwise` occurs before `py:when` blocks. + """ + + def __call__(self, stream, ctxt): + if self.expr: + self.value = self.expr.evaluate(ctxt) + self.matched = False + ctxt.push(__choose=self) + for event in stream: + yield event + ctxt.pop() + + +class WhenDirective(Directive): + """Implementation of the `py:when` directive for nesting in a parent with + the `py:choose` directive. See the documentation of `py:choose` for + usage. + """ + def __call__(self, stream, ctxt): + choose = ctxt['__choose'] + if choose.matched: + return [] + value = self.expr.evaluate(ctxt) + try: + if value == choose.value: + choose.matched = True + return stream + except AttributeError: + if value: + choose.matched = True + return stream + return [] + + +class OtherwiseDirective(Directive): + """Implementation of the `py:otherwise` directive for nesting in a parent + with the `py:choose` directive. See the documentation of `py:choose` for + usage. + """ + def __call__(self, stream, ctxt): + choose = ctxt['__choose'] + if choose.matched: + return [] + choose.matched = True + return stream + + class Template(object): """Can parse a template and transform it into the corresponding output based on context data. @@ -519,7 +636,10 @@ ('replace', ReplaceDirective), ('content', ContentDirective), ('attrs', AttrsDirective), - ('strip', StripDirective)] + ('strip', StripDirective), + ('choose', ChooseDirective), + ('when', WhenDirective), + ('otherwise', OtherwiseDirective)] _dir_by_name = dict(directives) _dir_order = [directive[1] for directive in directives]