^instance$
^instance/
-^jinja/.*\.so$
-^(build|dist|Jinja\.egg-info)/
+^jinja2/.*\.so$
+^docs/_.*
+^(build|dist|Jinja2\.egg-info)/
\.py[co]$
\.DS_Store$
--------------
.. autoclass:: jinja2.environment.Environment
- :members: from_string, get_template, join_path
+ :members: from_string, get_template, join_path, overlay
.. attribute:: shared
.. attribute:: filters
A dict of filters for this environment. As long as no template was
- loaded it's safe to add new filters or remove old.
+ loaded it's safe to add new filters or remove old. For custom filters
+ see :ref:`writing-filters`.
.. attribute:: tests
A dict of test funcitons for this environment. As long as no
- template way loaded it's safe to modify this dict.
+ template way loaded it's safe to modify this dict. For custom tests
+ see :ref:`writing-tests`.
.. attribute:: globals
A dict of global variables. These variables are always available
in a template and (if the optimizer is enabled) may not be
override by templates. As long as no template was loaded it's safe
- to modify this dict.
+ to modify this dict. For more details see :ref:`global-namespace`.
.. autoclass:: jinja2.Template
:members: render, stream, generate, module
+ .. attribute:: globals
+
+ foo
+
+ .. attribute:: name
+
+ foo
+
.. autoclass:: jinja2.environment.TemplateStream
:members: disable_buffering, enable_buffering
.. autoclass:: jinja2.runtime.StrictUndefined
+The Context
+-----------
+
+.. autoclass:: jinja2.runtime.TemplateContext
+ :members: super, get, get_exported, get_all
+
+ .. attribute:: parent
+
+ A dict of read only, global variables the template looks up. These
+ can either come from another :class:`TemplateContext`, from the
+ :attr:`Environment.globals` or :attr:`Template.globals`. It must not
+ be altered.
+
+ .. attribute:: vars
+
+ The template local variables. This list contains environment and
+ context functions from the :attr:`parent` scope as well as local
+ modifications and exported variables from the template. The template
+ will modify this dict during template evaluation but filters and
+ context functions are not allowed to modify it.
+
+ .. attribute:: environment
+
+ The environment that loaded the template.
+
+ .. attribute:: exported_vars
+
+ This set contains all the names the template exports. The values for
+ the names are in the :attr:`vars` dict. In order to get a copy of the
+ exported variables as dict, :meth:`get_exported` can be used.
+
+ .. attribute:: name
+
+ The load name of the template owning this context.
+
+ .. attribute:: blocks
+
+ A dict with the current mapping of blocks in the template. The keys
+ in this dict are the names of the blocks, and the values a list of
+ blocks registered. The last item in each list is the current active
+ block (latest in the inheritance chain).
+
+
Loaders
-------
Loaders are responsible for loading templates from a resource such as the
-file system and for keeping the compiled modules in memory. These work like
-Python's `sys.modules` which keeps the imported templates in memory. Unlike
-`sys.modules` however this cache is limited in size by default and templates
-are automatically reloaded. Each loader that extends :class:`BaseLoader`
-supports this caching and accepts two parameters to configure it:
-
-`cache_size`
- The size of the cache. Per default this is ``50`` which means that if
- more than 50 templates are loaded the loader will clean out the least
- recently used template. If the cache size is set to ``0`` templates are
- recompiled all the time, if the cache size is ``-1`` the cache will not
- be cleaned.
-
-`auto_reload`
- Some loaders load templates from locations where the template sources
- may change (ie: file system or database). If `auto_reload` is set to
- `True` (default) every time a template is requested the loader checks
- if the source changed and if yes, it will reload the template. For
- higher performance it's possible to disable that.
+file system. The environment will keep the compiled modules in memory like
+Python's `sys.modules`. Unlike `sys.modules` however this cache is limited in
+size by default and templates are automatically reloaded.
.. autoclass:: jinja2.loaders.FileSystemLoader
.. autoclass:: jinja2.exceptions.TemplateSyntaxError
.. autoclass:: jinja2.exceptions.TemplateAssertionError
+
+
+.. _writing-filters:
+
+Custom Filters
+--------------
+
+Custom filters are just regular Python functions that take the left side of
+the filter as first argument and the the arguments passed to the filter as
+extra arguments or keyword arguments.
+
+For example in the filter ``{{ 42|myfilter(23) }}`` the function would be
+called with ``myfilter(42, 23)``. Here for example a simple filter that can
+be applied to datetime objects to format them::
+
+ def datetimeformat(value, format='%H:%M / %d-%m-%Y'):
+ return value.strftime(format)
+
+You can register it on the template environment by updating the
+:attr:`~Environment.filters` dict on the environment::
+
+ environment.filters['datetimeformat'] = datetimeformat
+
+Inside the template it can then be used as follows:
+
+.. sourcecode:: jinja
+
+ written on: {{ article.pub_date|datetimeformat }}
+ publication date: {{ article.pub_date|datetimeformat('%d-%m-%Y') }}
+
+Filters can also be passed the current template context or environment. This
+is useful if a filters wants to return an undefined value or check the current
+:attr:`~Environment.autoescape` setting. For this purpose two decorators
+exist: :func:`environmentfilter` and :func:`contextfilter`.
+
+Here a small example filter that breaks a text into HTML line breaks and
+paragraphs and marks the return value as safe HTML string if autoescaping is
+enabled::
+
+ import re
+ from jinja2 import environmentfilter, Markup, escape
+
+ _paragraph_re = re.compile(r'(?:\r\n|\r|\n){2,}')
+
+ @environmentfilter
+ def nl2br(environment, value):
+ result = u'\n\n'.join(u'<p>%s</p>' % p.replace('\n', '<br>\n')
+ for p in _paragraph_re.split(escape(value)))
+ if environment.autoescape:
+ result = Markup(result)
+ return result
+
+Context filters work the same just that the first argument is the current
+active :class:`TemplateContext` rather then the environment.
+
+
+.. _writing-tests:
+
+Custom Tests
+------------
+
+Tests work like filters just that there is no way for a filter to get access
+to the environment or context and that they can't be chained. The return
+value of a filter should be `True` or `False`. The purpose of a filter is to
+give the template designers the possibility to perform type and conformability
+checks.
+
+Here a simple filter that checks if a variable is a prime number::
+
+ import math
+
+ def is_prime(n):
+ if n == 2:
+ return True
+ for i in xrange(2, int(math.ceil(math.sqrt(n))) + 1):
+ if n % i == 0:
+ return False
+ return True
+
+
+You can register it on the template environment by updating the
+:attr:`~Environment.tests` dict on the environment::
+
+ environment.tests['prime'] = is_prime
+
+A template designer can then use the test like this:
+
+.. sourcecode:: jinja
+
+ {% if 42 is prime %}
+ 42 is a prime number
+ {% else %}
+ 42 is not a prime number
+ {% endif %}
+
+
+.. _global-namespace:
+
+The Global Namespace
+--------------------
+
+Variables stored in the :attr:`Environment.globals` or :attr:`Template.globals`
+dicts are special as they are available for imported templates too and will be
+used by the optimizer in future releases to evaluates parts of the template at
+compile time. This is the place where you can put variables and functions
+that should be available all the time.
\definecolor{TitleColor}{rgb}{0.7,0,0}
\definecolor{InnerLinkColor}{rgb}{0.7,0,0}
\definecolor{OuterLinkColor}{rgb}{0.8,0,0}
-\definecolor{VerbatimColor}{rgb}{0.98,0.98,0.98}
+\definecolor{VerbatimColor}{rgb}{0.985,0.985,0.985}
\definecolor{VerbatimBorderColor}{rgb}{0.8,0.8,0.8}
'''
Name.Tag: 'bold #686868',
Name.Decorator: '#686868',
- String: '#BE9B5D',
+ String: '#AA891C',
Number: '#444444',
Generic.Heading: 'bold #000080',
---------------------
.. jinjatests::
+
+
+List of Global Functions
+------------------------
+
+The following functions are available in the global scope by default:
+
+.. function:: range([start,] stop[, step])
+
+ Return a list containing an arithmetic progression of integers.
+ range(i, j) returns [i, i+1, i+2, ..., j-1]; start (!) defaults to 0.
+ When step is given, it specifies the increment (or decrement).
+ For example, range(4) returns [0, 1, 2, 3]. The end point is omitted!
+ These are exactly the valid indices for a list of 4 elements.
+
+.. function:: lipsum(n=5, html=True, min=20, max=100)
+
+ Generates some lorem ipsum for the template. Per default five paragraphs
+ with HTML are generated each paragraph between 20 and 100 words. If html
+ is disabled regular text is returned. This is useful to generate simple
+ contents for layout testing.
# decorators and public utilities
from jinja2.filters import environmentfilter, contextfilter
from jinja2.utils import Markup, escape, environmentfunction, contextfunction
+
+__all__ = [
+ 'Environment', 'Template', 'BaseLoader', 'FileSystemLoader',
+ 'PackageLoader', 'DictLoader', 'FunctionLoader', 'PrefixLoader',
+ 'ChoiceLoader', 'Undefined', 'DebugUndefined', 'StrictUndefined',
+ 'TemplateError', 'UndefinedError', 'TemplateNotFound',
+ 'TemplateSyntaxError', 'TemplateAssertionError', 'environmentfilter',
+ 'contextfilter', 'Markup', 'escape', 'environmentfunction',
+ 'contextfunction'
+]
from jinja2.utils import generate_lorem_ipsum
+BLOCK_START_STRING = '{%'
+BLOCK_END_STRING = '%}'
+VARIABLE_START_STRING = '{{'
+VARIABLE_END_STRING = '}}'
+COMMENT_START_STRING = '{#'
+COMMENT_END_STRING = '#}'
+LINE_STATEMENT_PREFIX = None
+
+
DEFAULT_NAMESPACE = {
'range': xrange,
'lipsum': generate_lorem_ipsum
}
+
+
+# export all constants
+__all__ = tuple(x for x in locals() if x.isupper())
:license: BSD, see LICENSE for more details.
"""
import sys
+from jinja2.defaults import *
from jinja2.lexer import Lexer
from jinja2.parser import Parser
from jinja2.optimizer import optimize
from jinja2.compiler import generate
from jinja2.runtime import Undefined, TemplateContext, concat
from jinja2.debug import translate_exception
-from jinja2.utils import import_string, LRUCache, Markup
-from jinja2.defaults import DEFAULT_FILTERS, DEFAULT_TESTS, DEFAULT_NAMESPACE
+from jinja2.utils import import_string, LRUCache, Markup, missing
# for direct template usage we have up to ten living environments
return env
-def template_from_code(environment, code, globals, uptodate=None,
- template_class=None):
- """Generate a new template object from code. It's used in the
- template constructor and the loader `load` implementation.
+def create_cache(size):
+ """Return the cache class for the given size."""
+ if size == 0:
+ return None
+ if size < 0:
+ return {}
+ return LRUCache(size)
+
+
+def load_extensions(environment, extensions):
+ """Load the extensions from the list and bind it to the environment.
+ Returns a new list of instanciated environments.
"""
- t = object.__new__(template_class or environment.template_class)
- namespace = {
- 'environment': environment,
- '__jinja_template__': t
- }
- exec code in namespace
- t.environment = environment
- t.name = namespace['name']
- t.filename = code.co_filename
- t.root_render_func = namespace['root']
- t.blocks = namespace['blocks']
- t.globals = globals
-
- # debug and loader helpers
- t._debug_info = namespace['debug_info']
- t._uptodate = uptodate
-
- return t
+ result = []
+ for extension in extensions:
+ if isinstance(extension, basestring):
+ extension = import_string(extension)
+ result.append(extension(environment))
+ return result
+
+
+def _environment_sanity_check(environment):
+ """Perform a sanity check on the environment."""
+ assert issubclass(environment.undefined, Undefined), 'undefined must ' \
+ 'be a subclass of undefined because filters depend on it.'
+ assert environment.block_start_string != \
+ environment.variable_start_string != \
+ environment.comment_start_string, 'block, variable and comment ' \
+ 'start strings must be different'
class Environment(object):
`loader`
The template loader for this environment.
+
+ `cache_size`
+ The size of the cache. Per default this is ``50`` which means that if
+ more than 50 templates are loaded the loader will clean out the least
+ recently used template. If the cache size is set to ``0`` templates are
+ recompiled all the time, if the cache size is ``-1`` the cache will not
+ be cleaned.
+
+ `auto_reload`
+ Some loaders load templates from locations where the template sources
+ may change (ie: file system or database). If `auto_reload` is set to
+ `True` (default) every time a template is requested the loader checks
+ if the source changed and if yes, it will reload the template. For
+ higher performance it's possible to disable that.
"""
#: if this environment is sandboxed. Modifying this variable won't make
#: have a look at jinja2.sandbox
sandboxed = False
+ #: True if the environment is just an overlay
+ overlay = False
+
#: shared environments have this set to `True`. A shared environment
#: must not be modified
shared = False
def __init__(self,
- block_start_string='{%',
- block_end_string='%}',
- variable_start_string='{{',
- variable_end_string='}}',
- comment_start_string='{#',
- comment_end_string='#}',
- line_statement_prefix=None,
+ block_start_string=BLOCK_START_STRING,
+ block_end_string=BLOCK_END_STRING,
+ variable_start_string=VARIABLE_START_STRING,
+ variable_end_string=VARIABLE_END_STRING,
+ comment_start_string=COMMENT_START_STRING,
+ comment_end_string=COMMENT_END_STRING,
+ line_statement_prefix=LINE_STATEMENT_PREFIX,
trim_blocks=False,
extensions=(),
optimized=True,
undefined=Undefined,
finalize=None,
autoescape=False,
- loader=None):
+ loader=None,
+ cache_size=50,
+ auto_reload=True):
# !!Important notice!!
# The constructor accepts quite a few arguments that should be
# passed by keyword rather than position. However it's important to
# - unittests
# 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.
-
- # santity checks
- assert issubclass(undefined, Undefined), 'undefined must be ' \
- 'a subclass of undefined because filters depend on it.'
- assert block_start_string != variable_start_string != \
- comment_start_string, 'block, variable and comment ' \
- 'start strings must be different'
+ # existing already.
# lexer / parser information
self.block_start_string = block_start_string
# set the loader provided
self.loader = loader
-
- # create lexer
- self.lexer = Lexer(self)
+ self.cache = create_cache(cache_size)
+ self.auto_reload = auto_reload
# load extensions
- self.extensions = []
- for extension in extensions:
- if isinstance(extension, basestring):
- extension = import_string(extension)
- self.extensions.append(extension(self))
+ self.extensions = load_extensions(self, extensions)
+
+ _environment_sanity_check(self)
+
+ def overlay(self, block_start_string=missing, block_end_string=missing,
+ variable_start_string=missing, variable_end_string=missing,
+ comment_start_string=missing, comment_end_string=missing,
+ line_statement_prefix=missing, trim_blocks=missing,
+ extensions=missing, optimized=missing, undefined=missing,
+ finalize=missing, autoescape=missing, loader=missing,
+ cache_size=missing, auto_reload=missing):
+ """Create a new overlay environment that shares all the data with the
+ current environment except of cache and the overriden attributes.
+ Extensions cannot be removed for a overlayed environment. A overlayed
+ environment automatically gets all the extensions of the environment it
+ is linked to plus optional extra extensions.
+
+ Creating overlays should happen after the initial environment was set
+ up completely. Not all attributes are truly linked, some are just
+ copied over so modifications on the original environment may not shine
+ through.
+ """
+ args = dict(locals())
+ del args['self'], args['cache_size'], args['extensions']
+
+ rv = object.__new__(self.__class__)
+ rv.__dict__.update(self.__dict__)
+ rv.overlay = True
+ rv.linked_to = self
+
+ for key, value in args.iteritems():
+ if value is not missing:
+ setattr(rv, key, value)
+
+ if cache_size is not missing:
+ rv.cache = create_cache(cache_size)
+
+ rv.extensions = []
+ for extension in self.extensions:
+ rv.extensions.append(extension.bind(self))
+ if extensions is not missing:
+ rv.extensions.extend(load_extensions(extensions))
+
+ _environment_sanity_check(rv)
+ return rv
+
+ @property
+ def lexer(self):
+ """Return a fresh lexer for the environment."""
+ return Lexer(self)
def subscribe(self, obj, argument):
"""Get an item or attribute of an object."""
raise TypeError('no loader for this environment specified')
if parent is not None:
name = self.join_path(name, parent)
- return self.loader.load(self, name, self.make_globals(globals))
+
+ if self.cache is not None:
+ template = self.cache.get(name)
+ if template is not None and (not self.auto_reload or \
+ template.is_up_to_date):
+ return template
+
+ template = self.loader.load(self, name, self.make_globals(globals))
+ if self.cache is not None:
+ self.cache[name] = template
+ return template
def from_string(self, source, globals=None, template_class=None):
"""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)
+ cls = template_class or self.template_class
+ return cls.from_code(self, self.compile(source, globals=globals),
+ globals, None)
def make_globals(self, d):
"""Return a dict for the globals."""
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, autoescape)
+ undefined, finalize, autoescape, None, 0, False)
return env.from_string(source, template_class=cls)
+ @classmethod
+ def from_code(cls, environment, code, globals, uptodate=None):
+ """Creates a template object from compiled code and the globals. This
+ is used by the loaders and environment to create a template object.
+ """
+ t = object.__new__(cls)
+ namespace = {
+ 'environment': environment,
+ '__jinja_template__': t
+ }
+ exec code in namespace
+ t.environment = environment
+ t.name = namespace['name']
+ t.filename = code.co_filename
+ t.root_render_func = namespace['root']
+ t.blocks = namespace['blocks']
+ t.globals = globals
+
+ # debug and loader helpers
+ t._debug_info = namespace['debug_info']
+ t._uptodate = uptodate
+
+ return t
+
def render(self, *args, **kwargs):
"""This method accepts the same arguments as the `dict` constructor:
A dict, a dict subclass or some keyword arguments. If no arguments
class Extension(object):
- """Instances of this class store parser extensions."""
+ """Extensions can be used to add extra functionality to the Jinja template
+ system at the parser level. This is a supported but currently
+ undocumented interface. Custom extensions are bound to an environment but
+ may not store environment specific data on `self`. The reason for this is
+ that an extension can be bound to another environment (for overlays) by
+ creating a copy and reassigning the `environment` attribute.
+ """
#: if this extension parses this is the list of tags it's listening to.
tags = set()
def __init__(self, environment):
self.environment = environment
+ def bind(self, environment):
+ """Create a copy of this extension bound to another environment."""
+ rv = object.__new__(self.__class__)
+ rv.__dict__.update(self.__dict__)
+ rv.environment = environment
+ return rv
+
def parse(self, parser):
"""Called if one of the tags matched."""
options.get('trim_blocks', '').lower() in ('1', 'on', 'yes', 'true'),
tuple(extensions),
# fill with defaults so that environments are shared
- # with other spontaneus environments.
- True, Undefined, None, False
+ # with other spontaneus environments. The rest of the
+ # arguments are optimizer, undefined, finalize, autoescape,
+ # loader, cache size and auto reloading setting
+ True, Undefined, None, False, None, 0, False
)
node = environment.parse(fileobj.read().decode(encoding))
Jinja loader classes.
+ XXX: move caching from the loaders to environment.get_template and add
+ environment overlays that allow to redefine escaping and other things but
+ shared the globals and filter mappings.
+
:copyright: 2008 by Armin Ronacher.
:license: BSD, see LICENSE for more details.
"""
from os import path
from jinja2.exceptions import TemplateNotFound
-from jinja2.environment import template_from_code
from jinja2.utils import LRUCache
return source, path, lambda: mtime != getmtime(path)
"""
- def __init__(self, cache_size=50, auto_reload=True):
- if cache_size == 0:
- self.cache = None
- elif cache_size < 0:
- self.cache = {}
- else:
- self.cache = LRUCache(cache_size)
- self.auto_reload = auto_reload
-
def get_source(self, environment, template):
"""Get the template source, filename and reload helper for a template.
It's passed the environment and template name and has to return a
"""
if globals is None:
globals = {}
-
- if self.cache is not None:
- template = self.cache.get(name)
- if template is not None and (not self.auto_reload or \
- template.is_up_to_date):
- return template
-
source, filename, uptodate = self.get_source(environment, name)
code = environment.compile(source, name, filename, globals)
- template = template_from_code(environment, code, globals, uptodate)
- if self.cache is not None:
- self.cache[name] = template
- return template
+ return environment.template_class.from_code(environment, code,
+ globals, uptodate)
class FileSystemLoader(BaseLoader):
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)
+ def __init__(self, searchpath, encoding='utf-8'):
if isinstance(searchpath, basestring):
searchpath = [searchpath]
self.searchpath = list(searchpath)
"""
def __init__(self, package_name, package_path='templates',
- encoding='utf-8', cache_size=50, auto_reload=True):
- BaseLoader.__init__(self, cache_size, auto_reload)
+ encoding='utf-8'):
from pkg_resources import DefaultProvider, ResourceManager, get_provider
provider = get_provider(package_name)
self.encoding = encoding
Because auto reloading is rarely useful this is disabled per default.
"""
- def __init__(self, mapping, cache_size=50, auto_reload=False):
- BaseLoader.__init__(self, cache_size, auto_reload)
+ def __init__(self, mapping):
self.mapping = mapping
def get_source(self, environment, template):
return value.
"""
- def __init__(self, load_func, cache_size=50, auto_reload=True):
- BaseLoader.__init__(self, cache_size, auto_reload)
+ def __init__(self, load_func):
self.load_func = load_func
def get_source(self, environment, template):
class PrefixLoader(BaseLoader):
"""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:
+ to a prefix. The prefix is delimited from the template by a slash per
+ default, which can be changed by setting the `delimiter` argument to
+ something else.
>>> loader = PrefixLoader({
... 'app1': PackageLoader('mypackage.app1'),
by loading ``'app2/index.html'`` the file from the second.
"""
- def __init__(self, mapping, delimiter='/', cache_size=50,
- auto_reload=True):
- BaseLoader.__init__(self, cache_size, auto_reload)
+ def __init__(self, mapping, delimiter='/'):
self.mapping = mapping
self.delimiter = delimiter
class ChoiceLoader(BaseLoader):
"""This loader works like the `PrefixLoader` just that no prefix is
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.
+ is tried.
>>> loader = ChoiceLoader([
... FileSystemLoader('/path/to/user/templates'),
from a different location.
"""
- def __init__(self, loaders, cache_size=50, auto_reload=True):
- BaseLoader.__init__(self, cache_size, auto_reload)
+ def __init__(self, loaders):
self.loaders = loaders
def get_source(self, environment, template):
import sys
from types import FunctionType
from itertools import chain, imap
-from jinja2.utils import Markup, partial, soft_unicode, escape
+from jinja2.utils import Markup, partial, soft_unicode, escape, missing
from jinja2.exceptions import UndefinedError, TemplateRuntimeError
'markup_join', 'unicode_join']
-# special singleton representing missing values for the runtime
-missing = type('MissingType', (), {'__repr__': lambda x: 'missing'})()
-
-
# concatenate a list of strings and convert them to unicode.
# unfortunately there is a bug in python 2.4 and lower that causes
# unicode.join trash the traceback.
raise TypeError(_test_gen_bug)
yield None
u''.join(_test_gen_bug())
-except TypeError, e:
- if e.args and e.args[0] is _test_gen_bug:
+except TypeError, _error:
+ if _error.args and _error.args[0] is _test_gen_bug:
concat = u''.join
else:
def concat(gen):
except:
exc_type, exc_value, tb = sys.exc_info()
raise exc_type, exc_value, tb.tb_next
-del _test_gen_bug
+ del _test_gen_bug, _error
def markup_join(*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
- update and other methods will not work as they seem (they don't update
- the exported variables for example).
-
- The context is immutable. Modifications on `parent` must not happen and
- modifications on `vars` are allowed from generated template code. However
- functions that are passed the template context may not modify the context
- in any way.
+ """The template context holds the variables of a template. It stores the
+ values passed to the template and also the names the template exports.
+ Creating instances is neither supported nor useful as it's created
+ automatically at various stages of the template evaluation and should not
+ be created by hand.
+
+ The context is immutable. Modifications on :attr:`parent` **must not**
+ happen and modifications on :attr:`vars` are allowed from generated
+ template code only. Template filters and global functions marked as
+ :func:`contextfunction`\s get the active context passed as first argument
+ and are allowed to access the context read-only.
+
+ The template context supports read only dict operations (`get`,
+ `__getitem__`, `__contains__`) however `__getitem__` doesn't fail with
+ a `KeyError` but returns an :attr:`Undefined` object.
"""
def __init__(self, environment, parent, name, blocks):
return render
def get(self, key, default=None):
- """For dict compatibility"""
+ """Returns an item from the template context, if it doesn't exist
+ `default` is returned.
+ """
if key in self.vars:
return self.vars[key]
if key in self.parent:
"""Get a new dict with the exported variables."""
return dict((k, self.vars[k]) for k in self.exported_vars)
- def get_root(self):
- """Return a new dict with all the non local variables."""
- return dict(self.parent)
-
def get_all(self):
- """Return a copy of the complete context as dict."""
+ """Return a copy of the complete context as dict including the
+ global variables.
+ """
return dict(self.parent, **self.vars)
- def clone(self):
- """Return a copy of the context without the locals."""
- return self.__class__(self.environment, self.parent,
- self.name, self.blocks)
-
def __contains__(self, name):
return name in self.vars or name in self.parent
_simple_email_re = re.compile(r'^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$')
+# special singleton representing missing values for the runtime
+missing = type('MissingType', (), {'__repr__': lambda x: 'missing'})()
+
+
def contextfunction(f):
"""This decorator can be used to mark a callable as context callable. A
context callable is passed the active context as first argument if it
raise TemplateNotFound(name)
-loader = GlobalLoader(cache_size=0)
-simple_env = Environment(trim_blocks=True, loader=loader)
+loader = GlobalLoader()
+simple_env = Environment(trim_blocks=True, loader=loader, cache_size=0)
class Module(py.test.collect.Module):