# HG changeset patch # User cmlenz # Date 1156509781 0 # Node ID 92353f28ae54cc9e3184c6353af2ba3f121bf1f3 # Parent c5e0a1c86173445211d6326230cde5f006f27af2 Remove the (hopefully) last instance where directives store state in instance variables, allowing templates to be cached and reused in a threadsafe manner. Closes #39. Many thanks to Christian Boos for the patch! diff --git a/markup/template.py b/markup/template.py --- a/markup/template.py +++ b/markup/template.py @@ -111,6 +111,16 @@ """Set a variable in the current scope.""" self.frames[0][key] = value + def _find(self, key, default=None): + """Retrieve a given variable's value and frame it was found in. + + Intented for internal use by directives. + """ + for frame in self.frames: + if key in frame: + return frame[key], frame + return default, None + def get(self, key, default=None): """Get a variable's value, starting at the current scope and going upward. @@ -586,10 +596,10 @@ ATTRIBUTE = 'test' def __call__(self, stream, ctxt, directives): + frame = dict({'_choose.matched': False}) if self.expr: - self.value = self.expr.evaluate(ctxt) - self.matched = False - ctxt.push(dict(_choose=self)) + frame['_choose.value'] = self.expr.evaluate(ctxt) + ctxt.push(frame) for event in _apply_directives(stream, ctxt, directives): yield event ctxt.pop() @@ -605,26 +615,26 @@ ATTRIBUTE = 'test' def __call__(self, stream, ctxt, directives): - choose = ctxt['_choose'] - if not choose: + matched, frame = ctxt._find('_choose.matched') + if not frame: raise TemplateSyntaxError('"when" directives can only be used ' 'inside a "choose" directive', *stream.next()[2]) - if choose.matched: + if matched: return [] if not self.expr: raise TemplateSyntaxError('"when" directive has no test condition', *stream.next()[2]) value = self.expr.evaluate(ctxt) - try: - if value == choose.value: - choose.matched = True - return _apply_directives(stream, ctxt, directives) - except AttributeError: - if value: - choose.matched = True - return _apply_directives(stream, ctxt, directives) - return [] + if '_choose.value' in frame: + matched = (value == frame['_choose.value']) + else: + matched = bool(value) + frame['_choose.matched'] = matched + if not matched: + return [] + + return _apply_directives(stream, ctxt, directives) class OtherwiseDirective(Directive): @@ -634,14 +644,15 @@ See the documentation of `py:choose` for usage. """ def __call__(self, stream, ctxt, directives): - choose = ctxt['_choose'] - if not choose: + matched, frame = ctxt._find('_choose.matched') + if not frame: raise TemplateSyntaxError('an "otherwise" directive can only be ' 'used inside a "choose" directive', *stream.next()[2]) - if choose.matched: + if matched: return [] - choose.matched = True + frame['_choose.matched'] = True + return _apply_directives(stream, ctxt, directives) diff --git a/markup/tests/template.py b/markup/tests/template.py --- a/markup/tests/template.py +++ b/markup/tests/template.py @@ -110,6 +110,46 @@ """, str(tmpl.generate())) + def test_complex_nesting(self): + """ + Verify more complex nesting. + """ + tmpl = Template(""" +
+
+ OK + FAIL +
+
+
""") + self.assertEqual(""" +
+
+ OK +
+
+
""", str(tmpl.generate())) + + def test_complex_nesting_otherwise(self): + """ + Verify more complex nesting using otherwise. + """ + tmpl = Template(""" +
+
+ FAIL + OK +
+
+
""") + self.assertEqual(""" +
+
+ OK +
+
+
""", str(tmpl.generate())) + def test_when_with_strip(self): """ Verify that a when directive with a strip directive actually strips of