1192 lines
		
	
	
		
			46 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			1192 lines
		
	
	
		
			46 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # -*- coding: utf-8 -*-
 | |
| """
 | |
|     jinja2.environment
 | |
|     ~~~~~~~~~~~~~~~~~~
 | |
| 
 | |
|     Provides a class that holds runtime and parsing time options.
 | |
| 
 | |
|     :copyright: (c) 2010 by the Jinja Team.
 | |
|     :license: BSD, see LICENSE for more details.
 | |
| """
 | |
| import os
 | |
| import sys
 | |
| from jinja2 import nodes
 | |
| from jinja2.defaults import BLOCK_START_STRING, \
 | |
|      BLOCK_END_STRING, VARIABLE_START_STRING, VARIABLE_END_STRING, \
 | |
|      COMMENT_START_STRING, COMMENT_END_STRING, LINE_STATEMENT_PREFIX, \
 | |
|      LINE_COMMENT_PREFIX, TRIM_BLOCKS, NEWLINE_SEQUENCE, \
 | |
|      DEFAULT_FILTERS, DEFAULT_TESTS, DEFAULT_NAMESPACE, \
 | |
|      KEEP_TRAILING_NEWLINE, LSTRIP_BLOCKS
 | |
| from jinja2.lexer import get_lexer, TokenStream
 | |
| from jinja2.parser import Parser
 | |
| from jinja2.nodes import EvalContext
 | |
| from jinja2.optimizer import optimize
 | |
| from jinja2.compiler import generate
 | |
| from jinja2.runtime import Undefined, new_context
 | |
| from jinja2.exceptions import TemplateSyntaxError, TemplateNotFound, \
 | |
|      TemplatesNotFound, TemplateRuntimeError
 | |
| from jinja2.utils import import_string, LRUCache, Markup, missing, \
 | |
|      concat, consume, internalcode
 | |
| from jinja2._compat import imap, ifilter, string_types, iteritems, \
 | |
|      text_type, reraise, implements_iterator, implements_to_string, \
 | |
|      get_next, encode_filename, PY2, PYPY
 | |
| from functools import reduce
 | |
| 
 | |
| 
 | |
| # for direct template usage we have up to ten living environments
 | |
| _spontaneous_environments = LRUCache(10)
 | |
| 
 | |
| # the function to create jinja traceback objects.  This is dynamically
 | |
| # imported on the first exception in the exception handler.
 | |
| _make_traceback = None
 | |
| 
 | |
| 
 | |
| def get_spontaneous_environment(*args):
 | |
|     """Return a new spontaneous environment.  A spontaneous environment is an
 | |
|     unnamed and unaccessible (in theory) environment that is used for
 | |
|     templates generated from a string and not from the file system.
 | |
|     """
 | |
|     try:
 | |
|         env = _spontaneous_environments.get(args)
 | |
|     except TypeError:
 | |
|         return Environment(*args)
 | |
|     if env is not None:
 | |
|         return env
 | |
|     _spontaneous_environments[args] = env = Environment(*args)
 | |
|     env.shared = True
 | |
|     return env
 | |
| 
 | |
| 
 | |
| 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 copy_cache(cache):
 | |
|     """Create an empty copy of the given cache."""
 | |
|     if cache is None:
 | |
|         return None
 | |
|     elif type(cache) is dict:
 | |
|         return {}
 | |
|     return LRUCache(cache.capacity)
 | |
| 
 | |
| 
 | |
| def load_extensions(environment, extensions):
 | |
|     """Load the extensions from the list and bind it to the environment.
 | |
|     Returns a dict of instantiated environments.
 | |
|     """
 | |
|     result = {}
 | |
|     for extension in extensions:
 | |
|         if isinstance(extension, string_types):
 | |
|             extension = import_string(extension)
 | |
|         result[extension.identifier] = 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'
 | |
|     assert environment.newline_sequence in ('\r', '\r\n', '\n'), \
 | |
|            'newline_sequence set to unknown line ending string.'
 | |
|     return environment
 | |
| 
 | |
| 
 | |
| class Environment(object):
 | |
|     r"""The core component of Jinja is the `Environment`.  It contains
 | |
|     important shared variables like configuration, filters, tests,
 | |
|     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 ``'{{'``.
 | |
| 
 | |
|         `variable_end_string`
 | |
|             The string marking the end 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 also :ref:`line-statements`.
 | |
| 
 | |
|         `line_comment_prefix`
 | |
|             If given and a string, this will be used as prefix for line based
 | |
|             based comments.  See also :ref:`line-statements`.
 | |
| 
 | |
|             .. versionadded:: 2.2
 | |
| 
 | |
|         `trim_blocks`
 | |
|             If this is set to ``True`` the first newline after a block is
 | |
|             removed (block, not variable tag!).  Defaults to `False`.
 | |
| 
 | |
|         `lstrip_blocks`
 | |
|             If this is set to ``True`` leading spaces and tabs are stripped
 | |
|             from the start of a line to a block.  Defaults to `False`.
 | |
| 
 | |
|         `newline_sequence`
 | |
|             The sequence that starts a newline.  Must be one of ``'\r'``,
 | |
|             ``'\n'`` or ``'\r\n'``.  The default is ``'\n'`` which is a
 | |
|             useful default for Linux and OS X systems as well as web
 | |
|             applications.
 | |
| 
 | |
|         `keep_trailing_newline`
 | |
|             Preserve the trailing newline when rendering templates.
 | |
|             The default is ``False``, which causes a single newline,
 | |
|             if present, to be stripped from the end of the template.
 | |
| 
 | |
|             .. versionadded:: 2.7
 | |
| 
 | |
|         `extensions`
 | |
|             List of Jinja extensions to use.  This can either be import paths
 | |
|             as strings or extension classes.  For more information have a
 | |
|             look at :ref:`the extensions documentation <jinja-extensions>`.
 | |
| 
 | |
|         `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 can be used to process the result of a variable
 | |
|             expression before it is output.  For example one can convert
 | |
|             `None` implicitly into an empty string here.
 | |
| 
 | |
|         `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`.  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.
 | |
| 
 | |
|         `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.
 | |
| 
 | |
|         `bytecode_cache`
 | |
|             If set to a bytecode cache object, this object will provide a
 | |
|             cache for the internal Jinja bytecode so that templates don't
 | |
|             have to be parsed if they were not changed.
 | |
| 
 | |
|             See :ref:`bytecode-cache` for more information.
 | |
|     """
 | |
