You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							1073 lines
						
					
					
						
							33 KiB
						
					
					
				
			
		
		
	
	
							1073 lines
						
					
					
						
							33 KiB
						
					
					
				# -*- coding: utf-8 -*-
 | 
						|
"""
 | 
						|
    jinja2.filters
 | 
						|
    ~~~~~~~~~~~~~~
 | 
						|
 | 
						|
    Bundled jinja filters.
 | 
						|
 | 
						|
    :copyright: (c) 2017 by the Jinja Team.
 | 
						|
    :license: BSD, see LICENSE for more details.
 | 
						|
"""
 | 
						|
import re
 | 
						|
import math
 | 
						|
 | 
						|
from random import choice
 | 
						|
from itertools import groupby
 | 
						|
from collections import namedtuple
 | 
						|
from jinja2.utils import Markup, escape, pformat, urlize, soft_unicode, \
 | 
						|
     unicode_urlencode, htmlsafe_json_dumps
 | 
						|
from jinja2.runtime import Undefined
 | 
						|
from jinja2.exceptions import FilterArgumentError
 | 
						|
from jinja2._compat import imap, string_types, text_type, iteritems, PY2
 | 
						|
 | 
						|
 | 
						|
_word_re = re.compile(r'\w+', re.UNICODE)
 | 
						|
_word_beginning_split_re = re.compile(r'([-\s\(\{\[\<]+)', re.UNICODE)
 | 
						|
 | 
						|
 | 
						|
def contextfilter(f):
 | 
						|
    """Decorator for marking context dependent filters. The current
 | 
						|
    :class:`Context` will be passed as first argument.
 | 
						|
    """
 | 
						|
    f.contextfilter = True
 | 
						|
    return f
 | 
						|
 | 
						|
 | 
						|
def evalcontextfilter(f):
 | 
						|
    """Decorator for marking eval-context dependent filters.  An eval
 | 
						|
    context object is passed as first argument.  For more information
 | 
						|
    about the eval context, see :ref:`eval-context`.
 | 
						|
 | 
						|
    .. versionadded:: 2.4
 | 
						|
    """
 | 
						|
    f.evalcontextfilter = True
 | 
						|
    return f
 | 
						|
 | 
						|
 | 
						|
def environmentfilter(f):
 | 
						|
    """Decorator for marking environment dependent filters.  The current
 | 
						|
    :class:`Environment` is passed to the filter as first argument.
 | 
						|
    """
 | 
						|
    f.environmentfilter = True
 | 
						|
    return f
 | 
						|
 | 
						|
 | 
						|
def make_attrgetter(environment, attribute):
 | 
						|
    """Returns a callable that looks up the given attribute from a
 | 
						|
    passed object with the rules of the environment.  Dots are allowed
 | 
						|
    to access attributes of attributes.  Integer parts in paths are
 | 
						|
    looked up as integers.
 | 
						|
    """
 | 
						|
    if not isinstance(attribute, string_types) \
 | 
						|
       or ('.' not in attribute and not attribute.isdigit()):
 | 
						|
        return lambda x: environment.getitem(x, attribute)
 | 
						|
    attribute = attribute.split('.')
 | 
						|
    def attrgetter(item):
 | 
						|
        for part in attribute:
 | 
						|
            if part.isdigit():
 | 
						|
                part = int(part)
 | 
						|
            item = environment.getitem(item, part)
 | 
						|
        return item
 | 
						|
    return attrgetter
 | 
						|
 | 
						|
 | 
						|
def do_forceescape(value):
 | 
						|
    """Enforce HTML escaping.  This will probably double escape variables."""
 | 
						|
    if hasattr(value, '__html__'):
 | 
						|
        value = value.__html__()
 | 
						|
    return escape(text_type(value))
 | 
						|
 | 
						|
 | 
						|
def do_urlencode(value):
 | 
						|
    """Escape strings for use in URLs (uses UTF-8 encoding).  It accepts both
 | 
						|
    dictionaries and regular strings as well as pairwise iterables.
 | 
						|
 | 
						|
    .. versionadded:: 2.7
 | 
						|
    """
 | 
						|
    itemiter = None
 | 
						|
    if isinstance(value, dict):
 | 
						|
        itemiter = iteritems(value)
 | 
						|
    elif not isinstance(value, string_types):
 | 
						|
        try:
 | 
						|
            itemiter = iter(value)
 | 
						|
        except TypeError:
 | 
						|
            pass
 | 
						|
    if itemiter is None:
 | 
						|
        return unicode_urlencode(value)
 | 
						|
    return u'&'.join(unicode_urlencode(k) + '=' +
 | 
						|
                     unicode_urlencode(v, for_qs=True)
 | 
						|
                     for k, v in itemiter)
 | 
						|
 | 
						|
 | 
						|
@evalcontextfilter
 | 
						|
