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() |