| 
 | |
|     #: if this environment is sandboxed.  Modifying this variable won't make
 | |
|     #: the environment sandboxed though.  For a real sandboxed environment
 | |
|     #: have a look at jinja2.sandbox.  This flag alone controls the code
 | |
|     #: generation by the compiler.
 | |
|     sandboxed = False
 | |
| 
 | |
|     #: True if the environment is just an overlay
 | |
|     overlayed = False
 | |
| 
 | |
|     #: the environment this environment is linked to if it is an overlay
 | |
|     linked_to = None
 | |
| 
 | |
|     #: shared environments have this set to `True`.  A shared environment
 | |
|     #: must not be modified
 | |
|     shared = False
 | |
| 
 | |
|     #: these are currently EXPERIMENTAL undocumented features.
 | |
|     exception_handler = None
 | |
|     exception_formatter = None
 | |
| 
 | |
|     def __init__(self,
 | |
|                  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,
 | |
|                  line_comment_prefix=LINE_COMMENT_PREFIX,
 | |
|                  trim_blocks=TRIM_BLOCKS,
 | |
|                  lstrip_blocks=LSTRIP_BLOCKS,
 | |
|                  newline_sequence=NEWLINE_SEQUENCE,
 | |
|                  keep_trailing_newline=KEEP_TRAILING_NEWLINE,
 | |
|                  extensions=(),
 | |
|                  optimized=True,
 | |
|                  undefined=Undefined,
 | |
|                  finalize=None,
 | |
|                  autoescape=False,
 | |
|                  loader=None,
 | |
|                  cache_size=50,
 | |
|                  auto_reload=True,
 | |
|                  bytecode_cache=None):
 | |
|         # !!Important notice!!
 | |
|         #   The constructor accepts quite a few arguments that should be
 | |
|         #   passed by keyword rather than position.  However it's important to
 | |
|         #   not change the order of arguments because it's used at least
 | |
|         #   internally in those cases:
 | |
|         #       -   spontaneous environments (i18n extension and Template)
 | |
|         #       -   unittests
 | |
|         #   If parameter changes are required only add parameters at the end
 | |
|         #   and don't change the arguments (or the defaults!) of the arguments
 | |
|         #   existing already.
 | |
| 
 | |
|         # lexer / parser information
 | |
|         self.block_start_string = block_start_string
 | |
|         self.block_end_string = block_end_string
 | |
|         self.variable_start_string = variable_start_string
 | |
|         self.variable_end_string = variable_end_string
 | |
|         self.comment_start_string = comment_start_string
 | |
|         self.comment_end_string = comment_end_string
 | |
|         self.line_statement_prefix = line_statement_prefix
 | |
|         self.line_comment_prefix = line_comment_prefix
 | |
|         self.trim_blocks = trim_blocks
 | |
|         self.lstrip_blocks = lstrip_blocks
 | |
|         self.newline_sequence = newline_sequence
 | |
|         self.keep_trailing_newline = keep_trailing_newline
 | |
| 
 | |
|         # runtime information
 | |
|         self.undefined = undefined
 | |
|         self.optimized = optimized
 | |
|         self.finalize = finalize
 | |
|         self.autoescape = autoescape
 | |
| 
 | |
|         # defaults
 | |
|         self.filters = DEFAULT_FILTERS.copy()
 | |
|         self.tests = DEFAULT_TESTS.copy()
 | |
|         self.globals = DEFAULT_NAMESPACE.copy()
 | |
| 
 | |
|         # set the loader provided
 | |
|         self.loader = loader
 | |
|         self.cache = create_cache(cache_size)
 | |
|         self.bytecode_cache = bytecode_cache
 | |
|         self.auto_reload = auto_reload
 | |
| 
 | |
|         # load extensions
 | |
|         self.extensions = load_extensions(self, extensions)
 | |
| 
 | |
|         _environment_sanity_check(self)
 | |
| 
 | |
|     def add_extension(self, extension):
 | |
|         """Adds an extension after the environment was created.
 | |
| 
 | |
|         .. versionadded:: 2.5
 | |
|         """
 | |
|         self.extensions.update(load_extensions(self, [extension]))
 | |
| 
 | |
|     def extend(self, **attributes):
 | |
|         """Add the items to the instance of the environment if they do not exist
 | |
|         yet.  This is used by :ref:`extensions <writing-extensions>` to register
 | |
|         callbacks and configuration values without breaking inheritance.
 | |
|         """
 | |
|         for key, value in iteritems(attributes):
 | |
|             if not hasattr(self, key):
 | |
|                 setattr(self, key, value)
 | |
| 
 | |
|     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, line_comment_prefix=missing,
 | |
|                 trim_blocks=missing, lstrip_blocks=missing,
 | |
|                 extensions=missing, optimized=missing,
 | |