def do_replace(eval_ctx, s, old, new, count=None):
 | 
						|
    """Return a copy of the value with all occurrences of a substring
 | 
						|
    replaced with a new one. The first argument is the substring
 | 
						|
    that should be replaced, the second is the replacement string.
 | 
						|
    If the optional third argument ``count`` is given, only the first
 | 
						|
    ``count`` occurrences are replaced:
 | 
						|
 | 
						|
    .. sourcecode:: jinja
 | 
						|
 | 
						|
        {{ "Hello World"|replace("Hello", "Goodbye") }}
 | 
						|
            -> Goodbye World
 | 
						|
 | 
						|
        {{ "aaaaargh"|replace("a", "d'oh, ", 2) }}
 | 
						|
            -> d'oh, d'oh, aaargh
 | 
						|
    """
 | 
						|
    if count is None:
 | 
						|
        count = -1
 | 
						|
    if not eval_ctx.autoescape:
 | 
						|
        return text_type(s).replace(text_type(old), text_type(new), count)
 | 
						|
    if hasattr(old, '__html__') or hasattr(new, '__html__') and \
 | 
						|
       not hasattr(s, '__html__'):
 | 
						|
        s = escape(s)
 | 
						|
    else:
 | 
						|
        s = soft_unicode(s)
 | 
						|
    return s.replace(soft_unicode(old), soft_unicode(new), count)
 | 
						|
 | 
						|
 | 
						|
def do_upper(s):
 | 
						|
    """Convert a value to uppercase."""
 | 
						|
    return soft_unicode(s).upper()
 | 
						|
 | 
						|
 | 
						|
def do_lower(s):
 | 
						|
    """Convert a value to lowercase."""
 | 
						|
    return soft_unicode(s).lower()
 | 
						|
 | 
						|
 | 
						|
@evalcontextfilter
 | 
						|
def do_xmlattr(_eval_ctx, d, autospace=True):
 | 
						|
    """Create an SGML/XML attribute string based on the items in a dict.
 | 
						|
    All values that are neither `none` nor `undefined` are automatically
 | 
						|
    escaped:
 | 
						|
 | 
						|
    .. sourcecode:: html+jinja
 | 
						|
 | 
						|
        <ul{{ {'class': 'my_list', 'missing': none,
 | 
						|
                'id': 'list-%d'|format(variable)}|xmlattr }}>
 | 
						|
        ...
 | 
						|
        </ul>
 | 
						|
 | 
						|
    Results in something like this:
 | 
						|
 | 
						|
    .. sourcecode:: html
 | 
						|
 | 
						|
        <ul class="my_list" id="list-42">
 | 
						|
        ...
 | 
						|
        </ul>
 | 
						|
 | 
						|
    As you can see it automatically prepends a space in front of the item
 | 
						|
    if the filter returned something unless the second parameter is false.
 | 
						|
    """
 | 
						|
    rv = u' '.join(
 | 
						|
        u'%s="%s"' % (escape(key), escape(value))
 | 
						|
        for key, value in iteritems(d)
 | 
						|
        if value is not None and not isinstance(value, Undefined)
 | 
						|
    )
 | 
						|
    if autospace and rv:
 | 
						|
        rv = u' ' + rv
 | 
						|
    if _eval_ctx.autoescape:
 | 
						|
        rv = Markup(rv)
 | 
						|
    return rv
 | 
						|
 | 
						|
 | 
						|
def do_capitalize(s):
 | 
						|
    """Capitalize a value. The first character will be uppercase, all others
 | 
						|
    lowercase.
 | 
						|
    """
 | 
						|
    return soft_unicode(s).capitalize()
 | 
						|
 | 
						|
 | 
						|
def do_title(s):
 | 
						|
    """Return a titlecased version of the value. I.e. words will start with
 | 
						|
    uppercase letters, all remaining characters are lowercase.
 | 
						|
    """
 | 
						|
    return ''.join(
 | 
						|
        [item[0].upper() + item[1:].lower()
 | 
						|
         for item in _word_beginning_split_re.split(soft_unicode(s))
 | 
						|
         if item])
 | 
						|
 | 
						|
 | 
						|
def do_dictsort(value, case_sensitive=False, by='key'):
 | 
						|
    """Sort a dict and yield (key, value) pairs. Because python dicts are
 | 
						|
    unsorted you may want to use this function to order them by either
 | 
						|
    key or value:
 | 
						|
 | 
						|
    .. sourcecode:: jinja
 | 
						|
 | 
						|
        {% for item in mydict|dictsort %}
 | 
						|
            sort the dict by key, case insensitive
 | 
						|
 | 
						|
        {% for item in mydict|dictsort(true) %}
 | 
						|
            sort the dict by key, case sensitive
 | 
						|
 | 
						|
        {% for item in mydict|dictsort(false, 'value') %}
 | 
						|
            sort the dict by value, case insensitive
 | 
						|
    """
 | 
						|
    if by == 'key':
 | 
						|
        pos = 0
 | 
						|
    elif by == 'value':
 | 
						|
        pos = 1
 | 
						|
    else:
 | 
						|
        raise FilterArgumentError('You can only sort by either '
 | 
						|
                                  '"key" or "value"')
 | 
						|
    def sort_func(item):
 | 
						|
        value = item[pos]
 | 
						|
        if isinstance(value, string_types) and not case_sensitive:
 | 
						|
            value = value.lower()
 | 
						|
        return value
 | 
						|
 | 
						|
    return sorted(value.items(), key=sort_func)
 | 
						|
 | 
						|
 | 
						|
@environmentfilter
 | 
						|
