Flask update
This commit is contained in:
@@ -27,11 +27,7 @@
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
__docformat__ = 'restructuredtext en'
|
||||
try:
|
||||
__version__ = __import__('pkg_resources') \
|
||||
.get_distribution('Jinja2').version
|
||||
except Exception:
|
||||
__version__ = 'unknown'
|
||||
__version__ = '2.7-dev'
|
||||
|
||||
# high level interface
|
||||
from jinja2.environment import Environment, Template
|
||||
|
||||
+26
-14
@@ -20,7 +20,6 @@ import marshal
|
||||
import tempfile
|
||||
import cPickle as pickle
|
||||
import fnmatch
|
||||
from cStringIO import StringIO
|
||||
try:
|
||||
from hashlib import sha1
|
||||
except ImportError:
|
||||
@@ -28,6 +27,26 @@ except ImportError:
|
||||
from jinja2.utils import open_if_exists
|
||||
|
||||
|
||||
# marshal works better on 3.x, one hack less required
|
||||
if sys.version_info > (3, 0):
|
||||
from io import BytesIO
|
||||
marshal_dump = marshal.dump
|
||||
marshal_load = marshal.load
|
||||
else:
|
||||
from cStringIO import StringIO as BytesIO
|
||||
|
||||
def marshal_dump(code, f):
|
||||
if isinstance(f, file):
|
||||
marshal.dump(code, f)
|
||||
else:
|
||||
f.write(marshal.dumps(code))
|
||||
|
||||
def marshal_load(f):
|
||||
if isinstance(f, file):
|
||||
return marshal.load(f)
|
||||
return marshal.loads(f.read())
|
||||
|
||||
|
||||
bc_version = 2
|
||||
|
||||
# magic version used to only change with new jinja versions. With 2.6
|
||||
@@ -71,12 +90,7 @@ class Bucket(object):
|
||||
if self.checksum != checksum:
|
||||
self.reset()
|
||||
return
|
||||
# now load the code. Because marshal is not able to load
|
||||
# from arbitrary streams we have to work around that
|
||||
if isinstance(f, file):
|
||||
self.code = marshal.load(f)
|
||||
else:
|
||||
self.code = marshal.loads(f.read())
|
||||
self.code = marshal_load(f)
|
||||
|
||||
def write_bytecode(self, f):
|
||||
"""Dump the bytecode into the file or file like object passed."""
|
||||
@@ -84,18 +98,15 @@ class Bucket(object):
|
||||
raise TypeError('can\'t write empty bucket')
|
||||
f.write(bc_magic)
|
||||
pickle.dump(self.checksum, f, 2)
|
||||
if isinstance(f, file):
|
||||
marshal.dump(self.code, f)
|
||||
else:
|
||||
f.write(marshal.dumps(self.code))
|
||||
marshal_dump(self.code, f)
|
||||
|
||||
def bytecode_from_string(self, string):
|
||||
"""Load bytecode from a string."""
|
||||
self.load_bytecode(StringIO(string))
|
||||
self.load_bytecode(BytesIO(string))
|
||||
|
||||
def bytecode_to_string(self):
|
||||
"""Return the bytecode as string."""
|
||||
out = StringIO()
|
||||
out = BytesIO()
|
||||
self.write_bytecode(out)
|
||||
return out.getvalue()
|
||||
|
||||
@@ -153,9 +164,10 @@ class BytecodeCache(object):
|
||||
"""Returns the unique hash key for this template name."""
|
||||
hash = sha1(name.encode('utf-8'))
|
||||
if filename is not None:
|
||||
filename = '|' + filename
|
||||
if isinstance(filename, unicode):
|
||||
filename = filename.encode('utf-8')
|
||||
hash.update('|' + filename)
|
||||
hash.update(filename)
|
||||
return hash.hexdigest()
|
||||
|
||||
def get_source_checksum(self, source):
|
||||
|
||||
@@ -127,12 +127,10 @@ class Identifiers(object):
|
||||
self.undeclared.discard(name)
|
||||
self.declared.add(name)
|
||||
|
||||
def is_declared(self, name, local_only=False):
|
||||
def is_declared(self, name):
|
||||
"""Check if a name is declared in this or an outer scope."""
|
||||
if name in self.declared_locally or name in self.declared_parameter:
|
||||
return True
|
||||
if local_only:
|
||||
return False
|
||||
return name in self.declared
|
||||
|
||||
def copy(self):
|
||||
@@ -193,12 +191,12 @@ class Frame(object):
|
||||
rv.identifiers.__dict__.update(self.identifiers.__dict__)
|
||||
return rv
|
||||
|
||||
def inspect(self, nodes, hard_scope=False):
|
||||
def inspect(self, nodes):
|
||||
"""Walk the node and check for identifiers. If the scope is hard (eg:
|
||||
enforce on a python level) overrides from outer scopes are tracked
|
||||
differently.
|
||||
"""
|
||||
visitor = FrameIdentifierVisitor(self.identifiers, hard_scope)
|
||||
visitor = FrameIdentifierVisitor(self.identifiers)
|
||||
for node in nodes:
|
||||
visitor.visit(node)
|
||||
|
||||
@@ -275,9 +273,8 @@ class UndeclaredNameVisitor(NodeVisitor):
|
||||
class FrameIdentifierVisitor(NodeVisitor):
|
||||
"""A visitor for `Frame.inspect`."""
|
||||
|
||||
def __init__(self, identifiers, hard_scope):
|
||||
def __init__(self, identifiers):
|
||||
self.identifiers = identifiers
|
||||
self.hard_scope = hard_scope
|
||||
|
||||
def visit_Name(self, node):
|
||||
"""All assignments to names go through this function."""
|
||||
@@ -286,7 +283,7 @@ class FrameIdentifierVisitor(NodeVisitor):
|
||||
elif node.ctx == 'param':
|
||||
self.identifiers.declared_parameter.add(node.name)
|
||||
elif node.ctx == 'load' and not \
|
||||
self.identifiers.is_declared(node.name, self.hard_scope):
|
||||
self.identifiers.is_declared(node.name):
|
||||
self.identifiers.undeclared.add(node.name)
|
||||
|
||||
def visit_If(self, node):
|
||||
@@ -658,7 +655,7 @@ class CodeGenerator(NodeVisitor):
|
||||
children = node.iter_child_nodes()
|
||||
children = list(children)
|
||||
func_frame = frame.inner()
|
||||
func_frame.inspect(children, hard_scope=True)
|
||||
func_frame.inspect(children)
|
||||
|
||||
# variables that are undeclared (accessed before declaration) and
|
||||
# declared locally *and* part of an outside scope raise a template
|
||||
|
||||
@@ -45,7 +45,13 @@ class TracebackFrameProxy(object):
|
||||
|
||||
def set_next(self, next):
|
||||
if tb_set_next is not None:
|
||||
tb_set_next(self.tb, next and next.tb or None)
|
||||
try:
|
||||
tb_set_next(self.tb, next and next.tb or None)
|
||||
except Exception:
|
||||
# this function can fail due to all the hackery it does
|
||||
# on various python implementations. We just catch errors
|
||||
# down and ignore them if necessary.
|
||||
pass
|
||||
self._tb_next = next
|
||||
|
||||
@property
|
||||
|
||||
+28
-16
@@ -53,7 +53,7 @@ def make_attrgetter(environment, attribute):
|
||||
passed object with the rules of the environment. Dots are allowed
|
||||
to access attributes of attributes.
|
||||
"""
|
||||
if '.' not in attribute:
|
||||
if not isinstance(attribute, basestring) or '.' not in attribute:
|
||||
return lambda x: environment.getitem(x, attribute)
|
||||
attribute = attribute.split('.')
|
||||
def attrgetter(item):
|
||||
@@ -338,21 +338,33 @@ def do_random(environment, seq):
|
||||
|
||||
|
||||
def do_filesizeformat(value, binary=False):
|
||||
"""Format the value like a 'human-readable' file size (i.e. 13 KB,
|
||||
4.1 MB, 102 bytes, etc). Per default decimal prefixes are used (mega,
|
||||
giga, etc.), if the second parameter is set to `True` the binary
|
||||
prefixes are used (mebi, gibi).
|
||||
"""Format the value like a 'human-readable' file size (i.e. 13 kB,
|
||||
4.1 MB, 102 Bytes, etc). Per default decimal prefixes are used (Mega,
|
||||
Giga, etc.), if the second parameter is set to `True` the binary
|
||||
prefixes are used (Mebi, Gibi).
|
||||
"""
|
||||
bytes = float(value)
|
||||
base = binary and 1024 or 1000
|
||||
middle = binary and 'i' or ''
|
||||
if bytes < base:
|
||||
return "%d Byte%s" % (bytes, bytes != 1 and 's' or '')
|
||||
elif bytes < base * base:
|
||||
return "%.1f K%sB" % (bytes / base, middle)
|
||||
elif bytes < base * base * base:
|
||||
return "%.1f M%sB" % (bytes / (base * base), middle)
|
||||
return "%.1f G%sB" % (bytes / (base * base * base), middle)
|
||||
prefixes = [
|
||||
(binary and "KiB" or "kB"),
|
||||
(binary and "MiB" or "MB"),
|
||||
(binary and "GiB" or "GB"),
|
||||
(binary and "TiB" or "TB"),
|
||||
(binary and "PiB" or "PB"),
|
||||
(binary and "EiB" or "EB"),
|
||||
(binary and "ZiB" or "ZB"),
|
||||
(binary and "YiB" or "YB")
|
||||
]
|
||||
if bytes == 1:
|
||||
return "1 Byte"
|
||||
elif bytes < base:
|
||||
return "%d Bytes" % bytes
|
||||
else:
|
||||
for i, prefix in enumerate(prefixes):
|
||||
unit = base * base ** (i + 1)
|
||||
if bytes < unit:
|
||||
return "%.1f %s" % ((bytes / unit), prefix)
|
||||
return "%.1f %s" % ((bytes / unit), prefix)
|
||||
|
||||
|
||||
def do_pprint(value, verbose=False):
|
||||
@@ -431,8 +443,8 @@ def do_truncate(s, length=255, killwords=False, end='...'):
|
||||
result.append(end)
|
||||
return u' '.join(result)
|
||||
|
||||
|
||||
def do_wordwrap(s, width=79, break_long_words=True):
|
||||
@environmentfilter
|
||||
def do_wordwrap(environment, s, width=79, break_long_words=True):
|
||||
"""
|
||||
Return a copy of the string passed to the filter wrapped after
|
||||
``79`` characters. You can override this default using the first
|
||||
@@ -440,7 +452,7 @@ def do_wordwrap(s, width=79, break_long_words=True):
|
||||
split words apart if they are longer than `width`.
|
||||
"""
|
||||
import textwrap
|
||||
return u'\n'.join(textwrap.wrap(s, width=width, expand_tabs=False,
|
||||
return environment.newline_sequence.join(textwrap.wrap(s, width=width, expand_tabs=False,
|
||||
replace_whitespace=False,
|
||||
break_long_words=break_long_words))
|
||||
|
||||
|
||||
@@ -251,8 +251,7 @@ class PackageLoader(BaseLoader):
|
||||
for filename in self.provider.resource_listdir(path):
|
||||
fullname = path + '/' + filename
|
||||
if self.provider.resource_isdir(fullname):
|
||||
for item in _walk(fullname):
|
||||
results.append(item)
|
||||
_walk(fullname)
|
||||
else:
|
||||
results.append(fullname[offset:].lstrip('/'))
|
||||
_walk(path)
|
||||
|
||||
@@ -75,8 +75,6 @@ class TemplateReference(object):
|
||||
|
||||
def __getitem__(self, name):
|
||||
blocks = self.__context.blocks[name]
|
||||
wrap = self.__context.eval_ctx.autoescape and \
|
||||
Markup or (lambda x: x)
|
||||
return BlockReference(name, self.__context, blocks, 0)
|
||||
|
||||
def __repr__(self):
|
||||
|
||||
@@ -11,6 +11,12 @@
|
||||
import re
|
||||
from jinja2.runtime import Undefined
|
||||
|
||||
try:
|
||||
from collections import Mapping as MappingType
|
||||
except ImportError:
|
||||
import UserDict
|
||||
MappingType = (UserDict.UserDict, UserDict.DictMixin, dict)
|
||||
|
||||
# nose, nothing here to test
|
||||
__test__ = False
|
||||
|
||||
@@ -83,6 +89,14 @@ def test_string(value):
|
||||
return isinstance(value, basestring)
|
||||
|
||||
|
||||
def test_mapping(value):
|
||||
"""Return true if the object is a mapping (dict etc.).
|
||||
|
||||
.. versionadded:: 2.6
|
||||
"""
|
||||
return isinstance(value, MappingType)
|
||||
|
||||
|
||||
def test_number(value):
|
||||
"""Return true if the variable is a number."""
|
||||
return isinstance(value, (int, long, float, complex))
|
||||
@@ -137,6 +151,7 @@ TESTS = {
|
||||
'lower': test_lower,
|
||||
'upper': test_upper,
|
||||
'string': test_string,
|
||||
'mapping': test_mapping,
|
||||
'number': test_number,
|
||||
'sequence': test_sequence,
|
||||
'iterable': test_iterable,
|
||||
|
||||
@@ -85,8 +85,8 @@ class FilterTestCase(JinjaTestCase):
|
||||
)
|
||||
out = tmpl.render()
|
||||
assert out == (
|
||||
'100 Bytes|1.0 KB|1.0 MB|1.0 GB|1000.0 GB|'
|
||||
'100 Bytes|1000 Bytes|976.6 KiB|953.7 MiB|931.3 GiB'
|
||||
'100 Bytes|0.0 kB|0.0 MB|0.0 GB|0.0 TB|100 Bytes|'
|
||||
'1000 Bytes|1.0 KiB|0.9 MiB|0.9 GiB'
|
||||
)
|
||||
|
||||
def test_first(self):
|
||||
@@ -288,6 +288,13 @@ class FilterTestCase(JinjaTestCase):
|
||||
""
|
||||
]
|
||||
|
||||
def test_groupby_tuple_index(self):
|
||||
tmpl = env.from_string('''
|
||||
{%- for grouper, list in [('a', 1), ('a', 2), ('b', 1)]|groupby(0) -%}
|
||||
{{ grouper }}{% for x in list %}:{{ x.1 }}{% endfor %}|
|
||||
{%- endfor %}''')
|
||||
assert tmpl.render() == 'a:1:2|b:1|'
|
||||
|
||||
def test_groupby_multidot(self):
|
||||
class Date(object):
|
||||
def __init__(self, day, month, year):
|
||||
|
||||
@@ -48,10 +48,16 @@ class TestsTestCase(JinjaTestCase):
|
||||
{{ range is callable }}
|
||||
{{ 42 is callable }}
|
||||
{{ range(5) is iterable }}
|
||||
{{ {} is mapping }}
|
||||
{{ mydict is mapping }}
|
||||
{{ [] is mapping }}
|
||||
''')
|
||||
assert tmpl.render().split() == [
|
||||
class MyDict(dict):
|
||||
pass
|
||||
assert tmpl.render(mydict=MyDict()).split() == [
|
||||
'False', 'True', 'False', 'True', 'True', 'False',
|
||||
'True', 'True', 'True', 'True', 'False', 'True'
|
||||
'True', 'True', 'True', 'True', 'False', 'True',
|
||||
'True', 'True', 'False'
|
||||
]
|
||||
|
||||
def test_sequence(self):
|
||||
|
||||
Reference in New Issue
Block a user