|                 undefined=missing, finalize=missing, autoescape=missing,
 | |
|                 loader=missing, cache_size=missing, auto_reload=missing,
 | |
|                 bytecode_cache=missing):
 | |
|         """Create a new overlay environment that shares all the data with the
 | |
|         current environment except of cache and the overridden attributes.
 | |
|         Extensions cannot be removed for an overlayed environment.  An 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.overlayed = True
 | |
|         rv.linked_to = self
 | |
| 
 | |
|         for key, value in iteritems(args):
 | |
|             if value is not missing:
 | |
|                 setattr(rv, key, value)
 | |
| 
 | |
|         if cache_size is not missing:
 | |
|             rv.cache = create_cache(cache_size)
 | |
|         else:
 | |
|             rv.cache = copy_cache(self.cache)
 | |
| 
 | |
|         rv.extensions = {}
 | |
|         for key, value in iteritems(self.extensions):
 | |
|             rv.extensions[key] = value.bind(rv)
 | |
|         if extensions is not missing:
 | |
|             rv.extensions.update(load_extensions(rv, extensions))
 | |
| 
 | |
|         return _environment_sanity_check(rv)
 | |
| 
 | |
|     lexer = property(get_lexer, doc="The lexer for this environment.")
 | |
| 
 | |
|     def iter_extensions(self):
 | |
|         """Iterates over the extensions by priority."""
 | |
|         return iter(sorted(self.extensions.values(),
 | |
|                            key=lambda x: x.priority))
 | |
| 
 | |
|     def getitem(self, obj, argument):
 | |
|         """Get an item or attribute of an object but prefer the item."""
 | |
|         try:
 | |
|             return obj[argument]
 | |
|         except (TypeError, LookupError):
 | |
|             if isinstance(argument, string_types):
 | |
|                 try:
 | |
|                     attr = str(argument)
 | |
|                 except Exception:
 | |
|                     pass
 | |
|                 else:
 | |
|                     try:
 | |
|                         return getattr(obj, attr)
 | |
|                     except AttributeError:
 | |
|                         pass
 | |
|             return self.undefined(obj=obj, name=argument)
 | |
| 
 | |
|     def getattr(self, obj, attribute):
 | |
|         """Get an item or attribute of an object but prefer the attribute.
 | |
|         Unlike :meth:`getitem` the attribute *must* be a bytestring.
 | |
|         """
 | |
|         try:
 | |
|             return getattr(obj, attribute)
 | |
|         except AttributeError:
 | |
|             pass
 | |
|         try:
 | |
|             return obj[attribute]
 | |
|         except (TypeError, LookupError, AttributeError):
 | |
|             return self.undefined(obj=obj, name=attribute)
 | |
| 
 | |
|     def call_filter(self, name, value, args=None, kwargs=None,
 | |
|                     context=None, eval_ctx=None):
 | |
|         """Invokes a filter on a value the same way the compiler does it.
 | |
| 
 | |
|         .. versionadded:: 2.7
 | |
|         """
 | |
|         func = self.filters.get(name)
 | |
|         if func is None:
 | |
|             raise TemplateRuntimeError('no filter named %r' % name)
 | |
|         args = [value] + list(args or ())
 | |
|         if getattr(func, 'contextfilter', False):
 | |
|             if context is None:
 | |
|                 raise TemplateRuntimeError('Attempted to invoke context '
 | |
|                                            'filter without context')
 | |
|             args.insert(0, context)
 | |
|         elif getattr(func, 'evalcontextfilter', False):
 | |
|             if eval_ctx is None:
 | |
|                 if context is not None:
 | |
|                     eval_ctx = context.eval_ctx
 | |
|                 else:
 | |
|                     eval_ctx = EvalContext(self)
 | |
|             args.insert(0, eval_ctx)
 | |
|         elif getattr(func, 'environmentfilter', False):
 | |
|             args.insert(0, self)
 | |
|         return func(*args, **(kwargs or {}))
 | |
| 
 | |
|     def call_test(self, name, value, args=None, kwargs=None):
 | |
|         """Invokes a test on a value the same way the compiler does it.
 | |
| 
 | |
|         .. versionadded:: 2.7
 | |
|         """
 | |
|         func = self.tests.get(name)
 | |
|         if func is None:
 | |
|             raise TemplateRuntimeError('no test named %r' % name)
 | |
|         return func(value, *(args or ()), **(kwargs or {}))
 | |
| 
 | |
|     @internalcode
 | |
|     def parse(self, source, name=None, filename=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.  This is useful for debugging or to
 | |
|         extract information from templates.
 | |
| 
 | |
|         If you are :ref:`developing Jinja2 extensions <writing-extensions>`
 | |
|         this gives you a good overview of the node tree generated.
 | |
|         """
 | |
|         try:
 | |
|             return self._parse(source, name, filename)
 | |
|         except TemplateSyntaxError:
 | |
|             exc_info = sys.exc_info()
 | |
|         self.handle_exception(exc_info, source_hint=source)
 | |
| 
 | |
|     def _parse(self, source, name, filename):
 | |
|         """Internal parsing function used by `parse` and `compile`."""
 | |
|         return Parser(self, source, name, encode_filename(filename)).parse()
 | |
| 
 | |
|     def lex(self, source, name=None, filename=None):
 | |
|         """Lex the given sourcecode and return a generator that yields
 | |
|         tokens as tuples in the form ``(lineno, token_type, value)``.
 | |
|         This can be useful for :ref:`extension development <writing-extensions>`
 | |
|         and debugging templates.
 | |
| 
 | |
|         This does not perform preprocessing.  If you want the preprocessing
 | |
|         of the extensions to be applied you have to filter source through
 | |
|         the :meth:`preprocess` method.
 | |
|         """
 | |