def do_sort(environment, value, reverse=False, case_sensitive=False,
 | 
						|
            attribute=None):
 | 
						|
    """Sort an iterable.  Per default it sorts ascending, if you pass it
 | 
						|
    true as first argument it will reverse the sorting.
 | 
						|
 | 
						|
    If the iterable is made of strings the third parameter can be used to
 | 
						|
    control the case sensitiveness of the comparison which is disabled by
 | 
						|
    default.
 | 
						|
 | 
						|
    .. sourcecode:: jinja
 | 
						|
 | 
						|
        {% for item in iterable|sort %}
 | 
						|
            ...
 | 
						|
        {% endfor %}
 | 
						|
 | 
						|
    It is also possible to sort by an attribute (for example to sort
 | 
						|
    by the date of an object) by specifying the `attribute` parameter:
 | 
						|
 | 
						|
    .. sourcecode:: jinja
 | 
						|
 | 
						|
        {% for item in iterable|sort(attribute='date') %}
 | 
						|
            ...
 | 
						|
        {% endfor %}
 | 
						|
 | 
						|
    .. versionchanged:: 2.6
 | 
						|
       The `attribute` parameter was added.
 | 
						|
    """
 | 
						|
    if not case_sensitive:
 | 
						|
        def sort_func(item):
 | 
						|
            if isinstance(item, string_types):
 | 
						|
                item = item.lower()
 | 
						|
            return item
 | 
						|
    else:
 | 
						|
        sort_func = None
 | 
						|
    if attribute is not None:
 | 
						|
        getter = make_attrgetter(environment, attribute)
 | 
						|
        def sort_func(item, processor=sort_func or (lambda x: x)):
 | 
						|
            return processor(getter(item))
 | 
						|
    return sorted(value, key=sort_func, reverse=reverse)
 | 
						|
 | 
						|
 | 
						|
def do_default(value, default_value=u'', boolean=False):
 | 
						|
    """If the value is undefined it will return the passed default value,
 | 
						|
    otherwise the value of the variable:
 | 
						|
 | 
						|
    .. sourcecode:: jinja
 | 
						|
 | 
						|
        {{ my_variable|default('my_variable is not defined') }}
 | 
						|
 | 
						|
    This will output the value of ``my_variable`` if the variable was
 | 
						|
    defined, otherwise ``'my_variable is not defined'``. If you want
 | 
						|
    to use default with variables that evaluate to false you have to
 | 
						|
    set the second parameter to `true`:
 | 
						|
 | 
						|
    .. sourcecode:: jinja
 | 
						|
 | 
						|
        {{ ''|default('the string was empty', true) }}
 | 
						|
    """
 | 
						|
    if isinstance(value, Undefined) or (boolean and not value):
 | 
						|
        return default_value
 | 
						|
    return value
 | 
						|
 | 
						|
 | 
						|
@evalcontextfilter
 | 
						|
def do_join(eval_ctx, value, d=u'', attribute=None):
 | 
						|
    """Return a string which is the concatenation of the strings in the
 | 
						|
    sequence. The separator between elements is an empty string per
 | 
						|
    default, you can define it with the optional parameter:
 | 
						|
 | 
						|
    .. sourcecode:: jinja
 | 
						|
 | 
						|
        {{ [1, 2, 3]|join('|') }}
 | 
						|
            -> 1|2|3
 | 
						|
 | 
						|
        {{ [1, 2, 3]|join }}
 | 
						|
            -> 123
 | 
						|
 | 
						|
    It is also possible to join certain attributes of an object:
 | 
						|
 | 
						|
    .. sourcecode:: jinja
 | 
						|
 | 
						|
        {{ users|join(', ', attribute='username') }}
 | 
						|
 | 
						|
    .. versionadded:: 2.6
 | 
						|
       The `attribute` parameter was added.
 | 
						|
    """
 | 
						|
    if attribute is not None:
 | 
						|
        value = imap(make_attrgetter(eval_ctx.environment, attribute), value)
 | 
						|
 | 
						|
    # no automatic escaping?  joining is a lot eaiser then
 | 
						|
    if not eval_ctx.autoescape:
 | 
						|
        return text_type(d).join(imap(text_type, value))
 | 
						|
 | 
						|
    # 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 not hasattr(d, '__html__'):
 | 
						|
        value = list(value)
 | 
						|
        do_escape = False
 | 
						|
        for idx, item in enumerate(value):
 | 
						|
            if hasattr(item, '__html__'):
 | 
						|
                do_escape = True
 | 
						|
            else:
 | 
						|
                value[idx] = text_type(item)
 | 
						|
        if do_escape:
 | 
						|
            d = escape(d)
 | 
						|
        else:
 | 
						|
            d = text_type(d)
 | 
						|
        return d.join(value)
 | 
						|
 | 
						|
    # no html involved, to normal joining
 | 
						|
    return soft_unicode(d).join(imap(soft_unicode, value))
 | 
						|
 | 
						|
 | 
						|
def do_center(value, width=80):
 | 
						|
    """Centers the value in a field of a given width."""
 | 
						|
    return text_type(value).center(width)
 | 
						|
 | 
						|
 | 
						|
@environmentfilter
 | 
						|
def do_first(environment, seq):
 | 
						|
    """Return the first item of a sequence."""
 | 
						|
    try:
 | 
						|
        return next(iter(seq))
 | 
						|
    except StopIteration:
 | 
						|
        return environment.undefined('No first item, sequence was empty.')
 | 
						|
 | 
						|
 | 
						|
@environmentfilter
 | 
						|
def do_last(environment, seq):
 | 
						|
    """Return the last item of a sequence."""
 | 
						|
    try:
 | 
						|
        return next(iter(reversed(seq)))
 | 
						|
    except StopIteration:
 | 
						|
        return environment.undefined('No last item, sequence was empty.')
 | 
						|
 | 
						|
 | 
						|
