From 1da23d19ec379321672a1aa93b8aab81fd9362de Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Mon, 5 Apr 2010 18:11:18 +0200 Subject: [PATCH] autoescape no longer is a plain boolean value but can also be a function to enable or disable autoescaping for certain filenames (or file extensions for that matter) --HG-- branch : trunk --- docs/api.rst | 32 ++++++++++++++++++++++++++++++++ jinja2/compiler.py | 2 +- jinja2/environment.py | 8 +++++++- jinja2/nodes.py | 7 +++++-- jinja2/runtime.py | 6 +++--- jinja2/testsuite/api.py | 19 ++++++++++++++++++- 6 files changed, 66 insertions(+), 8 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index b46a88c..89fe11d 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -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 diff --git a/jinja2/compiler.py b/jinja2/compiler.py index 2952b66..117bd8c 100644 --- a/jinja2/compiler.py +++ b/jinja2/compiler.py @@ -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') diff --git a/jinja2/environment.py b/jinja2/environment.py index a250185..25cec88 100644 --- a/jinja2/environment.py +++ b/jinja2/environment.py @@ -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. diff --git a/jinja2/nodes.py b/jinja2/nodes.py index 15461e8..b6696c7 100644 --- a/jinja2/nodes.py +++ b/jinja2/nodes.py @@ -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): diff --git a/jinja2/runtime.py b/jinja2/runtime.py index 318e654..fef7b25 100644 --- a/jinja2/runtime.py +++ b/jinja2/runtime.py @@ -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 diff --git a/jinja2/testsuite/api.py b/jinja2/testsuite/api.py index 194e9a1..4b49819 100644 --- a/jinja2/testsuite/api.py +++ b/jinja2/testsuite/api.py @@ -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='') == '' + t = env.get_template('test.html') + assert t.render(foo='') == '<foo>' + t = env.from_string('{{ foo }}') + assert t.render(foo='') == '' + class MetaTestCase(JinjaTestCase): -- 2.26.2