|         source = text_type(source)
 | |
|         try:
 | |
|             return self.lexer.tokeniter(source, name, filename)
 | |
|         except TemplateSyntaxError:
 | |
|             exc_info = sys.exc_info()
 | |
|         self.handle_exception(exc_info, source_hint=source)
 | |
| 
 | |
|     def preprocess(self, source, name=None, filename=None):
 | |
|         """Preprocesses the source with all extensions.  This is automatically
 | |
|         called for all parsing and compiling methods but *not* for :meth:`lex`
 | |
|         because there you usually only want the actual source tokenized.
 | |
|         """
 | |
|         return reduce(lambda s, e: e.preprocess(s, name, filename),
 | |
|                       self.iter_extensions(), text_type(source))
 | |
| 
 | |
|     def _tokenize(self, source, name, filename=None, state=None):
 | |
|         """Called by the parser to do the preprocessing and filtering
 | |
|         for all the extensions.  Returns a :class:`~jinja2.lexer.TokenStream`.
 | |
|         """
 | |
|         source = self.preprocess(source, name, filename)
 | |
|         stream = self.lexer.tokenize(source, name, filename, state)
 | |
|         for ext in self.iter_extensions():
 | |
|             stream = ext.filter_stream(stream)
 | |
|             if not isinstance(stream, TokenStream):
 | |
|                 stream = TokenStream(stream, name, filename)
 | |
|         return stream
 | |
| 
 | |
|     def _generate(self, source, name, filename, defer_init=False):
 | |
|         """Internal hook that can be overridden to hook a different generate
 | |
|         method in.
 | |
| 
 | |
|         .. versionadded:: 2.5
 | |
|         """
 | |
|         return generate(source, self, name, filename, defer_init=defer_init)
 | |
| 
 | |
|     def _compile(self, source, filename):
 | |
|         """Internal hook that can be overridden to hook a different compile
 | |
|         method in.
 | |
| 
 | |
|         .. versionadded:: 2.5
 | |
|         """
 | |
|         return compile(source, filename, 'exec')
 | |
| 
 | |
|     @internalcode
 | |
|     def compile(self, source, name=None, filename=None, raw=False,
 | |
|                 defer_init=False):
 | |
|         """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 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.
 | |
| 
 | |
|         `defer_init` is use internally to aid the module code generator.  This
 | |
|         causes the generated code to be able to import without the global
 | |
|         environment variable to be set.
 | |
| 
 | |
|         .. versionadded:: 2.4
 | |
|            `defer_init` parameter added.
 | |
|         """
 | |
|         source_hint = None
 | |
|         try:
 | |
|             if isinstance(source, string_types):
 | |
|                 source_hint = source
 | |
|                 source = self._parse(source, name, filename)
 | |
|             if self.optimized:
 | |
|                 source = optimize(source, self)
 | |
|             source = self._generate(source, name, filename,
 | |
|                                     defer_init=defer_init)
 | |
|             if raw:
 | |
|                 return source
 | |
|             if filename is None:
 | |
|                 filename = '<template>'
 | |
|             else:
 | |
|                 filename = encode_filename(filename)
 | |
|             return self._compile(source, filename)
 | |
|         except TemplateSyntaxError:
 | |
|             exc_info = sys.exc_info()
 | |
|         self.handle_exception(exc_info, source_hint=source)
 | |
| 
 | |
|     def compile_expression(self, source, undefined_to_none=True):
 | |
|         """A handy helper method that returns a callable that accepts keyword
 | |
|         arguments that appear as variables in the expression.  If called it
 | |
|         returns the result of the expression.
 | |
| 
 | |
|         This is useful if applications want to use the same rules as Jinja
 | |
|         in template "configuration files" or similar situations.
 | |
| 
 | |
|         Example usage:
 | |
| 
 | |
|         >>> env = Environment()
 | |
|         >>> expr = env.compile_expression('foo == 42')
 | |
|         >>> expr(foo=23)
 | |
|         False
 | |
|         >>> expr(foo=42)
 | |
|         True
 | |
| 
 | |
|         Per default the return value is converted to `None` if the
 | |
|         expression returns an undefined value.  This can be changed
 | |
|         by setting `undefined_to_none` to `False`.
 | |
| 
 | |
|         >>> env.compile_expression('var')() is None
 | |
|         True
 | |
|         >>> env.compile_expression('var', undefined_to_none=False)()
 | |
|         Undefined
 | |
| 
 | |
|         .. versionadded:: 2.1
 | |
|         """
 | |
|         parser = Parser(self, source, state='variable')
 | |
|         exc_info = None
 | |
|         try:
 | |
|             expr = parser.parse_expression()
 | |
|             if not parser.stream.eos:
 | |
|                 raise TemplateSyntaxError('chunk after expression',
 | |
|                                           parser.stream.current.lineno,
 | |
|                                           None, None)
 | |
|             expr.set_environment(self)
 | |
|         except TemplateSyntaxError:
 | |
|             exc_info = sys.exc_info()
 | |
|         if exc_info is not None:
 | |
|             self.handle_exception(exc_info, source_hint=source)
 | |
|         body = [nodes.Assign(nodes.Name('result', 'store'), expr, lineno=1)]
 | |
|         template = self.from_string(nodes.Template(body, lineno=1))
 | |
|         return TemplateExpression(template, undefined_to_none)
 | |
| 
 | |
|     def compile_templates(self, target, extensions=None, filter_func=None,
 | |
|                           zip='deflated', log_function=None,
 | |
|                           ignore_errors=True, py_compile=False):
 | |