@environmentfilter
 | 
						|
def do_random(environment, seq):
 | 
						|
    """Return a random item from the sequence."""
 | 
						|
    try:
 | 
						|
        return choice(seq)
 | 
						|
    except IndexError:
 | 
						|
        return environment.undefined('No random item, sequence was empty.')
 | 
						|
 | 
						|
 | 
						|
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).
 | 
						|
    """
 | 
						|
    bytes = float(value)
 | 
						|
    base = binary and 1024 or 1000
 | 
						|
    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 ** (i + 2)
 | 
						|
            if bytes < unit:
 | 
						|
                return '%.1f %s' % ((base * bytes / unit), prefix)
 | 
						|
        return '%.1f %s' % ((base * bytes / unit), prefix)
 | 
						|
 | 
						|
 | 
						|
def do_pprint(value, verbose=False):
 | 
						|
    """Pretty print a variable. Useful for debugging.
 | 
						|
 | 
						|
    With Jinja 1.2 onwards you can pass it a parameter.  If this parameter
 | 
						|
    is truthy the output will be more verbose (this requires `pretty`)
 | 
						|
    """
 | 
						|
    return pformat(value, verbose=verbose)
 | 
						|
 | 
						|
 | 
						|
@evalcontextfilter
 | 
						|
def do_urlize(eval_ctx, value, trim_url_limit=None, nofollow=False,
 | 
						|
              target=None, rel=None):
 | 
						|
    """Converts URLs in plain text into clickable links.
 | 
						|
 | 
						|
    If you pass the filter an additional integer it will shorten the urls
 | 
						|
    to that number. Also a third argument exists that makes the urls
 | 
						|
    "nofollow":
 | 
						|
 | 
						|
    .. sourcecode:: jinja
 | 
						|
 | 
						|
        {{ mytext|urlize(40, true) }}
 | 
						|
            links are shortened to 40 chars and defined with rel="nofollow"
 | 
						|
 | 
						|
    If *target* is specified, the ``target`` attribute will be added to the
 | 
						|
    ``<a>`` tag:
 | 
						|
 | 
						|
    .. sourcecode:: jinja
 | 
						|
 | 
						|
       {{ mytext|urlize(40, target='_blank') }}
 | 
						|
 | 
						|
    .. versionchanged:: 2.8+
 | 
						|
       The *target* parameter was added.
 | 
						|
    """
 | 
						|
    policies = eval_ctx.environment.policies
 | 
						|
    rel = set((rel or '').split() or [])
 | 
						|
    if nofollow:
 | 
						|
        rel.add('nofollow')
 | 
						|
    rel.update((policies['urlize.rel'] or '').split())
 | 
						|
    if target is None:
 | 
						|
        target = policies['urlize.target']
 | 
						|
    rel = ' '.join(sorted(rel)) or None
 | 
						|
    rv = urlize(value, trim_url_limit, rel=rel, target=target)
 | 
						|
    if eval_ctx.autoescape:
 | 
						|
        rv = Markup(rv)
 | 
						|
    return rv
 | 
						|
 | 
						|
 | 
						|
def do_indent(s, width=4, indentfirst=False):
 | 
						|
    """Return a copy of the passed string, each line indented by
 | 
						|
    4 spaces. The first line is not indented. If you want to
 | 
						|
    change the number of spaces or indent the first line too
 | 
						|
    you can pass additional parameters to the filter:
 | 
						|
 | 
						|
    .. sourcecode:: jinja
 | 
						|
 | 
						|
        {{ mytext|indent(2, true) }}
 | 
						|
            indent by two spaces and indent the first line too.
 | 
						|
    """
 | 
						|
    indention = u' ' * width
 | 
						|
    rv = (u'\n' + indention).join(s.splitlines())
 | 
						|
    if indentfirst:
 | 
						|
        rv = indention + rv
 | 
						|
    return rv
 | 
						|
 | 
						|
 | 
						|
@environmentfilter
 | 
						|
def do_truncate(env, s, length=255, killwords=False, end='...', leeway=None):
 | 
						|
    """Return a truncated copy of the string. The length is specified
 | 
						|
    with the first parameter which defaults to ``255``. If the second
 | 
						|
    parameter is ``true`` the filter will cut the text at length. Otherwise
 | 
						|
    it will discard the last word. If the text was in fact
 | 
						|
    truncated it will append an ellipsis sign (``"..."``). If you want a
 | 
						|
    different ellipsis sign than ``"..."`` you can specify it using the
 | 
						|
    third parameter. Strings that only exceed the length by the tolerance
 | 
						|
    margin given in the fourth parameter will not be truncated.
 | 
						|
 | 
						|
    .. sourcecode:: jinja
 | 
						|
 | 
						|
        {{ "foo bar baz qux"|truncate(9) }}
 | 
						|
            -> "foo..."
 | 
						|
        {{ "foo bar baz qux"|truncate(9, True) }}
 | 
						|
            -> "foo ba..."
 | 
						|
        {{ "foo bar baz qux"|truncate(11) }}
 | 
						|
            -> "foo bar baz qux"
 | 
						|
        {{ "foo bar baz qux"|truncate(11, False, '...', 0) }}
 | 
						|
            -> "foo bar..."
 | 
						|
 | 
						|
    The default leeway on newer Jinja2 versions is 5 and was 0 before but
 | 
						|
    can be reconfigured globally.
 | 
						|
    """
 | 
						|
    if leeway is None:
 | 
						|
        leeway = env.policies['truncate.leeway']
 | 
						|
    assert length >= len(end), 'expected length >= %s, got %s' % (len(end), length)
 | 
						|
    assert leeway >= 0, 'expected leeway >= 0, got %s' % leeway
 | 
						|
    if len(s) <= length + leeway:
 | 
						|
        return s
 | 
						|
    if killwords:
 | 
						|
        return s[:length - len(end)] + end
 | 
						|
    result = s[:length - len(end)].rsplit(' ', 1)[0]
 | 
						|
    return result + end
 | 
						|
 | 
						|
 | 
						|
@environmentfilter
 | 
						|
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
 | 
						|
    ``79`` characters.  You can override this default using the first
 | 
						|
    parameter.  If you set the second parameter to `false` Jinja will not
 | 
						|
    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
 | 
						|
    return wrapstring.join(textwrap.wrap(s, width=width, expand_tabs=False,
 | 
						|
                                   replace_whitespace=False,
 | 
						|
                                   break_long_words=break_long_words))
 | 
						|
 | 
						|
 | 
						|
def do_wordcount(s):
 | 
						|
    """Count the words in that string."""
 | 
						|
    return len(_word_re.findall(s))
 | 
						|
 | 
						|
 | 
						|
def do_int(value, default=0, base=10):
 | 
						|
    """Convert the value into an integer. If the
 | 
						|
    conversion doesn't work it will return ``0``. You can
 | 
						|
    override this default using the first parameter. You
 | 
						|
    can also override the default base (10) in the second
 | 
						|
    parameter, which handles input with prefixes such as
 | 
						|
    0b, 0o and 0x for bases 2, 8 and 16 respectively.
 | 
						|
    The base is ignored for decimal numbers and non-string values.
 | 
						|
    """
 | 
						|
    try:
 | 
						|
        if isinstance(value, string_types):
 | 
						|
            return int(value, base)
 | 
						|
        return int(value)
 | 
						|
    except (TypeError, ValueError):
 | 
						|
        # this quirk is necessary so that "42.23"|int gives 42.
 | 
						|
        try:
 | 
						|
            return int(float(value))
 | 
						|
        except (TypeError, ValueError):
 | 
						|
            return default
 | 
						|
 | 
						|
 | 
						|
def do_float(value, default=0.0):
 | 
						|
    """Convert the value into a floating point number. If the
 | 
						|
    conversion doesn't work it will return ``0.0``. You can
 | 
						|
    override this default using the first parameter.
 | 
						|
    """
 | 
						|
    try:
 | 
						|
        return float(value)
 | 
						|
    except (TypeError, ValueError):
 | 
						|
        return default
 | 
						|
 | 
						|
 | 
						|
def do_format(value, *args, **kwargs):
 | 
						|
    """
 | 
						|
    Apply python string formatting on an object:
 | 
						|
 | 
						|
    .. sourcecode:: jinja
 | 
						|
 | 
						|
        {{ "%s - %s"|format("Hello?", "Foo!") }}
 | 
						|
            -> Hello? - Foo!
 | 
						|
    """
 | 
						|
    if args and kwargs:
 | 
						|
        raise FilterArgumentError('can\'t handle positional and keyword '
 | 
						|
                                  'arguments at the same time')
 | 
						|
    return soft_unicode(value) % (kwargs or args)
 | 
						|
 | 
						|
 | 
						|
def do_trim(value):
 | 
						|
    """Strip leading and trailing whitespace."""
 | 
						|
    return soft_unicode(value).strip()
 | 
						|
 | 
						|
 | 
						|
def do_striptags(value):
 | 
						|
    """Strip SGML/XML tags and replace adjacent whitespace by one space.
 | 
						|
    """
 | 
						|
    if hasattr(value, '__html__'):
 | 
						|
        value = value.__html__()
 | 
						|
    return Markup(text_type(value)).striptags()
 | 
						|
 | 
						|
 | 
						|
def do_slice(value, slices, fill_with=None):
 | 
						|
    """Slice an iterator and return a list of lists containing
 | 
						|
    those items. Useful if you want to create a div containing
 | 
						|
    three ul tags that represent columns:
 | 
						|
 | 
						|
    .. sourcecode:: html+jinja
 | 
						|
 | 
						|
        <div class="columwrapper">
 | 
						|
          {%- for column in items|slice(3) %}
 | 
						|
            <ul class="column-{{ loop.index }}">
 | 
						|
            {%- for item in column %}
 | 
						|
              <li>{{ item }}</li>
 | 
						|
            {%- endfor %}
 | 
						|
            </ul>
 | 
						|
          {%- endfor %}
 | 
						|
        </div>
 | 
						|
 | 
						|
    If you pass it a second argument it's used to fill missing
 | 
						|
    values on the last iteration.
 | 
						|
    """
 | 
						|
    seq = list(value)
 | 
						|
    length = len(seq)
 | 
						|
    items_per_slice = length // slices
 | 
						|
    slices_with_extra = length % slices
 | 
						|
    offset = 0
 | 
						|
    for slice_number in range(slices):
 | 
						|
        start = offset + slice_number * items_per_slice
 | 
						|
        if slice_number < slices_with_extra:
 | 
						|
            offset += 1
 | 
						|
        end = offset + (slice_number + 1) * items_per_slice
 | 
						|
        tmp = seq[start:end]
 | 
						|
        if fill_with is not None and slice_number >= slices_with_extra:
 | 
						|
            tmp.append(fill_with)
 | 
						|
        yield tmp
 | 
						|
 | 
						|
 | 
						|
def do_batch(value, linecount, fill_with=None):
 | 
						|
    """
 | 
						|
    A filter that batches items. It works pretty much like `slice`
 | 
						|
    just the other way round. It returns a list of lists with the
 | 
						|
    given number of items. If you provide a second parameter this
 | 
						|
    is used to fill up missing items. See this example:
 | 
						|
 | 
						|
    .. sourcecode:: html+jinja
 | 
						|
 | 
						|
        <table>
 | 
						|
        {%- for row in items|batch(3, ' ') %}
 | 
						|
          <tr>
 | 
						|
          {%- for column in row %}
 | 
						|
            <td>{{ column }}</td>
 | 
						|
          {%- endfor %}
 | 
						|
          </tr>
 | 
						|
        {%- endfor %}
 | 
						|
        </table>
 | 
						|
    """
 | 
						|
    tmp = []
 | 
						|
    for item in value:
 | 
						|
        if len(tmp) == linecount:
 | 
						|
            yield tmp
 | 
						|
            tmp = []
 | 
						|
        tmp.append(item)
 | 
						|
    if tmp:
 | 
						|
        if fill_with is not None and len(tmp) < linecount:
 | 
						|
            tmp += [fill_with] * (linecount - len(tmp))
 | 
						|
        yield tmp
 | 
						|
 | 
						|
 | 
						|
def do_round(value, precision=0, method='common'):
 | 
						|
    """Round the number to a given precision. The first
 | 
						|
    parameter specifies the precision (default is ``0``), the
 | 
						|
    second the rounding method:
 | 
						|
 | 
						|
    - ``'common'`` rounds either up or down
 | 
						|
    - ``'ceil'`` always rounds up
 | 
						|
    - ``'floor'`` always rounds down
 | 
						|
 | 
						|
    If you don't specify a method ``'common'`` is used.
 | 
						|
 | 
						|
    .. sourcecode:: jinja
 | 
						|
 | 
						|
        {{ 42.55|round }}
 | 
						|
            -> 43.0
 | 
						|
        {{ 42.55|round(1, 'floor') }}
 | 
						|
            -> 42.5
 | 
						|
 | 
						|
    Note that even if rounded to 0 precision, a float is returned.  If
 | 
						|
    you need a real integer, pipe it through `int`:
 | 
						|
 | 
						|
    .. sourcecode:: jinja
 | 
						|
 | 
						|
        {{ 42.55|round|int }}
 | 
						|
            -> 43
 | 
						|
    """
 | 
						|
    if not method in ('common', 'ceil', 'floor'):
 | 
						|
        raise FilterArgumentError('method must be common, ceil or floor')
 | 
						|
    if method == 'common':
 | 
						|
        return round(value, precision)
 | 
						|
    func = getattr(math, method)
 | 
						|
    return func(value * (10 ** precision)) / (10 ** precision)
 | 
						|
 | 
						|
 | 
						|
# Use a regular tuple repr here.  This is what we did in the past and we
 | 
						|
# really want to hide this custom type as much as possible.  In particular
 | 
						|
# we do not want to accidentally expose an auto generated repr in case
 | 
						|
# people start to print this out in comments or something similar for
 | 
						|
# debugging.
 | 
						|
_GroupTuple = namedtuple('_GroupTuple', ['grouper', 'list'])
 | 
						|
_GroupTuple.__repr__ = tuple.__repr__
 | 
						|
_GroupTuple.__str__ = tuple.__str__
 | 
						|
 | 
						|
@environmentfilter
 | 
						|
def do_groupby(environment, value, attribute):
 | 
						|
    """Group a sequence of objects by a common attribute.
 | 
						|
 | 
						|
    If you for example have a list of dicts or objects that represent persons
 | 
						|
    with `gender`, `first_name` and `last_name` attributes and you want to
 | 
						|
    group all users by genders you can do something like the following
 | 
						|
    snippet:
 | 
						|
 | 
						|
    .. sourcecode:: html+jinja
 | 
						|
 | 
						|
        <ul>
 | 
						|
        {% for group in persons|groupby('gender') %}
 | 
						|
            <li>{{ group.grouper }}<ul>
 | 
						|
            {% for person in group.list %}
 | 
						|
                <li>{{ person.first_name }} {{ person.last_name }}</li>
 | 
						|
            {% endfor %}</ul></li>
 | 
						|
        {% endfor %}
 | 
						|
        </ul>
 | 
						|
 | 
						|
    Additionally it's possible to use tuple unpacking for the grouper and
 | 
						|
    list:
 | 
						|
 | 
						|
    .. sourcecode:: html+jinja
 | 
						|
 | 
						|
        <ul>
 | 
						|
        {% for grouper, list in persons|groupby('gender') %}
 | 
						|
            ...
 | 
						|
        {% endfor %}
 | 
						|
        </ul>
 | 
						|
 | 
						|
    As you can see the item we're grouping by is stored in the `grouper`
 | 
						|
    attribute and the `list` contains all the objects that have this grouper
 | 
						|
    in common.
 | 
						|
 | 
						|
    .. versionchanged:: 2.6
 | 
						|
       It's now possible to use dotted notation to group by the child
 | 
						|
       attribute of another attribute.
 | 
						|
    """
 | 
						|
    expr = make_attrgetter(environment, attribute)
 | 
						|
    return [_GroupTuple(key, list(values)) for key, values
 | 
						|
            in groupby(sorted(value, key=expr), expr)]
 | 
						|
 | 
						|
 | 
						|
@environmentfilter
 | 
						|
def do_sum(environment, iterable, attribute=None, start=0):
 | 
						|
    """Returns the sum of a sequence of numbers plus the value of parameter
 | 
						|
    'start' (which defaults to 0).  When the sequence is empty it returns
 | 
						|
    start.
 | 
						|
 | 
						|
    It is also possible to sum up only certain attributes:
 | 
						|
 | 
						|
    .. sourcecode:: jinja
 | 
						|
 | 
						|
        Total: {{ items|sum(attribute='price') }}
 | 
						|
 | 
						|
    .. versionchanged:: 2.6
 | 
						|
       The `attribute` parameter was added to allow suming up over
 | 
						|
       attributes.  Also the `start` parameter was moved on to the right.
 | 
						|
    """
 | 
						|
    if attribute is not None:
 | 
						|
        iterable = imap(make_attrgetter(environment, attribute), iterable)
 | 
						|
    return sum(iterable, start)
 | 
						|
 | 
						|
 | 
						|
def do_list(value):
 | 
						|
    """Convert the value into a list.  If it was a string the returned list
 | 
						|
    will be a list of characters.
 | 
						|
    """
 | 
						|
    return list(value)
 | 
						|
 | 
						|
 | 
						|
def do_mark_safe(value):
 | 
						|
    """Mark the value as safe which means that in an environment with automatic
 | 
						|
    escaping enabled this variable will not be escaped.
 | 
						|
    """
 | 
						|
    return Markup(value)
 | 
						|
 | 
						|
 | 
						|
def do_mark_unsafe(value):
 | 
						|
    """Mark a value as unsafe.  This is the reverse operation for :func:`safe`."""
 | 
						|
    return text_type(value)
 | 
						|
 | 
						|
 | 
						|
def do_reverse(value):
 | 
						|
    """Reverse the object or return an iterator that iterates over it the other
 | 
						|
    way round.
 | 
						|
    """
 | 
						|
    if isinstance(value, string_types):
 | 
						|
        return value[::-1]
 | 
						|
    try:
 | 
						|
        return reversed(value)
 | 
						|
    except TypeError:
 | 
						|
        try:
 | 
						|
            rv = list(value)
 | 
						|
            rv.reverse()
 | 
						|
            return rv
 | 
						|
        except TypeError:
 | 
						|
            raise FilterArgumentError('argument must be iterable')
 | 
						|
 | 
						|
 | 
						|
@environmentfilter
 | 
						|
def do_attr(environment, obj, name):
 | 
						|
    """Get an attribute of an object.  ``foo|attr("bar")`` works like
 | 
						|
    ``foo.bar`` just that always an attribute is returned and items are not
 | 
						|
    looked up.
 | 
						|
 | 
						|
    See :ref:`Notes on subscriptions <notes-on-subscriptions>` for more details.
 | 
						|
    """
 | 
						|
    try:
 | 
						|
        name = str(name)
 | 
						|
    except UnicodeError:
 | 
						|
        pass
 | 
						|
    else:
 | 
						|
        try:
 | 
						|
            value = getattr(obj, name)
 | 
						|
        except AttributeError:
 | 
						|
            pass
 | 
						|
        else:
 | 
						|
            if environment.sandboxed and not \
 | 
						|
               environment.is_safe_attribute(obj, name, value):
 | 
						|
                return environment.unsafe_undefined(obj, name)
 | 
						|
            return value
 | 
						|
    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
 | 
						|
    """
 | 
						|
    seq, func = prepare_map(args, kwargs)
 | 
						|
    if seq:
 | 
						|
        for item in seq:
 | 
						|
            yield func(item)
 | 
						|
 | 
						|
 | 
						|
