# HG changeset patch # User cmlenz # Date 1175543011 0 # Node ID 9f11c745fac9bf4edb9be6930396918ce70e2b6c # Parent 2c38ec4e2dffdd71c61e872f417e0ffa6e0ac152 Add support for adding custom template filters by passing a custom callback function to the `TemplateLoader`. Closes #89 (see added unit test). diff --git a/genshi/filters.py b/genshi/filters.py --- a/genshi/filters.py +++ b/genshi/filters.py @@ -62,11 +62,10 @@ data = {} self.data = data - def __call__(self, stream, ctxt=None): + def __call__(self, stream): """Apply the filter to the given stream. :param stream: the markup event stream to filter - :param ctxt: the template context (unused) """ in_form = in_select = in_option = in_textarea = False select_value = option_value = textarea_value = None @@ -251,11 +250,10 @@ self.uri_attrs = uri_attrs self.safe_schemes = safe_schemes - def __call__(self, stream, ctxt=None): + def __call__(self, stream): """Apply the filter to the given stream. :param stream: the markup event stream to filter - :param ctxt: the template context (unused) """ waiting_for = None diff --git a/genshi/template/loader.py b/genshi/template/loader.py --- a/genshi/template/loader.py +++ b/genshi/template/loader.py @@ -72,7 +72,8 @@ >>> os.remove(path) """ def __init__(self, search_path=None, auto_reload=False, - default_encoding=None, max_cache_size=25, default_class=None): + default_encoding=None, max_cache_size=25, default_class=None, + callback=None): """Create the template laoder. :param search_path: a list of absolute path names that should be @@ -86,6 +87,11 @@ cache :param default_class: the default `Template` subclass to use when instantiating templates + :param callback: (optional) a callback function that is invoked after a + template was initialized by this loader; the function + is passed the template object as only argument. This + callback can be used for example to add any desired + filters to the template """ from genshi.template.markup import MarkupTemplate @@ -97,6 +103,9 @@ self.auto_reload = auto_reload self.default_encoding = default_encoding self.default_class = default_class or MarkupTemplate + if callback is not None and not callable(callback): + raise TypeError('The "callback" parameter needs to be callable') + self.callback = callback self._cache = LRUCache(max_cache_size) self._mtime = {} self._lock = threading.Lock() @@ -186,10 +195,12 @@ dirname = '' tmpl = cls(fileobj, basedir=dirname, filename=filename, loader=self, encoding=encoding) + if self.callback: + self.callback(tmpl) + self._cache[filename] = tmpl + self._mtime[filename] = os.path.getmtime(filepath) finally: fileobj.close() - self._cache[filename] = tmpl - self._mtime[filename] = os.path.getmtime(filepath) return tmpl except IOError: continue diff --git a/genshi/template/tests/loader.py b/genshi/template/tests/loader.py --- a/genshi/template/tests/loader.py +++ b/genshi/template/tests/loader.py @@ -17,6 +17,7 @@ import tempfile import unittest +from genshi.core import TEXT from genshi.template.loader import TemplateLoader from genshi.template.markup import MarkupTemplate @@ -189,6 +190,35 @@ loader = TemplateLoader([self.dirname], default_encoding='utf-8') loader.load('tmpl.html', encoding='iso-8859-1') + def test_load_with_callback(self): + fileobj = open(os.path.join(self.dirname, 'tmpl.html'), 'w') + try: + fileobj.write(""" +

Hello

+ """) + finally: + fileobj.close() + + def template_loaded(template): + def my_filter(stream, ctxt): + for kind, data, pos in stream: + if kind is TEXT and data.strip(): + data = ', '.join([data, data.lower()]) + yield kind, data, pos + template.filters.insert(0, my_filter) + + loader = TemplateLoader([self.dirname], callback=template_loaded) + tmpl = loader.load('tmpl.html') + self.assertEqual(""" +

Hello, hello

+ """, tmpl.generate().render()) + + # Make sure the filter is only added once + tmpl = loader.load('tmpl.html') + self.assertEqual(""" +

Hello, hello

+ """, tmpl.generate().render()) + def suite(): suite = unittest.TestSuite()