|         """Finds all the templates the loader can find, compiles them
 | |
|         and stores them in `target`.  If `zip` is `None`, instead of in a
 | |
|         zipfile, the templates will be will be stored in a directory.
 | |
|         By default a deflate zip algorithm is used, to switch to
 | |
|         the stored algorithm, `zip` can be set to ``'stored'``.
 | |
| 
 | |
|         `extensions` and `filter_func` are passed to :meth:`list_templates`.
 | |
|         Each template returned will be compiled to the target folder or
 | |
|         zipfile.
 | |
| 
 | |
|         By default template compilation errors are ignored.  In case a
 | |
|         log function is provided, errors are logged.  If you want template
 | |
|         syntax errors to abort the compilation you can set `ignore_errors`
 | |
|         to `False` and you will get an exception on syntax errors.
 | |
| 
 | |
|         If `py_compile` is set to `True` .pyc files will be written to the
 | |
|         target instead of standard .py files.  This flag does not do anything
 | |
|         on pypy and Python 3 where pyc files are not picked up by itself and
 | |
|         don't give much benefit.
 | |
| 
 | |
|         .. versionadded:: 2.4
 | |
|         """
 | |
|         from jinja2.loaders import ModuleLoader
 | |
| 
 | |
|         if log_function is None:
 | |
|             log_function = lambda x: None
 | |
| 
 | |
|         if py_compile:
 | |
|             if not PY2 or PYPY:
 | |
|                 from warnings import warn
 | |
|                 warn(Warning('py_compile has no effect on pypy or Python 3'))
 | |
|                 py_compile = False
 | |
|             else:
 | |
|                 import imp, marshal
 | |
|                 py_header = imp.get_magic() + \
 | |
|                     u'\xff\xff\xff\xff'.encode('iso-8859-15')
 | |
| 
 | |
|                 # Python 3.3 added a source filesize to the header
 | |
|                 if sys.version_info >= (3, 3):
 | |
|                     py_header += u'\x00\x00\x00\x00'.encode('iso-8859-15')
 | |
| 
 | |
|         def write_file(filename, data, mode):
 | |
|             if zip:
 | |
|                 info = ZipInfo(filename)
 | |
|                 info.external_attr = 0o755 << 16
 | |
|                 zip_file.writestr(info, data)
 | |
|             else:
 | |
|                 f = open(os.path.join(target, filename), mode)
 | |
|                 try:
 | |
|                     f.write(data)
 | |
|                 finally:
 | |
|                     f.close()
 | |
| 
 | |
|         if zip is not None:
 | |
|             from zipfile import ZipFile, ZipInfo, ZIP_DEFLATED, ZIP_STORED
 | |
|             zip_file = ZipFile(target, 'w', dict(deflated=ZIP_DEFLATED,
 | |
|                                                  stored=ZIP_STORED)[zip])
 | |
|             log_function('Compiling into Zip archive "%s"' % target)
 | |
|         else:
 | |
|             if not os.path.isdir(target):
 | |
|                 os.makedirs(target)
 | |
|             log_function('Compiling into folder "%s"' % target)
 | |
| 
 | |
|         try:
 | |
|             for name in self.list_templates(extensions, filter_func):
 | |
|                 source, filename, _ = self.loader.get_source(self, name)
 | |
|                 try:
 | |
|                     code = self.compile(source, name, filename, True, True)
 | |
|                 except TemplateSyntaxError as e:
 | |
|                     if not ignore_errors:
 | |
|                         raise
 | |
|                     log_function('Could not compile "%s": %s' % (name, e))
 | |
|                     continue
 | |
| 
 | |
|                 filename = ModuleLoader.get_module_filename(name)
 | |
| 
 | |
|                 if py_compile:
 | |
|                     c = self._compile(code, encode_filename(filename))
 | |
|                     write_file(filename + 'c', py_header +
 | |
|                                marshal.dumps(c), 'wb')
 | |
|                     log_function('Byte-compiled "%s" as %s' %
 | |
|                                  (name, filename + 'c'))
 | |
|                 else:
 | |
|                     write_file(filename, code, 'w')
 | |
|                     log_function('Compiled "%s" as %s' % (name, filename))
 | |
|         finally:
 | |
|             if zip:
 | |
|                 zip_file.close()
 | |
| 
 | |
|         log_function('Finished compiling templates')
 | |
| 
 | |
|     def list_templates(self, extensions=None, filter_func=None):
 | |
|         """Returns a list of templates for this environment.  This requires
 | |
|         that the loader supports the loader's
 | |
|         :meth:`~BaseLoader.list_templates` method.
 | |
| 
 | |
|         If there are other files in the template folder besides the
 | |
|         actual templates, the returned list can be filtered.  There are two
 | |
|         ways: either `extensions` is set to a list of file extensions for
 | |
|         templates, or a `filter_func` can be provided which is a callable that
 | |
|         is passed a template name and should return `True` if it should end up
 | |
|         in the result list.
 | |
| 
 | |
|         If the loader does not support that, a :exc:`TypeError` is raised.
 | |
| 
 | |
|         .. versionadded:: 2.4
 | |
|         """
 | |
|         x = self.loader.list_templates()
 | |
|         if extensions is not None:
 | |
|             if filter_func is not None:
 | |
|                 raise TypeError('either extensions or filter_func '
 | |
|                                 'can be passed, but not both')
 | |
|             filter_func = lambda x: '.' in x and \
 | |
|                                     x.rsplit('.', 1)[1] in extensions
 | |
|         if filter_func is not None:
 | |