@contextfilter
 | 
						|
def do_select(*args, **kwargs):
 | 
						|
    """Filters a sequence of objects by applying a test to each object,
 | 
						|
    and only selecting the objects with the test succeeding.
 | 
						|
 | 
						|
    If no test is specified, each object will be evaluated as a boolean.
 | 
						|
 | 
						|
    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 applying a test to each object,
 | 
						|
    and rejecting the objects with the test succeeding.
 | 
						|
 | 
						|
    If no test is specified, each object will be evaluated as a boolean.
 | 
						|
 | 
						|
    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 applying a test to the specified
 | 
						|
    attribute of each object, and only selecting the objects with the
 | 
						|
    test succeeding.
 | 
						|
 | 
						|
    If no test is specified, the attribute's value will be evaluated as
 | 
						|
    a boolean.
 | 
						|
 | 
						|
    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 applying a test to the specified
 | 
						|
    attribute of each object, and rejecting the objects with the test
 | 
						|
    succeeding.
 | 
						|
 | 
						|
    If no test is specified, the attribute's value will be evaluated as
 | 
						|
    a boolean.
 | 
						|
 | 
						|
    .. sourcecode:: jinja
 | 
						|
 | 
						|
        {{ users|rejectattr("is_active") }}
 | 
						|
        {{ users|rejectattr("email", "none") }}
 | 
						|
 | 
						|
    .. versionadded:: 2.7
 | 
						|
    """
 | 
						|
    return select_or_reject(args, kwargs, lambda x: not x, True)
 | 
						|
 | 
						|
 | 
						|
