Updated jinja version
This commit is contained in:
parent
2059972bf7
commit
304fd03e64
@ -18,7 +18,7 @@ string(REPLACE "opencv_" "" OPENCV_MATLAB_MODULES "${OPENCV_MODULE_${the_module}
|
|||||||
${OPENCV_MODULE_${the_module}_OPT_DEPS}")
|
${OPENCV_MODULE_${the_module}_OPT_DEPS}")
|
||||||
foreach(module ${OPENCV_MATLAB_MODULES})
|
foreach(module ${OPENCV_MATLAB_MODULES})
|
||||||
if (HAVE_opencv_${module})
|
if (HAVE_opencv_${module})
|
||||||
list(APPEND opencv_hdrs "${OPENCV_MODULE_opencv_${module}_LOCATION}/include/opencv2/${module}/${module}.hpp")
|
list(APPEND opencv_hdrs "${OPENCV_MODULE_opencv_${module}_LOCATION}/include/opencv2/${module}.hpp")
|
||||||
endif()
|
endif()
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
|
@ -29,3 +29,5 @@ Patches and suggestions:
|
|||||||
- Peter van Dijk (Habbie)
|
- Peter van Dijk (Habbie)
|
||||||
- Stefan Ebner
|
- Stefan Ebner
|
||||||
- Rene Leonhardt
|
- Rene Leonhardt
|
||||||
|
- Thomas Waldmann
|
||||||
|
- Cory Benfield (Lukasa)
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
:license: BSD, see LICENSE for more details.
|
:license: BSD, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
__version__ = '2.7-dev'
|
__version__ = '2.8-dev'
|
||||||
|
|
||||||
# high level interface
|
# high level interface
|
||||||
from jinja2.environment import Environment, Template
|
from jinja2.environment import Environment, Template
|
||||||
|
109
modules/matlab/generator/jinja2/_compat.py
Normal file
109
modules/matlab/generator/jinja2/_compat.py
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
jinja2._compat
|
||||||
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Some py2/py3 compatibility support based on a stripped down
|
||||||
|
version of six so we don't have to depend on a specific version
|
||||||
|
of it.
|
||||||
|
|
||||||
|
:copyright: Copyright 2013 by the Jinja team, see AUTHORS.
|
||||||
|
:license: BSD, see LICENSE for details.
|
||||||
|
"""
|
||||||
|
import sys
|
||||||
|
|
||||||
|
PY2 = sys.version_info[0] == 2
|
||||||
|
PYPY = hasattr(sys, 'pypy_translation_info')
|
||||||
|
_identity = lambda x: x
|
||||||
|
|
||||||
|
|
||||||
|
if not PY2:
|
||||||
|
unichr = chr
|
||||||
|
range_type = range
|
||||||
|
text_type = str
|
||||||
|
string_types = (str,)
|
||||||
|
|
||||||
|
iterkeys = lambda d: iter(d.keys())
|
||||||
|
itervalues = lambda d: iter(d.values())
|
||||||
|
iteritems = lambda d: iter(d.items())
|
||||||
|
|
||||||
|
import pickle
|
||||||
|
from io import BytesIO, StringIO
|
||||||
|
NativeStringIO = StringIO
|
||||||
|
|
||||||
|
def reraise(tp, value, tb=None):
|
||||||
|
if value.__traceback__ is not tb:
|
||||||
|
raise value.with_traceback(tb)
|
||||||
|
raise value
|
||||||
|
|
||||||
|
ifilter = filter
|
||||||
|
imap = map
|
||||||
|
izip = zip
|
||||||
|
intern = sys.intern
|
||||||
|
|
||||||
|
implements_iterator = _identity
|
||||||
|
implements_to_string = _identity
|
||||||
|
encode_filename = _identity
|
||||||
|
get_next = lambda x: x.__next__
|
||||||
|
|
||||||
|
else:
|
||||||
|
unichr = unichr
|
||||||
|
text_type = unicode
|
||||||
|
range_type = xrange
|
||||||
|
string_types = (str, unicode)
|
||||||
|
|
||||||
|
iterkeys = lambda d: d.iterkeys()
|
||||||
|
itervalues = lambda d: d.itervalues()
|
||||||
|
iteritems = lambda d: d.iteritems()
|
||||||
|
|
||||||
|
import cPickle as pickle
|
||||||
|
from cStringIO import StringIO as BytesIO, StringIO
|
||||||
|
NativeStringIO = BytesIO
|
||||||
|
|
||||||
|
exec('def reraise(tp, value, tb=None):\n raise tp, value, tb')
|
||||||
|
|
||||||
|
from itertools import imap, izip, ifilter
|
||||||
|
intern = intern
|
||||||
|
|
||||||
|
def implements_iterator(cls):
|
||||||
|
cls.next = cls.__next__
|
||||||
|
del cls.__next__
|
||||||
|
return cls
|
||||||
|
|
||||||
|
def implements_to_string(cls):
|
||||||
|
cls.__unicode__ = cls.__str__
|
||||||
|
cls.__str__ = lambda x: x.__unicode__().encode('utf-8')
|
||||||
|
return cls
|
||||||
|
|
||||||
|
get_next = lambda x: x.next
|
||||||
|
|
||||||
|
def encode_filename(filename):
|
||||||
|
if isinstance(filename, unicode):
|
||||||
|
return filename.encode('utf-8')
|
||||||
|
return filename
|
||||||
|
|
||||||
|
|
||||||
|
def with_metaclass(meta, *bases):
|
||||||
|
# This requires a bit of explanation: the basic idea is to make a
|
||||||
|
# dummy metaclass for one level of class instanciation that replaces
|
||||||
|
# itself with the actual metaclass. Because of internal type checks
|
||||||
|
# we also need to make sure that we downgrade the custom metaclass
|
||||||
|
# for one level to something closer to type (that's why __call__ and
|
||||||
|
# __init__ comes back from type etc.).
|
||||||
|
#
|
||||||
|
# This has the advantage over six.with_metaclass in that it does not
|
||||||
|
# introduce dummy classes into the final MRO.
|
||||||
|
class metaclass(meta):
|
||||||
|
__call__ = type.__call__
|
||||||
|
__init__ = type.__init__
|
||||||
|
def __new__(cls, name, this_bases, d):
|
||||||
|
if this_bases is None:
|
||||||
|
return type.__new__(cls, name, (), d)
|
||||||
|
return meta(name, bases, d)
|
||||||
|
return metaclass('temporary_class', None, {})
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
from urllib.parse import quote_from_bytes as url_quote
|
||||||
|
except ImportError:
|
||||||
|
from urllib import quote as url_quote
|
@ -1,49 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
jinja2._markupsafe._bundle
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
This script pulls in markupsafe from a source folder and
|
|
||||||
bundles it with Jinja2. It does not pull in the speedups
|
|
||||||
module though.
|
|
||||||
|
|
||||||
:copyright: Copyright 2010 by the Jinja team, see AUTHORS.
|
|
||||||
:license: BSD, see LICENSE for details.
|
|
||||||
"""
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
|
|
||||||
|
|
||||||
def rewrite_imports(lines):
|
|
||||||
for idx, line in enumerate(lines):
|
|
||||||
new_line = re.sub(r'(import|from)\s+markupsafe\b',
|
|
||||||
r'\1 jinja2._markupsafe', line)
|
|
||||||
if new_line != line:
|
|
||||||
lines[idx] = new_line
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
if len(sys.argv) != 2:
|
|
||||||
print 'error: only argument is path to markupsafe'
|
|
||||||
sys.exit(1)
|
|
||||||
basedir = os.path.dirname(__file__)
|
|
||||||
markupdir = sys.argv[1]
|
|
||||||
for filename in os.listdir(markupdir):
|
|
||||||
if filename.endswith('.py'):
|
|
||||||
f = open(os.path.join(markupdir, filename))
|
|
||||||
try:
|
|
||||||
lines = list(f)
|
|
||||||
finally:
|
|
||||||
f.close()
|
|
||||||
rewrite_imports(lines)
|
|
||||||
f = open(os.path.join(basedir, filename), 'w')
|
|
||||||
try:
|
|
||||||
for line in lines:
|
|
||||||
f.write(line)
|
|
||||||
finally:
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
@ -18,22 +18,17 @@ from os import path, listdir
|
|||||||
import sys
|
import sys
|
||||||
import marshal
|
import marshal
|
||||||
import tempfile
|
import tempfile
|
||||||
import cPickle as pickle
|
|
||||||
import fnmatch
|
import fnmatch
|
||||||
try:
|
from hashlib import sha1
|
||||||
from hashlib import sha1
|
|
||||||
except ImportError:
|
|
||||||
from sha import new as sha1
|
|
||||||
from jinja2.utils import open_if_exists
|
from jinja2.utils import open_if_exists
|
||||||
|
from jinja2._compat import BytesIO, pickle, PY2
|
||||||
|
|
||||||
|
|
||||||
# marshal works better on 3.x, one hack less required
|
# marshal works better on 3.x, one hack less required
|
||||||
if sys.version_info > (3, 0):
|
if not PY2:
|
||||||
from io import BytesIO
|
|
||||||
marshal_dump = marshal.dump
|
marshal_dump = marshal.dump
|
||||||
marshal_load = marshal.load
|
marshal_load = marshal.load
|
||||||
else:
|
else:
|
||||||
from cStringIO import StringIO as BytesIO
|
|
||||||
|
|
||||||
def marshal_dump(code, f):
|
def marshal_dump(code, f):
|
||||||
if isinstance(f, file):
|
if isinstance(f, file):
|
||||||
@ -282,15 +277,26 @@ class MemcachedBytecodeCache(BytecodeCache):
|
|||||||
|
|
||||||
This bytecode cache does not support clearing of used items in the cache.
|
This bytecode cache does not support clearing of used items in the cache.
|
||||||
The clear method is a no-operation function.
|
The clear method is a no-operation function.
|
||||||
|
|
||||||
|
.. versionadded:: 2.7
|
||||||
|
Added support for ignoring memcache errors through the
|
||||||
|
`ignore_memcache_errors` parameter.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, client, prefix='jinja2/bytecode/', timeout=None):
|
def __init__(self, client, prefix='jinja2/bytecode/', timeout=None,
|
||||||
|
ignore_memcache_errors=True):
|
||||||
self.client = client
|
self.client = client
|
||||||
self.prefix = prefix
|
self.prefix = prefix
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
|
self.ignore_memcache_errors = ignore_memcache_errors
|
||||||
|
|
||||||
def load_bytecode(self, bucket):
|
def load_bytecode(self, bucket):
|
||||||
code = self.client.get(self.prefix + bucket.key)
|
try:
|
||||||
|
code = self.client.get(self.prefix + bucket.key)
|
||||||
|
except Exception:
|
||||||
|
if not self.ignore_memcache_errors:
|
||||||
|
raise
|
||||||
|
code = None
|
||||||
if code is not None:
|
if code is not None:
|
||||||
bucket.bytecode_from_string(code)
|
bucket.bytecode_from_string(code)
|
||||||
|
|
||||||
@ -298,4 +304,8 @@ class MemcachedBytecodeCache(BytecodeCache):
|
|||||||
args = (self.prefix + bucket.key, bucket.bytecode_to_string())
|
args = (self.prefix + bucket.key, bucket.bytecode_to_string())
|
||||||
if self.timeout is not None:
|
if self.timeout is not None:
|
||||||
args += (self.timeout,)
|
args += (self.timeout,)
|
||||||
self.client.set(*args)
|
try:
|
||||||
|
self.client.set(*args)
|
||||||
|
except Exception:
|
||||||
|
if not self.ignore_memcache_errors:
|
||||||
|
raise
|
||||||
|
@ -8,14 +8,16 @@
|
|||||||
:copyright: (c) 2010 by the Jinja Team.
|
:copyright: (c) 2010 by the Jinja Team.
|
||||||
:license: BSD, see LICENSE for more details.
|
:license: BSD, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
from cStringIO import StringIO
|
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
from keyword import iskeyword as is_python_keyword
|
||||||
from jinja2 import nodes
|
from jinja2 import nodes
|
||||||
from jinja2.nodes import EvalContext
|
from jinja2.nodes import EvalContext
|
||||||
from jinja2.visitor import NodeVisitor
|
from jinja2.visitor import NodeVisitor
|
||||||
from jinja2.exceptions import TemplateAssertionError
|
from jinja2.exceptions import TemplateAssertionError
|
||||||
from jinja2.utils import Markup, concat, escape, is_python_keyword, next
|
from jinja2.utils import Markup, concat, escape
|
||||||
|
from jinja2._compat import range_type, text_type, string_types, \
|
||||||
|
iteritems, NativeStringIO, imap
|
||||||
|
|
||||||
|
|
||||||
operators = {
|
operators = {
|
||||||
@ -29,14 +31,6 @@ operators = {
|
|||||||
'notin': 'not in'
|
'notin': 'not in'
|
||||||
}
|
}
|
||||||
|
|
||||||
try:
|
|
||||||
exec '(0 if 0 else 0)'
|
|
||||||
except SyntaxError:
|
|
||||||
have_condexpr = False
|
|
||||||
else:
|
|
||||||
have_condexpr = True
|
|
||||||
|
|
||||||
|
|
||||||
# what method to iterate over items do we want to use for dict iteration
|
# what method to iterate over items do we want to use for dict iteration
|
||||||
# in generated code? on 2.x let's go with iteritems, on 3.x with items
|
# in generated code? on 2.x let's go with iteritems, on 3.x with items
|
||||||
if hasattr(dict, 'iteritems'):
|
if hasattr(dict, 'iteritems'):
|
||||||
@ -51,7 +45,11 @@ def unoptimize_before_dead_code():
|
|||||||
def f():
|
def f():
|
||||||
if 0: dummy(x)
|
if 0: dummy(x)
|
||||||
return f
|
return f
|
||||||
unoptimize_before_dead_code = bool(unoptimize_before_dead_code().func_closure)
|
|
||||||
|
# The getattr is necessary for pypy which does not set this attribute if
|
||||||
|
# no closure is on the function
|
||||||
|
unoptimize_before_dead_code = bool(
|
||||||
|
getattr(unoptimize_before_dead_code(), '__closure__', None))
|
||||||
|
|
||||||
|
|
||||||
def generate(node, environment, name, filename, stream=None,
|
def generate(node, environment, name, filename, stream=None,
|
||||||
@ -69,8 +67,8 @@ def has_safe_repr(value):
|
|||||||
"""Does the node have a safe representation?"""
|
"""Does the node have a safe representation?"""
|
||||||
if value is None or value is NotImplemented or value is Ellipsis:
|
if value is None or value is NotImplemented or value is Ellipsis:
|
||||||
return True
|
return True
|
||||||
if isinstance(value, (bool, int, long, float, complex, basestring,
|
if isinstance(value, (bool, int, float, complex, range_type,
|
||||||
xrange, Markup)):
|
Markup) + string_types):
|
||||||
return True
|
return True
|
||||||
if isinstance(value, (tuple, list, set, frozenset)):
|
if isinstance(value, (tuple, list, set, frozenset)):
|
||||||
for item in value:
|
for item in value:
|
||||||
@ -78,7 +76,7 @@ def has_safe_repr(value):
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
elif isinstance(value, dict):
|
elif isinstance(value, dict):
|
||||||
for key, value in value.iteritems():
|
for key, value in iteritems(value):
|
||||||
if not has_safe_repr(key):
|
if not has_safe_repr(key):
|
||||||
return False
|
return False
|
||||||
if not has_safe_repr(value):
|
if not has_safe_repr(value):
|
||||||
@ -368,7 +366,7 @@ class CodeGenerator(NodeVisitor):
|
|||||||
def __init__(self, environment, name, filename, stream=None,
|
def __init__(self, environment, name, filename, stream=None,
|
||||||
defer_init=False):
|
defer_init=False):
|
||||||
if stream is None:
|
if stream is None:
|
||||||
stream = StringIO()
|
stream = NativeStringIO()
|
||||||
self.environment = environment
|
self.environment = environment
|
||||||
self.name = name
|
self.name = name
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
@ -542,7 +540,7 @@ class CodeGenerator(NodeVisitor):
|
|||||||
self.write(', ')
|
self.write(', ')
|
||||||
self.visit(kwarg, frame)
|
self.visit(kwarg, frame)
|
||||||
if extra_kwargs is not None:
|
if extra_kwargs is not None:
|
||||||
for key, value in extra_kwargs.iteritems():
|
for key, value in iteritems(extra_kwargs):
|
||||||
self.write(', %s=%s' % (key, value))
|
self.write(', %s=%s' % (key, value))
|
||||||
if node.dyn_args:
|
if node.dyn_args:
|
||||||
self.write(', *')
|
self.write(', *')
|
||||||
@ -558,7 +556,7 @@ class CodeGenerator(NodeVisitor):
|
|||||||
self.visit(kwarg.value, frame)
|
self.visit(kwarg.value, frame)
|
||||||
self.write(', ')
|
self.write(', ')
|
||||||
if extra_kwargs is not None:
|
if extra_kwargs is not None:
|
||||||
for key, value in extra_kwargs.iteritems():
|
for key, value in iteritems(extra_kwargs):
|
||||||
self.write('%r: %s, ' % (key, value))
|
self.write('%r: %s, ' % (key, value))
|
||||||
if node.dyn_kwargs is not None:
|
if node.dyn_kwargs is not None:
|
||||||
self.write('}, **')
|
self.write('}, **')
|
||||||
@ -625,7 +623,7 @@ class CodeGenerator(NodeVisitor):
|
|||||||
|
|
||||||
def pop_scope(self, aliases, frame):
|
def pop_scope(self, aliases, frame):
|
||||||
"""Restore all aliases and delete unused variables."""
|
"""Restore all aliases and delete unused variables."""
|
||||||
for name, alias in aliases.iteritems():
|
for name, alias in iteritems(aliases):
|
||||||
self.writeline('l_%s = %s' % (name, alias))
|
self.writeline('l_%s = %s' % (name, alias))
|
||||||
to_delete = set()
|
to_delete = set()
|
||||||
for name in frame.identifiers.declared_locally:
|
for name in frame.identifiers.declared_locally:
|
||||||
@ -663,16 +661,16 @@ class CodeGenerator(NodeVisitor):
|
|||||||
# it without aliasing all the variables.
|
# it without aliasing all the variables.
|
||||||
# this could be fixed in Python 3 where we have the nonlocal
|
# this could be fixed in Python 3 where we have the nonlocal
|
||||||
# keyword or if we switch to bytecode generation
|
# keyword or if we switch to bytecode generation
|
||||||
overriden_closure_vars = (
|
overridden_closure_vars = (
|
||||||
func_frame.identifiers.undeclared &
|
func_frame.identifiers.undeclared &
|
||||||
func_frame.identifiers.declared &
|
func_frame.identifiers.declared &
|
||||||
(func_frame.identifiers.declared_locally |
|
(func_frame.identifiers.declared_locally |
|
||||||
func_frame.identifiers.declared_parameter)
|
func_frame.identifiers.declared_parameter)
|
||||||
)
|
)
|
||||||
if overriden_closure_vars:
|
if overridden_closure_vars:
|
||||||
self.fail('It\'s not possible to set and access variables '
|
self.fail('It\'s not possible to set and access variables '
|
||||||
'derived from an outer scope! (affects: %s)' %
|
'derived from an outer scope! (affects: %s)' %
|
||||||
', '.join(sorted(overriden_closure_vars)), node.lineno)
|
', '.join(sorted(overridden_closure_vars)), node.lineno)
|
||||||
|
|
||||||
# remove variables from a closure from the frame's undeclared
|
# remove variables from a closure from the frame's undeclared
|
||||||
# identifiers.
|
# identifiers.
|
||||||
@ -827,7 +825,7 @@ class CodeGenerator(NodeVisitor):
|
|||||||
self.outdent(2 + (not self.has_known_extends))
|
self.outdent(2 + (not self.has_known_extends))
|
||||||
|
|
||||||
# at this point we now have the blocks collected and can visit them too.
|
# at this point we now have the blocks collected and can visit them too.
|
||||||
for name, block in self.blocks.iteritems():
|
for name, block in iteritems(self.blocks):
|
||||||
block_frame = Frame(eval_ctx)
|
block_frame = Frame(eval_ctx)
|
||||||
block_frame.inspect(block.body)
|
block_frame.inspect(block.body)
|
||||||
block_frame.block = name
|
block_frame.block = name
|
||||||
@ -894,12 +892,13 @@ class CodeGenerator(NodeVisitor):
|
|||||||
self.indent()
|
self.indent()
|
||||||
self.writeline('raise TemplateRuntimeError(%r)' %
|
self.writeline('raise TemplateRuntimeError(%r)' %
|
||||||
'extended multiple times')
|
'extended multiple times')
|
||||||
self.outdent()
|
|
||||||
|
|
||||||
# if we have a known extends already we don't need that code here
|
# if we have a known extends already we don't need that code here
|
||||||
# as we know that the template execution will end here.
|
# as we know that the template execution will end here.
|
||||||
if self.has_known_extends:
|
if self.has_known_extends:
|
||||||
raise CompilerExit()
|
raise CompilerExit()
|
||||||
|
else:
|
||||||
|
self.outdent()
|
||||||
|
|
||||||
self.writeline('parent_template = environment.get_template(', node)
|
self.writeline('parent_template = environment.get_template(', node)
|
||||||
self.visit(node.template, frame)
|
self.visit(node.template, frame)
|
||||||
@ -930,7 +929,7 @@ class CodeGenerator(NodeVisitor):
|
|||||||
|
|
||||||
func_name = 'get_or_select_template'
|
func_name = 'get_or_select_template'
|
||||||
if isinstance(node.template, nodes.Const):
|
if isinstance(node.template, nodes.Const):
|
||||||
if isinstance(node.template.value, basestring):
|
if isinstance(node.template.value, string_types):
|
||||||
func_name = 'get_template'
|
func_name = 'get_template'
|
||||||
elif isinstance(node.template.value, (tuple, list)):
|
elif isinstance(node.template.value, (tuple, list)):
|
||||||
func_name = 'select_template'
|
func_name = 'select_template'
|
||||||
@ -950,9 +949,16 @@ class CodeGenerator(NodeVisitor):
|
|||||||
self.indent()
|
self.indent()
|
||||||
|
|
||||||
if node.with_context:
|
if node.with_context:
|
||||||
|
self.writeline('include_context = template.new_context('
|
||||||
|
'context.parent, True, locals())')
|
||||||
|
self.writeline('for name, context_blocks in context.'
|
||||||
|
'blocks.%s():' % dict_item_iter)
|
||||||
|
self.indent()
|
||||||
|
self.writeline('include_context.blocks.setdefault('
|
||||||
|
'name, [])[0:0] = context_blocks')
|
||||||
|
self.outdent()
|
||||||
self.writeline('for event in template.root_render_func('
|
self.writeline('for event in template.root_render_func('
|
||||||
'template.new_context(context.parent, True, '
|
'include_context):')
|
||||||
'locals())):')
|
|
||||||
else:
|
else:
|
||||||
self.writeline('for event in template.module._body_stream:')
|
self.writeline('for event in template.module._body_stream:')
|
||||||
|
|
||||||
@ -1032,7 +1038,7 @@ class CodeGenerator(NodeVisitor):
|
|||||||
discarded_names[0])
|
discarded_names[0])
|
||||||
else:
|
else:
|
||||||
self.writeline('context.exported_vars.difference_'
|
self.writeline('context.exported_vars.difference_'
|
||||||
'update((%s))' % ', '.join(map(repr, discarded_names)))
|
'update((%s))' % ', '.join(imap(repr, discarded_names)))
|
||||||
|
|
||||||
def visit_For(self, node, frame):
|
def visit_For(self, node, frame):
|
||||||
# when calculating the nodes for the inner frame we have to exclude
|
# when calculating the nodes for the inner frame we have to exclude
|
||||||
@ -1060,7 +1066,7 @@ class CodeGenerator(NodeVisitor):
|
|||||||
|
|
||||||
# otherwise we set up a buffer and add a function def
|
# otherwise we set up a buffer and add a function def
|
||||||
else:
|
else:
|
||||||
self.writeline('def loop(reciter, loop_render_func):', node)
|
self.writeline('def loop(reciter, loop_render_func, depth=0):', node)
|
||||||
self.indent()
|
self.indent()
|
||||||
self.buffer(loop_frame)
|
self.buffer(loop_frame)
|
||||||
aliases = {}
|
aliases = {}
|
||||||
@ -1068,6 +1074,7 @@ class CodeGenerator(NodeVisitor):
|
|||||||
# make sure the loop variable is a special one and raise a template
|
# make sure the loop variable is a special one and raise a template
|
||||||
# assertion error if a loop tries to write to loop
|
# assertion error if a loop tries to write to loop
|
||||||
if extended_loop:
|
if extended_loop:
|
||||||
|
self.writeline('l_loop = missing')
|
||||||
loop_frame.identifiers.add_special('loop')
|
loop_frame.identifiers.add_special('loop')
|
||||||
for name in node.find_all(nodes.Name):
|
for name in node.find_all(nodes.Name):
|
||||||
if name.ctx == 'store' and name.name == 'loop':
|
if name.ctx == 'store' and name.name == 'loop':
|
||||||
@ -1118,7 +1125,7 @@ class CodeGenerator(NodeVisitor):
|
|||||||
self.visit(node.iter, loop_frame)
|
self.visit(node.iter, loop_frame)
|
||||||
|
|
||||||
if node.recursive:
|
if node.recursive:
|
||||||
self.write(', recurse=loop_render_func):')
|
self.write(', loop_render_func, depth):')
|
||||||
else:
|
else:
|
||||||
self.write(extended_loop and '):' or ':')
|
self.write(extended_loop and '):' or ':')
|
||||||
|
|
||||||
@ -1216,9 +1223,9 @@ class CodeGenerator(NodeVisitor):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if self.environment.finalize:
|
if self.environment.finalize:
|
||||||
finalize = lambda x: unicode(self.environment.finalize(x))
|
finalize = lambda x: text_type(self.environment.finalize(x))
|
||||||
else:
|
else:
|
||||||
finalize = unicode
|
finalize = text_type
|
||||||
|
|
||||||
# if we are inside a frame that requires output checking, we do so
|
# if we are inside a frame that requires output checking, we do so
|
||||||
outdent_later = False
|
outdent_later = False
|
||||||
@ -1367,7 +1374,7 @@ class CodeGenerator(NodeVisitor):
|
|||||||
public_names[0])
|
public_names[0])
|
||||||
else:
|
else:
|
||||||
self.writeline('context.exported_vars.update((%s))' %
|
self.writeline('context.exported_vars.update((%s))' %
|
||||||
', '.join(map(repr, public_names)))
|
', '.join(imap(repr, public_names)))
|
||||||
|
|
||||||
# -- Expression Visitors
|
# -- Expression Visitors
|
||||||
|
|
||||||
@ -1555,22 +1562,13 @@ class CodeGenerator(NodeVisitor):
|
|||||||
'expression on %s evaluated to false and '
|
'expression on %s evaluated to false and '
|
||||||
'no else section was defined.' % self.position(node)))
|
'no else section was defined.' % self.position(node)))
|
||||||
|
|
||||||
if not have_condexpr:
|
self.write('(')
|
||||||
self.write('((')
|
self.visit(node.expr1, frame)
|
||||||
self.visit(node.test, frame)
|
self.write(' if ')
|
||||||
self.write(') and (')
|
self.visit(node.test, frame)
|
||||||
self.visit(node.expr1, frame)
|
self.write(' else ')
|
||||||
self.write(',) or (')
|
write_expr2()
|
||||||
write_expr2()
|
self.write(')')
|
||||||
self.write(',))[0]')
|
|
||||||
else:
|
|
||||||
self.write('(')
|
|
||||||
self.visit(node.expr1, frame)
|
|
||||||
self.write(' if ')
|
|
||||||
self.visit(node.test, frame)
|
|
||||||
self.write(' else ')
|
|
||||||
write_expr2()
|
|
||||||
self.write(')')
|
|
||||||
|
|
||||||
def visit_Call(self, node, frame, forward_caller=False):
|
def visit_Call(self, node, frame, forward_caller=False):
|
||||||
if self.environment.sandboxed:
|
if self.environment.sandboxed:
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
:copyright: (c) 2010 by the Jinja Team.
|
:copyright: (c) 2010 by the Jinja Team.
|
||||||
:license: BSD, see LICENSE for more details.
|
:license: BSD, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
from jinja2._compat import range_type
|
||||||
from jinja2.utils import generate_lorem_ipsum, Cycler, Joiner
|
from jinja2.utils import generate_lorem_ipsum, Cycler, Joiner
|
||||||
|
|
||||||
|
|
||||||
@ -21,13 +22,15 @@ COMMENT_END_STRING = '#}'
|
|||||||
LINE_STATEMENT_PREFIX = None
|
LINE_STATEMENT_PREFIX = None
|
||||||
LINE_COMMENT_PREFIX = None
|
LINE_COMMENT_PREFIX = None
|
||||||
TRIM_BLOCKS = False
|
TRIM_BLOCKS = False
|
||||||
|
LSTRIP_BLOCKS = False
|
||||||
NEWLINE_SEQUENCE = '\n'
|
NEWLINE_SEQUENCE = '\n'
|
||||||
|
KEEP_TRAILING_NEWLINE = False
|
||||||
|
|
||||||
|
|
||||||
# default filters, tests and namespace
|
# default filters, tests and namespace
|
||||||
from jinja2.filters import FILTERS as DEFAULT_FILTERS
|
from jinja2.filters import FILTERS as DEFAULT_FILTERS
|
||||||
DEFAULT_NAMESPACE = {
|
DEFAULT_NAMESPACE = {
|
||||||
'range': xrange,
|
'range': range_type,
|
||||||
'dict': lambda **kw: kw,
|
'dict': lambda **kw: kw,
|
||||||
'lipsum': generate_lorem_ipsum,
|
'lipsum': generate_lorem_ipsum,
|
||||||
'cycler': Cycler,
|
'cycler': Cycler,
|
||||||
|
@ -11,16 +11,26 @@
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from jinja2 import nodes
|
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.lexer import get_lexer, TokenStream
|
||||||
from jinja2.parser import Parser
|
from jinja2.parser import Parser
|
||||||
|
from jinja2.nodes import EvalContext
|
||||||
from jinja2.optimizer import optimize
|
from jinja2.optimizer import optimize
|
||||||
from jinja2.compiler import generate
|
from jinja2.compiler import generate
|
||||||
from jinja2.runtime import Undefined, new_context
|
from jinja2.runtime import Undefined, new_context
|
||||||
from jinja2.exceptions import TemplateSyntaxError, TemplateNotFound, \
|
from jinja2.exceptions import TemplateSyntaxError, TemplateNotFound, \
|
||||||
TemplatesNotFound
|
TemplatesNotFound, TemplateRuntimeError
|
||||||
from jinja2.utils import import_string, LRUCache, Markup, missing, \
|
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
|
# for direct template usage we have up to ten living environments
|
||||||
@ -71,7 +81,7 @@ def load_extensions(environment, extensions):
|
|||||||
"""
|
"""
|
||||||
result = {}
|
result = {}
|
||||||
for extension in extensions:
|
for extension in extensions:
|
||||||
if isinstance(extension, basestring):
|
if isinstance(extension, string_types):
|
||||||
extension = import_string(extension)
|
extension = import_string(extension)
|
||||||
result[extension.identifier] = extension(environment)
|
result[extension.identifier] = extension(environment)
|
||||||
return result
|
return result
|
||||||
@ -134,12 +144,23 @@ class Environment(object):
|
|||||||
If this is set to ``True`` the first newline after a block is
|
If this is set to ``True`` the first newline after a block is
|
||||||
removed (block, not variable tag!). Defaults to `False`.
|
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`
|
`newline_sequence`
|
||||||
The sequence that starts a newline. Must be one of ``'\r'``,
|
The sequence that starts a newline. Must be one of ``'\r'``,
|
||||||
``'\n'`` or ``'\r\n'``. The default is ``'\n'`` which is a
|
``'\n'`` or ``'\r\n'``. The default is ``'\n'`` which is a
|
||||||
useful default for Linux and OS X systems as well as web
|
useful default for Linux and OS X systems as well as web
|
||||||
applications.
|
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`
|
`extensions`
|
||||||
List of Jinja extensions to use. This can either be import paths
|
List of Jinja extensions to use. This can either be import paths
|
||||||
as strings or extension classes. For more information have a
|
as strings or extension classes. For more information have a
|
||||||
@ -224,7 +245,9 @@ class Environment(object):
|
|||||||
line_statement_prefix=LINE_STATEMENT_PREFIX,
|
line_statement_prefix=LINE_STATEMENT_PREFIX,
|
||||||
line_comment_prefix=LINE_COMMENT_PREFIX,
|
line_comment_prefix=LINE_COMMENT_PREFIX,
|
||||||
trim_blocks=TRIM_BLOCKS,
|
trim_blocks=TRIM_BLOCKS,
|
||||||
|
lstrip_blocks=LSTRIP_BLOCKS,
|
||||||
newline_sequence=NEWLINE_SEQUENCE,
|
newline_sequence=NEWLINE_SEQUENCE,
|
||||||
|
keep_trailing_newline=KEEP_TRAILING_NEWLINE,
|
||||||
extensions=(),
|
extensions=(),
|
||||||
optimized=True,
|
optimized=True,
|
||||||
undefined=Undefined,
|
undefined=Undefined,
|
||||||
@ -255,7 +278,9 @@ class Environment(object):
|
|||||||
self.line_statement_prefix = line_statement_prefix
|
self.line_statement_prefix = line_statement_prefix
|
||||||
self.line_comment_prefix = line_comment_prefix
|
self.line_comment_prefix = line_comment_prefix
|
||||||
self.trim_blocks = trim_blocks
|
self.trim_blocks = trim_blocks
|
||||||
|
self.lstrip_blocks = lstrip_blocks
|
||||||
self.newline_sequence = newline_sequence
|
self.newline_sequence = newline_sequence
|
||||||
|
self.keep_trailing_newline = keep_trailing_newline
|
||||||
|
|
||||||
# runtime information
|
# runtime information
|
||||||
self.undefined = undefined
|
self.undefined = undefined
|
||||||
@ -269,7 +294,6 @@ class Environment(object):
|
|||||||
|
|
||||||
# set the loader provided
|
# set the loader provided
|
||||||
self.loader = loader
|
self.loader = loader
|
||||||
self.bytecode_cache = None
|
|
||||||
self.cache = create_cache(cache_size)
|
self.cache = create_cache(cache_size)
|
||||||
self.bytecode_cache = bytecode_cache
|
self.bytecode_cache = bytecode_cache
|
||||||
self.auto_reload = auto_reload
|
self.auto_reload = auto_reload
|
||||||
@ -291,7 +315,7 @@ class Environment(object):
|
|||||||
yet. This is used by :ref:`extensions <writing-extensions>` to register
|
yet. This is used by :ref:`extensions <writing-extensions>` to register
|
||||||
callbacks and configuration values without breaking inheritance.
|
callbacks and configuration values without breaking inheritance.
|
||||||
"""
|
"""
|
||||||
for key, value in attributes.iteritems():
|
for key, value in iteritems(attributes):
|
||||||
if not hasattr(self, key):
|
if not hasattr(self, key):
|
||||||
setattr(self, key, value)
|
setattr(self, key, value)
|
||||||
|
|
||||||
@ -299,7 +323,8 @@ class Environment(object):
|
|||||||
variable_start_string=missing, variable_end_string=missing,
|
variable_start_string=missing, variable_end_string=missing,
|
||||||
comment_start_string=missing, comment_end_string=missing,
|
comment_start_string=missing, comment_end_string=missing,
|
||||||
line_statement_prefix=missing, line_comment_prefix=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,
|
undefined=missing, finalize=missing, autoescape=missing,
|
||||||
loader=missing, cache_size=missing, auto_reload=missing,
|
loader=missing, cache_size=missing, auto_reload=missing,
|
||||||
bytecode_cache=missing):
|
bytecode_cache=missing):
|
||||||
@ -322,7 +347,7 @@ class Environment(object):
|
|||||||
rv.overlayed = True
|
rv.overlayed = True
|
||||||
rv.linked_to = self
|
rv.linked_to = self
|
||||||
|
|
||||||
for key, value in args.iteritems():
|
for key, value in iteritems(args):
|
||||||
if value is not missing:
|
if value is not missing:
|
||||||
setattr(rv, key, value)
|
setattr(rv, key, value)
|
||||||
|
|
||||||
@ -332,7 +357,7 @@ class Environment(object):
|
|||||||
rv.cache = copy_cache(self.cache)
|
rv.cache = copy_cache(self.cache)
|
||||||
|
|
||||||
rv.extensions = {}
|
rv.extensions = {}
|
||||||
for key, value in self.extensions.iteritems():
|
for key, value in iteritems(self.extensions):
|
||||||
rv.extensions[key] = value.bind(rv)
|
rv.extensions[key] = value.bind(rv)
|
||||||
if extensions is not missing:
|
if extensions is not missing:
|
||||||
rv.extensions.update(load_extensions(rv, extensions))
|
rv.extensions.update(load_extensions(rv, extensions))
|
||||||
@ -351,7 +376,7 @@ class Environment(object):
|
|||||||
try:
|
try:
|
||||||
return obj[argument]
|
return obj[argument]
|
||||||
except (TypeError, LookupError):
|
except (TypeError, LookupError):
|
||||||
if isinstance(argument, basestring):
|
if isinstance(argument, string_types):
|
||||||
try:
|
try:
|
||||||
attr = str(argument)
|
attr = str(argument)
|
||||||
except Exception:
|
except Exception:
|
||||||
@ -376,6 +401,42 @@ class Environment(object):
|
|||||||
except (TypeError, LookupError, AttributeError):
|
except (TypeError, LookupError, AttributeError):
|
||||||
return self.undefined(obj=obj, name=attribute)
|
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
|
@internalcode
|
||||||
def parse(self, source, name=None, filename=None):
|
def parse(self, source, name=None, filename=None):
|
||||||
"""Parse the sourcecode and return the abstract syntax tree. This
|
"""Parse the sourcecode and return the abstract syntax tree. This
|
||||||
@ -394,7 +455,7 @@ class Environment(object):
|
|||||||
|
|
||||||
def _parse(self, source, name, filename):
|
def _parse(self, source, name, filename):
|
||||||
"""Internal parsing function used by `parse` and `compile`."""
|
"""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):
|
def lex(self, source, name=None, filename=None):
|
||||||
"""Lex the given sourcecode and return a generator that yields
|
"""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
|
of the extensions to be applied you have to filter source through
|
||||||
the :meth:`preprocess` method.
|
the :meth:`preprocess` method.
|
||||||
"""
|
"""
|
||||||
source = unicode(source)
|
source = text_type(source)
|
||||||
try:
|
try:
|
||||||
return self.lexer.tokeniter(source, name, filename)
|
return self.lexer.tokeniter(source, name, filename)
|
||||||
except TemplateSyntaxError:
|
except TemplateSyntaxError:
|
||||||
@ -419,7 +480,7 @@ class Environment(object):
|
|||||||
because there you usually only want the actual source tokenized.
|
because there you usually only want the actual source tokenized.
|
||||||
"""
|
"""
|
||||||
return reduce(lambda s, e: e.preprocess(s, name, filename),
|
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):
|
def _tokenize(self, source, name, filename=None, state=None):
|
||||||
"""Called by the parser to do the preprocessing and filtering
|
"""Called by the parser to do the preprocessing and filtering
|
||||||
@ -473,7 +534,7 @@ class Environment(object):
|
|||||||
"""
|
"""
|
||||||
source_hint = None
|
source_hint = None
|
||||||
try:
|
try:
|
||||||
if isinstance(source, basestring):
|
if isinstance(source, string_types):
|
||||||
source_hint = source
|
source_hint = source
|
||||||
source = self._parse(source, name, filename)
|
source = self._parse(source, name, filename)
|
||||||
if self.optimized:
|
if self.optimized:
|
||||||
@ -485,7 +546,7 @@ class Environment(object):
|
|||||||
if filename is None:
|
if filename is None:
|
||||||
filename = '<template>'
|
filename = '<template>'
|
||||||
else:
|
else:
|
||||||
filename = _encode_filename(filename)
|
filename = encode_filename(filename)
|
||||||
return self._compile(source, filename)
|
return self._compile(source, filename)
|
||||||
except TemplateSyntaxError:
|
except TemplateSyntaxError:
|
||||||
exc_info = sys.exc_info()
|
exc_info = sys.exc_info()
|
||||||
@ -555,7 +616,9 @@ class Environment(object):
|
|||||||
to `False` and you will get an exception on syntax 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
|
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
|
.. versionadded:: 2.4
|
||||||
"""
|
"""
|
||||||
@ -565,18 +628,23 @@ class Environment(object):
|
|||||||
log_function = lambda x: None
|
log_function = lambda x: None
|
||||||
|
|
||||||
if py_compile:
|
if py_compile:
|
||||||
import imp, marshal
|
if not PY2 or PYPY:
|
||||||
py_header = imp.get_magic() + \
|
from warnings import warn
|
||||||
u'\xff\xff\xff\xff'.encode('iso-8859-15')
|
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
|
# Python 3.3 added a source filesize to the header
|
||||||
if sys.version_info >= (3, 3):
|
if sys.version_info >= (3, 3):
|
||||||
py_header += u'\x00\x00\x00\x00'.encode('iso-8859-15')
|
py_header += u'\x00\x00\x00\x00'.encode('iso-8859-15')
|
||||||
|
|
||||||
def write_file(filename, data, mode):
|
def write_file(filename, data, mode):
|
||||||
if zip:
|
if zip:
|
||||||
info = ZipInfo(filename)
|
info = ZipInfo(filename)
|
||||||
info.external_attr = 0755 << 16L
|
info.external_attr = 0o755 << 16
|
||||||
zip_file.writestr(info, data)
|
zip_file.writestr(info, data)
|
||||||
else:
|
else:
|
||||||
f = open(os.path.join(target, filename), mode)
|
f = open(os.path.join(target, filename), mode)
|
||||||
@ -600,7 +668,7 @@ class Environment(object):
|
|||||||
source, filename, _ = self.loader.get_source(self, name)
|
source, filename, _ = self.loader.get_source(self, name)
|
||||||
try:
|
try:
|
||||||
code = self.compile(source, name, filename, True, True)
|
code = self.compile(source, name, filename, True, True)
|
||||||
except TemplateSyntaxError, e:
|
except TemplateSyntaxError as e:
|
||||||
if not ignore_errors:
|
if not ignore_errors:
|
||||||
raise
|
raise
|
||||||
log_function('Could not compile "%s": %s' % (name, e))
|
log_function('Could not compile "%s": %s' % (name, e))
|
||||||
@ -609,7 +677,7 @@ class Environment(object):
|
|||||||
filename = ModuleLoader.get_module_filename(name)
|
filename = ModuleLoader.get_module_filename(name)
|
||||||
|
|
||||||
if py_compile:
|
if py_compile:
|
||||||
c = self._compile(code, _encode_filename(filename))
|
c = self._compile(code, encode_filename(filename))
|
||||||
write_file(filename + 'c', py_header +
|
write_file(filename + 'c', py_header +
|
||||||
marshal.dumps(c), 'wb')
|
marshal.dumps(c), 'wb')
|
||||||
log_function('Byte-compiled "%s" as %s' %
|
log_function('Byte-compiled "%s" as %s' %
|
||||||
@ -647,7 +715,7 @@ class Environment(object):
|
|||||||
filter_func = lambda x: '.' in x and \
|
filter_func = lambda x: '.' in x and \
|
||||||
x.rsplit('.', 1)[1] in extensions
|
x.rsplit('.', 1)[1] in extensions
|
||||||
if filter_func is not None:
|
if filter_func is not None:
|
||||||
x = filter(filter_func, x)
|
x = ifilter(filter_func, x)
|
||||||
return x
|
return x
|
||||||
|
|
||||||
def handle_exception(self, exc_info=None, rendered=False, source_hint=None):
|
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:
|
if self.exception_handler is not None:
|
||||||
self.exception_handler(traceback)
|
self.exception_handler(traceback)
|
||||||
exc_type, exc_value, tb = traceback.standard_exc_info
|
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):
|
def join_path(self, template, parent):
|
||||||
"""Join a template with the parent. By default all the lookups are
|
"""Join a template with the parent. By default all the lookups are
|
||||||
@ -757,7 +825,7 @@ class Environment(object):
|
|||||||
|
|
||||||
.. versionadded:: 2.3
|
.. 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)
|
return self.get_template(template_name_or_list, parent, globals)
|
||||||
elif isinstance(template_name_or_list, Template):
|
elif isinstance(template_name_or_list, Template):
|
||||||
return template_name_or_list
|
return template_name_or_list
|
||||||
@ -819,7 +887,9 @@ class Template(object):
|
|||||||
line_statement_prefix=LINE_STATEMENT_PREFIX,
|
line_statement_prefix=LINE_STATEMENT_PREFIX,
|
||||||
line_comment_prefix=LINE_COMMENT_PREFIX,
|
line_comment_prefix=LINE_COMMENT_PREFIX,
|
||||||
trim_blocks=TRIM_BLOCKS,
|
trim_blocks=TRIM_BLOCKS,
|
||||||
|
lstrip_blocks=LSTRIP_BLOCKS,
|
||||||
newline_sequence=NEWLINE_SEQUENCE,
|
newline_sequence=NEWLINE_SEQUENCE,
|
||||||
|
keep_trailing_newline=KEEP_TRAILING_NEWLINE,
|
||||||
extensions=(),
|
extensions=(),
|
||||||
optimized=True,
|
optimized=True,
|
||||||
undefined=Undefined,
|
undefined=Undefined,
|
||||||
@ -829,8 +899,9 @@ class Template(object):
|
|||||||
block_start_string, block_end_string, variable_start_string,
|
block_start_string, block_end_string, variable_start_string,
|
||||||
variable_end_string, comment_start_string, comment_end_string,
|
variable_end_string, comment_start_string, comment_end_string,
|
||||||
line_statement_prefix, line_comment_prefix, trim_blocks,
|
line_statement_prefix, line_comment_prefix, trim_blocks,
|
||||||
newline_sequence, frozenset(extensions), optimized, undefined,
|
lstrip_blocks, newline_sequence, keep_trailing_newline,
|
||||||
finalize, autoescape, None, 0, False, None)
|
frozenset(extensions), optimized, undefined, finalize, autoescape,
|
||||||
|
None, 0, False, None)
|
||||||
return env.from_string(source, template_class=cls)
|
return env.from_string(source, template_class=cls)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -842,7 +913,7 @@ class Template(object):
|
|||||||
'environment': environment,
|
'environment': environment,
|
||||||
'__file__': code.co_filename
|
'__file__': code.co_filename
|
||||||
}
|
}
|
||||||
exec code in namespace
|
exec(code, namespace)
|
||||||
rv = cls._from_namespace(environment, namespace, globals)
|
rv = cls._from_namespace(environment, namespace, globals)
|
||||||
rv._uptodate = uptodate
|
rv._uptodate = uptodate
|
||||||
return rv
|
return rv
|
||||||
@ -976,7 +1047,7 @@ class Template(object):
|
|||||||
@property
|
@property
|
||||||
def debug_info(self):
|
def debug_info(self):
|
||||||
"""The debug info mapping."""
|
"""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('&')]
|
self._debug_info.split('&')]
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
@ -987,6 +1058,7 @@ class Template(object):
|
|||||||
return '<%s %s>' % (self.__class__.__name__, name)
|
return '<%s %s>' % (self.__class__.__name__, name)
|
||||||
|
|
||||||
|
|
||||||
|
@implements_to_string
|
||||||
class TemplateModule(object):
|
class TemplateModule(object):
|
||||||
"""Represents an imported template. All the exported names of the
|
"""Represents an imported template. All the exported names of the
|
||||||
template are available as attributes on this object. Additionally
|
template are available as attributes on this object. Additionally
|
||||||
@ -1002,13 +1074,6 @@ class TemplateModule(object):
|
|||||||
return Markup(concat(self._body_stream))
|
return Markup(concat(self._body_stream))
|
||||||
|
|
||||||
def __str__(self):
|
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)
|
return concat(self._body_stream)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
@ -1038,6 +1103,7 @@ class TemplateExpression(object):
|
|||||||
return rv
|
return rv
|
||||||
|
|
||||||
|
|
||||||
|
@implements_iterator
|
||||||
class TemplateStream(object):
|
class TemplateStream(object):
|
||||||
"""A template stream works pretty much like an ordinary python generator
|
"""A template stream works pretty much like an ordinary python generator
|
||||||
but it can buffer multiple items to reduce the number of total iterations.
|
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')
|
Template('Hello {{ name }}!').stream(name='foo').dump('hello.html')
|
||||||
"""
|
"""
|
||||||
close = False
|
close = False
|
||||||
if isinstance(fp, basestring):
|
if isinstance(fp, string_types):
|
||||||
fp = file(fp, 'w')
|
fp = open(fp, encoding is None and 'w' or 'wb')
|
||||||
close = True
|
close = True
|
||||||
try:
|
try:
|
||||||
if encoding is not None:
|
if encoding is not None:
|
||||||
@ -1082,7 +1148,7 @@ class TemplateStream(object):
|
|||||||
|
|
||||||
def disable_buffering(self):
|
def disable_buffering(self):
|
||||||
"""Disable the output buffering."""
|
"""Disable the output buffering."""
|
||||||
self._next = self._gen.next
|
self._next = get_next(self._gen)
|
||||||
self.buffered = False
|
self.buffered = False
|
||||||
|
|
||||||
def enable_buffering(self, size=5):
|
def enable_buffering(self, size=5):
|
||||||
@ -1110,12 +1176,12 @@ class TemplateStream(object):
|
|||||||
c_size = 0
|
c_size = 0
|
||||||
|
|
||||||
self.buffered = True
|
self.buffered = True
|
||||||
self._next = generator(self._gen.next).next
|
self._next = get_next(generator(get_next(self._gen)))
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def next(self):
|
def __next__(self):
|
||||||
return self._next()
|
return self._next()
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,24 +8,40 @@
|
|||||||
:copyright: (c) 2010 by the Jinja Team.
|
:copyright: (c) 2010 by the Jinja Team.
|
||||||
:license: BSD, see LICENSE for more details.
|
:license: BSD, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
from jinja2._compat import imap, text_type, PY2, implements_to_string
|
||||||
|
|
||||||
|
|
||||||
class TemplateError(Exception):
|
class TemplateError(Exception):
|
||||||
"""Baseclass for all template errors."""
|
"""Baseclass for all template errors."""
|
||||||
|
|
||||||
def __init__(self, message=None):
|
if PY2:
|
||||||
if message is not None:
|
def __init__(self, message=None):
|
||||||
message = unicode(message).encode('utf-8')
|
|
||||||
Exception.__init__(self, message)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def message(self):
|
|
||||||
if self.args:
|
|
||||||
message = self.args[0]
|
|
||||||
if message is not None:
|
if message is not None:
|
||||||
return message.decode('utf-8', 'replace')
|
message = text_type(message).encode('utf-8')
|
||||||
|
Exception.__init__(self, message)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def message(self):
|
||||||
|
if self.args:
|
||||||
|
message = self.args[0]
|
||||||
|
if message is not None:
|
||||||
|
return message.decode('utf-8', 'replace')
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return self.message or u''
|
||||||
|
else:
|
||||||
|
def __init__(self, message=None):
|
||||||
|
Exception.__init__(self, message)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def message(self):
|
||||||
|
if self.args:
|
||||||
|
message = self.args[0]
|
||||||
|
if message is not None:
|
||||||
|
return message
|
||||||
|
|
||||||
|
|
||||||
|
@implements_to_string
|
||||||
class TemplateNotFound(IOError, LookupError, TemplateError):
|
class TemplateNotFound(IOError, LookupError, TemplateError):
|
||||||
"""Raised if a template does not exist."""
|
"""Raised if a template does not exist."""
|
||||||
|
|
||||||
@ -42,13 +58,6 @@ class TemplateNotFound(IOError, LookupError, TemplateError):
|
|||||||
self.templates = [name]
|
self.templates = [name]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.message.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 self.message
|
return self.message
|
||||||
|
|
||||||
|
|
||||||
@ -63,11 +72,12 @@ class TemplatesNotFound(TemplateNotFound):
|
|||||||
def __init__(self, names=(), message=None):
|
def __init__(self, names=(), message=None):
|
||||||
if message is None:
|
if message is None:
|
||||||
message = u'none of the templates given were found: ' + \
|
message = u'none of the templates given were found: ' + \
|
||||||
u', '.join(map(unicode, names))
|
u', '.join(imap(text_type, names))
|
||||||
TemplateNotFound.__init__(self, names and names[-1] or None, message)
|
TemplateNotFound.__init__(self, names and names[-1] or None, message)
|
||||||
self.templates = list(names)
|
self.templates = list(names)
|
||||||
|
|
||||||
|
|
||||||
|
@implements_to_string
|
||||||
class TemplateSyntaxError(TemplateError):
|
class TemplateSyntaxError(TemplateError):
|
||||||
"""Raised to tell the user that there is a problem with the template."""
|
"""Raised to tell the user that there is a problem with the template."""
|
||||||
|
|
||||||
@ -83,13 +93,6 @@ class TemplateSyntaxError(TemplateError):
|
|||||||
self.translated = False
|
self.translated = False
|
||||||
|
|
||||||
def __str__(self):
|
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):
|
|
||||||
# for translated errors we only return the message
|
# for translated errors we only return the message
|
||||||
if self.translated:
|
if self.translated:
|
||||||
return self.message
|
return self.message
|
||||||
|
@ -10,13 +10,15 @@
|
|||||||
"""
|
"""
|
||||||
import re
|
import re
|
||||||
import math
|
import math
|
||||||
|
|
||||||
from random import choice
|
from random import choice
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
from itertools import imap, groupby
|
from itertools import groupby
|
||||||
from jinja2.utils import Markup, escape, pformat, urlize, soft_unicode, \
|
from jinja2.utils import Markup, escape, pformat, urlize, soft_unicode, \
|
||||||
unicode_urlencode
|
unicode_urlencode
|
||||||
from jinja2.runtime import Undefined
|
from jinja2.runtime import Undefined
|
||||||
from jinja2.exceptions import FilterArgumentError
|
from jinja2.exceptions import FilterArgumentError
|
||||||
|
from jinja2._compat import imap, string_types, text_type, iteritems
|
||||||
|
|
||||||
|
|
||||||
_word_re = re.compile(r'\w+(?u)')
|
_word_re = re.compile(r'\w+(?u)')
|
||||||
@ -52,13 +54,17 @@ def environmentfilter(f):
|
|||||||
def make_attrgetter(environment, attribute):
|
def make_attrgetter(environment, attribute):
|
||||||
"""Returns a callable that looks up the given attribute from a
|
"""Returns a callable that looks up the given attribute from a
|
||||||
passed object with the rules of the environment. Dots are allowed
|
passed object with the rules of the environment. Dots are allowed
|
||||||
to access attributes of attributes.
|
to access attributes of attributes. Integer parts in paths are
|
||||||
|
looked up as integers.
|
||||||
"""
|
"""
|
||||||
if not isinstance(attribute, basestring) or '.' not in attribute:
|
if not isinstance(attribute, string_types) \
|
||||||
|
or ('.' not in attribute and not attribute.isdigit()):
|
||||||
return lambda x: environment.getitem(x, attribute)
|
return lambda x: environment.getitem(x, attribute)
|
||||||
attribute = attribute.split('.')
|
attribute = attribute.split('.')
|
||||||
def attrgetter(item):
|
def attrgetter(item):
|
||||||
for part in attribute:
|
for part in attribute:
|
||||||
|
if part.isdigit():
|
||||||
|
part = int(part)
|
||||||
item = environment.getitem(item, part)
|
item = environment.getitem(item, part)
|
||||||
return item
|
return item
|
||||||
return attrgetter
|
return attrgetter
|
||||||
@ -68,7 +74,7 @@ def do_forceescape(value):
|
|||||||
"""Enforce HTML escaping. This will probably double escape variables."""
|
"""Enforce HTML escaping. This will probably double escape variables."""
|
||||||
if hasattr(value, '__html__'):
|
if hasattr(value, '__html__'):
|
||||||
value = value.__html__()
|
value = value.__html__()
|
||||||
return escape(unicode(value))
|
return escape(text_type(value))
|
||||||
|
|
||||||
|
|
||||||
def do_urlencode(value):
|
def do_urlencode(value):
|
||||||
@ -79,8 +85,8 @@ def do_urlencode(value):
|
|||||||
"""
|
"""
|
||||||
itemiter = None
|
itemiter = None
|
||||||
if isinstance(value, dict):
|
if isinstance(value, dict):
|
||||||
itemiter = value.iteritems()
|
itemiter = iteritems(value)
|
||||||
elif not isinstance(value, basestring):
|
elif not isinstance(value, string_types):
|
||||||
try:
|
try:
|
||||||
itemiter = iter(value)
|
itemiter = iter(value)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
@ -110,7 +116,7 @@ def do_replace(eval_ctx, s, old, new, count=None):
|
|||||||
if count is None:
|
if count is None:
|
||||||
count = -1
|
count = -1
|
||||||
if not eval_ctx.autoescape:
|
if not eval_ctx.autoescape:
|
||||||
return unicode(s).replace(unicode(old), unicode(new), count)
|
return text_type(s).replace(text_type(old), text_type(new), count)
|
||||||
if hasattr(old, '__html__') or hasattr(new, '__html__') and \
|
if hasattr(old, '__html__') or hasattr(new, '__html__') and \
|
||||||
not hasattr(s, '__html__'):
|
not hasattr(s, '__html__'):
|
||||||
s = escape(s)
|
s = escape(s)
|
||||||
@ -155,7 +161,7 @@ def do_xmlattr(_eval_ctx, d, autospace=True):
|
|||||||
"""
|
"""
|
||||||
rv = u' '.join(
|
rv = u' '.join(
|
||||||
u'%s="%s"' % (escape(key), escape(value))
|
u'%s="%s"' % (escape(key), escape(value))
|
||||||
for key, value in d.iteritems()
|
for key, value in iteritems(d)
|
||||||
if value is not None and not isinstance(value, Undefined)
|
if value is not None and not isinstance(value, Undefined)
|
||||||
)
|
)
|
||||||
if autospace and rv:
|
if autospace and rv:
|
||||||
@ -177,7 +183,7 @@ def do_title(s):
|
|||||||
uppercase letters, all remaining characters are lowercase.
|
uppercase letters, all remaining characters are lowercase.
|
||||||
"""
|
"""
|
||||||
rv = []
|
rv = []
|
||||||
for item in re.compile(r'([-\s]+)(?u)').split(s):
|
for item in re.compile(r'([-\s]+)(?u)').split(soft_unicode(s)):
|
||||||
if not item:
|
if not item:
|
||||||
continue
|
continue
|
||||||
rv.append(item[0].upper() + item[1:])
|
rv.append(item[0].upper() + item[1:])
|
||||||
@ -194,7 +200,7 @@ def do_dictsort(value, case_sensitive=False, by='key'):
|
|||||||
{% for item in mydict|dictsort %}
|
{% for item in mydict|dictsort %}
|
||||||
sort the dict by key, case insensitive
|
sort the dict by key, case insensitive
|
||||||
|
|
||||||
{% for item in mydict|dicsort(true) %}
|
{% for item in mydict|dictsort(true) %}
|
||||||
sort the dict by key, case sensitive
|
sort the dict by key, case sensitive
|
||||||
|
|
||||||
{% for item in mydict|dictsort(false, 'value') %}
|
{% for item in mydict|dictsort(false, 'value') %}
|
||||||
@ -210,7 +216,7 @@ def do_dictsort(value, case_sensitive=False, by='key'):
|
|||||||
'"key" or "value"')
|
'"key" or "value"')
|
||||||
def sort_func(item):
|
def sort_func(item):
|
||||||
value = item[pos]
|
value = item[pos]
|
||||||
if isinstance(value, basestring) and not case_sensitive:
|
if isinstance(value, string_types) and not case_sensitive:
|
||||||
value = value.lower()
|
value = value.lower()
|
||||||
return value
|
return value
|
||||||
|
|
||||||
@ -247,7 +253,7 @@ def do_sort(environment, value, reverse=False, case_sensitive=False,
|
|||||||
"""
|
"""
|
||||||
if not case_sensitive:
|
if not case_sensitive:
|
||||||
def sort_func(item):
|
def sort_func(item):
|
||||||
if isinstance(item, basestring):
|
if isinstance(item, string_types):
|
||||||
item = item.lower()
|
item = item.lower()
|
||||||
return item
|
return item
|
||||||
else:
|
else:
|
||||||
@ -276,7 +282,7 @@ def do_default(value, default_value=u'', boolean=False):
|
|||||||
|
|
||||||
{{ ''|default('the string was empty', true) }}
|
{{ ''|default('the string was empty', true) }}
|
||||||
"""
|
"""
|
||||||
if (boolean and not value) or isinstance(value, Undefined):
|
if isinstance(value, Undefined) or (boolean and not value):
|
||||||
return default_value
|
return default_value
|
||||||
return value
|
return value
|
||||||
|
|
||||||
@ -309,7 +315,7 @@ def do_join(eval_ctx, value, d=u'', attribute=None):
|
|||||||
|
|
||||||
# no automatic escaping? joining is a lot eaiser then
|
# no automatic escaping? joining is a lot eaiser then
|
||||||
if not eval_ctx.autoescape:
|
if not eval_ctx.autoescape:
|
||||||
return unicode(d).join(imap(unicode, value))
|
return text_type(d).join(imap(text_type, value))
|
||||||
|
|
||||||
# if the delimiter doesn't have an html representation we check
|
# if the delimiter doesn't have an html representation we check
|
||||||
# if any of the items has. If yes we do a coercion to Markup
|
# if any of the items has. If yes we do a coercion to Markup
|
||||||
@ -320,11 +326,11 @@ def do_join(eval_ctx, value, d=u'', attribute=None):
|
|||||||
if hasattr(item, '__html__'):
|
if hasattr(item, '__html__'):
|
||||||
do_escape = True
|
do_escape = True
|
||||||
else:
|
else:
|
||||||
value[idx] = unicode(item)
|
value[idx] = text_type(item)
|
||||||
if do_escape:
|
if do_escape:
|
||||||
d = escape(d)
|
d = escape(d)
|
||||||
else:
|
else:
|
||||||
d = unicode(d)
|
d = text_type(d)
|
||||||
return d.join(value)
|
return d.join(value)
|
||||||
|
|
||||||
# no html involved, to normal joining
|
# no html involved, to normal joining
|
||||||
@ -333,14 +339,14 @@ def do_join(eval_ctx, value, d=u'', attribute=None):
|
|||||||
|
|
||||||
def do_center(value, width=80):
|
def do_center(value, width=80):
|
||||||
"""Centers the value in a field of a given width."""
|
"""Centers the value in a field of a given width."""
|
||||||
return unicode(value).center(width)
|
return text_type(value).center(width)
|
||||||
|
|
||||||
|
|
||||||
@environmentfilter
|
@environmentfilter
|
||||||
def do_first(environment, seq):
|
def do_first(environment, seq):
|
||||||
"""Return the first item of a sequence."""
|
"""Return the first item of a sequence."""
|
||||||
try:
|
try:
|
||||||
return iter(seq).next()
|
return next(iter(seq))
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
return environment.undefined('No first item, sequence was empty.')
|
return environment.undefined('No first item, sequence was empty.')
|
||||||
|
|
||||||
@ -349,7 +355,7 @@ def do_first(environment, seq):
|
|||||||
def do_last(environment, seq):
|
def do_last(environment, seq):
|
||||||
"""Return the last item of a sequence."""
|
"""Return the last item of a sequence."""
|
||||||
try:
|
try:
|
||||||
return iter(reversed(seq)).next()
|
return next(iter(reversed(seq)))
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
return environment.undefined('No last item, sequence was empty.')
|
return environment.undefined('No last item, sequence was empty.')
|
||||||
|
|
||||||
@ -443,16 +449,17 @@ def do_truncate(s, length=255, killwords=False, end='...'):
|
|||||||
"""Return a truncated copy of the string. The length is specified
|
"""Return a truncated copy of the string. The length is specified
|
||||||
with the first parameter which defaults to ``255``. If the second
|
with the first parameter which defaults to ``255``. If the second
|
||||||
parameter is ``true`` the filter will cut the text at length. Otherwise
|
parameter is ``true`` the filter will cut the text at length. Otherwise
|
||||||
it will try to save the last word. If the text was in fact
|
it will discard the last word. If the text was in fact
|
||||||
truncated it will append an ellipsis sign (``"..."``). If you want a
|
truncated it will append an ellipsis sign (``"..."``). If you want a
|
||||||
different ellipsis sign than ``"..."`` you can specify it using the
|
different ellipsis sign than ``"..."`` you can specify it using the
|
||||||
third parameter.
|
third parameter.
|
||||||
|
|
||||||
.. sourcecode jinja::
|
.. sourcecode:: jinja
|
||||||
|
|
||||||
{{ mytext|truncate(300, false, '»') }}
|
{{ "foo bar"|truncate(5) }}
|
||||||
truncate mytext to 300 chars, don't split up words, use a
|
-> "foo ..."
|
||||||
right pointing double arrow as ellipsis sign.
|
{{ "foo bar"|truncate(5, True) }}
|
||||||
|
-> "foo b..."
|
||||||
"""
|
"""
|
||||||
if len(s) <= length:
|
if len(s) <= length:
|
||||||
return s
|
return s
|
||||||
@ -470,15 +477,23 @@ def do_truncate(s, length=255, killwords=False, end='...'):
|
|||||||
return u' '.join(result)
|
return u' '.join(result)
|
||||||
|
|
||||||
@environmentfilter
|
@environmentfilter
|
||||||
def do_wordwrap(environment, s, width=79, break_long_words=True):
|
def do_wordwrap(environment, s, width=79, break_long_words=True,
|
||||||
|
wrapstring=None):
|
||||||
"""
|
"""
|
||||||
Return a copy of the string passed to the filter wrapped after
|
Return a copy of the string passed to the filter wrapped after
|
||||||
``79`` characters. You can override this default using the first
|
``79`` characters. You can override this default using the first
|
||||||
parameter. If you set the second parameter to `false` Jinja will not
|
parameter. If you set the second parameter to `false` Jinja will not
|
||||||
split words apart if they are longer than `width`.
|
split words apart if they are longer than `width`. By default, the newlines
|
||||||
|
will be the default newlines for the environment, but this can be changed
|
||||||
|
using the wrapstring keyword argument.
|
||||||
|
|
||||||
|
.. versionadded:: 2.7
|
||||||
|
Added support for the `wrapstring` parameter.
|
||||||
"""
|
"""
|
||||||
|
if not wrapstring:
|
||||||
|
wrapstring = environment.newline_sequence
|
||||||
import textwrap
|
import textwrap
|
||||||
return environment.newline_sequence.join(textwrap.wrap(s, width=width, expand_tabs=False,
|
return wrapstring.join(textwrap.wrap(s, width=width, expand_tabs=False,
|
||||||
replace_whitespace=False,
|
replace_whitespace=False,
|
||||||
break_long_words=break_long_words))
|
break_long_words=break_long_words))
|
||||||
|
|
||||||
@ -539,7 +554,7 @@ def do_striptags(value):
|
|||||||
"""
|
"""
|
||||||
if hasattr(value, '__html__'):
|
if hasattr(value, '__html__'):
|
||||||
value = value.__html__()
|
value = value.__html__()
|
||||||
return Markup(unicode(value)).striptags()
|
return Markup(text_type(value)).striptags()
|
||||||
|
|
||||||
|
|
||||||
def do_slice(value, slices, fill_with=None):
|
def do_slice(value, slices, fill_with=None):
|
||||||
@ -567,7 +582,7 @@ def do_slice(value, slices, fill_with=None):
|
|||||||
items_per_slice = length // slices
|
items_per_slice = length // slices
|
||||||
slices_with_extra = length % slices
|
slices_with_extra = length % slices
|
||||||
offset = 0
|
offset = 0
|
||||||
for slice_number in xrange(slices):
|
for slice_number in range(slices):
|
||||||
start = offset + slice_number * items_per_slice
|
start = offset + slice_number * items_per_slice
|
||||||
if slice_number < slices_with_extra:
|
if slice_number < slices_with_extra:
|
||||||
offset += 1
|
offset += 1
|
||||||
@ -692,7 +707,8 @@ class _GroupTuple(tuple):
|
|||||||
grouper = property(itemgetter(0))
|
grouper = property(itemgetter(0))
|
||||||
list = property(itemgetter(1))
|
list = property(itemgetter(1))
|
||||||
|
|
||||||
def __new__(cls, (key, value)):
|
def __new__(cls, xxx_todo_changeme):
|
||||||
|
(key, value) = xxx_todo_changeme
|
||||||
return tuple.__new__(cls, (key, list(value)))
|
return tuple.__new__(cls, (key, list(value)))
|
||||||
|
|
||||||
|
|
||||||
@ -733,14 +749,14 @@ def do_mark_safe(value):
|
|||||||
|
|
||||||
def do_mark_unsafe(value):
|
def do_mark_unsafe(value):
|
||||||
"""Mark a value as unsafe. This is the reverse operation for :func:`safe`."""
|
"""Mark a value as unsafe. This is the reverse operation for :func:`safe`."""
|
||||||
return unicode(value)
|
return text_type(value)
|
||||||
|
|
||||||
|
|
||||||
def do_reverse(value):
|
def do_reverse(value):
|
||||||
"""Reverse the object or return an iterator the iterates over it the other
|
"""Reverse the object or return an iterator the iterates over it the other
|
||||||
way round.
|
way round.
|
||||||
"""
|
"""
|
||||||
if isinstance(value, basestring):
|
if isinstance(value, string_types):
|
||||||
return value[::-1]
|
return value[::-1]
|
||||||
try:
|
try:
|
||||||
return reversed(value)
|
return reversed(value)
|
||||||
@ -778,6 +794,145 @@ def do_attr(environment, obj, name):
|
|||||||
return environment.undefined(obj=obj, name=name)
|
return environment.undefined(obj=obj, name=name)
|
||||||
|
|
||||||
|
|
||||||
|
@contextfilter
|
||||||
|
def do_map(*args, **kwargs):
|
||||||
|
"""Applies a filter on a sequence of objects or looks up an attribute.
|
||||||
|
This is useful when dealing with lists of objects but you are really
|
||||||
|
only interested in a certain value of it.
|
||||||
|
|
||||||
|
The basic usage is mapping on an attribute. Imagine you have a list
|
||||||
|
of users but you are only interested in a list of usernames:
|
||||||
|
|
||||||
|
.. sourcecode:: jinja
|
||||||
|
|
||||||
|
Users on this page: {{ users|map(attribute='username')|join(', ') }}
|
||||||
|
|
||||||
|
Alternatively you can let it invoke a filter by passing the name of the
|
||||||
|
filter and the arguments afterwards. A good example would be applying a
|
||||||
|
text conversion filter on a sequence:
|
||||||
|
|
||||||
|
.. sourcecode:: jinja
|
||||||
|
|
||||||
|
Users on this page: {{ titles|map('lower')|join(', ') }}
|
||||||
|
|
||||||
|
.. versionadded:: 2.7
|
||||||
|
"""
|
||||||
|
context = args[0]
|
||||||
|
seq = args[1]
|
||||||
|
|
||||||
|
if len(args) == 2 and 'attribute' in kwargs:
|
||||||
|
attribute = kwargs.pop('attribute')
|
||||||
|
if kwargs:
|
||||||
|
raise FilterArgumentError('Unexpected keyword argument %r' %
|
||||||
|
next(iter(kwargs)))
|
||||||
|
func = make_attrgetter(context.environment, attribute)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
name = args[2]
|
||||||
|
args = args[3:]
|
||||||
|
except LookupError:
|
||||||
|
raise FilterArgumentError('map requires a filter argument')
|
||||||
|
func = lambda item: context.environment.call_filter(
|
||||||
|
name, item, args, kwargs, context=context)
|
||||||
|
|
||||||
|
if seq:
|
||||||
|
for item in seq:
|
||||||
|
yield func(item)
|
||||||
|
|
||||||
|
|
||||||
|
@contextfilter
|
||||||
|
def do_select(*args, **kwargs):
|
||||||
|
"""Filters a sequence of objects by appying a test to the object and only
|
||||||
|
selecting the ones with the test succeeding.
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
|
||||||
|
.. sourcecode:: jinja
|
||||||
|
|
||||||
|
{{ numbers|select("odd") }}
|
||||||
|
{{ numbers|select("odd") }}
|
||||||
|
|
||||||
|
.. versionadded:: 2.7
|
||||||
|
"""
|
||||||
|
return _select_or_reject(args, kwargs, lambda x: x, False)
|
||||||
|
|
||||||
|
|
||||||
|
@contextfilter
|
||||||
|
def do_reject(*args, **kwargs):
|
||||||
|
"""Filters a sequence of objects by appying a test to the object and
|
||||||
|
rejecting the ones with the test succeeding.
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
|
||||||
|
.. sourcecode:: jinja
|
||||||
|
|
||||||
|
{{ numbers|reject("odd") }}
|
||||||
|
|
||||||
|
.. versionadded:: 2.7
|
||||||
|
"""
|
||||||
|
return _select_or_reject(args, kwargs, lambda x: not x, False)
|
||||||
|
|
||||||
|
|
||||||
|
@contextfilter
|
||||||
|
def do_selectattr(*args, **kwargs):
|
||||||
|
"""Filters a sequence of objects by appying a test to an attribute of an
|
||||||
|
object and only selecting the ones with the test succeeding.
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
|
||||||
|
.. sourcecode:: jinja
|
||||||
|
|
||||||
|
{{ users|selectattr("is_active") }}
|
||||||
|
{{ users|selectattr("email", "none") }}
|
||||||
|
|
||||||
|
.. versionadded:: 2.7
|
||||||
|
"""
|
||||||
|
return _select_or_reject(args, kwargs, lambda x: x, True)
|
||||||
|
|
||||||
|
|
||||||
|
@contextfilter
|
||||||
|
def do_rejectattr(*args, **kwargs):
|
||||||
|
"""Filters a sequence of objects by appying a test to an attribute of an
|
||||||
|
object or the attribute and rejecting the ones with the test succeeding.
|
||||||
|
|
||||||
|
.. sourcecode:: jinja
|
||||||
|
|
||||||
|
{{ users|rejectattr("is_active") }}
|
||||||
|
{{ users|rejectattr("email", "none") }}
|
||||||
|
|
||||||
|
.. versionadded:: 2.7
|
||||||
|
"""
|
||||||
|
return _select_or_reject(args, kwargs, lambda x: not x, True)
|
||||||
|
|
||||||
|
|
||||||
|
def _select_or_reject(args, kwargs, modfunc, lookup_attr):
|
||||||
|
context = args[0]
|
||||||
|
seq = args[1]
|
||||||
|
if lookup_attr:
|
||||||
|
try:
|
||||||
|
attr = args[2]
|
||||||
|
except LookupError:
|
||||||
|
raise FilterArgumentError('Missing parameter for attribute name')
|
||||||
|
transfunc = make_attrgetter(context.environment, attr)
|
||||||
|
off = 1
|
||||||
|
else:
|
||||||
|
off = 0
|
||||||
|
transfunc = lambda x: x
|
||||||
|
|
||||||
|
try:
|
||||||
|
name = args[2 + off]
|
||||||
|
args = args[3 + off:]
|
||||||
|
func = lambda item: context.environment.call_test(
|
||||||
|
name, item, args, kwargs)
|
||||||
|
except LookupError:
|
||||||
|
func = bool
|
||||||
|
|
||||||
|
if seq:
|
||||||
|
for item in seq:
|
||||||
|
if modfunc(func(transfunc(item))):
|
||||||
|
yield item
|
||||||
|
|
||||||
|
|
||||||
FILTERS = {
|
FILTERS = {
|
||||||
'attr': do_attr,
|
'attr': do_attr,
|
||||||
'replace': do_replace,
|
'replace': do_replace,
|
||||||
@ -802,7 +957,10 @@ FILTERS = {
|
|||||||
'capitalize': do_capitalize,
|
'capitalize': do_capitalize,
|
||||||
'first': do_first,
|
'first': do_first,
|
||||||
'last': do_last,
|
'last': do_last,
|
||||||
|
'map': do_map,
|
||||||
'random': do_random,
|
'random': do_random,
|
||||||
|
'reject': do_reject,
|
||||||
|
'rejectattr': do_rejectattr,
|
||||||
'filesizeformat': do_filesizeformat,
|
'filesizeformat': do_filesizeformat,
|
||||||
'pprint': do_pprint,
|
'pprint': do_pprint,
|
||||||
'truncate': do_truncate,
|
'truncate': do_truncate,
|
||||||
@ -816,6 +974,8 @@ FILTERS = {
|
|||||||
'format': do_format,
|
'format': do_format,
|
||||||
'trim': do_trim,
|
'trim': do_trim,
|
||||||
'striptags': do_striptags,
|
'striptags': do_striptags,
|
||||||
|
'select': do_select,
|
||||||
|
'selectattr': do_selectattr,
|
||||||
'slice': do_slice,
|
'slice': do_slice,
|
||||||
'batch': do_batch,
|
'batch': do_batch,
|
||||||
'sum': do_sum,
|
'sum': do_sum,
|
||||||
|
@ -15,10 +15,13 @@
|
|||||||
:license: BSD, see LICENSE for more details.
|
:license: BSD, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from jinja2.exceptions import TemplateSyntaxError
|
from jinja2.exceptions import TemplateSyntaxError
|
||||||
from jinja2.utils import LRUCache, next
|
from jinja2.utils import LRUCache
|
||||||
|
from jinja2._compat import iteritems, implements_iterator, text_type, \
|
||||||
|
intern
|
||||||
|
|
||||||
|
|
||||||
# cache for the lexers. Exists in order to be able to have multiple
|
# cache for the lexers. Exists in order to be able to have multiple
|
||||||
@ -126,7 +129,7 @@ operators = {
|
|||||||
';': TOKEN_SEMICOLON
|
';': TOKEN_SEMICOLON
|
||||||
}
|
}
|
||||||
|
|
||||||
reverse_operators = dict([(v, k) for k, v in operators.iteritems()])
|
reverse_operators = dict([(v, k) for k, v in iteritems(operators)])
|
||||||
assert len(operators) == len(reverse_operators), 'operators dropped'
|
assert len(operators) == len(reverse_operators), 'operators dropped'
|
||||||
operator_re = re.compile('(%s)' % '|'.join(re.escape(x) for x in
|
operator_re = re.compile('(%s)' % '|'.join(re.escape(x) for x in
|
||||||
sorted(operators, key=lambda x: -len(x))))
|
sorted(operators, key=lambda x: -len(x))))
|
||||||
@ -197,7 +200,7 @@ def compile_rules(environment):
|
|||||||
|
|
||||||
if environment.line_statement_prefix is not None:
|
if environment.line_statement_prefix is not None:
|
||||||
rules.append((len(environment.line_statement_prefix), 'linestatement',
|
rules.append((len(environment.line_statement_prefix), 'linestatement',
|
||||||
r'^\s*' + e(environment.line_statement_prefix)))
|
r'^[ \t\v]*' + e(environment.line_statement_prefix)))
|
||||||
if environment.line_comment_prefix is not None:
|
if environment.line_comment_prefix is not None:
|
||||||
rules.append((len(environment.line_comment_prefix), 'linecomment',
|
rules.append((len(environment.line_comment_prefix), 'linecomment',
|
||||||
r'(?:^|(?<=\S))[^\S\r\n]*' +
|
r'(?:^|(?<=\S))[^\S\r\n]*' +
|
||||||
@ -262,6 +265,7 @@ class Token(tuple):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@implements_iterator
|
||||||
class TokenStreamIterator(object):
|
class TokenStreamIterator(object):
|
||||||
"""The iterator for tokenstreams. Iterate over the stream
|
"""The iterator for tokenstreams. Iterate over the stream
|
||||||
until the eof token is reached.
|
until the eof token is reached.
|
||||||
@ -273,7 +277,7 @@ class TokenStreamIterator(object):
|
|||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def next(self):
|
def __next__(self):
|
||||||
token = self.stream.current
|
token = self.stream.current
|
||||||
if token.type is TOKEN_EOF:
|
if token.type is TOKEN_EOF:
|
||||||
self.stream.close()
|
self.stream.close()
|
||||||
@ -282,6 +286,7 @@ class TokenStreamIterator(object):
|
|||||||
return token
|
return token
|
||||||
|
|
||||||
|
|
||||||
|
@implements_iterator
|
||||||
class TokenStream(object):
|
class TokenStream(object):
|
||||||
"""A token stream is an iterable that yields :class:`Token`\s. The
|
"""A token stream is an iterable that yields :class:`Token`\s. The
|
||||||
parser however does not iterate over it but calls :meth:`next` to go
|
parser however does not iterate over it but calls :meth:`next` to go
|
||||||
@ -289,7 +294,7 @@ class TokenStream(object):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, generator, name, filename):
|
def __init__(self, generator, name, filename):
|
||||||
self._next = iter(generator).next
|
self._iter = iter(generator)
|
||||||
self._pushed = deque()
|
self._pushed = deque()
|
||||||
self.name = name
|
self.name = name
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
@ -300,8 +305,9 @@ class TokenStream(object):
|
|||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return TokenStreamIterator(self)
|
return TokenStreamIterator(self)
|
||||||
|
|
||||||
def __nonzero__(self):
|
def __bool__(self):
|
||||||
return bool(self._pushed) or self.current.type is not TOKEN_EOF
|
return bool(self._pushed) or self.current.type is not TOKEN_EOF
|
||||||
|
__nonzero__ = __bool__ # py2
|
||||||
|
|
||||||
eos = property(lambda x: not x, doc="Are we at the end of the stream?")
|
eos = property(lambda x: not x, doc="Are we at the end of the stream?")
|
||||||
|
|
||||||
@ -319,7 +325,7 @@ class TokenStream(object):
|
|||||||
|
|
||||||
def skip(self, n=1):
|
def skip(self, n=1):
|
||||||
"""Got n tokens ahead."""
|
"""Got n tokens ahead."""
|
||||||
for x in xrange(n):
|
for x in range(n):
|
||||||
next(self)
|
next(self)
|
||||||
|
|
||||||
def next_if(self, expr):
|
def next_if(self, expr):
|
||||||
@ -333,14 +339,14 @@ class TokenStream(object):
|
|||||||
"""Like :meth:`next_if` but only returns `True` or `False`."""
|
"""Like :meth:`next_if` but only returns `True` or `False`."""
|
||||||
return self.next_if(expr) is not None
|
return self.next_if(expr) is not None
|
||||||
|
|
||||||
def next(self):
|
def __next__(self):
|
||||||
"""Go one token ahead and return the old one"""
|
"""Go one token ahead and return the old one"""
|
||||||
rv = self.current
|
rv = self.current
|
||||||
if self._pushed:
|
if self._pushed:
|
||||||
self.current = self._pushed.popleft()
|
self.current = self._pushed.popleft()
|
||||||
elif self.current.type is not TOKEN_EOF:
|
elif self.current.type is not TOKEN_EOF:
|
||||||
try:
|
try:
|
||||||
self.current = self._next()
|
self.current = next(self._iter)
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
self.close()
|
self.close()
|
||||||
return rv
|
return rv
|
||||||
@ -348,7 +354,7 @@ class TokenStream(object):
|
|||||||
def close(self):
|
def close(self):
|
||||||
"""Close the stream."""
|
"""Close the stream."""
|
||||||
self.current = Token(self.current.lineno, TOKEN_EOF, '')
|
self.current = Token(self.current.lineno, TOKEN_EOF, '')
|
||||||
self._next = None
|
self._iter = None
|
||||||
self.closed = True
|
self.closed = True
|
||||||
|
|
||||||
def expect(self, expr):
|
def expect(self, expr):
|
||||||
@ -383,7 +389,9 @@ def get_lexer(environment):
|
|||||||
environment.line_statement_prefix,
|
environment.line_statement_prefix,
|
||||||
environment.line_comment_prefix,
|
environment.line_comment_prefix,
|
||||||
environment.trim_blocks,
|
environment.trim_blocks,
|
||||||
environment.newline_sequence)
|
environment.lstrip_blocks,
|
||||||
|
environment.newline_sequence,
|
||||||
|
environment.keep_trailing_newline)
|
||||||
lexer = _lexer_cache.get(key)
|
lexer = _lexer_cache.get(key)
|
||||||
if lexer is None:
|
if lexer is None:
|
||||||
lexer = Lexer(environment)
|
lexer = Lexer(environment)
|
||||||
@ -425,7 +433,44 @@ class Lexer(object):
|
|||||||
# block suffix if trimming is enabled
|
# block suffix if trimming is enabled
|
||||||
block_suffix_re = environment.trim_blocks and '\\n?' or ''
|
block_suffix_re = environment.trim_blocks and '\\n?' or ''
|
||||||
|
|
||||||
|
# strip leading spaces if lstrip_blocks is enabled
|
||||||
|
prefix_re = {}
|
||||||
|
if environment.lstrip_blocks:
|
||||||
|
# use '{%+' to manually disable lstrip_blocks behavior
|
||||||
|
no_lstrip_re = e('+')
|
||||||
|
# detect overlap between block and variable or comment strings
|
||||||
|
block_diff = c(r'^%s(.*)' % e(environment.block_start_string))
|
||||||
|
# make sure we don't mistake a block for a variable or a comment
|
||||||
|
m = block_diff.match(environment.comment_start_string)
|
||||||
|
no_lstrip_re += m and r'|%s' % e(m.group(1)) or ''
|
||||||
|
m = block_diff.match(environment.variable_start_string)
|
||||||
|
no_lstrip_re += m and r'|%s' % e(m.group(1)) or ''
|
||||||
|
|
||||||
|
# detect overlap between comment and variable strings
|
||||||
|
comment_diff = c(r'^%s(.*)' % e(environment.comment_start_string))
|
||||||
|
m = comment_diff.match(environment.variable_start_string)
|
||||||
|
no_variable_re = m and r'(?!%s)' % e(m.group(1)) or ''
|
||||||
|
|
||||||
|
lstrip_re = r'^[ \t]*'
|
||||||
|
block_prefix_re = r'%s%s(?!%s)|%s\+?' % (
|
||||||
|
lstrip_re,
|
||||||
|
e(environment.block_start_string),
|
||||||
|
no_lstrip_re,
|
||||||
|
e(environment.block_start_string),
|
||||||
|
)
|
||||||
|
comment_prefix_re = r'%s%s%s|%s\+?' % (
|
||||||
|
lstrip_re,
|
||||||
|
e(environment.comment_start_string),
|
||||||
|
no_variable_re,
|
||||||
|
e(environment.comment_start_string),
|
||||||
|
)
|
||||||
|
prefix_re['block'] = block_prefix_re
|
||||||
|
prefix_re['comment'] = comment_prefix_re
|
||||||
|
else:
|
||||||
|
block_prefix_re = '%s' % e(environment.block_start_string)
|
||||||
|
|
||||||
self.newline_sequence = environment.newline_sequence
|
self.newline_sequence = environment.newline_sequence
|
||||||
|
self.keep_trailing_newline = environment.keep_trailing_newline
|
||||||
|
|
||||||
# global lexing rules
|
# global lexing rules
|
||||||
self.rules = {
|
self.rules = {
|
||||||
@ -434,11 +479,11 @@ class Lexer(object):
|
|||||||
(c('(.*?)(?:%s)' % '|'.join(
|
(c('(.*?)(?:%s)' % '|'.join(
|
||||||
[r'(?P<raw_begin>(?:\s*%s\-|%s)\s*raw\s*(?:\-%s\s*|%s))' % (
|
[r'(?P<raw_begin>(?:\s*%s\-|%s)\s*raw\s*(?:\-%s\s*|%s))' % (
|
||||||
e(environment.block_start_string),
|
e(environment.block_start_string),
|
||||||
e(environment.block_start_string),
|
block_prefix_re,
|
||||||
e(environment.block_end_string),
|
e(environment.block_end_string),
|
||||||
e(environment.block_end_string)
|
e(environment.block_end_string)
|
||||||
)] + [
|
)] + [
|
||||||
r'(?P<%s_begin>\s*%s\-|%s)' % (n, r, r)
|
r'(?P<%s_begin>\s*%s\-|%s)' % (n, r, prefix_re.get(n,r))
|
||||||
for n, r in root_tag_rules
|
for n, r in root_tag_rules
|
||||||
])), (TOKEN_DATA, '#bygroup'), '#bygroup'),
|
])), (TOKEN_DATA, '#bygroup'), '#bygroup'),
|
||||||
# data
|
# data
|
||||||
@ -472,7 +517,7 @@ class Lexer(object):
|
|||||||
TOKEN_RAW_BEGIN: [
|
TOKEN_RAW_BEGIN: [
|
||||||
(c('(.*?)((?:\s*%s\-|%s)\s*endraw\s*(?:\-%s\s*|%s%s))' % (
|
(c('(.*?)((?:\s*%s\-|%s)\s*endraw\s*(?:\-%s\s*|%s%s))' % (
|
||||||
e(environment.block_start_string),
|
e(environment.block_start_string),
|
||||||
e(environment.block_start_string),
|
block_prefix_re,
|
||||||
e(environment.block_end_string),
|
e(environment.block_end_string),
|
||||||
e(environment.block_end_string),
|
e(environment.block_end_string),
|
||||||
block_suffix_re
|
block_suffix_re
|
||||||
@ -526,7 +571,7 @@ class Lexer(object):
|
|||||||
value = self._normalize_newlines(value[1:-1]) \
|
value = self._normalize_newlines(value[1:-1]) \
|
||||||
.encode('ascii', 'backslashreplace') \
|
.encode('ascii', 'backslashreplace') \
|
||||||
.decode('unicode-escape')
|
.decode('unicode-escape')
|
||||||
except Exception, e:
|
except Exception as e:
|
||||||
msg = str(e).split(':')[-1].strip()
|
msg = str(e).split(':')[-1].strip()
|
||||||
raise TemplateSyntaxError(msg, lineno, name, filename)
|
raise TemplateSyntaxError(msg, lineno, name, filename)
|
||||||
# if we can express it as bytestring (ascii only)
|
# if we can express it as bytestring (ascii only)
|
||||||
@ -549,7 +594,14 @@ class Lexer(object):
|
|||||||
"""This method tokenizes the text and returns the tokens in a
|
"""This method tokenizes the text and returns the tokens in a
|
||||||
generator. Use this method if you just want to tokenize a template.
|
generator. Use this method if you just want to tokenize a template.
|
||||||
"""
|
"""
|
||||||
source = '\n'.join(unicode(source).splitlines())
|
source = text_type(source)
|
||||||
|
lines = source.splitlines()
|
||||||
|
if self.keep_trailing_newline and source:
|
||||||
|
for newline in ('\r\n', '\r', '\n'):
|
||||||
|
if source.endswith(newline):
|
||||||
|
lines.append('')
|
||||||
|
break
|
||||||
|
source = '\n'.join(lines)
|
||||||
pos = 0
|
pos = 0
|
||||||
lineno = 1
|
lineno = 1
|
||||||
stack = ['root']
|
stack = ['root']
|
||||||
@ -590,7 +642,7 @@ class Lexer(object):
|
|||||||
# yield for the current token the first named
|
# yield for the current token the first named
|
||||||
# group that matched
|
# group that matched
|
||||||
elif token == '#bygroup':
|
elif token == '#bygroup':
|
||||||
for key, value in m.groupdict().iteritems():
|
for key, value in iteritems(m.groupdict()):
|
||||||
if value is not None:
|
if value is not None:
|
||||||
yield lineno, key, value
|
yield lineno, key, value
|
||||||
lineno += value.count('\n')
|
lineno += value.count('\n')
|
||||||
@ -647,7 +699,7 @@ class Lexer(object):
|
|||||||
stack.pop()
|
stack.pop()
|
||||||
# resolve the new state by group checking
|
# resolve the new state by group checking
|
||||||
elif new_state == '#bygroup':
|
elif new_state == '#bygroup':
|
||||||
for key, value in m.groupdict().iteritems():
|
for key, value in iteritems(m.groupdict()):
|
||||||
if value is not None:
|
if value is not None:
|
||||||
stack.append(key)
|
stack.append(key)
|
||||||
break
|
break
|
||||||
|
@ -13,12 +13,10 @@ import sys
|
|||||||
import weakref
|
import weakref
|
||||||
from types import ModuleType
|
from types import ModuleType
|
||||||
from os import path
|
from os import path
|
||||||
try:
|
from hashlib import sha1
|
||||||
from hashlib import sha1
|
|
||||||
except ImportError:
|
|
||||||
from sha import new as sha1
|
|
||||||
from jinja2.exceptions import TemplateNotFound
|
from jinja2.exceptions import TemplateNotFound
|
||||||
from jinja2.utils import LRUCache, open_if_exists, internalcode
|
from jinja2.utils import open_if_exists, internalcode
|
||||||
|
from jinja2._compat import string_types, iteritems
|
||||||
|
|
||||||
|
|
||||||
def split_template_path(template):
|
def split_template_path(template):
|
||||||
@ -153,7 +151,7 @@ class FileSystemLoader(BaseLoader):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, searchpath, encoding='utf-8'):
|
def __init__(self, searchpath, encoding='utf-8'):
|
||||||
if isinstance(searchpath, basestring):
|
if isinstance(searchpath, string_types):
|
||||||
searchpath = [searchpath]
|
searchpath = [searchpath]
|
||||||
self.searchpath = list(searchpath)
|
self.searchpath = list(searchpath)
|
||||||
self.encoding = encoding
|
self.encoding = encoding
|
||||||
@ -274,7 +272,7 @@ class DictLoader(BaseLoader):
|
|||||||
def get_source(self, environment, template):
|
def get_source(self, environment, template):
|
||||||
if template in self.mapping:
|
if template in self.mapping:
|
||||||
source = self.mapping[template]
|
source = self.mapping[template]
|
||||||
return source, None, lambda: source != self.mapping.get(template)
|
return source, None, lambda: source == self.mapping.get(template)
|
||||||
raise TemplateNotFound(template)
|
raise TemplateNotFound(template)
|
||||||
|
|
||||||
def list_templates(self):
|
def list_templates(self):
|
||||||
@ -306,7 +304,7 @@ class FunctionLoader(BaseLoader):
|
|||||||
rv = self.load_func(template)
|
rv = self.load_func(template)
|
||||||
if rv is None:
|
if rv is None:
|
||||||
raise TemplateNotFound(template)
|
raise TemplateNotFound(template)
|
||||||
elif isinstance(rv, basestring):
|
elif isinstance(rv, string_types):
|
||||||
return rv, None, None
|
return rv, None, None
|
||||||
return rv
|
return rv
|
||||||
|
|
||||||
@ -359,7 +357,7 @@ class PrefixLoader(BaseLoader):
|
|||||||
|
|
||||||
def list_templates(self):
|
def list_templates(self):
|
||||||
result = []
|
result = []
|
||||||
for prefix, loader in self.mapping.iteritems():
|
for prefix, loader in iteritems(self.mapping):
|
||||||
for template in loader.list_templates():
|
for template in loader.list_templates():
|
||||||
result.append(prefix + self.delimiter + template)
|
result.append(prefix + self.delimiter + template)
|
||||||
return result
|
return result
|
||||||
@ -431,7 +429,7 @@ class ModuleLoader(BaseLoader):
|
|||||||
# create a fake module that looks for the templates in the
|
# create a fake module that looks for the templates in the
|
||||||
# path given.
|
# path given.
|
||||||
mod = _TemplateModule(package_name)
|
mod = _TemplateModule(package_name)
|
||||||
if isinstance(path, basestring):
|
if isinstance(path, string_types):
|
||||||
path = [path]
|
path = [path]
|
||||||
else:
|
else:
|
||||||
path = list(path)
|
path = list(path)
|
||||||
|
@ -9,7 +9,8 @@
|
|||||||
:license: BSD, see LICENSE for more details.
|
:license: BSD, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
import re
|
import re
|
||||||
from itertools import imap
|
from _compat import text_type, string_types, int_types, \
|
||||||
|
unichr, PY2
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['Markup', 'soft_unicode', 'escape', 'escape_silent']
|
__all__ = ['Markup', 'soft_unicode', 'escape', 'escape_silent']
|
||||||
@ -19,7 +20,7 @@ _striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)')
|
|||||||
_entity_re = re.compile(r'&([^;]+);')
|
_entity_re = re.compile(r'&([^;]+);')
|
||||||
|
|
||||||
|
|
||||||
class Markup(unicode):
|
class Markup(text_type):
|
||||||
r"""Marks a string as being safe for inclusion in HTML/XML output without
|
r"""Marks a string as being safe for inclusion in HTML/XML output without
|
||||||
needing to be escaped. This implements the `__html__` interface a couple
|
needing to be escaped. This implements the `__html__` interface a couple
|
||||||
of frameworks and web applications use. :class:`Markup` is a direct
|
of frameworks and web applications use. :class:`Markup` is a direct
|
||||||
@ -68,65 +69,65 @@ class Markup(unicode):
|
|||||||
if hasattr(base, '__html__'):
|
if hasattr(base, '__html__'):
|
||||||
base = base.__html__()
|
base = base.__html__()
|
||||||
if encoding is None:
|
if encoding is None:
|
||||||
return unicode.__new__(cls, base)
|
return text_type.__new__(cls, base)
|
||||||
return unicode.__new__(cls, base, encoding, errors)
|
return text_type.__new__(cls, base, encoding, errors)
|
||||||
|
|
||||||
def __html__(self):
|
def __html__(self):
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __add__(self, other):
|
def __add__(self, other):
|
||||||
if hasattr(other, '__html__') or isinstance(other, basestring):
|
if isinstance(other, string_types) or hasattr(other, '__html__'):
|
||||||
return self.__class__(unicode(self) + unicode(escape(other)))
|
return self.__class__(super(Markup, self).__add__(self.escape(other)))
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
|
|
||||||
def __radd__(self, other):
|
def __radd__(self, other):
|
||||||
if hasattr(other, '__html__') or isinstance(other, basestring):
|
if hasattr(other, '__html__') or isinstance(other, string_types):
|
||||||
return self.__class__(unicode(escape(other)) + unicode(self))
|
return self.escape(other).__add__(self)
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
|
|
||||||
def __mul__(self, num):
|
def __mul__(self, num):
|
||||||
if isinstance(num, (int, long)):
|
if isinstance(num, int_types):
|
||||||
return self.__class__(unicode.__mul__(self, num))
|
return self.__class__(text_type.__mul__(self, num))
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
__rmul__ = __mul__
|
__rmul__ = __mul__
|
||||||
|
|
||||||
def __mod__(self, arg):
|
def __mod__(self, arg):
|
||||||
if isinstance(arg, tuple):
|
if isinstance(arg, tuple):
|
||||||
arg = tuple(imap(_MarkupEscapeHelper, arg))
|
arg = tuple(_MarkupEscapeHelper(x, self.escape) for x in arg)
|
||||||
else:
|
else:
|
||||||
arg = _MarkupEscapeHelper(arg)
|
arg = _MarkupEscapeHelper(arg, self.escape)
|
||||||
return self.__class__(unicode.__mod__(self, arg))
|
return self.__class__(text_type.__mod__(self, arg))
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '%s(%s)' % (
|
return '%s(%s)' % (
|
||||||
self.__class__.__name__,
|
self.__class__.__name__,
|
||||||
unicode.__repr__(self)
|
text_type.__repr__(self)
|
||||||
)
|
)
|
||||||
|
|
||||||
def join(self, seq):
|
def join(self, seq):
|
||||||
return self.__class__(unicode.join(self, imap(escape, seq)))
|
return self.__class__(text_type.join(self, map(self.escape, seq)))
|
||||||
join.__doc__ = unicode.join.__doc__
|
join.__doc__ = text_type.join.__doc__
|
||||||
|
|
||||||
def split(self, *args, **kwargs):
|
def split(self, *args, **kwargs):
|
||||||
return map(self.__class__, unicode.split(self, *args, **kwargs))
|
return list(map(self.__class__, text_type.split(self, *args, **kwargs)))
|
||||||
split.__doc__ = unicode.split.__doc__
|
split.__doc__ = text_type.split.__doc__
|
||||||
|
|
||||||
def rsplit(self, *args, **kwargs):
|
def rsplit(self, *args, **kwargs):
|
||||||
return map(self.__class__, unicode.rsplit(self, *args, **kwargs))
|
return list(map(self.__class__, text_type.rsplit(self, *args, **kwargs)))
|
||||||
rsplit.__doc__ = unicode.rsplit.__doc__
|
rsplit.__doc__ = text_type.rsplit.__doc__
|
||||||
|
|
||||||
def splitlines(self, *args, **kwargs):
|
def splitlines(self, *args, **kwargs):
|
||||||
return map(self.__class__, unicode.splitlines(self, *args, **kwargs))
|
return list(map(self.__class__, text_type.splitlines(self, *args, **kwargs)))
|
||||||
splitlines.__doc__ = unicode.splitlines.__doc__
|
splitlines.__doc__ = text_type.splitlines.__doc__
|
||||||
|
|
||||||
def unescape(self):
|
def unescape(self):
|
||||||
r"""Unescape markup again into an unicode string. This also resolves
|
r"""Unescape markup again into an text_type string. This also resolves
|
||||||
known HTML4 and XHTML entities:
|
known HTML4 and XHTML entities:
|
||||||
|
|
||||||
>>> Markup("Main » <em>About</em>").unescape()
|
>>> Markup("Main » <em>About</em>").unescape()
|
||||||
u'Main \xbb <em>About</em>'
|
u'Main \xbb <em>About</em>'
|
||||||
"""
|
"""
|
||||||
from jinja2._markupsafe._constants import HTML_ENTITIES
|
from _constants import HTML_ENTITIES
|
||||||
def handle_match(m):
|
def handle_match(m):
|
||||||
name = m.group(1)
|
name = m.group(1)
|
||||||
if name in HTML_ENTITIES:
|
if name in HTML_ENTITIES:
|
||||||
@ -139,10 +140,10 @@ class Markup(unicode):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
return u''
|
return u''
|
||||||
return _entity_re.sub(handle_match, unicode(self))
|
return _entity_re.sub(handle_match, text_type(self))
|
||||||
|
|
||||||
def striptags(self):
|
def striptags(self):
|
||||||
r"""Unescape markup into an unicode string and strip all tags. This
|
r"""Unescape markup into an text_type string and strip all tags. This
|
||||||
also resolves known HTML4 and XHTML entities. Whitespace is
|
also resolves known HTML4 and XHTML entities. Whitespace is
|
||||||
normalized to one:
|
normalized to one:
|
||||||
|
|
||||||
@ -164,10 +165,10 @@ class Markup(unicode):
|
|||||||
return rv
|
return rv
|
||||||
|
|
||||||
def make_wrapper(name):
|
def make_wrapper(name):
|
||||||
orig = getattr(unicode, name)
|
orig = getattr(text_type, name)
|
||||||
def func(self, *args, **kwargs):
|
def func(self, *args, **kwargs):
|
||||||
args = _escape_argspec(list(args), enumerate(args))
|
args = _escape_argspec(list(args), enumerate(args), self.escape)
|
||||||
_escape_argspec(kwargs, kwargs.iteritems())
|
#_escape_argspec(kwargs, kwargs.iteritems(), None)
|
||||||
return self.__class__(orig(self, *args, **kwargs))
|
return self.__class__(orig(self, *args, **kwargs))
|
||||||
func.__name__ = orig.__name__
|
func.__name__ = orig.__name__
|
||||||
func.__doc__ = orig.__doc__
|
func.__doc__ = orig.__doc__
|
||||||
@ -180,25 +181,29 @@ class Markup(unicode):
|
|||||||
locals()[method] = make_wrapper(method)
|
locals()[method] = make_wrapper(method)
|
||||||
|
|
||||||
# new in python 2.5
|
# new in python 2.5
|
||||||
if hasattr(unicode, 'partition'):
|
if hasattr(text_type, 'partition'):
|
||||||
partition = make_wrapper('partition'),
|
def partition(self, sep):
|
||||||
rpartition = make_wrapper('rpartition')
|
return tuple(map(self.__class__,
|
||||||
|
text_type.partition(self, self.escape(sep))))
|
||||||
|
def rpartition(self, sep):
|
||||||
|
return tuple(map(self.__class__,
|
||||||
|
text_type.rpartition(self, self.escape(sep))))
|
||||||
|
|
||||||
# new in python 2.6
|
# new in python 2.6
|
||||||
if hasattr(unicode, 'format'):
|
if hasattr(text_type, 'format'):
|
||||||
format = make_wrapper('format')
|
format = make_wrapper('format')
|
||||||
|
|
||||||
# not in python 3
|
# not in python 3
|
||||||
if hasattr(unicode, '__getslice__'):
|
if hasattr(text_type, '__getslice__'):
|
||||||
__getslice__ = make_wrapper('__getslice__')
|
__getslice__ = make_wrapper('__getslice__')
|
||||||
|
|
||||||
del method, make_wrapper
|
del method, make_wrapper
|
||||||
|
|
||||||
|
|
||||||
def _escape_argspec(obj, iterable):
|
def _escape_argspec(obj, iterable, escape):
|
||||||
"""Helper for various string-wrapped functions."""
|
"""Helper for various string-wrapped functions."""
|
||||||
for key, value in iterable:
|
for key, value in iterable:
|
||||||
if hasattr(value, '__html__') or isinstance(value, basestring):
|
if hasattr(value, '__html__') or isinstance(value, string_types):
|
||||||
obj[key] = escape(value)
|
obj[key] = escape(value)
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
@ -206,13 +211,13 @@ def _escape_argspec(obj, iterable):
|
|||||||
class _MarkupEscapeHelper(object):
|
class _MarkupEscapeHelper(object):
|
||||||
"""Helper for Markup.__mod__"""
|
"""Helper for Markup.__mod__"""
|
||||||
|
|
||||||
def __init__(self, obj):
|
def __init__(self, obj, escape):
|
||||||
self.obj = obj
|
self.obj = obj
|
||||||
|
self.escape = escape
|
||||||
|
|
||||||
__getitem__ = lambda s, x: _MarkupEscapeHelper(s.obj[x])
|
__getitem__ = lambda s, x: _MarkupEscapeHelper(s.obj[x], s.escape)
|
||||||
__str__ = lambda s: str(escape(s.obj))
|
__unicode__ = __str__ = lambda s: text_type(s.escape(s.obj))
|
||||||
__unicode__ = lambda s: unicode(escape(s.obj))
|
__repr__ = lambda s: str(s.escape(repr(s.obj)))
|
||||||
__repr__ = lambda s: str(escape(repr(s.obj)))
|
|
||||||
__int__ = lambda s: int(s.obj)
|
__int__ = lambda s: int(s.obj)
|
||||||
__float__ = lambda s: float(s.obj)
|
__float__ = lambda s: float(s.obj)
|
||||||
|
|
||||||
@ -220,6 +225,10 @@ class _MarkupEscapeHelper(object):
|
|||||||
# we have to import it down here as the speedups and native
|
# we have to import it down here as the speedups and native
|
||||||
# modules imports the markup type which is define above.
|
# modules imports the markup type which is define above.
|
||||||
try:
|
try:
|
||||||
from jinja2._markupsafe._speedups import escape, escape_silent, soft_unicode
|
from _speedups import escape, escape_silent, soft_unicode
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from jinja2._markupsafe._native import escape, escape_silent, soft_unicode
|
from _native import escape, escape_silent, soft_unicode
|
||||||
|
|
||||||
|
if not PY2:
|
||||||
|
soft_str = soft_unicode
|
||||||
|
__all__.append('soft_str')
|
24
modules/matlab/generator/jinja2/markupsafe/_compat.py
Normal file
24
modules/matlab/generator/jinja2/markupsafe/_compat.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
markupsafe._compat
|
||||||
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Compatibility module for different Python versions.
|
||||||
|
|
||||||
|
:copyright: (c) 2013 by Armin Ronacher.
|
||||||
|
:license: BSD, see LICENSE for more details.
|
||||||
|
"""
|
||||||
|
import sys
|
||||||
|
|
||||||
|
PY2 = sys.version_info[0] == 2
|
||||||
|
|
||||||
|
if not PY2:
|
||||||
|
text_type = str
|
||||||
|
string_types = (str,)
|
||||||
|
unichr = chr
|
||||||
|
int_types = (int,)
|
||||||
|
else:
|
||||||
|
text_type = unicode
|
||||||
|
string_types = (str, unicode)
|
||||||
|
unichr = unichr
|
||||||
|
int_types = (int, long)
|
267
modules/matlab/generator/jinja2/markupsafe/_constants.py
Normal file
267
modules/matlab/generator/jinja2/markupsafe/_constants.py
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
markupsafe._constants
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Highlevel implementation of the Markup string.
|
||||||
|
|
||||||
|
:copyright: (c) 2010 by Armin Ronacher.
|
||||||
|
:license: BSD, see LICENSE for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
HTML_ENTITIES = {
|
||||||
|
'AElig': 198,
|
||||||
|
'Aacute': 193,
|
||||||
|
'Acirc': 194,
|
||||||
|
'Agrave': 192,
|
||||||
|
'Alpha': 913,
|
||||||
|
'Aring': 197,
|
||||||
|
'Atilde': 195,
|
||||||
|
'Auml': 196,
|
||||||
|
'Beta': 914,
|
||||||
|
'Ccedil': 199,
|
||||||
|
'Chi': 935,
|
||||||
|
'Dagger': 8225,
|
||||||
|
'Delta': 916,
|
||||||
|
'ETH': 208,
|
||||||
|
'Eacute': 201,
|
||||||
|
'Ecirc': 202,
|
||||||
|
'Egrave': 200,
|
||||||
|
'Epsilon': 917,
|
||||||
|
'Eta': 919,
|
||||||
|
'Euml': 203,
|
||||||
|
'Gamma': 915,
|
||||||
|
'Iacute': 205,
|
||||||
|
'Icirc': 206,
|
||||||
|
'Igrave': 204,
|
||||||
|
'Iota': 921,
|
||||||
|
'Iuml': 207,
|
||||||
|
'Kappa': 922,
|
||||||
|
'Lambda': 923,
|
||||||
|
'Mu': 924,
|
||||||
|
'Ntilde': 209,
|
||||||
|
'Nu': 925,
|
||||||
|
'OElig': 338,
|
||||||
|
'Oacute': 211,
|
||||||
|
'Ocirc': 212,
|
||||||
|
'Ograve': 210,
|
||||||
|
'Omega': 937,
|
||||||
|
'Omicron': 927,
|
||||||
|
'Oslash': 216,
|
||||||
|
'Otilde': 213,
|
||||||
|
'Ouml': 214,
|
||||||
|
'Phi': 934,
|
||||||
|
'Pi': 928,
|
||||||
|
'Prime': 8243,
|
||||||
|
'Psi': 936,
|
||||||
|
'Rho': 929,
|
||||||
|
'Scaron': 352,
|
||||||
|
'Sigma': 931,
|
||||||
|
'THORN': 222,
|
||||||
|
'Tau': 932,
|
||||||
|
'Theta': 920,
|
||||||
|
'Uacute': 218,
|
||||||
|
'Ucirc': 219,
|
||||||
|
'Ugrave': 217,
|
||||||
|
'Upsilon': 933,
|
||||||
|
'Uuml': 220,
|
||||||
|
'Xi': 926,
|
||||||
|
'Yacute': 221,
|
||||||
|
'Yuml': 376,
|
||||||
|
'Zeta': 918,
|
||||||
|
'aacute': 225,
|
||||||
|
'acirc': 226,
|
||||||
|
'acute': 180,
|
||||||
|
'aelig': 230,
|
||||||
|
'agrave': 224,
|
||||||
|
'alefsym': 8501,
|
||||||
|
'alpha': 945,
|
||||||
|
'amp': 38,
|
||||||
|
'and': 8743,
|
||||||
|
'ang': 8736,
|
||||||
|
'apos': 39,
|
||||||
|
'aring': 229,
|
||||||
|
'asymp': 8776,
|
||||||
|
'atilde': 227,
|
||||||
|
'auml': 228,
|
||||||
|
'bdquo': 8222,
|
||||||
|
'beta': 946,
|
||||||
|
'brvbar': 166,
|
||||||
|
'bull': 8226,
|
||||||
|
'cap': 8745,
|
||||||
|
'ccedil': 231,
|
||||||
|
'cedil': 184,
|
||||||
|
'cent': 162,
|
||||||
|
'chi': 967,
|
||||||
|
'circ': 710,
|
||||||
|
'clubs': 9827,
|
||||||
|
'cong': 8773,
|
||||||
|
'copy': 169,
|
||||||
|
'crarr': 8629,
|
||||||
|
'cup': 8746,
|
||||||
|
'curren': 164,
|
||||||
|
'dArr': 8659,
|
||||||
|
'dagger': 8224,
|
||||||
|
'darr': 8595,
|
||||||
|
'deg': 176,
|
||||||
|
'delta': 948,
|
||||||
|
'diams': 9830,
|
||||||
|
'divide': 247,
|
||||||
|
'eacute': 233,
|
||||||
|
'ecirc': 234,
|
||||||
|
'egrave': 232,
|
||||||
|
'empty': 8709,
|
||||||
|
'emsp': 8195,
|
||||||
|
'ensp': 8194,
|
||||||
|
'epsilon': 949,
|
||||||
|
'equiv': 8801,
|
||||||
|
'eta': 951,
|
||||||
|
'eth': 240,
|
||||||
|
'euml': 235,
|
||||||
|
'euro': 8364,
|
||||||
|
'exist': 8707,
|
||||||
|
'fnof': 402,
|
||||||
|
'forall': 8704,
|
||||||
|
'frac12': 189,
|
||||||
|
'frac14': 188,
|
||||||
|
'frac34': 190,
|
||||||
|
'frasl': 8260,
|
||||||
|
'gamma': 947,
|
||||||
|
'ge': 8805,
|
||||||
|
'gt': 62,
|
||||||
|
'hArr': 8660,
|
||||||
|
'harr': 8596,
|
||||||
|
'hearts': 9829,
|
||||||
|
'hellip': 8230,
|
||||||
|
'iacute': 237,
|
||||||
|
'icirc': 238,
|
||||||
|
'iexcl': 161,
|
||||||
|
'igrave': 236,
|
||||||
|
'image': 8465,
|
||||||
|
'infin': 8734,
|
||||||
|
'int': 8747,
|
||||||
|
'iota': 953,
|
||||||
|
'iquest': 191,
|
||||||
|
'isin': 8712,
|
||||||
|
'iuml': 239,
|
||||||
|
'kappa': 954,
|
||||||
|
'lArr': 8656,
|
||||||
|
'lambda': 955,
|
||||||
|
'lang': 9001,
|
||||||
|
'laquo': 171,
|
||||||
|
'larr': 8592,
|
||||||
|
'lceil': 8968,
|
||||||
|
'ldquo': 8220,
|
||||||
|
'le': 8804,
|
||||||
|
'lfloor': 8970,
|
||||||
|
'lowast': 8727,
|
||||||
|
'loz': 9674,
|
||||||
|
'lrm': 8206,
|
||||||
|
'lsaquo': 8249,
|
||||||
|
'lsquo': 8216,
|
||||||
|
'lt': 60,
|
||||||
|
'macr': 175,
|
||||||
|
'mdash': 8212,
|
||||||
|
'micro': 181,
|
||||||
|
'middot': 183,
|
||||||
|
'minus': 8722,
|
||||||
|
'mu': 956,
|
||||||
|
'nabla': 8711,
|
||||||
|
'nbsp': 160,
|
||||||
|
'ndash': 8211,
|
||||||
|
'ne': 8800,
|
||||||
|
'ni': 8715,
|
||||||
|
'not': 172,
|
||||||
|
'notin': 8713,
|
||||||
|
'nsub': 8836,
|
||||||
|
'ntilde': 241,
|
||||||
|
'nu': 957,
|
||||||
|
'oacute': 243,
|
||||||
|
'ocirc': 244,
|
||||||
|
'oelig': 339,
|
||||||
|
'ograve': 242,
|
||||||
|
'oline': 8254,
|
||||||
|
'omega': 969,
|
||||||
|
'omicron': 959,
|
||||||
|
'oplus': 8853,
|
||||||
|
'or': 8744,
|
||||||
|
'ordf': 170,
|
||||||
|
'ordm': 186,
|
||||||
|
'oslash': 248,
|
||||||
|
'otilde': 245,
|
||||||
|
'otimes': 8855,
|
||||||
|
'ouml': 246,
|
||||||
|
'para': 182,
|
||||||
|
'part': 8706,
|
||||||
|
'permil': 8240,
|
||||||
|
'perp': 8869,
|
||||||
|
'phi': 966,
|
||||||
|
'pi': 960,
|
||||||
|
'piv': 982,
|
||||||
|
'plusmn': 177,
|
||||||
|
'pound': 163,
|
||||||
|
'prime': 8242,
|
||||||
|
'prod': 8719,
|
||||||
|
'prop': 8733,
|
||||||
|
'psi': 968,
|
||||||
|
'quot': 34,
|
||||||
|
'rArr': 8658,
|
||||||
|
'radic': 8730,
|
||||||
|
'rang': 9002,
|
||||||
|
'raquo': 187,
|
||||||
|
'rarr': 8594,
|
||||||
|
'rceil': 8969,
|
||||||
|
'rdquo': 8221,
|
||||||
|
'real': 8476,
|
||||||
|
'reg': 174,
|
||||||
|
'rfloor': 8971,
|
||||||
|
'rho': 961,
|
||||||
|
'rlm': 8207,
|
||||||
|
'rsaquo': 8250,
|
||||||
|
'rsquo': 8217,
|
||||||
|
'sbquo': 8218,
|
||||||
|
'scaron': 353,
|
||||||
|
'sdot': 8901,
|
||||||
|
'sect': 167,
|
||||||
|
'shy': 173,
|
||||||
|
'sigma': 963,
|
||||||
|
'sigmaf': 962,
|
||||||
|
'sim': 8764,
|
||||||
|
'spades': 9824,
|
||||||
|
'sub': 8834,
|
||||||
|
'sube': 8838,
|
||||||
|
'sum': 8721,
|
||||||
|
'sup': 8835,
|
||||||
|
'sup1': 185,
|
||||||
|
'sup2': 178,
|
||||||
|
'sup3': 179,
|
||||||
|
'supe': 8839,
|
||||||
|
'szlig': 223,
|
||||||
|
'tau': 964,
|
||||||
|
'there4': 8756,
|
||||||
|
'theta': 952,
|
||||||
|
'thetasym': 977,
|
||||||
|
'thinsp': 8201,
|
||||||
|
'thorn': 254,
|
||||||
|
'tilde': 732,
|
||||||
|
'times': 215,
|
||||||
|
'trade': 8482,
|
||||||
|
'uArr': 8657,
|
||||||
|
'uacute': 250,
|
||||||
|
'uarr': 8593,
|
||||||
|
'ucirc': 251,
|
||||||
|
'ugrave': 249,
|
||||||
|
'uml': 168,
|
||||||
|
'upsih': 978,
|
||||||
|
'upsilon': 965,
|
||||||
|
'uuml': 252,
|
||||||
|
'weierp': 8472,
|
||||||
|
'xi': 958,
|
||||||
|
'yacute': 253,
|
||||||
|
'yen': 165,
|
||||||
|
'yuml': 255,
|
||||||
|
'zeta': 950,
|
||||||
|
'zwj': 8205,
|
||||||
|
'zwnj': 8204
|
||||||
|
}
|
@ -8,7 +8,7 @@
|
|||||||
:copyright: (c) 2010 by Armin Ronacher.
|
:copyright: (c) 2010 by Armin Ronacher.
|
||||||
:license: BSD, see LICENSE for more details.
|
:license: BSD, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
from jinja2._markupsafe import Markup
|
from _compat import text_type
|
||||||
|
|
||||||
|
|
||||||
def escape(s):
|
def escape(s):
|
||||||
@ -18,7 +18,7 @@ def escape(s):
|
|||||||
"""
|
"""
|
||||||
if hasattr(s, '__html__'):
|
if hasattr(s, '__html__'):
|
||||||
return s.__html__()
|
return s.__html__()
|
||||||
return Markup(unicode(s)
|
return Markup(text_type(s)
|
||||||
.replace('&', '&')
|
.replace('&', '&')
|
||||||
.replace('>', '>')
|
.replace('>', '>')
|
||||||
.replace('<', '<')
|
.replace('<', '<')
|
||||||
@ -40,6 +40,6 @@ def soft_unicode(s):
|
|||||||
"""Make a string unicode if it isn't already. That way a markup
|
"""Make a string unicode if it isn't already. That way a markup
|
||||||
string is not converted back to unicode.
|
string is not converted back to unicode.
|
||||||
"""
|
"""
|
||||||
if not isinstance(s, unicode):
|
if not isinstance(s, text_type):
|
||||||
s = unicode(s)
|
s = text_type(s)
|
||||||
return s
|
return s
|
@ -12,14 +12,16 @@
|
|||||||
:copyright: (c) 2010 by the Jinja Team.
|
:copyright: (c) 2010 by the Jinja Team.
|
||||||
:license: BSD, see LICENSE for more details.
|
:license: BSD, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
import types
|
||||||
import operator
|
import operator
|
||||||
from itertools import chain, izip
|
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from jinja2.utils import Markup, MethodType, FunctionType
|
from jinja2.utils import Markup
|
||||||
|
from jinja2._compat import izip, with_metaclass, text_type
|
||||||
|
|
||||||
|
|
||||||
#: the types we support for context functions
|
#: the types we support for context functions
|
||||||
_context_function_types = (FunctionType, MethodType)
|
_context_function_types = (types.FunctionType, types.MethodType)
|
||||||
|
|
||||||
|
|
||||||
_binop_to_func = {
|
_binop_to_func = {
|
||||||
@ -102,9 +104,9 @@ def get_eval_context(node, ctx):
|
|||||||
return ctx
|
return ctx
|
||||||
|
|
||||||
|
|
||||||
class Node(object):
|
class Node(with_metaclass(NodeType, object)):
|
||||||
"""Baseclass for all Jinja2 nodes. There are a number of nodes available
|
"""Baseclass for all Jinja2 nodes. There are a number of nodes available
|
||||||
of different types. There are three major types:
|
of different types. There are four major types:
|
||||||
|
|
||||||
- :class:`Stmt`: statements
|
- :class:`Stmt`: statements
|
||||||
- :class:`Expr`: expressions
|
- :class:`Expr`: expressions
|
||||||
@ -118,7 +120,6 @@ class Node(object):
|
|||||||
The `environment` attribute is set at the end of the parsing process for
|
The `environment` attribute is set at the end of the parsing process for
|
||||||
all nodes automatically.
|
all nodes automatically.
|
||||||
"""
|
"""
|
||||||
__metaclass__ = NodeType
|
|
||||||
fields = ()
|
fields = ()
|
||||||
attributes = ('lineno', 'environment')
|
attributes = ('lineno', 'environment')
|
||||||
abstract = True
|
abstract = True
|
||||||
@ -142,7 +143,7 @@ class Node(object):
|
|||||||
setattr(self, attr, attributes.pop(attr, None))
|
setattr(self, attr, attributes.pop(attr, None))
|
||||||
if attributes:
|
if attributes:
|
||||||
raise TypeError('unknown attribute %r' %
|
raise TypeError('unknown attribute %r' %
|
||||||
iter(attributes).next())
|
next(iter(attributes)))
|
||||||
|
|
||||||
def iter_fields(self, exclude=None, only=None):
|
def iter_fields(self, exclude=None, only=None):
|
||||||
"""This method iterates over all fields that are defined and yields
|
"""This method iterates over all fields that are defined and yields
|
||||||
@ -440,7 +441,7 @@ class Const(Literal):
|
|||||||
constant value in the generated code, otherwise it will raise
|
constant value in the generated code, otherwise it will raise
|
||||||
an `Impossible` exception.
|
an `Impossible` exception.
|
||||||
"""
|
"""
|
||||||
from compiler import has_safe_repr
|
from .compiler import has_safe_repr
|
||||||
if not has_safe_repr(value):
|
if not has_safe_repr(value):
|
||||||
raise Impossible()
|
raise Impossible()
|
||||||
return cls(value, lineno=lineno, environment=environment)
|
return cls(value, lineno=lineno, environment=environment)
|
||||||
@ -687,7 +688,7 @@ class Concat(Expr):
|
|||||||
|
|
||||||
def as_const(self, eval_ctx=None):
|
def as_const(self, eval_ctx=None):
|
||||||
eval_ctx = get_eval_context(self, eval_ctx)
|
eval_ctx = get_eval_context(self, eval_ctx)
|
||||||
return ''.join(unicode(x.as_const(eval_ctx)) for x in self.nodes)
|
return ''.join(text_type(x.as_const(eval_ctx)) for x in self.nodes)
|
||||||
|
|
||||||
|
|
||||||
class Compare(Expr):
|
class Compare(Expr):
|
||||||
|
@ -10,8 +10,8 @@
|
|||||||
"""
|
"""
|
||||||
from jinja2 import nodes
|
from jinja2 import nodes
|
||||||
from jinja2.exceptions import TemplateSyntaxError, TemplateAssertionError
|
from jinja2.exceptions import TemplateSyntaxError, TemplateAssertionError
|
||||||
from jinja2.utils import next
|
|
||||||
from jinja2.lexer import describe_token, describe_token_expr
|
from jinja2.lexer import describe_token, describe_token_expr
|
||||||
|
from jinja2._compat import imap
|
||||||
|
|
||||||
|
|
||||||
#: statements that callinto
|
#: statements that callinto
|
||||||
@ -53,7 +53,7 @@ class Parser(object):
|
|||||||
def _fail_ut_eof(self, name, end_token_stack, lineno):
|
def _fail_ut_eof(self, name, end_token_stack, lineno):
|
||||||
expected = []
|
expected = []
|
||||||
for exprs in end_token_stack:
|
for exprs in end_token_stack:
|
||||||
expected.extend(map(describe_token_expr, exprs))
|
expected.extend(imap(describe_token_expr, exprs))
|
||||||
if end_token_stack:
|
if end_token_stack:
|
||||||
currently_looking = ' or '.join(
|
currently_looking = ' or '.join(
|
||||||
"'%s'" % describe_token_expr(expr)
|
"'%s'" % describe_token_expr(expr)
|
||||||
|
@ -8,12 +8,14 @@
|
|||||||
:copyright: (c) 2010 by the Jinja Team.
|
:copyright: (c) 2010 by the Jinja Team.
|
||||||
:license: BSD.
|
:license: BSD.
|
||||||
"""
|
"""
|
||||||
from itertools import chain, imap
|
from itertools import chain
|
||||||
from jinja2.nodes import EvalContext, _context_function_types
|
from jinja2.nodes import EvalContext, _context_function_types
|
||||||
from jinja2.utils import Markup, partial, soft_unicode, escape, missing, \
|
from jinja2.utils import Markup, soft_unicode, escape, missing, concat, \
|
||||||
concat, internalcode, next, object_type_repr
|
internalcode, object_type_repr
|
||||||
from jinja2.exceptions import UndefinedError, TemplateRuntimeError, \
|
from jinja2.exceptions import UndefinedError, TemplateRuntimeError, \
|
||||||
TemplateNotFound
|
TemplateNotFound
|
||||||
|
from jinja2._compat import imap, text_type, iteritems, \
|
||||||
|
implements_iterator, implements_to_string, string_types, PY2
|
||||||
|
|
||||||
|
|
||||||
# these variables are exported to the template runtime
|
# these variables are exported to the template runtime
|
||||||
@ -23,9 +25,8 @@ __all__ = ['LoopContext', 'TemplateReference', 'Macro', 'Markup',
|
|||||||
'TemplateNotFound']
|
'TemplateNotFound']
|
||||||
|
|
||||||
#: the name of the function that is used to convert something into
|
#: the name of the function that is used to convert something into
|
||||||
#: a string. 2to3 will adopt that automatically and the generated
|
#: a string. We can just use the text type here.
|
||||||
#: code can take advantage of it.
|
to_string = text_type
|
||||||
to_string = unicode
|
|
||||||
|
|
||||||
#: the identity function. Useful for certain things in the environment
|
#: the identity function. Useful for certain things in the environment
|
||||||
identity = lambda x: x
|
identity = lambda x: x
|
||||||
@ -46,7 +47,7 @@ def markup_join(seq):
|
|||||||
|
|
||||||
def unicode_join(seq):
|
def unicode_join(seq):
|
||||||
"""Simple args to unicode conversion and concatenation."""
|
"""Simple args to unicode conversion and concatenation."""
|
||||||
return concat(imap(unicode, seq))
|
return concat(imap(text_type, seq))
|
||||||
|
|
||||||
|
|
||||||
def new_context(environment, template_name, blocks, vars=None,
|
def new_context(environment, template_name, blocks, vars=None,
|
||||||
@ -63,7 +64,7 @@ def new_context(environment, template_name, blocks, vars=None,
|
|||||||
# we don't want to modify the dict passed
|
# we don't want to modify the dict passed
|
||||||
if shared:
|
if shared:
|
||||||
parent = dict(parent)
|
parent = dict(parent)
|
||||||
for key, value in locals.iteritems():
|
for key, value in iteritems(locals):
|
||||||
if key[:2] == 'l_' and value is not missing:
|
if key[:2] == 'l_' and value is not missing:
|
||||||
parent[key[2:]] = value
|
parent[key[2:]] = value
|
||||||
return Context(environment, parent, template_name, blocks)
|
return Context(environment, parent, template_name, blocks)
|
||||||
@ -119,7 +120,7 @@ class Context(object):
|
|||||||
# create the initial mapping of blocks. Whenever template inheritance
|
# create the initial mapping of blocks. Whenever template inheritance
|
||||||
# takes place the runtime will update this mapping with the new blocks
|
# takes place the runtime will update this mapping with the new blocks
|
||||||
# from the template.
|
# from the template.
|
||||||
self.blocks = dict((k, [v]) for k, v in blocks.iteritems())
|
self.blocks = dict((k, [v]) for k, v in iteritems(blocks))
|
||||||
|
|
||||||
def super(self, name, current):
|
def super(self, name, current):
|
||||||
"""Render a parent block."""
|
"""Render a parent block."""
|
||||||
@ -171,6 +172,16 @@ class Context(object):
|
|||||||
"""
|
"""
|
||||||
if __debug__:
|
if __debug__:
|
||||||
__traceback_hide__ = True
|
__traceback_hide__ = True
|
||||||
|
|
||||||
|
# Allow callable classes to take a context
|
||||||
|
fn = __obj.__call__
|
||||||
|
for fn_type in ('contextfunction',
|
||||||
|
'evalcontextfunction',
|
||||||
|
'environmentfunction'):
|
||||||
|
if hasattr(fn, fn_type):
|
||||||
|
__obj = fn
|
||||||
|
break
|
||||||
|
|
||||||
if isinstance(__obj, _context_function_types):
|
if isinstance(__obj, _context_function_types):
|
||||||
if getattr(__obj, 'contextfunction', 0):
|
if getattr(__obj, 'contextfunction', 0):
|
||||||
args = (__self,) + args
|
args = (__self,) + args
|
||||||
@ -191,7 +202,7 @@ class Context(object):
|
|||||||
self.parent, True, None, locals)
|
self.parent, True, None, locals)
|
||||||
context.vars.update(self.vars)
|
context.vars.update(self.vars)
|
||||||
context.eval_ctx = self.eval_ctx
|
context.eval_ctx = self.eval_ctx
|
||||||
context.blocks.update((k, list(v)) for k, v in self.blocks.iteritems())
|
context.blocks.update((k, list(v)) for k, v in iteritems(self.blocks))
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def _all(meth):
|
def _all(meth):
|
||||||
@ -205,7 +216,7 @@ class Context(object):
|
|||||||
items = _all('items')
|
items = _all('items')
|
||||||
|
|
||||||
# not available on python 3
|
# not available on python 3
|
||||||
if hasattr(dict, 'iterkeys'):
|
if PY2:
|
||||||
iterkeys = _all('iterkeys')
|
iterkeys = _all('iterkeys')
|
||||||
itervalues = _all('itervalues')
|
itervalues = _all('itervalues')
|
||||||
iteritems = _all('iteritems')
|
iteritems = _all('iteritems')
|
||||||
@ -269,11 +280,12 @@ class BlockReference(object):
|
|||||||
class LoopContext(object):
|
class LoopContext(object):
|
||||||
"""A loop context for dynamic iteration."""
|
"""A loop context for dynamic iteration."""
|
||||||
|
|
||||||
def __init__(self, iterable, recurse=None):
|
def __init__(self, iterable, recurse=None, depth0=0):
|
||||||
self._iterator = iter(iterable)
|
self._iterator = iter(iterable)
|
||||||
self._recurse = recurse
|
self._recurse = recurse
|
||||||
self._after = self._safe_next()
|
self._after = self._safe_next()
|
||||||
self.index0 = -1
|
self.index0 = -1
|
||||||
|
self.depth0 = depth0
|
||||||
|
|
||||||
# try to get the length of the iterable early. This must be done
|
# try to get the length of the iterable early. This must be done
|
||||||
# here because there are some broken iterators around where there
|
# here because there are some broken iterators around where there
|
||||||
@ -295,6 +307,7 @@ class LoopContext(object):
|
|||||||
index = property(lambda x: x.index0 + 1)
|
index = property(lambda x: x.index0 + 1)
|
||||||
revindex = property(lambda x: x.length - x.index0)
|
revindex = property(lambda x: x.length - x.index0)
|
||||||
revindex0 = property(lambda x: x.length - x.index)
|
revindex0 = property(lambda x: x.length - x.index)
|
||||||
|
depth = property(lambda x: x.depth0 + 1)
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return self.length
|
return self.length
|
||||||
@ -313,7 +326,7 @@ class LoopContext(object):
|
|||||||
if self._recurse is None:
|
if self._recurse is None:
|
||||||
raise TypeError('Tried to call non recursive loop. Maybe you '
|
raise TypeError('Tried to call non recursive loop. Maybe you '
|
||||||
"forgot the 'recursive' modifier.")
|
"forgot the 'recursive' modifier.")
|
||||||
return self._recurse(iterable, self._recurse)
|
return self._recurse(iterable, self._recurse, self.depth0 + 1)
|
||||||
|
|
||||||
# a nifty trick to enhance the error message if someone tried to call
|
# a nifty trick to enhance the error message if someone tried to call
|
||||||
# the the loop without or with too many arguments.
|
# the the loop without or with too many arguments.
|
||||||
@ -340,6 +353,7 @@ class LoopContext(object):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@implements_iterator
|
||||||
class LoopContextIterator(object):
|
class LoopContextIterator(object):
|
||||||
"""The iterator for a loop context."""
|
"""The iterator for a loop context."""
|
||||||
__slots__ = ('context',)
|
__slots__ = ('context',)
|
||||||
@ -350,7 +364,7 @@ class LoopContextIterator(object):
|
|||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def next(self):
|
def __next__(self):
|
||||||
ctx = self.context
|
ctx = self.context
|
||||||
ctx.index0 += 1
|
ctx.index0 += 1
|
||||||
if ctx._after is _last_iteration:
|
if ctx._after is _last_iteration:
|
||||||
@ -424,6 +438,7 @@ class Macro(object):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@implements_to_string
|
||||||
class Undefined(object):
|
class Undefined(object):
|
||||||
"""The default undefined type. This undefined type can be printed and
|
"""The default undefined type. This undefined type can be printed and
|
||||||
iterated over, but every other access will raise an :exc:`UndefinedError`:
|
iterated over, but every other access will raise an :exc:`UndefinedError`:
|
||||||
@ -455,7 +470,7 @@ class Undefined(object):
|
|||||||
if self._undefined_hint is None:
|
if self._undefined_hint is None:
|
||||||
if self._undefined_obj is missing:
|
if self._undefined_obj is missing:
|
||||||
hint = '%r is undefined' % self._undefined_name
|
hint = '%r is undefined' % self._undefined_name
|
||||||
elif not isinstance(self._undefined_name, basestring):
|
elif not isinstance(self._undefined_name, string_types):
|
||||||
hint = '%s has no element %r' % (
|
hint = '%s has no element %r' % (
|
||||||
object_type_repr(self._undefined_obj),
|
object_type_repr(self._undefined_obj),
|
||||||
self._undefined_name
|
self._undefined_name
|
||||||
@ -483,13 +498,6 @@ class Undefined(object):
|
|||||||
_fail_with_undefined_error
|
_fail_with_undefined_error
|
||||||
|
|
||||||
def __str__(self):
|
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 u''
|
return u''
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
@ -506,6 +514,7 @@ class Undefined(object):
|
|||||||
return 'Undefined'
|
return 'Undefined'
|
||||||
|
|
||||||
|
|
||||||
|
@implements_to_string
|
||||||
class DebugUndefined(Undefined):
|
class DebugUndefined(Undefined):
|
||||||
"""An undefined that returns the debug info when printed.
|
"""An undefined that returns the debug info when printed.
|
||||||
|
|
||||||
@ -521,7 +530,7 @@ class DebugUndefined(Undefined):
|
|||||||
"""
|
"""
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
if self._undefined_hint is None:
|
if self._undefined_hint is None:
|
||||||
if self._undefined_obj is missing:
|
if self._undefined_obj is missing:
|
||||||
return u'{{ %s }}' % self._undefined_name
|
return u'{{ %s }}' % self._undefined_name
|
||||||
@ -532,6 +541,7 @@ class DebugUndefined(Undefined):
|
|||||||
return u'{{ undefined value printed: %s }}' % self._undefined_hint
|
return u'{{ undefined value printed: %s }}' % self._undefined_hint
|
||||||
|
|
||||||
|
|
||||||
|
@implements_to_string
|
||||||
class StrictUndefined(Undefined):
|
class StrictUndefined(Undefined):
|
||||||
"""An undefined that barks on print and iteration as well as boolean
|
"""An undefined that barks on print and iteration as well as boolean
|
||||||
tests and all kinds of comparisons. In other words: you can do nothing
|
tests and all kinds of comparisons. In other words: you can do nothing
|
||||||
@ -552,7 +562,7 @@ class StrictUndefined(Undefined):
|
|||||||
UndefinedError: 'foo' is undefined
|
UndefinedError: 'foo' is undefined
|
||||||
"""
|
"""
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
__iter__ = __unicode__ = __str__ = __len__ = __nonzero__ = __eq__ = \
|
__iter__ = __str__ = __len__ = __nonzero__ = __eq__ = \
|
||||||
__ne__ = __bool__ = Undefined._fail_with_undefined_error
|
__ne__ = __bool__ = Undefined._fail_with_undefined_error
|
||||||
|
|
||||||
|
|
||||||
|
@ -9,25 +9,18 @@
|
|||||||
:license: BSD, see LICENSE for more details.
|
:license: BSD, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
import re
|
import re
|
||||||
import sys
|
|
||||||
import errno
|
import errno
|
||||||
try:
|
|
||||||
from urllib.parse import quote_from_bytes as url_quote
|
|
||||||
except ImportError:
|
|
||||||
from urllib import quote as url_quote
|
|
||||||
try:
|
|
||||||
from thread import allocate_lock
|
|
||||||
except ImportError:
|
|
||||||
from dummy_thread import allocate_lock
|
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from itertools import imap
|
from threading import Lock
|
||||||
|
from jinja2._compat import text_type, string_types, implements_iterator, \
|
||||||
|
url_quote
|
||||||
|
|
||||||
|
|
||||||
_word_split_re = re.compile(r'(\s+)')
|
_word_split_re = re.compile(r'(\s+)')
|
||||||
_punctuation_re = re.compile(
|
_punctuation_re = re.compile(
|
||||||
'^(?P<lead>(?:%s)*)(?P<middle>.*?)(?P<trail>(?:%s)*)$' % (
|
'^(?P<lead>(?:%s)*)(?P<middle>.*?)(?P<trail>(?:%s)*)$' % (
|
||||||
'|'.join(imap(re.escape, ('(', '<', '<'))),
|
'|'.join(map(re.escape, ('(', '<', '<'))),
|
||||||
'|'.join(imap(re.escape, ('.', ',', ')', '>', '\n', '>')))
|
'|'.join(map(re.escape, ('.', ',', ')', '>', '\n', '>')))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
_simple_email_re = re.compile(r'^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$')
|
_simple_email_re = re.compile(r'^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$')
|
||||||
@ -42,77 +35,7 @@ missing = type('MissingType', (), {'__repr__': lambda x: 'missing'})()
|
|||||||
# internal code
|
# internal code
|
||||||
internal_code = set()
|
internal_code = set()
|
||||||
|
|
||||||
|
concat = u''.join
|
||||||
# concatenate a list of strings and convert them to unicode.
|
|
||||||
# unfortunately there is a bug in python 2.4 and lower that causes
|
|
||||||
# unicode.join trash the traceback.
|
|
||||||
_concat = u''.join
|
|
||||||
try:
|
|
||||||
def _test_gen_bug():
|
|
||||||
raise TypeError(_test_gen_bug)
|
|
||||||
yield None
|
|
||||||
_concat(_test_gen_bug())
|
|
||||||
except TypeError, _error:
|
|
||||||
if not _error.args or _error.args[0] is not _test_gen_bug:
|
|
||||||
def concat(gen):
|
|
||||||
try:
|
|
||||||
return _concat(list(gen))
|
|
||||||
except Exception:
|
|
||||||
# this hack is needed so that the current frame
|
|
||||||
# does not show up in the traceback.
|
|
||||||
exc_type, exc_value, tb = sys.exc_info()
|
|
||||||
raise exc_type, exc_value, tb.tb_next
|
|
||||||
else:
|
|
||||||
concat = _concat
|
|
||||||
del _test_gen_bug, _error
|
|
||||||
|
|
||||||
|
|
||||||
# for python 2.x we create ourselves a next() function that does the
|
|
||||||
# basics without exception catching.
|
|
||||||
try:
|
|
||||||
next = next
|
|
||||||
except NameError:
|
|
||||||
def next(x):
|
|
||||||
return x.next()
|
|
||||||
|
|
||||||
|
|
||||||
# if this python version is unable to deal with unicode filenames
|
|
||||||
# when passed to encode we let this function encode it properly.
|
|
||||||
# This is used in a couple of places. As far as Jinja is concerned
|
|
||||||
# filenames are unicode *or* bytestrings in 2.x and unicode only in
|
|
||||||
# 3.x because compile cannot handle bytes
|
|
||||||
if sys.version_info < (3, 0):
|
|
||||||
def _encode_filename(filename):
|
|
||||||
if isinstance(filename, unicode):
|
|
||||||
return filename.encode('utf-8')
|
|
||||||
return filename
|
|
||||||
else:
|
|
||||||
def _encode_filename(filename):
|
|
||||||
assert filename is None or isinstance(filename, str), \
|
|
||||||
'filenames must be strings'
|
|
||||||
return filename
|
|
||||||
|
|
||||||
from keyword import iskeyword as is_python_keyword
|
|
||||||
|
|
||||||
|
|
||||||
# common types. These do exist in the special types module too which however
|
|
||||||
# does not exist in IronPython out of the box. Also that way we don't have
|
|
||||||
# to deal with implementation specific stuff here
|
|
||||||
class _C(object):
|
|
||||||
def method(self): pass
|
|
||||||
def _func():
|
|
||||||
yield None
|
|
||||||
FunctionType = type(_func)
|
|
||||||
GeneratorType = type(_func())
|
|
||||||
MethodType = type(_C.method)
|
|
||||||
CodeType = type(_C.method.func_code)
|
|
||||||
try:
|
|
||||||
raise TypeError()
|
|
||||||
except TypeError:
|
|
||||||
_tb = sys.exc_info()[2]
|
|
||||||
TracebackType = type(_tb)
|
|
||||||
FrameType = type(_tb.tb_frame)
|
|
||||||
del _C, _tb, _func
|
|
||||||
|
|
||||||
|
|
||||||
def contextfunction(f):
|
def contextfunction(f):
|
||||||
@ -156,7 +79,7 @@ def environmentfunction(f):
|
|||||||
|
|
||||||
def internalcode(f):
|
def internalcode(f):
|
||||||
"""Marks the function as internally used"""
|
"""Marks the function as internally used"""
|
||||||
internal_code.add(f.func_code)
|
internal_code.add(f.__code__)
|
||||||
return f
|
return f
|
||||||
|
|
||||||
|
|
||||||
@ -226,7 +149,7 @@ def open_if_exists(filename, mode='rb'):
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return open(filename, mode)
|
return open(filename, mode)
|
||||||
except IOError, e:
|
except IOError as e:
|
||||||
if e.errno not in (errno.ENOENT, errno.EISDIR):
|
if e.errno not in (errno.ENOENT, errno.EISDIR):
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@ -275,7 +198,7 @@ def urlize(text, trim_url_limit=None, nofollow=False):
|
|||||||
trim_url = lambda x, limit=trim_url_limit: limit is not None \
|
trim_url = lambda x, limit=trim_url_limit: limit is not None \
|
||||||
and (x[:limit] + (len(x) >=limit and '...'
|
and (x[:limit] + (len(x) >=limit and '...'
|
||||||
or '')) or x
|
or '')) or x
|
||||||
words = _word_split_re.split(unicode(escape(text)))
|
words = _word_split_re.split(text_type(escape(text)))
|
||||||
nofollow_attr = nofollow and ' rel="nofollow"' or ''
|
nofollow_attr = nofollow and ' rel="nofollow"' or ''
|
||||||
for i, word in enumerate(words):
|
for i, word in enumerate(words):
|
||||||
match = _punctuation_re.match(word)
|
match = _punctuation_re.match(word)
|
||||||
@ -284,6 +207,7 @@ def urlize(text, trim_url_limit=None, nofollow=False):
|
|||||||
if middle.startswith('www.') or (
|
if middle.startswith('www.') or (
|
||||||
'@' not in middle and
|
'@' not in middle and
|
||||||
not middle.startswith('http://') and
|
not middle.startswith('http://') and
|
||||||
|
not middle.startswith('https://') and
|
||||||
len(middle) > 0 and
|
len(middle) > 0 and
|
||||||
middle[0] in _letters + _digits and (
|
middle[0] in _letters + _digits and (
|
||||||
middle.endswith('.org') or
|
middle.endswith('.org') or
|
||||||
@ -311,7 +235,7 @@ def generate_lorem_ipsum(n=5, html=True, min=20, max=100):
|
|||||||
words = LOREM_IPSUM_WORDS.split()
|
words = LOREM_IPSUM_WORDS.split()
|
||||||
result = []
|
result = []
|
||||||
|
|
||||||
for _ in xrange(n):
|
for _ in range(n):
|
||||||
next_capitalized = True
|
next_capitalized = True
|
||||||
last_comma = last_fullstop = 0
|
last_comma = last_fullstop = 0
|
||||||
word = None
|
word = None
|
||||||
@ -319,7 +243,7 @@ def generate_lorem_ipsum(n=5, html=True, min=20, max=100):
|
|||||||
p = []
|
p = []
|
||||||
|
|
||||||
# each paragraph contains out of 20 to 100 words.
|
# each paragraph contains out of 20 to 100 words.
|
||||||
for idx, _ in enumerate(xrange(randrange(min, max))):
|
for idx, _ in enumerate(range(randrange(min, max))):
|
||||||
while True:
|
while True:
|
||||||
word = choice(words)
|
word = choice(words)
|
||||||
if word != last:
|
if word != last:
|
||||||
@ -361,11 +285,11 @@ def unicode_urlencode(obj, charset='utf-8'):
|
|||||||
If non strings are provided they are converted to their unicode
|
If non strings are provided they are converted to their unicode
|
||||||
representation first.
|
representation first.
|
||||||
"""
|
"""
|
||||||
if not isinstance(obj, basestring):
|
if not isinstance(obj, string_types):
|
||||||
obj = unicode(obj)
|
obj = text_type(obj)
|
||||||
if isinstance(obj, unicode):
|
if isinstance(obj, text_type):
|
||||||
obj = obj.encode(charset)
|
obj = obj.encode(charset)
|
||||||
return unicode(url_quote(obj))
|
return text_type(url_quote(obj))
|
||||||
|
|
||||||
|
|
||||||
class LRUCache(object):
|
class LRUCache(object):
|
||||||
@ -385,18 +309,10 @@ class LRUCache(object):
|
|||||||
# alias all queue methods for faster lookup
|
# alias all queue methods for faster lookup
|
||||||
self._popleft = self._queue.popleft
|
self._popleft = self._queue.popleft
|
||||||
self._pop = self._queue.pop
|
self._pop = self._queue.pop
|
||||||
if hasattr(self._queue, 'remove'):
|
self._remove = self._queue.remove
|
||||||
self._remove = self._queue.remove
|
self._wlock = Lock()
|
||||||
self._wlock = allocate_lock()
|
|
||||||
self._append = self._queue.append
|
self._append = self._queue.append
|
||||||
|
|
||||||
def _remove(self, obj):
|
|
||||||
"""Python 2.4 compatibility."""
|
|
||||||
for idx, item in enumerate(self._queue):
|
|
||||||
if item == obj:
|
|
||||||
del self._queue[idx]
|
|
||||||
break
|
|
||||||
|
|
||||||
def __getstate__(self):
|
def __getstate__(self):
|
||||||
return {
|
return {
|
||||||
'capacity': self.capacity,
|
'capacity': self.capacity,
|
||||||
@ -429,11 +345,15 @@ class LRUCache(object):
|
|||||||
"""Set `default` if the key is not in the cache otherwise
|
"""Set `default` if the key is not in the cache otherwise
|
||||||
leave unchanged. Return the value of this key.
|
leave unchanged. Return the value of this key.
|
||||||
"""
|
"""
|
||||||
|
self._wlock.acquire()
|
||||||
try:
|
try:
|
||||||
return self[key]
|
try:
|
||||||
except KeyError:
|
return self[key]
|
||||||
self[key] = default
|
except KeyError:
|
||||||
return default
|
self[key] = default
|
||||||
|
return default
|
||||||
|
finally:
|
||||||
|
self._wlock.release()
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
"""Clear the cache."""
|
"""Clear the cache."""
|
||||||
@ -464,17 +384,21 @@ class LRUCache(object):
|
|||||||
|
|
||||||
Raise a `KeyError` if it does not exist.
|
Raise a `KeyError` if it does not exist.
|
||||||
"""
|
"""
|
||||||
rv = self._mapping[key]
|
self._wlock.acquire()
|
||||||
if self._queue[-1] != key:
|
try:
|
||||||
try:
|
rv = self._mapping[key]
|
||||||
self._remove(key)
|
if self._queue[-1] != key:
|
||||||
except ValueError:
|
try:
|
||||||
# if something removed the key from the container
|
self._remove(key)
|
||||||
# when we read, ignore the ValueError that we would
|
except ValueError:
|
||||||
# get otherwise.
|
# if something removed the key from the container
|
||||||
pass
|
# when we read, ignore the ValueError that we would
|
||||||
self._append(key)
|
# get otherwise.
|
||||||
return rv
|
pass
|
||||||
|
self._append(key)
|
||||||
|
return rv
|
||||||
|
finally:
|
||||||
|
self._wlock.release()
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
def __setitem__(self, key, value):
|
||||||
"""Sets the value for an item. Moves the item up so that it
|
"""Sets the value for an item. Moves the item up so that it
|
||||||
@ -483,11 +407,7 @@ class LRUCache(object):
|
|||||||
self._wlock.acquire()
|
self._wlock.acquire()
|
||||||
try:
|
try:
|
||||||
if key in self._mapping:
|
if key in self._mapping:
|
||||||
try:
|
self._remove(key)
|
||||||
self._remove(key)
|
|
||||||
except ValueError:
|
|
||||||
# __getitem__ is not locked, it might happen
|
|
||||||
pass
|
|
||||||
elif len(self._mapping) == self.capacity:
|
elif len(self._mapping) == self.capacity:
|
||||||
del self._mapping[self._popleft()]
|
del self._mapping[self._popleft()]
|
||||||
self._append(key)
|
self._append(key)
|
||||||
@ -557,6 +477,7 @@ except ImportError:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@implements_iterator
|
||||||
class Cycler(object):
|
class Cycler(object):
|
||||||
"""A cycle helper for templates."""
|
"""A cycle helper for templates."""
|
||||||
|
|
||||||
@ -575,7 +496,7 @@ class Cycler(object):
|
|||||||
"""Returns the current item."""
|
"""Returns the current item."""
|
||||||
return self.items[self.pos]
|
return self.items[self.pos]
|
||||||
|
|
||||||
def next(self):
|
def __next__(self):
|
||||||
"""Goes one item ahead and returns it."""
|
"""Goes one item ahead and returns it."""
|
||||||
rv = self.current
|
rv = self.current
|
||||||
self.pos = (self.pos + 1) % len(self.items)
|
self.pos = (self.pos + 1) % len(self.items)
|
||||||
@ -596,25 +517,5 @@ class Joiner(object):
|
|||||||
return self.sep
|
return self.sep
|
||||||
|
|
||||||
|
|
||||||
# try markupsafe first, if that fails go with Jinja2's bundled version
|
# Imported here because that's where it was in the past
|
||||||
# of markupsafe. Markupsafe was previously Jinja2's implementation of
|
from markupsafe import Markup, escape, soft_unicode
|
||||||
# the Markup object but was moved into a separate package in a patchlevel
|
|
||||||
# release
|
|
||||||
try:
|
|
||||||
from markupsafe import Markup, escape, soft_unicode
|
|
||||||
except ImportError:
|
|
||||||
from jinja2._markupsafe import Markup, escape, soft_unicode
|
|
||||||
|
|
||||||
|
|
||||||
# partials
|
|
||||||
try:
|
|
||||||
from functools import partial
|
|
||||||
except ImportError:
|
|
||||||
class partial(object):
|
|
||||||
def __init__(self, _func, *args, **kwargs):
|
|
||||||
self._func = _func
|
|
||||||
self._args = args
|
|
||||||
self._kwargs = kwargs
|
|
||||||
def __call__(self, *args, **kwargs):
|
|
||||||
kwargs.update(self._kwargs)
|
|
||||||
return self._func(*(self._args + args), **kwargs)
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user