|             x = ifilter(filter_func, x)
 | |
|         return x
 | |
| 
 | |
|     def handle_exception(self, exc_info=None, rendered=False, source_hint=None):
 | |
|         """Exception handling helper.  This is used internally to either raise
 | |
|         rewritten exceptions or return a rendered traceback for the template.
 | |
|         """
 | |
|         global _make_traceback
 | |
|         if exc_info is None:
 | |
|             exc_info = sys.exc_info()
 | |
| 
 | |
|         # the debugging module is imported when it's used for the first time.
 | |
|         # we're doing a lot of stuff there and for applications that do not
 | |
|         # get any exceptions in template rendering there is no need to load
 | |
|         # all of that.
 | |
|         if _make_traceback is None:
 | |
|             from jinja2.debug import make_traceback as _make_traceback
 | |
|         traceback = _make_traceback(exc_info, source_hint)
 | |
|         if rendered and self.exception_formatter is not None:
 | |
|             return self.exception_formatter(traceback)
 | |
|         if self.exception_handler is not None:
 | |
|             self.exception_handler(traceback)
 | |
|         exc_type, exc_value, tb = traceback.standard_exc_info
 | |
|         reraise(exc_type, exc_value, tb)
 | |
| 
 | |
|     def join_path(self, template, parent):
 | |
|         """Join a template with the parent.  By default all the lookups are
 | |
|         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
 | |
| 
 | |
|     @internalcode
 | |
|     def _load_template(self, name, globals):
 | |
|         if self.loader is None:
 | |
|             raise TypeError('no loader for this environment specified')
 | |
|         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, globals)
 | |
|         if self.cache is not None:
 | |
|             self.cache[name] = template
 | |
|         return template
 | |
| 
 | |
|     @internalcode
 | |
|     def get_template(self, name, parent=None, globals=None):
 | |
|         """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 template wide globals.
 | |
|         These variables are available in the context at render time.
 | |
| 
 | |
|         If the template does not exist a :exc:`TemplateNotFound` exception is
 | |
|         raised.
 | |
| 
 | |
|         .. versionchanged:: 2.4
 | |
|            If `name` is a :class:`Template` object it is returned from the
 | |
|            function unchanged.
 | |
|         """
 | |
|         if isinstance(name, Template):
 | |
|             return name
 | |
|         if parent is not None:
 | |
|             name = self.join_path(name, parent)
 | |
|         return self._load_template(name, self.make_globals(globals))
 | |
| 
 | |
|     @internalcode
 | |
|     def select_template(self, names, parent=None, globals=None):
 | |
|         """Works like :meth:`get_template` but tries a number of templates
 | |
|         before it fails.  If it cannot find any of the templates, it will
 | |
|         raise a :exc:`TemplatesNotFound` exception.
 | |
| 
 | |
|         .. versionadded:: 2.3
 | |
| 
 | |
|         .. versionchanged:: 2.4
 | |
|            If `names` contains a :class:`Template` object it is returned
 | |
|            from the function unchanged.
 | |
|         """
 | |
|         if not names:
 | |
|             raise TemplatesNotFound(message=u'Tried to select from an empty list '
 | |
|                                             u'of templates.')
 | |
|         globals = self.make_globals(globals)
 | |
|         for name in names:
 | |
|             if isinstance(name, Template):
 | |
|                 return name
 | |
|             if parent is not None:
 | |
|                 name = self.join_path(name, parent)
 | |
|             try:
 | |
|                 return self._load_template(name, globals)
 | |
|             except TemplateNotFound:
 | |
|                 pass
 | |
|         raise TemplatesNotFound(names)
 | |
| 
 | |
|     @internalcode
 | |
|     def get_or_select_template(self, template_name_or_list,
 | |
|                                parent=None, globals=None):
 | |
|         """Does a typecheck and dispatches to :meth:`select_template`
 | |
|         if an iterable of template names is given, otherwise to
 | |
|         :meth:`get_template`.
 | |
| 
 | |
|         .. versionadded:: 2.3
 | |
|         """
 | |
|         if isinstance(template_name_or_list, string_types):
 | |
|             return self.get_template(template_name_or_list, parent, globals)
 | |
|         elif isinstance(template_name_or_list, Template):
 | |
|             return template_name_or_list
 | |
|         return self.select_template(template_name_or_list, parent, globals)
 | |
| 
 | |
|     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)
 | |
|         cls = template_class or self.template_class
 | |
|         return cls.from_code(self, self.compile(source), globals, None)
 | |
| 
 | |
|     def make_globals(self, d):
 | |
|         """Return a dict for the globals."""
 | |
|         if not d:
 | |
|             return self.globals
 | |
|         return dict(self.globals, **d)
 | |
| 
 | |
| 
 | |
| class Template(object):
 | |
|     """The central template object.  This class represents a compiled template
 | |
|     and is used to evaluate it.
 | |
| 
 | |
|     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.
 | |
| 
 | |
|     Every template object has a few methods and members that are guaranteed
 | |
|     to exist.  However it's important that a template object should be
 | |
|     considered immutable.  Modifications on the object are not supported.
 | |
| 
 | |
|     Template objects created from the constructor rather than an environment
 | |
|     do have an `environment` attribute that points to a temporary environment
 | |
|     that is probably shared with other templates created with the constructor
 | |
|     and compatible settings.
 | |
| 
 | |
|     >>> template = Template('Hello {{ name }}!')
 | |
|     >>> template.render(name='John Doe')
 | |
|     u'Hello John Doe!'
 | |
| 
 | |
|     >>> stream = template.stream(name='John Doe')
 | |
|     >>> stream.next()
 | |