@evalcontextfilter
 | 
						|
def do_tojson(eval_ctx, value, indent=None):
 | 
						|
    """Dumps a structure to JSON so that it's safe to use in ``<script>``
 | 
						|
    tags.  It accepts the same arguments and returns a JSON string.  Note that
 | 
						|
    this is available in templates through the ``|tojson`` filter which will
 | 
						|
    also mark the result as safe.  Due to how this function escapes certain
 | 
						|
    characters this is safe even if used outside of ``<script>`` tags.
 | 
						|
 | 
						|
    The following characters are escaped in strings:
 | 
						|
 | 
						|
    -   ``<``
 | 
						|
    -   ``>``
 | 
						|
    -   ``&``
 | 
						|
    -   ``'``
 | 
						|
 | 
						|
    This makes it safe to embed such strings in any place in HTML with the
 | 
						|
    notable exception of double quoted attributes.  In that case single
 | 
						|
    quote your attributes or HTML escape it in addition.
 | 
						|
 | 
						|
    The indent parameter can be used to enable pretty printing.  Set it to
 | 
						|
    the number of spaces that the structures should be indented with.
 | 
						|
 | 
						|
    Note that this filter is for use in HTML contexts only.
 | 
						|
 | 
						|
    .. versionadded:: 2.9
 | 
						|
    """
 | 
						|
    policies = eval_ctx.environment.policies
 | 
						|
    dumper = policies['json.dumps_function']
 | 
						|
    options = policies['json.dumps_kwargs']
 | 
						|
    if indent is not None:
 | 
						|
        options = dict(options)
 | 
						|
        options['indent'] = indent
 | 
						|
    return htmlsafe_json_dumps(value, dumper=dumper, **options)
 | 
						|
 | 
						|
 | 
						|
