autoescape no longer is a plain boolean value but can also be a function
authorArmin Ronacher <armin.ronacher@active-4.com>
Mon, 5 Apr 2010 16:11:18 +0000 (18:11 +0200)
committerArmin Ronacher <armin.ronacher@active-4.com>
Mon, 5 Apr 2010 16:11:18 +0000 (18:11 +0200)
to enable or disable autoescaping for certain filenames (or file
extensions for that matter)

--HG--
branch : trunk

docs/api.rst
jinja2/compiler.py
jinja2/environment.py
jinja2/nodes.py
jinja2/runtime.py
jinja2/testsuite/api.py

index b46a88c46adf5d3228b557793ebae2b2ebd5f442..89fe11da09911f5c654da5c3b50f9d967f0aa7f0 100644 (file)
@@ -220,6 +220,38 @@ useful if you want to dig deeper into Jinja2 or :ref:`develop extensions
     :members: disable_buffering, enable_buffering, dump
 
 
+Autoescaping
+------------
+
+.. versionadded:: 2.4
+
+As of Jinja 2.4 the preferred way to do autoescaping is to enable the
+:ref:`autoescape-extension` and to configure a sensible default for
+autoescaping.  This makes it possible to enable and disable autoescaping
+on a per-template basis (HTML versus text for instance).
+
+Here a recommended setup that enables autoescaping for templates ending
+in ``'.html'``, ``'.htm'`` and ``'.xml'`` and disabling it by default
+for all other extensions::
+
+    def guess_autoescape(template_name):
+        if template_name is None or '.' not in template_name:
+            return False
+        ext = template_name.rsplit('.', 1)[1]
+        return ext in ('html', 'htm', 'xml')
+
+    env = Environment(autoescape=guess_autoescape,
+                      loader=PackageLoader('mypackage'),
+                      extensions=['jinja2.ext.autoescape'])
+
+When implementing a guessing autoescape function, make sure you also
+accept `None` as valid template name.  This will be passed when generating
+templates from strings.
+
+Inside the templates the behaviour can be temporarily changed by using
+the `autoescape` block (see :ref:`autoescape-overrides`).
+
+
 .. _identifier-naming:
 
 Notes on Identifiers
index 2952b66e454cd8f9c9aa562ddca659cd03e15519..117bd8ccc67619a3c63798de38955718dec40d52 100644 (file)
@@ -761,7 +761,7 @@ class CodeGenerator(NodeVisitor):
 
     def visit_Template(self, node, frame=None):
         assert frame is None, 'no root frame allowed'
-        eval_ctx = EvalContext(self.environment)
+        eval_ctx = EvalContext(self.environment, self.name)
 
         from jinja2.runtime import __all__ as exported
         self.writeline('from __future__ import division')
index a250185b565e6a2b7f3d4a40664c61d7a43ec462..25cec88b1c5063b7d1b201c960b5f800ec10f223 100644 (file)
@@ -160,7 +160,13 @@ class Environment(object):
         `autoescape`
             If set to true the XML/HTML autoescaping feature is enabled by
             default.  For more details about auto escaping see
-            :class:`~jinja2.utils.Markup`.
+            :class:`~jinja2.utils.Markup`.  As of Jinja 2.4 this can also
+            be a callable that is passed the template name and has to
+            return `True` or `False` depending on autoescape should be
+            enabled by default.
+
+            .. versionchanged:: 2.4
+               `autoescape` can now be a function
 
         `loader`
             The template loader for this environment.
index 15461e8a73f854570909816da4b356a2545542e4..b6696c7e6d16bb106e2ca8f4c8571644b777665b 100644 (file)
@@ -72,8 +72,11 @@ class EvalContext(object):
     to it in extensions.
     """
 
-    def __init__(self, environment):
-        self.autoescape = environment.autoescape
+    def __init__(self, environment, template_name=None):
+        if callable(environment.autoescape):
+            self.autoescape = environment.autoescape(template_name)
+        else:
+            self.autoescape = environment.autoescape
         self.volatile = False
 
     def save(self):
index 318e654def3064d16b09270944b5d42d7ba9fcc6..fef7b2580986fa3dfef39dd28e90df79893c984b 100644 (file)
@@ -77,7 +77,7 @@ class TemplateReference(object):
 
     def __getitem__(self, name):
         blocks = self.__context.blocks[name]
-        wrap = self.__context.environment.autoescape and \
+        wrap = self.__context.eval_ctx.autoescape and \
                Markup or (lambda x: x)
         return BlockReference(name, self.__context, blocks, 0)
 
@@ -114,7 +114,7 @@ class Context(object):
         self.parent = parent
         self.vars = {}
         self.environment = environment
-        self.eval_ctx = EvalContext(self.environment)
+        self.eval_ctx = EvalContext(self.environment, name)
         self.exported_vars = set()
         self.name = name
 
@@ -257,7 +257,7 @@ class BlockReference(object):
     @internalcode
     def __call__(self):
         rv = concat(self._stack[self._depth](self._context))
-        if self._context.environment.autoescape:
+        if self._context.eval_ctx.autoescape:
             rv = Markup(rv)
         return rv
 
index 194e9a13ea450ab0b9645e681283d0b21df9f61c..4b49819be530c223abc7bea29c2f47db7176af5a 100644 (file)
@@ -17,7 +17,7 @@ from jinja2.testsuite import JinjaTestCase
 
 from jinja2 import Environment, Undefined, DebugUndefined, \
      StrictUndefined, UndefinedError, Template, meta, \
-     is_undefined, Template
+     is_undefined, Template, DictLoader
 from jinja2.utils import Cycler
 
 env = Environment()
@@ -76,6 +76,23 @@ class ExtendedAPITestCase(JinjaTestCase):
         assert env.get_or_select_template([t]) is t
         assert env.get_or_select_template(t) is t
 
+    def test_autoescape_autoselect(self):
+        def select_autoescape(name):
+            if name is None or '.' not in name:
+                return False
+            return name.endswith('.html')
+        env = Environment(autoescape=select_autoescape,
+                          loader=DictLoader({
+            'test.txt':     '{{ foo }}',
+            'test.html':    '{{ foo }}'
+        }))
+        t = env.get_template('test.txt')
+        assert t.render(foo='<foo>') == '<foo>'
+        t = env.get_template('test.html')
+        assert t.render(foo='<foo>') == '&lt;foo&gt;'
+        t = env.from_string('{{ foo }}')
+        assert t.render(foo='<foo>') == '<foo>'
+
 
 class MetaTestCase(JinjaTestCase):