Permalink
Browse files

Change get_default_filter to simple argument so you don't have to sub…

…class Renderer to use it
  • Loading branch information...
1 parent c225eb7 commit a9d97ddc7d741af8e492cbf6c13ea5833fd1909f @benhoyt committed Oct 2, 2012
Showing with 71 additions and 49 deletions.
  1. +21 −16 README.md
  2. +35 −23 symplate.py
  3. +15 −10 tests/test_filtering.py
View
@@ -241,22 +241,27 @@ If you need to change back to the default filter (`html_filter`), just say:
### Changing the default filter
-You can change the default filter by subclassing `Renderer` and overriding
-`get_default_filter()`. It's a function which is passed the template filename
-as its single argument, so you can use the filename or extension to do clever
-things if you want. By default it simply returns the string
-`'symplate.html_filter'`, but you could do something like this:
-
- class SmartRenderer(symplate.Renderer):
- def __init__(self, *args, **kwargs):
- super(SmartRenderer, self).__init__(*args, **kwargs)
- self.preamble += 'import json\n'
-
- def get_default_filter(self, filename):
- if filename.lower().endswith('.js.symp'):
- return 'json.dumps'
- else:
- return 'symplate.html_filter'
+You can change the default filter by passing `Renderer` the `default_filter`
+argument. If this is a string, it's used directly for setting the filter, as
+per the above:
+
+ # this default filter will make everything uppercase
+ renderer = symplate.Renderer(default_filter='lambda s: s.upper()')
+
+If it's not a string, it must be a function which takes a single `filename`
+(the template filename) as its argument. This is useful when, for example, you
+want to determine the default filter based on the template's file extension.
+A simple example:
+
+ # this default filter will use json.dumps() for .js.symp files and the
+ # normal HTML filter for other files
+ def get_default_filter(self, filename):
+ if filename.lower().endswith('.js.symp'):
+ return 'json.dumps'
+ else:
+ return 'symplate.html_filter'
+ renderer = symplate.Renderer(default_filter=get_default_filter,
+ preamble='import json\n')
Note the modified `premable` so the compiled template has the `json` module
available.
View
@@ -56,21 +56,27 @@ class Renderer(object):
"""Symplate renderer class. See __init__'s docs for more info."""
def __init__(self, template_dir, output_dir=None, extension='.symp',
- check_mtime=False, modify_path=True, preamble=''):
+ check_mtime=False, modify_path=True, preamble='',
+ default_filter='symplate.html_filter'):
"""Initialize a Renderer instance.
- * template_dir: directory your Symplate source files are in (the only
- required argument)
- * output_dir: directory compiled template (.py) files should go
- into, default is {template_dir}/../symplouts
- * extension: file extension for templates (set to '' if you want to
- specify explictly when calling render)
- * check_mtime: True means check template file's mtime on render(),
- which is slower and usually only used for debugging
- * modify_path: True means add output_dir/.. to sys.path for importing
- compiled template
- * preamble: extra code to include at top of compiled template,
- such as imports
+ * template_dir: directory your Symplate source files are in (the
+ only required argument)
+ * output_dir: directory compiled template (.py) files should go
+ into, default is {template_dir}/../symplouts
+ * extension: file extension for templates (set to '' if you want
+ to specify explictly when calling render)
+ * check_mtime: True means check template file's mtime on render(),
+ which is slower and usually only used for debugging
+ * modify_path: True means add output_dir/.. to sys.path for
+ importing compiled template
+ * preamble: extra code to include at top of compiled template,
+ such as imports
+ * default_filter: if a string, use directly as Python expression for
+ default filter; otherwise this must be a function
+ that takes a single filename argument (e.g., if you
+ want to use the file extension to determine the
+ default filter)
"""
self.template_dir = os.path.abspath(template_dir)
if output_dir is None:
@@ -80,18 +86,19 @@ def __init__(self, template_dir, output_dir=None, extension='.symp',
self.extension = extension
self.check_mtime = check_mtime
self.preamble = preamble
- self._module_cache = {}
+ self.default_filter = default_filter
+ self._module_cache = {}
if modify_path:
path_dir = os.path.abspath(os.path.join(output_dir, '..'))
sys.path.insert(0, path_dir)
- def get_default_filter(self, filename):
- """Return Python expression string to use as default filter for given
- template filename. Override this in subclasses to do fancy stuff like
- determine filter based on file extension.
- """
- return 'symplate.html_filter'
+ def _get_default_filter(self, filename):
+ """Return Python expression string to use as default filter."""
+ if isinstance(self.default_filter, basestring):
+ return self.default_filter
+ else:
+ return self.default_filter(filename)
def _compile_text(self, text, indent, template, line_num):
"""Compile the text parts of a template (the parts not inside {%...%}
@@ -205,7 +212,7 @@ def _render(_renderer, %s):
_output = []
_writes = _output.extend
-""" % (line[9:], self.get_default_filter(filename)))
+""" % (line[9:], self._get_default_filter(filename)))
if indent:
error('{% template ... %} must be at top level')
indent += ' '
@@ -287,7 +294,9 @@ def _make_output_dir(self, output_dir):
f.write('')
def compile(self, name, verbose=False):
- """Compile named template to .py in output directory."""
+ """Compile named template to .py in output directory. Print what we're
+ compiling iff verbose is True.
+ """
names = self._get_filenames(name)
if verbose:
print 'compiling %s -> %s' % (names['symplate'], names['py'])
@@ -319,7 +328,10 @@ def remove_if_exists(filename):
remove_if_exists(py_basename + '.pyo')
def compile_all(self, recursive=True, verbose=False):
- """Compile all templates in template_dir to .py files."""
+ """Compile all templates in template_dir to .py files. Recurse into
+ subdirectories iff recursive is True. Print what we're compiling iff
+ verbose is True.
+ """
for root, dirs, files in os.walk(self.template_dir):
for base_name in files:
if not base_name.endswith(self.extension):
View
@@ -20,17 +20,22 @@ def test_set_filter(self):
def test_raw(self):
self.assertEqual(self.render("{% template %}{{ !'<b>' }}"), '<b>')
- def test_override_default_filter(self):
- filenames = set()
- class OverrideRenderer(utils.Renderer):
- def get_default_filter(self, filename):
- filenames.add(filename)
- return 'lambda s: s.upper()'
- renderer = OverrideRenderer()
+ def test_override_default_filter_string(self):
+ renderer = utils.Renderer(default_filter='lambda s: s.upper()')
self.assertEqual(self.render("{% template %}{{ 'a&z' }}", _renderer=renderer), 'A&Z')
- self.assertEqual(self.render("{% template %}{{ 'z&a' }}", _renderer=renderer), 'Z&A')
- # at least test that filenames were different
- self.assertEqual(len(filenames), 2)
+
+ def test_override_default_filter_function(self):
+ filenames = []
+ def my_filter(filename):
+ filenames.append(filename)
+ return 'lambda s: s.lower()'
+ renderer = utils.Renderer(default_filter=my_filter)
+ self.assertEqual(self.render("{% template %}{{ 'A&Z' }}", _renderer=renderer), 'a&z')
+ self.assertEqual(self.render("{% template %}{{ 'Z&A' }}", _renderer=renderer), 'z&a')
+ # at least test that filenames were different, and that they end with .symp
+ self.assertEqual(len(set(filenames)), 2)
+ self.assertTrue(filenames[0].endswith('.symp'))
+ self.assertTrue(filenames[1].endswith('.symp'))
if __name__ == '__main__':
unittest.main()

0 comments on commit a9d97dd

Please sign in to comment.