Updated jinja version
This commit is contained in:
@@ -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_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
|
||||
@@ -71,7 +81,7 @@ def load_extensions(environment, extensions):
|
||||
"""
|
||||
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,
|
||||
@@ -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
|
||||
@@ -269,7 +294,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
|
||||
@@ -291,7 +315,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)
|
||||
|
||||
@@ -299,7 +323,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):
|
||||
@@ -322,7 +347,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)
|
||||
|
||||
@@ -332,7 +357,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))
|
||||
@@ -351,7 +376,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:
|
||||
@@ -376,6 +401,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 = 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(value, *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
|
||||
@@ -394,7 +455,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
|
||||
@@ -406,7 +467,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:
|
||||
@@ -419,7 +480,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
|
||||
@@ -473,7 +534,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:
|
||||
@@ -485,7 +546,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()
|
||||
@@ -555,7 +616,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
|
||||
"""
|
||||
@@ -565,18 +628,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')
|
||||
# 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)
|
||||
@@ -600,7 +668,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))
|
||||
@@ -609,7 +677,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' %
|
||||
@@ -647,7 +715,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):
|
||||
@@ -670,7 +738,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
|
||||
@@ -757,7 +825,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
|
||||
@@ -819,7 +887,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,
|
||||
@@ -829,8 +899,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
|
||||
@@ -842,7 +913,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
|
||||
@@ -976,7 +1047,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):
|
||||
@@ -987,6 +1058,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
|
||||
@@ -1002,13 +1074,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):
|
||||
@@ -1038,6 +1103,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.
|
||||
@@ -1063,8 +1129,8 @@ class TemplateStream(object):
|
||||
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:
|
||||
@@ -1082,7 +1148,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):
|
||||
@@ -1110,12 +1176,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()
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user