|     u'Hello John Doe!'
 | |
|     >>> stream.next()
 | |
|     Traceback (most recent call last):
 | |
|         ...
 | |
|     StopIteration
 | |
|     """
 | |
| 
 | |
|     def __new__(cls, source,
 | |
|                 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,
 | |
|                 line_comment_prefix=LINE_COMMENT_PREFIX,
 | |
|                 trim_blocks=TRIM_BLOCKS,
 | |
|                 lstrip_blocks=LSTRIP_BLOCKS,
 | |
|                 newline_sequence=NEWLINE_SEQUENCE,
 | |
|                 keep_trailing_newline=KEEP_TRAILING_NEWLINE,
 | |
|                 extensions=(),
 | |
|                 optimized=True,
 | |
|                 undefined=Undefined,
 | |
|                 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, line_comment_prefix, trim_blocks,
 | |
|             lstrip_blocks, newline_sequence, keep_trailing_newline,
 | |
|             frozenset(extensions), optimized, undefined, finalize, autoescape,
 | |
|             None, 0, False, None)
 | |
|         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.
 | |
|         """
 | |
|         namespace = {
 | |
|             'environment':  environment,
 | |
|             '__file__':     code.co_filename
 | |
|         }
 | |
|         exec(code, namespace)
 | |
|         rv = cls._from_namespace(environment, namespace, globals)
 | |
|         rv._uptodate = uptodate
 | |
|         return rv
 | |
| 
 | |
|     @classmethod
 | |
|     def from_module_dict(cls, environment, module_dict, globals):
 | |
|         """Creates a template object from a module.  This is used by the
 | |
|         module loader to create a template object.
 | |
| 
 | |
|         .. versionadded:: 2.4
 | |
|         """
 | |
|         return cls._from_namespace(environment, module_dict, globals)
 | |
| 
 | |
|     @classmethod
 | |
|     def _from_namespace(cls, environment, namespace, globals):
 | |
|         t = object.__new__(cls)
 | |
|         t.environment = environment
 | |
|         t.globals = globals
 | |
|         t.name = namespace['name']
 | |
|         t.filename = namespace['__file__']
 | |
|         t.blocks = namespace['blocks']
 | |
| 
 | |
|         # render function and module
 | |
|         t.root_render_func = namespace['root']
 | |
|         t._module = None
 | |
| 
 | |
|         # debug and loader helpers
 | |
|         t._debug_info = namespace['debug_info']
 | |
|         t._uptodate = None
 | |
| 
 | |
|         # store the reference
 | |
|         namespace['environment'] = environment
 | |
|         namespace['__jinja_template__'] = t
 | |
| 
 | |
|         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
 | |
|         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.
 | |
|         """
 | |
|         vars = dict(*args, **kwargs)
 | |
|         try:
 | |
|             return concat(self.root_render_func(self.new_context(vars)))
 | |
|         except Exception:
 | |
|             exc_info = sys.exc_info()
 | |
|         return self.environment.handle_exception(exc_info, True)
 | |
| 
 | |
|     def stream(self, *args, **kwargs):
 | |
|         """Works exactly like :meth:`generate` but returns a
 | |
|         :class:`TemplateStream`.
 | |
