| Index: third_party/jinja2/environment.py
|
| diff --git a/third_party/jinja2/environment.py b/third_party/jinja2/environment.py
|
| index 7a9a59fce46bd935c4a845f705e7a1631565a858..45fabada2eb238504e1c0ccda6e42fd9cf2e707e 100644
|
| --- a/third_party/jinja2/environment.py
|
| +++ b/third_party/jinja2/environment.py
|
| @@ -11,16 +11,26 @@
|
| import os
|
| import sys
|
| from jinja2 import nodes
|
| -from jinja2.defaults import *
|
| +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
|
| + TemplatesNotFound, TemplateRuntimeError
|
| from jinja2.utils import import_string, LRUCache, Markup, missing, \
|
| - concat, consume, internalcode, _encode_filename
|
| + 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
|
| @@ -67,11 +77,11 @@ def copy_cache(cache):
|
|
|
| def load_extensions(environment, extensions):
|
| """Load the extensions from the list and bind it to the environment.
|
| - Returns a dict of instanciated environments.
|
| + Returns a dict of instantiated environments.
|
| """
|
| result = {}
|
| for extension in extensions:
|
| - if isinstance(extension, basestring):
|
| + if isinstance(extension, string_types):
|
| extension = import_string(extension)
|
| result[extension.identifier] = extension(environment)
|
| return result
|
| @@ -134,12 +144,23 @@ class Environment(object):
|
| 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
|
| @@ -224,7 +245,9 @@ class Environment(object):
|
| 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,
|
| @@ -239,7 +262,7 @@ class Environment(object):
|
| # 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:
|
| - # - spontaneus environments (i18n extension and Template)
|
| + # - 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
|
| @@ -255,7 +278,9 @@ class Environment(object):
|
| 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
|
| @@ -270,7 +295,6 @@ class Environment(object):
|
|
|
| # set the loader provided
|
| self.loader = loader
|
| - self.bytecode_cache = None
|
| self.cache = create_cache(cache_size)
|
| self.bytecode_cache = bytecode_cache
|
| self.auto_reload = auto_reload
|
| @@ -292,7 +316,7 @@ class Environment(object):
|
| yet. This is used by :ref:`extensions <writing-extensions>` to register
|
| callbacks and configuration values without breaking inheritance.
|
| """
|
| - for key, value in attributes.iteritems():
|
| + for key, value in iteritems(attributes):
|
| if not hasattr(self, key):
|
| setattr(self, key, value)
|
|
|
| @@ -300,7 +324,8 @@ class Environment(object):
|
| 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, extensions=missing, optimized=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):
|
| @@ -323,7 +348,7 @@ class Environment(object):
|
| rv.overlayed = True
|
| rv.linked_to = self
|
|
|
| - for key, value in args.iteritems():
|
| + for key, value in iteritems(args):
|
| if value is not missing:
|
| setattr(rv, key, value)
|
|
|
| @@ -333,7 +358,7 @@ class Environment(object):
|
| rv.cache = copy_cache(self.cache)
|
|
|
| rv.extensions = {}
|
| - for key, value in self.extensions.iteritems():
|
| + 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))
|
| @@ -352,7 +377,7 @@ class Environment(object):
|
| try:
|
| return obj[argument]
|
| except (TypeError, LookupError):
|
| - if isinstance(argument, basestring):
|
| + if isinstance(argument, string_types):
|
| try:
|
| attr = str(argument)
|
| except Exception:
|
| @@ -377,6 +402,42 @@ class Environment(object):
|
| 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
|
| @@ -395,7 +456,7 @@ class Environment(object):
|
|
|
| def _parse(self, source, name, filename):
|
| """Internal parsing function used by `parse` and `compile`."""
|
| - return Parser(self, source, name, _encode_filename(filename)).parse()
|
| + 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
|
| @@ -407,7 +468,7 @@ class Environment(object):
|
| of the extensions to be applied you have to filter source through
|
| the :meth:`preprocess` method.
|
| """
|
| - source = unicode(source)
|
| + source = text_type(source)
|
| try:
|
| return self.lexer.tokeniter(source, name, filename)
|
| except TemplateSyntaxError:
|
| @@ -420,7 +481,7 @@ class Environment(object):
|
| because there you usually only want the actual source tokenized.
|
| """
|
| return reduce(lambda s, e: e.preprocess(s, name, filename),
|
| - self.iter_extensions(), unicode(source))
|
| + 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
|
| @@ -435,7 +496,7 @@ class Environment(object):
|
| return stream
|
|
|
| def _generate(self, source, name, filename, defer_init=False):
|
| - """Internal hook that can be overriden to hook a different generate
|
| + """Internal hook that can be overridden to hook a different generate
|
| method in.
|
|
|
| .. versionadded:: 2.5
|
| @@ -443,7 +504,7 @@ class Environment(object):
|
| return generate(source, self, name, filename, defer_init=defer_init)
|
|
|
| def _compile(self, source, filename):
|
| - """Internal hook that can be overriden to hook a different compile
|
| + """Internal hook that can be overridden to hook a different compile
|
| method in.
|
|
|
| .. versionadded:: 2.5
|
| @@ -474,7 +535,7 @@ class Environment(object):
|
| """
|
| source_hint = None
|
| try:
|
| - if isinstance(source, basestring):
|
| + if isinstance(source, string_types):
|
| source_hint = source
|
| source = self._parse(source, name, filename)
|
| if self.optimized:
|
| @@ -486,7 +547,7 @@ class Environment(object):
|
| if filename is None:
|
| filename = '<template>'
|
| else:
|
| - filename = _encode_filename(filename)
|
| + filename = encode_filename(filename)
|
| return self._compile(source, filename)
|
| except TemplateSyntaxError:
|
| exc_info = sys.exc_info()
|
| @@ -556,7 +617,9 @@ class Environment(object):
|
| 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.
|
| + 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
|
| """
|
| @@ -566,14 +629,23 @@ class Environment(object):
|
| log_function = lambda x: None
|
|
|
| if py_compile:
|
| - import imp, marshal
|
| - py_header = imp.get_magic() + \
|
| - u'\xff\xff\xff\xff'.encode('iso-8859-15')
|
| + 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 = 0755 << 16L
|
| + info.external_attr = 0o755 << 16
|
| zip_file.writestr(info, data)
|
| else:
|
| f = open(os.path.join(target, filename), mode)
|
| @@ -597,7 +669,7 @@ class Environment(object):
|
| source, filename, _ = self.loader.get_source(self, name)
|
| try:
|
| code = self.compile(source, name, filename, True, True)
|
| - except TemplateSyntaxError, e:
|
| + except TemplateSyntaxError as e:
|
| if not ignore_errors:
|
| raise
|
| log_function('Could not compile "%s": %s' % (name, e))
|
| @@ -606,7 +678,7 @@ class Environment(object):
|
| filename = ModuleLoader.get_module_filename(name)
|
|
|
| if py_compile:
|
| - c = self._compile(code, _encode_filename(filename))
|
| + c = self._compile(code, encode_filename(filename))
|
| write_file(filename + 'c', py_header +
|
| marshal.dumps(c), 'wb')
|
| log_function('Byte-compiled "%s" as %s' %
|
| @@ -644,7 +716,7 @@ class Environment(object):
|
| filter_func = lambda x: '.' in x and \
|
| x.rsplit('.', 1)[1] in extensions
|
| if filter_func is not None:
|
| - x = filter(filter_func, x)
|
| + x = ifilter(filter_func, x)
|
| return x
|
|
|
| def handle_exception(self, exc_info=None, rendered=False, source_hint=None):
|
| @@ -667,7 +739,7 @@ class Environment(object):
|
| if self.exception_handler is not None:
|
| self.exception_handler(traceback)
|
| exc_type, exc_value, tb = traceback.standard_exc_info
|
| - raise exc_type, exc_value, tb
|
| + reraise(exc_type, exc_value, tb)
|
|
|
| def join_path(self, template, parent):
|
| """Join a template with the parent. By default all the lookups are
|
| @@ -754,7 +826,7 @@ class Environment(object):
|
|
|
| .. versionadded:: 2.3
|
| """
|
| - if isinstance(template_name_or_list, basestring):
|
| + 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
|
| @@ -816,7 +888,9 @@ class Template(object):
|
| 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,
|
| @@ -826,8 +900,9 @@ class Template(object):
|
| 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, frozenset(extensions), optimized, undefined,
|
| - finalize, autoescape, None, 0, False, None)
|
| + 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
|
| @@ -839,7 +914,7 @@ class Template(object):
|
| 'environment': environment,
|
| '__file__': code.co_filename
|
| }
|
| - exec code in namespace
|
| + exec(code, namespace)
|
| rv = cls._from_namespace(environment, namespace, globals)
|
| rv._uptodate = uptodate
|
| return rv
|
| @@ -973,7 +1048,7 @@ class Template(object):
|
| @property
|
| def debug_info(self):
|
| """The debug info mapping."""
|
| - return [tuple(map(int, x.split('='))) for x in
|
| + return [tuple(imap(int, x.split('='))) for x in
|
| self._debug_info.split('&')]
|
|
|
| def __repr__(self):
|
| @@ -984,6 +1059,7 @@ class Template(object):
|
| 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
|
| @@ -999,13 +1075,6 @@ class TemplateModule(object):
|
| return Markup(concat(self._body_stream))
|
|
|
| def __str__(self):
|
| - return unicode(self).encode('utf-8')
|
| -
|
| - # unicode goes after __str__ because we configured 2to3 to rename
|
| - # __unicode__ to __str__. because the 2to3 tree is not designed to
|
| - # remove nodes from it, we leave the above __str__ around and let
|
| - # it override at runtime.
|
| - def __unicode__(self):
|
| return concat(self._body_stream)
|
|
|
| def __repr__(self):
|
| @@ -1035,6 +1104,7 @@ class TemplateExpression(object):
|
| 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.
|
| @@ -1053,15 +1123,15 @@ class TemplateStream(object):
|
| 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 specifiy an `encoding`.
|
| + before writing specify an `encoding`.
|
|
|
| Example usage::
|
|
|
| Template('Hello {{ name }}!').stream(name='foo').dump('hello.html')
|
| """
|
| close = False
|
| - if isinstance(fp, basestring):
|
| - fp = file(fp, 'w')
|
| + if isinstance(fp, string_types):
|
| + fp = open(fp, encoding is None and 'w' or 'wb')
|
| close = True
|
| try:
|
| if encoding is not None:
|
| @@ -1079,7 +1149,7 @@ class TemplateStream(object):
|
|
|
| def disable_buffering(self):
|
| """Disable the output buffering."""
|
| - self._next = self._gen.next
|
| + self._next = get_next(self._gen)
|
| self.buffered = False
|
|
|
| def enable_buffering(self, size=5):
|
| @@ -1107,12 +1177,12 @@ class TemplateStream(object):
|
| c_size = 0
|
|
|
| self.buffered = True
|
| - self._next = generator(self._gen.next).next
|
| + self._next = get_next(generator(get_next(self._gen)))
|
|
|
| def __iter__(self):
|
| return self
|
|
|
| - def next(self):
|
| + def __next__(self):
|
| return self._next()
|
|
|
|
|
|
|