def prepare_map(args, kwargs):
 | 
						|
    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)
 | 
						|
 | 
						|
    return seq, func
 | 
						|
 | 
						|
 | 
						|
def prepare_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
 | 
						|
 | 
						|
    return seq, lambda item: modfunc(func(transfunc(item)))
 | 
						|
 | 
						|
 | 
						|
def select_or_reject(args, kwargs, modfunc, lookup_attr):
 | 
						|
    seq, func = prepare_select_or_reject(args, kwargs, modfunc, lookup_attr)
 | 
						|
    if seq:
 | 
						|
        for item in seq:
 | 
						|
            if func(item):
 | 
						|
                yield item
 | 
						|
 | 
						|
 | 
						|
FILTERS = {
 | 
						|
    'abs':                  abs,
 | 
						|
    'attr':                 do_attr,
 | 
						|
    'batch':                do_batch,
 | 
						|
    'capitalize':           do_capitalize,
 | 
						|
    'center':               do_center,
 | 
						|
    'count':                len,
 | 
						|
    'd':                    do_default,
 | 
						|
    'default':              do_default,
 | 
						|
    'dictsort':             do_dictsort,
 | 
						|
    'e':                    escape,
 | 
						|
    'escape':               escape,
 | 
						|
    'filesizeformat':       do_filesizeformat,
 | 
						|
    'first':                do_first,
 | 
						|
    'float':                do_float,
 | 
						|
    'forceescape':          do_forceescape,
 | 
						|
    'format':               do_format,
 | 
						|
    'groupby':              do_groupby,
 | 
						|
    'indent':               do_indent,
 | 
						|
    'int':                  do_int,
 | 
						|
    'join':                 do_join,
 | 
						|
    'last':                 do_last,
 | 
						|
    'length':               len,
 | 
						|
    'list':                 do_list,
 | 
						|
    'lower':                do_lower,
 | 
						|
    'map':                  do_map,
 | 
						|
    'pprint':               do_pprint,
 | 
						|
    'random':               do_random,
 | 
						|
    'reject':               do_reject,
 | 
						|
    'rejectattr':           do_rejectattr,
 | 
						|
    'replace':              do_replace,
 | 
						|
    'reverse':              do_reverse,
 | 
						|
    'round':                do_round,
 | 
						|
    'safe':                 do_mark_safe,
 | 
						|
    'select':               do_select,
 | 
						|
    'selectattr':           do_selectattr,
 | 
						|
    'slice':                do_slice,
 | 
						|
    'sort':                 do_sort,
 | 
						|
    'string':               soft_unicode,
 | 
						|
    'striptags':            do_striptags,
 | 
						|
    'sum':                  do_sum,
 | 
						|
    'title':                do_title,
 | 
						|
    'trim':                 do_trim,
 | 
						|
    'truncate':             do_truncate,
 | 
						|
    'upper':                do_upper,
 | 
						|
    'urlencode':            do_urlencode,
 | 
						|
    'urlize':               do_urlize,
 | 
						|
    'wordcount':            do_wordcount,
 | 
						|
    'wordwrap':             do_wordwrap,
 | 
						|
    'xmlattr':              do_xmlattr,
 | 
						|
    'tojson':               do_tojson,
 | 
						|
}
 | 
						|
 |