|         """
 | |
|         return TemplateStream(self.generate(*args, **kwargs))
 | |
| 
 | |
|     def generate(self, *args, **kwargs):
 | |
|         """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`.
 | |
|         """
 | |
|         vars = dict(*args, **kwargs)
 | |
|         try:
 | |
|             for event in self.root_render_func(self.new_context(vars)):
 | |
|                 yield event
 | |
|         except Exception:
 | |
|             exc_info = sys.exc_info()
 | |
|         else:
 | |
|             return
 | |
|         yield self.environment.handle_exception(exc_info, True)
 | |
| 
 | |
|     def new_context(self, vars=None, shared=False, locals=None):
 | |
|         """Create a new :class:`Context` for this template.  The vars
 | |
|         provided will be passed to the template.  Per default the globals
 | |
|         are added to the context.  If shared is set to `True` the data
 | |
|         is passed as it to the context without adding the globals.
 | |
| 
 | |
|         `locals` can be a dict of local variables for internal usage.
 | |
|         """
 | |
|         return new_context(self.environment, self.name, self.blocks,
 | |
|                            vars, shared, self.globals, locals)
 | |
| 
 | |
|     def make_module(self, vars=None, shared=False, locals=None):
 | |
|         """This method works like the :attr:`module` attribute when called
 | |
|         without arguments but it will evaluate the template on every call
 | |
|         rather than caching it.  It's also possible to provide
 | |
|         a dict which is then used as context.  The arguments are the same
 | |
|         as for the :meth:`new_context` method.
 | |
|         """
 | |
|         return TemplateModule(self, self.new_context(vars, shared, locals))
 | |
| 
 | |
|     @property
 | |
|     def module(self):
 | |
|         """The template as module.  This is used for imports in the
 | |
|         template runtime but is also useful if one wants to access
 | |
|         exported template variables from the Python layer:
 | |
| 
 | |
|         >>> t = Template('{% macro foo() %}42{% endmacro %}23')
 | |
|         >>> unicode(t.module)
 | |
|         u'23'
 | |
|         >>> t.module.foo()
 | |
|         u'42'
 | |
|         """
 | |
|         if self._module is not None:
 | |
|             return self._module
 | |
|         self._module = rv = self.make_module()
 | |
|         return rv
 | |
| 
 | |
|     def get_corresponding_lineno(self, lineno):
 | |
|         """Return the source line number of a line number in the
 | |
|         generated bytecode as they are not in sync.
 | |
|         """
 | |
|         for template_line, code_line in reversed(self.debug_info):
 | |
|             if code_line <= lineno:
 | |
|                 return template_line
 | |
|         return 1
 | |
| 
 | |
|     @property
 | |
|     def is_up_to_date(self):
 | |
|         """If this variable is `False` there is a newer version available."""
 | |
|         if self._uptodate is None:
 | |
|             return True
 | |
|         return self._uptodate()
 | |
| 
 | |
|     @property
 | |
|     def debug_info(self):
 | |
|         """The debug info mapping."""
 | |
|         return [tuple(imap(int, x.split('='))) for x in
 | |
|                 self._debug_info.split('&')]
 | |
| 
 | |
|     def __repr__(self):
 | |
|         if self.name is None:
 | |
|             name = 'memory:%x' % id(self)
 | |
|         else:
 | |
|             name = repr(self.name)
 | |
|         return '<%s %s>' % (self.__class__.__name__, name)
 | |
| 
 | |
| 
 | |
| @implements_to_string
 | |
| class TemplateModule(object):
 | |
|     """Represents an imported template.  All the exported names of the
 | |
|     template are available as attributes on this object.  Additionally
 | |
|     converting it into an unicode- or bytestrings renders the contents.
 | |
|     """
 | |
| 
 | |
|     def __init__(self, template, context):
 | |
|         self._body_stream = list(template.root_render_func(context))
 | |
|         self.__dict__.update(context.get_exported())
 | |
|         self.__name__ = template.name
 | |
| 
 | |
|     def __html__(self):
 | |
|         return Markup(concat(self._body_stream))
 | |
| 
 | |
|     def __str__(self):
 | |
|         return concat(self._body_stream)
 | |
| 
 | |
|     def __repr__(self):
 | |
|         if self.__name__ is None:
 | |
|             name = 'memory:%x' % id(self)
 | |
|         else:
 | |
|             name = repr(self.__name__)
 | |
|         return '<%s %s>' % (self.__class__.__name__, name)
 | |
| 
 | |
| 
 | |
| class TemplateExpression(object):
 | |
|     """The :meth:`jinja2.Environment.compile_expression` method returns an
 | |
|     instance of this object.  It encapsulates the expression-like access
 | |
|     to the template with an expression it wraps.
 | |
|     """
 | |
| 
 | |
|     def __init__(self, template, undefined_to_none):
 | |
|         self._template = template
 | |
|         self._undefined_to_none = undefined_to_none
 | |
| 
 | |
|     def __call__(self, *args, **kwargs):
 | |
|         context = self._template.new_context(dict(*args, **kwargs))
 | |
|         consume(self._template.root_render_func(context))
 | |
|         rv = context.vars['result']
 | |
|         if self._undefined_to_none and isinstance(rv, Undefined):
 | |
|             rv = None
 | |
|         return rv
 | |
| 
 | |
| 
 | |
| @implements_iterator
 | |
| class TemplateStream(object):
 | |
|     """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._gen = gen
 | |
|         self.disable_buffering()
 | |
| 
 | |
|     def dump(self, fp, encoding=None, errors='strict'):
 | |
|         """Dump the complete stream into a file or file-like object.
 | |
|         Per default unicode strings are written, if you want to encode
 | |
|         before writing specify an `encoding`.
 | |
| 
 | |
|         Example usage::
 | |
| 
 | |
|             Template('Hello {{ name }}!').stream(name='foo').dump('hello.html')
 | |
|         """
 | |
|         close = False
 | |
|         if isinstance(fp, string_types):
 | |
|             fp = open(fp, encoding is None and 'w' or 'wb')
 | |
|             close = True
 | |
|         try:
 | |
|             if encoding is not None:
 | |
|                 iterable = (x.encode(encoding, errors) for x in self)
 | |
|             else:
 | |
|                 iterable = self
 | |
|             if hasattr(fp, 'writelines'):
 | |
|                 fp.writelines(iterable)
 | |
|             else:
 | |
|                 for item in iterable:
 | |
|                     fp.write(item)
 | |
|         finally:
 | |
|             if close:
 | |
|                 fp.close()
 | |
| 
 | |
|     def disable_buffering(self):
 | |
|         """Disable the output buffering."""
 | |
|         self._next = get_next(self._gen)
 | |
|         self.buffered = False
 | |
| 
 | |
|     def enable_buffering(self, size=5):
 | |
|         """Enable buffering.  Buffer `size` items before yielding them."""
 | |
|         if size <= 1:
 | |
|             raise ValueError('buffer size too small')
 | |
| 
 | |
|         def generator(next):
 | |
|             buf = []
 | |
|             c_size = 0
 | |
|             push = buf.append
 | |
| 
 | |
|             while 1:
 | |
|                 try:
 | |
|                     while c_size < size:
 | |
|                         c = next()
 | |
|                         push(c)
 | |
|                         if c:
 | |
|                             c_size += 1
 | |
|                 except StopIteration:
 | |
|                     if not c_size:
 | |
|                         return
 | |
|                 yield concat(buf)
 | |
|                 del buf[:]
 | |
|                 c_size = 0
 | |
| 
 | |
|         self.buffered = True
 | |
|         self._next = get_next(generator(get_next(self._gen)))
 | |
| 
 | |
|     def __iter__(self):
 | |
|         return self
 | |
| 
 | |
|     def __next__(self):
 | |
|         return self._next()
 | |
| 
 | |
| 
 | |
| # hook in default template class.  if anyone reads this comment: ignore that
 | |
| # it's possible to use custom templates ;-)
 | |
| Environment.template_class = Template
 | 
