# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-#extensions = []
+extensions = ['sphinx.ext.autodoc']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
+pygments_style = 'autumn'
# Options for HTML output
# ------------------------
# The paper size ('letter' or 'a4').
-#latex_paper_size = 'letter'
+latex_paper_size = 'a4'
# The font size ('10pt', '11pt' or '12pt').
#latex_font_size = '10pt'
]
# Additional stuff for the LaTeX preamble.
-#latex_preamble = ''
+latex_preamble = ''
# Documents to append as an appendix to all manuals.
#latex_appendices = []
-.. Jinja2 documentation master file, created by sphinx-quickstart on Sun Apr 27 21:42:41 2008.
- You can adapt this file completely to your liking, but it should at least
- contain the root `toctree` directive.
-
Welcome to Jinja2's documentation!
==================================
.. toctree::
:maxdepth: 2
+ intro
+ api
+ templates
+
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
-
# undefined types
from jinja2.runtime import Undefined, DebugUndefined, StrictUndefined
+# exceptions
+from jinja2.exceptions import TemplateError, UndefinedError, \
+ TemplateNotFound, TemplateSyntaxError, TemplateAssertionError
+
# decorators and public utilities
from jinja2.filters import environmentfilter, contextfilter
from jinja2.utils import Markup, escape, environmentfunction, contextfunction
self.pull_locals(macro_frame)
self.writeline('%s = []' % buf)
self.blockvisit(node.body, macro_frame)
- self.writeline("return Markup(concat(%s))" % buf)
+ if self.environment.autoescape:
+ self.writeline('return Markup(concat(%s))' % buf)
+ else:
+ self.writeline("return concat(%s)" % buf)
self.outdent()
self.newline()
if frame.toplevel:
self.pull_locals(call_frame)
self.writeline('%s = []' % buf)
self.blockvisit(node.body, call_frame)
- self.writeline("return Markup(concat(%s))" % buf)
+ if self.environment.autoescape:
+ self.writeline("return Markup(concat(%s))" % buf)
+ else:
+ self.writeline('return concat(%s)' % buf)
self.outdent()
arg_tuple = ', '.join(repr(x.name) for x in node.args)
if len(node.args) == 1:
return
self.newline(node)
- if self.environment.finalize is unicode:
- finalizer = 'unicode'
- have_finalizer = False
- else:
- finalizer = 'environment.finalize'
- have_finalizer = True
# if we are in the toplevel scope and there was already an extends
# statement we have to add a check that disables our yield(s) here
else:
if frame.buffer is None:
self.writeline('yield ')
- self.write(finalizer + '(')
+ close = 1
+ if self.environment.autoescape:
+ self.write('escape(')
+ else:
+ self.write('unicode(')
+ if self.environment.finalize is not None:
+ self.write('environment.finalize(')
+ close += 1
self.visit(item, frame)
- self.write(')')
+ self.write(')' * close)
if frame.buffer is not None:
self.write(', ')
if frame.buffer is not None:
self.indent()
for argument in arguments:
self.newline(argument)
- if have_finalizer:
- self.write(finalizer + '(')
+ close = 0
+ if self.environment.autoescape:
+ self.write('escape(')
+ close += 1
+ if self.environment.finalize is not None:
+ self.write('environment.finalize(')
+ close += 1
self.visit(argument, frame)
- if have_finalizer:
- self.write(')')
- self.write(',')
+ self.write(')' * close + ',')
self.outdent()
self.writeline(')')
if frame.buffer is not None:
visit_Not = uaop('not ')
del binop, uaop
+ def visit_Concat(self, node, frame):
+ self.write('join((')
+ for arg in node.nodes:
+ self.visit(arg, frame)
+ self.write(', ')
+ self.write('))')
+
def visit_Compare(self, node, frame):
self.visit(node.expr, frame)
for op in node.ops:
class Environment(object):
- """The Jinja environment.
-
- The core component of Jinja is the `Environment`. It contains
+ """The core component of Jinja is the `Environment`. It contains
important shared variables like configuration, filters, tests,
- globals and others.
+ globals and others. Instances of this class may be modified if
+ they are not shared and if no template was loaded so far.
+ Modifications on environments after the first template was loaded
+ will lead to surprising effects and undefined behavior.
+
+ Here the possible initialization parameters:
+
+ `block_start_string`
+ The string marking the begin of a block. Defaults to ``'{%'``.
+
+ `block_end_string`
+ The string marking the end of a block. Defaults to ``'%}'``.
+
+ `variable_start_string`
+ The string marking the begin of a print statement.
+ Defaults to ``'{{'``.
+
+ `comment_start_string`
+ The string marking the begin of a comment. Defaults to ``'{#'``.
+
+ `comment_end_string`
+ The string marking the end of a comment. Defaults to ``'#}'``.
+
+ `line_statement_prefix`
+ If given and a string, this will be used as prefix for line based
+ statements.
+
+ `trim_blocks`
+ If this is set to ``True`` the first newline after a block is
+ removed (block, not variable tag!). Defaults to `False`.
+
+ `extensions`
+ List of Jinja extensions to use. This can either be import paths
+ as strings or extension classes.
+
+ `optimized`
+ should the optimizer be enabled? Default is `True`.
+
+ `undefined`
+ :class:`Undefined` or a subclass of it that is used to represent
+ undefined values in the template.
+
+ `finalize`
+ A callable that finalizes the variable. Per default no finalizing
+ is applied.
+
+ `autoescape`
+ If set to true the XML/HTML autoescaping feature is enabled.
+
+ `loader`
+ The template loader for this environment.
"""
#: if this environment is sandboxed. Modifying this variable won't make
extensions=(),
optimized=True,
undefined=Undefined,
- finalize=unicode,
+ finalize=None,
+ autoescape=False,
loader=None):
# !!Important notice!!
# The constructor accepts quite a few arguments that should be
# If parameter changes are required only add parameters at the end
# and don't change the arguments (or the defaults!) of the arguments
# up to (but excluding) loader.
- """Here the possible initialization parameters:
-
- ========================= ============================================
- `block_start_string` the string marking the begin of a block.
- this defaults to ``'{%'``.
- `block_end_string` the string marking the end of a block.
- defaults to ``'%}'``.
- `variable_start_string` the string marking the begin of a print
- statement. defaults to ``'{{'``.
- `comment_start_string` the string marking the begin of a
- comment. defaults to ``'{#'``.
- `comment_end_string` the string marking the end of a comment.
- defaults to ``'#}'``.
- `line_statement_prefix` If given and a string, this will be used as
- prefix for line based statements. See the
- documentation for more details.
- `trim_blocks` If this is set to ``True`` the first newline
- after a block is removed (block, not
- variable tag!). Defaults to ``False``.
- `extensions` List of Jinja extensions to use.
- `optimized` should the optimizer be enabled? Default is
- ``True``.
- `undefined` a subclass of `Undefined` that is used to
- represent undefined variables.
- `finalize` A callable that finalizes the variable. Per
- default this is `unicode`, other useful
- builtin finalizers are `escape`.
- `loader` the loader which should be used.
- ========================= ============================================
- """
# santity checks
assert issubclass(undefined, Undefined), 'undefined must be ' \
self.undefined = undefined
self.optimized = optimized
self.finalize = finalize
+ self.autoescape = autoescape
# defaults
self.filters = DEFAULT_FILTERS.copy()
return self.undefined(obj=obj, name=argument)
def parse(self, source, name=None):
- """Parse the sourcecode and return the abstract syntax tree. This tree
- of nodes is used by the compiler to convert the template into
- executable source- or bytecode.
+ """Parse the sourcecode and return the abstract syntax tree. This
+ tree of nodes is used by the compiler to convert the template into
+ executable source- or bytecode. This is useful for debugging or to
+ extract information from templates.
"""
return Parser(self, source, name).parse()
def lex(self, source, name=None):
- """Lex the given sourcecode and return a generator that yields tokens.
- The stream returned is not usable for Jinja but can be used if
- Jinja templates should be processed by other tools (for example
- syntax highlighting etc)
-
- The tuples are returned in the form ``(lineno, token, value)``.
+ """Lex the given sourcecode and return a generator that yields
+ tokens as tuples in the form ``(lineno, token_type, value)``.
"""
return self.lexer.tokeniter(source, name)
def compile(self, source, name=None, filename=None, globals=None,
raw=False):
- """Compile a node or source. The name is the load name of the
- template after it was joined using `join_path` if necessary,
- filename is the estimated filename of the template on the file
- system. If the template came from a database or memory this
- can be omitted.
+ """Compile a node or template source code. The `name` parameter is
+ the load name of the template after it was joined using
+ :meth:`join_path` if necessary, not the filename on the file system.
+ the `filename` parameter is the estimated filename of the template on
+ the file system. If the template came from a database or memory this
+ can be omitted. The `globals` parameter can be used to provide extra
+ variables at compile time for the template. In the future the
+ optimizer will be able to evaluate parts of the template at compile
+ time based on those variables.
+
+ The return value of this method is a python code object. If the `raw`
+ parameter is `True` the return value will be a string with python
+ code equivalent to the bytecode returned otherwise. This method is
+ mainly used internally.
"""
if isinstance(source, basestring):
source = self.parse(source, name)
def join_path(self, template, parent):
"""Join a template with the parent. By default all the lookups are
- relative to the loader root, but if the paths should be relative this
- function can be used to calculate the real filename."""
+ relative to the loader root so this method returns the `template`
+ parameter unchanged, but if the paths should be relative to the
+ parent template, this function can be used to calculate the real
+ template name.
+
+ Subclasses may override this method and implement template path
+ joining here.
+ """
return template
def get_template(self, name, parent=None, globals=None):
- """Load a template."""
+ """Load a template from the loader. If a loader is configured this
+ method ask the loader for the template and returns a :class:`Template`.
+ If the `parent` parameter is not `None`, :meth:`join_path` is called
+ to get the real template name before loading.
+
+ The `globals` parameter can be used to provide compile-time globals.
+ In the future this will allow the optimizer to render parts of the
+ templates at compile-time.
+
+ If the template does not exist a :exc:`TemplateNotFound` exception is
+ raised.
+ """
if self.loader is None:
raise TypeError('no loader for this environment specified')
if parent is not None:
return self.loader.load(self, name, self.make_globals(globals))
def from_string(self, source, globals=None, template_class=None):
- """Load a template from a string."""
+ """Load a template from a string. This parses the source given and
+ returns a :class:`Template` object.
+ """
globals = self.make_globals(globals)
return template_from_code(self, self.compile(source, globals=globals),
globals, None, template_class)
"""The central template object. This class represents a compiled template
and is used to evaluate it.
- Normally the template object is generated from an `Environment` but it
- also has a constructor that makes it possible to create a template
+ Normally the template object is generated from an :class:`Environment` but
+ it also has a constructor that makes it possible to create a template
instance directly using the constructor. It takes the same arguments as
the environment constructor but it's not possible to specify a loader.
extensions=(),
optimized=True,
undefined=Undefined,
- finalize=unicode):
+ finalize=None,
+ autoescape=False):
env = get_spontaneous_environment(
block_start_string, block_end_string, variable_start_string,
variable_end_string, comment_start_string, comment_end_string,
line_statement_prefix, trim_blocks, tuple(extensions), optimized,
- undefined, finalize)
+ undefined, finalize, autoescape)
return env.from_string(source, template_class=cls)
def render(self, *args, **kwargs):
- """Render the template into a string."""
+ """This method accepts the same arguments as the `dict` constructor:
+ A dict, a dict subclass or some keyword arguments. If no arguments
+ are given the context will be empty. These two calls do the same::
+
+ template.render(knights='that say nih')
+ template.render({'knights': 'that say nih'})
+
+ This will return the rendered template as unicode string.
+ """
try:
return concat(self.generate(*args, **kwargs))
except:
raise exc_type, exc_value, tb.tb_next
def stream(self, *args, **kwargs):
- """Return a `TemplateStream` that generates the template."""
+ """Works exactly like :meth:`generate` but returns a
+ :class:`TemplateStream`.
+ """
try:
return TemplateStream(self.generate(*args, **kwargs))
except:
raise exc_type, exc_value, tb.tb_next
def generate(self, *args, **kwargs):
- """Return a generator that generates the template."""
+ """For very large templates it can be useful to not render the whole
+ template at once but evaluate each statement after another and yield
+ piece for piece. This method basically does exactly that and returns
+ a generator that yields one item after another as unicode strings.
+
+ It accepts the same arguments as :meth:`render`.
+ """
# assemble the context
context = dict(*args, **kwargs)
self.blocks)
def include(self, vars=None):
- """Include this template. When passed a template context or dict
- the template is evaluated in that context and an `IncludedTemplate`
- object is returned. This object then exposes all the exported
- variables as attributes and renders the contents of the template
- when converted to unicode.
+ """Some templates may export macros or other variables. It's possible
+ to access those variables by "including" the template. This is mainly
+ used internally but may also be useful on the Python layer. If passed
+ a context, the template is evaluated in it, otherwise an empty context
+ with just the globals is used.
+
+ The return value is an included template object. Converting it to
+ unicode returns the rendered contents of the template, the exported
+ variables are accessable via the attribute syntax.
+
+ This example shows how it can be used:
+
+ >>> t = Template('{% say_hello(name) %}Hello {{ name }}!{% endmacro %}42')
+ >>> i = t.include()
+ >>> unicode(i)
+ u'42'
+ >>> i.say_hello('John')
+ u'Hello John!'
"""
if isinstance(vars, TemplateContext):
context = TemplateContext(self.environment, vars.parent,
class TemplateStream(object):
- """This class wraps a generator returned from `Template.generate` so that
- it's possible to buffer multiple elements so that it's possible to return
- them from a WSGI application which flushes after each iteration.
+ """A template stream works pretty much like an ordinary python generator
+ but it can buffer multiple items to reduce the number of total iterations.
+ Per default the output is unbuffered which means that for every unbuffered
+ instruction in the template one unicode string is yielded.
+
+ If buffering is enabled with a buffer size of 5, five items are combined
+ into a new unicode string. This is mainly useful if you are streaming
+ big templates to a client via WSGI which flushes after each iteration.
"""
def __init__(self, gen):
self.buffered = False
def enable_buffering(self, size=5):
- """Enable buffering. Buffer `size` items before yielding them."""
+ """Enable buffering. Buffer `size` items before yielding them."""
if size <= 1:
raise ValueError('buffer size too small')
Jinja exceptions.
- :copyright: 2007 by Armin Ronacher.
+ :copyright: 2008 by Armin Ronacher.
:license: BSD, see LICENSE for more details.
"""
class UndefinedError(TemplateError):
- """Raised if a template tries to operate on `Undefined`."""
+ """Raised if a template tries to operate on :class:`Undefined`."""
class TemplateNotFound(IOError, LookupError, TemplateError):
class TemplateRuntimeError(TemplateError):
- """Raised by the template engine if a tag encountered an error when
- rendering.
- """
+ """A runtime error."""
from jinja2.runtime import Undefined, concat
from jinja2.parser import statement_end_tokens
from jinja2.exceptions import TemplateAssertionError
-from jinja2.utils import import_string
+from jinja2.utils import import_string, Markup
# the only real useful gettext functions for a Jinja template. Note
tuple(extensions),
# fill with defaults so that environments are shared
# with other spontaneus environments.
- True, Undefined, unicode
+ True, Undefined, None, False
)
node = environment.parse(fileobj.read().decode(encoding))
return escape(unicode(value))
-def do_replace(s, old, new, count=None):
+@environmentfilter
+def do_replace(environment, s, old, new, count=None):
"""Return a copy of the value with all occurrences of a substring
replaced with a new one. The first argument is the substring
that should be replaced, the second is the replacement string.
"""
if count is None:
count = -1
+ if not environment.autoescape:
+ return unicode(s).replace(unicode(old), unicode(new), count)
if hasattr(old, '__html__') or hasattr(new, '__html__') and \
not hasattr(s, '__html__'):
s = escape(s)
return soft_unicode(s).lower()
-def do_xmlattr(d, autospace=False):
+@environmentfilter
+def do_xmlattr(_environment, *args, **kwargs):
"""Create an SGML/XML attribute string based on the items in a dict.
All values that are neither `none` nor `undefined` are automatically
escaped:
</ul>
As you can see it automatically prepends a space in front of the item
- if the filter returned something. You can disable this by passing
- `false` as only argument to the filter.
+ if the filter returned something.
"""
- if not hasattr(d, 'iteritems'):
- raise TypeError('a dict is required')
- result = []
- for key, value in d.iteritems():
- if value is not None and not isinstance(value, Undefined):
- result.append(u'%s="%s"' % (escape(key), escape(value)))
rv = u' '.join(
u'%s="%s"' % (escape(key), escape(value))
- for key, value in d.iteritems()
+ for key, value in dict(*args, **kwargs).iteritems()
if value is not None and not isinstance(value, Undefined)
)
- if autospace:
- rv = ' ' + rv
- return Markup(rv)
+ if rv:
+ rv = u' ' + rv
+ if _environment.autoescape:
+ rv = Markup(rv)
+ return rv
def do_capitalize(s):
return value
-def do_join(value, d=u''):
+@environmentfilter
+def do_join(environment, value, d=u''):
"""Return a string which is the concatenation of the strings in the
sequence. The separator between elements is an empty string per
default, you can define ith with the optional parameter:
{{ [1, 2, 3]|join }}
-> 123
"""
+ # no automatic escaping? joining is a lot eaiser then
+ if not environment.autoescape:
+ return unicode(d).join(imap(unicode, value))
+
# if the delimiter doesn't have an html representation we check
# if any of the items has. If yes we do a coercion to Markup
if not hasattr(d, '__html__'):
class BaseLoader(object):
"""Baseclass for all loaders. Subclass this and override `get_source` to
- implement a custom loading mechanism.
-
- The environment provides a `get_template` method that will automatically
- call the loader bound to an environment.
+ implement a custom loading mechanism. The environment provides a
+ `get_template` method that calls the loader's `load` method to get the
+ :class:`Template` object.
+
+ A very basic example for a loader that looks up templates on the file
+ system could look like this::
+
+ from jinja2 import BaseLoader, TemplateNotFound
+ from os.path import join, exists, getmtime
+
+ class MyLoader(BaseLoader):
+
+ def __init__(self, path, cache_size=50, auto_reload=True):
+ BaseLoader.__init__(self, cache_size, auto_reload)
+ self.path = path
+
+ def get_source(self, environment, template):
+ path = join(self.path, template)
+ if not exists(path):
+ raise TemplateNotFound(template)
+ mtime = getmtime(path)
+ with file(path) as f:
+ source = f.read().decode('utf-8')
+ return source, path, lambda: mtime != getmtime(path)
"""
def __init__(self, cache_size=50, auto_reload=True):
raise TemplateNotFound(template)
def load(self, environment, name, globals=None):
- """Loads a template. This method should not be overriden by
- subclasses unless `get_source` doesn't provide enough flexibility.
+ """Loads a template. This method looks up the template in the cache
+ or loads one by calling :meth:`get_source`. Subclasses should not
+ override this method as loaders working on collections of other
+ loaders (such as :class:`PrefixLoader` or :class:`ChoiceLoader`)
+ will not call this method but `get_source` directly.
"""
if globals is None:
globals = {}
class FileSystemLoader(BaseLoader):
- """Loads templates from the file system."""
+ """Loads templates from the file system. This loader can find templates
+ in folders on the file system and is the preferred way to load them.
+
+ The loader takes the path to the templates as string, or if multiple
+ locations are wanted a list of them which is then looked up in the
+ given order:
+
+ >>> loader = FileSystemLoader('/path/to/templates')
+ >>> loader = FileSystemLoader(['/path/to/templates', '/other/path'])
+
+ Per default the template encoding is ``'utf-8'`` which can be changed
+ by setting the `encoding` parameter to something else.
+ """
def __init__(self, searchpath, encoding='utf-8', cache_size=50,
auto_reload=True):
BaseLoader.__init__(self, cache_size, auto_reload)
if isinstance(searchpath, basestring):
searchpath = [searchpath]
- self.searchpath = searchpath
+ self.searchpath = list(searchpath)
self.encoding = encoding
def get_source(self, environment, template):
class PackageLoader(BaseLoader):
- """Load templates from python eggs."""
+ """Load templates from python eggs or packages. It is constructed with
+ the name of the python package and the path to the templates in that
+ package:
+
+ >>> loader = PackageLoader('mypackage', 'views')
- def __init__(self, package_name, package_path, charset='utf-8',
- cache_size=50, auto_reload=True):
+ If the package path is not given, ``'templates'`` is assumed.
+
+ Per default the template encoding is ``'utf-8'`` which can be changed
+ by setting the `encoding` parameter to something else. Due to the nature
+ of eggs it's only possible to reload templates if the package was loaded
+ from the file system and not a zip file.
+ """
+
+ def __init__(self, package_name, package_path='templates',
+ encoding='utf-8', cache_size=50, auto_reload=True):
BaseLoader.__init__(self, cache_size, auto_reload)
- import pkg_resources
- self._pkg = pkg_resources
- self.package_name = package_name
+ from pkg_resources import DefaultProvider, ResourceManager, get_provider
+ provider = get_provider(package_name)
+ self.encoding = encoding
+ self.manager = ResourceManager()
+ self.filesystem_bound = isinstance(provider, DefaultProvider)
+ self.provider = provider
self.package_path = package_path
def get_source(self, environment, template):
pieces = split_template_path(template)
- path = '/'.join((self.package_path,) + tuple(pieces))
- if not self._pkg.resource_exists(self.package_name, path):
+ p = '/'.join((self.package_path,) + tuple(pieces))
+ if not self.provider.has_resource(p):
raise TemplateNotFound(template)
- return self._pkg.resource_string(self.package_name, path), None, None
+
+ filename = uptodate = None
+ if self.filesystem_bound:
+ filename = self.provider.get_resource_filename(self.manager, p)
+ mtime = path.getmtime(filename)
+ def uptodate():
+ return path.getmtime(filename) != mtime
+
+ source = self.provider.get_resource_string(self.manager, p)
+ return source.decode(self.encoding), filename, uptodate
class DictLoader(BaseLoader):
- """Loads a template from a python dict. Used for unittests mostly."""
+ """Loads a template from a python dict. It's passed a dict of unicode
+ strings bound to template names. This loader is useful for unittesting:
+
+ >>> loader = DictLoader({'index.html': 'source here'})
+
+ Because auto reloading is rarely useful this is disabled per default.
+ """
- def __init__(self, mapping, cache_size=50):
- BaseLoader.__init__(self, cache_size, False)
+ def __init__(self, mapping, cache_size=50, auto_reload=False):
+ BaseLoader.__init__(self, cache_size, auto_reload)
self.mapping = mapping
def get_source(self, environment, template):
if template in self.mapping:
- return self.mapping[template], None, None
+ source = self.mapping[template]
+ return source, None, lambda: source != self.mapping[template]
raise TemplateNotFound(template)
function becomes the name of the template passed and has to return either
an unicode string with the template source, a tuple in the form ``(source,
filename, uptodatefunc)`` or `None` if the template does not exist.
+
+ >>> def load_template(name):
+ ... if name == 'index.html'
+ ... return '...'
+ ...
+ >>> loader = FunctionLoader(load_template)
+
+ The `uptodatefunc` is a function that is called if autoreload is enabled
+ and has to return `True` if the template is still up to date. For more
+ details have a look at :meth:`BaseLoader.get_source` which has the same
+ return value.
"""
def __init__(self, load_func, cache_size=50, auto_reload=True):
"""A loader that is passed a dict of loaders where each loader is bound
to a prefix. The caching is independent of the actual loaders so the
per loader cache settings are ignored. The prefix is delimited from the
- template by a slash.
+ template by a slash:
+
+ >>> loader = PrefixLoader({
+ ... 'app1': PackageLoader('mypackage.app1'),
+ ... 'app2': PackageLoader('mypackage.app2')
+ ... })
+
+ By loading ``'app1/index.html'`` the file from the app1 package is loaded,
+ by loading ``'app2/index.html'`` the file from the second.
"""
def __init__(self, mapping, delimiter='/', cache_size=50,
specified. If a template could not be found by one loader the next one
is tried. Like for the `PrefixLoader` the cache settings of the actual
loaders don't matter as the choice loader does the caching.
+
+ >>> loader = ChoiceLoader([
+ ... FileSystemLoader('/path/to/user/templates'),
+ ... PackageLoader('myapplication')
+ ])
+
+ This is useful if you want to allow users to override builtin templates
+ from a different location.
"""
def __init__(self, loaders, cache_size=50, auto_reload=True):
:license: GNU GPL.
"""
from types import FunctionType
-from jinja2.utils import Markup, partial
-from jinja2.exceptions import UndefinedError
+from itertools import chain, imap
+from jinja2.utils import Markup, partial, soft_unicode, escape
+from jinja2.exceptions import UndefinedError, TemplateRuntimeError
# these variables are exported to the template runtime
__all__ = ['LoopContext', 'TemplateContext', 'TemplateReference', 'Macro',
- 'Markup', 'missing', 'concat']
+ 'TemplateRuntimeError', 'Markup', 'missing', 'concat', 'escape',
+ 'markup_join', 'unicode_join']
# special singleton representing missing values for the runtime
concat = u''.join
+def markup_join(*args):
+ """Concatenation that escapes if necessary and converts to unicode."""
+ buf = []
+ iterator = imap(soft_unicode, args)
+ for arg in iterator:
+ buf.append(arg)
+ if hasattr(arg, '__html__'):
+ return Markup(u'').join(chain(buf, iterator))
+ return concat(buf)
+
+
+def unicode_join(*args):
+ """Simple args to unicode conversion and concatenation."""
+ return concat(imap(unicode, args))
+
+
class TemplateContext(object):
"""Holds the variables of the local template or of the global one. It's
not save to use this class outside of the compiled code. For example
except LookupError:
return self.environment.undefined('there is no parent block '
'called %r.' % name)
- render = lambda: Markup(concat(blocks[pos](self)))
+ wrap = self.environment.autoescape and Markup or (lambda x: x)
+ render = lambda: wrap(concat(blocks[pos](self)))
render.__name__ = render.name = name
return render
def __getitem__(self, name):
func = self.__context.blocks[name][-1]
- render = lambda: Markup(concat(func(self.__context)))
+ wrap = self.__context.environment.autoescape and \
+ Markup or (lambda x: x)
+ render = lambda: wrap(concat(func(self.__context)))
render.__name__ = render.name = name
return render
class Undefined(object):
- """The default undefined implementation. This undefined implementation
- can be printed and iterated over, but every other access will raise a
- `NameError`. Custom undefined classes must subclass this.
+ """The default undefined type. This undefined type can be printed and
+ iterated over, but every other access will raise an :exc:`UndefinedError`:
+
+ >>> foo = Undefined(name='foo')
+ >>> str(foo)
+ ''
+ >>> not foo
+ True
+ >>> foo + 42
+ Traceback (most recent call last):
+ ...
+ jinja2.exceptions.UndefinedError: 'foo' is undefined
"""
__slots__ = ('_undefined_hint', '_undefined_obj', '_undefined_name')
class DebugUndefined(Undefined):
- """An undefined that returns the debug info when printed."""
+ """An undefined that returns the debug info when printed.
+
+ >>> foo = DebugUndefined(name='foo')
+ >>> str(foo)
+ '{{ foo }}'
+ >>> not foo
+ True
+ >>> foo + 42
+ Traceback (most recent call last):
+ ...
+ jinja2.exceptions.UndefinedError: 'foo' is undefined
+ """
__slots__ = ()
def __unicode__(self):
"""An undefined that barks on print and iteration as well as boolean
tests and all kinds of comparisons. In other words: you can do nothing
with it except checking if it's defined using the `defined` test.
+
+ >>> foo = StrictUndefined(name='foo')
+ >>> str(foo)
+ Traceback (most recent call last):
+ ...
+ jinja2.exceptions.UndefinedError: 'foo' is undefined
+ >>> not foo
+ Traceback (most recent call last):
+ ...
+ jinja2.exceptions.UndefinedError: 'foo' is undefined
+ >>> foo + 42
+ Traceback (most recent call last):
+ ...
+ jinja2.exceptions.UndefinedError: 'foo' is undefined
"""
__slots__ = ()
__iter__ = __unicode__ = __len__ = __nonzero__ = __eq__ = __ne__ = \