| Index: mojo/public/third_party/jinja2/filters.py
|
| diff --git a/mojo/public/third_party/jinja2/filters.py b/mojo/public/third_party/jinja2/filters.py
|
| deleted file mode 100644
|
| index fd0db04aa41026500910347cf401fe620c556188..0000000000000000000000000000000000000000
|
| --- a/mojo/public/third_party/jinja2/filters.py
|
| +++ /dev/null
|
| @@ -1,987 +0,0 @@
|
| -# -*- coding: utf-8 -*-
|
| -"""
|
| - jinja2.filters
|
| - ~~~~~~~~~~~~~~
|
| -
|
| - Bundled jinja filters.
|
| -
|
| - :copyright: (c) 2010 by the Jinja Team.
|
| - :license: BSD, see LICENSE for more details.
|
| -"""
|
| -import re
|
| -import math
|
| -
|
| -from random import choice
|
| -from operator import itemgetter
|
| -from itertools import groupby
|
| -from jinja2.utils import Markup, escape, pformat, urlize, soft_unicode, \
|
| - unicode_urlencode
|
| -from jinja2.runtime import Undefined
|
| -from jinja2.exceptions import FilterArgumentError
|
| -from jinja2._compat import next, imap, string_types, text_type, iteritems
|
| -
|
| -
|
| -_word_re = re.compile(r'\w+(?u)')
|
| -
|
| -
|
| -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 evironment 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 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.
|
| - """
|
| - rv = []
|
| - for item in re.compile(r'([-\s]+)(?u)').split(s):
|
| - if not item:
|
| - continue
|
| - rv.append(item[0].upper() + item[1:].lower())
|
| - return ''.join(rv)
|
| -
|
| -
|
| -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 key, case insensitive, sorted
|
| - normally and ordered by value.
|
| - """
|
| - 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):
|
| - """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"
|
| - """
|
| - rv = urlize(value, trim_url_limit, nofollow)
|
| - 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
|
| -
|
| -
|
| -def do_truncate(s, length=255, killwords=False, end='...'):
|
| - """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.
|
| -
|
| - .. sourcecode:: jinja
|
| -
|
| - {{ "foo bar"|truncate(5) }}
|
| - -> "foo ..."
|
| - {{ "foo bar"|truncate(5, True) }}
|
| - -> "foo b..."
|
| - """
|
| - if len(s) <= length:
|
| - return s
|
| - elif killwords:
|
| - return s[:length] + end
|
| - words = s.split(' ')
|
| - result = []
|
| - m = 0
|
| - for word in words:
|
| - m += len(word) + 1
|
| - if m > length:
|
| - break
|
| - result.append(word)
|
| - result.append(end)
|
| - return u' '.join(result)
|
| -
|
| -@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):
|
| - """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.
|
| - """
|
| - try:
|
| - 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>
|
| - """
|
| - result = []
|
| - 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)
|
| -
|
| -
|
| -@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 sorted(map(_GroupTuple, groupby(sorted(value, key=expr), expr)))
|
| -
|
| -
|
| -class _GroupTuple(tuple):
|
| - __slots__ = ()
|
| - grouper = property(itemgetter(0))
|
| - list = property(itemgetter(1))
|
| -
|
| - def __new__(cls, xxx_todo_changeme):
|
| - (key, value) = xxx_todo_changeme
|
| - return tuple.__new__(cls, (key, list(value)))
|
| -
|
| -
|
| -@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 the 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
|
| - """
|
| - 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 either the object
|
| - or the attribute and only selecting the ones with the test succeeding.
|
| -
|
| - Example usage:
|
| -
|
| - .. sourcecode:: jinja
|
| -
|
| - {{ 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 either the object
|
| - or the attribute 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 either the object
|
| - or the attribute 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 either the 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 = {
|
| - 'attr': do_attr,
|
| - 'replace': do_replace,
|
| - 'upper': do_upper,
|
| - 'lower': do_lower,
|
| - 'escape': escape,
|
| - 'e': escape,
|
| - 'forceescape': do_forceescape,
|
| - 'capitalize': do_capitalize,
|
| - 'title': do_title,
|
| - 'default': do_default,
|
| - 'd': do_default,
|
| - 'join': do_join,
|
| - 'count': len,
|
| - 'dictsort': do_dictsort,
|
| - 'sort': do_sort,
|
| - 'length': len,
|
| - 'reverse': do_reverse,
|
| - 'center': do_center,
|
| - 'indent': do_indent,
|
| - 'title': do_title,
|
| - 'capitalize': do_capitalize,
|
| - 'first': do_first,
|
| - 'last': do_last,
|
| - 'map': do_map,
|
| - 'random': do_random,
|
| - 'reject': do_reject,
|
| - 'rejectattr': do_rejectattr,
|
| - 'filesizeformat': do_filesizeformat,
|
| - 'pprint': do_pprint,
|
| - 'truncate': do_truncate,
|
| - 'wordwrap': do_wordwrap,
|
| - 'wordcount': do_wordcount,
|
| - 'int': do_int,
|
| - 'float': do_float,
|
| - 'string': soft_unicode,
|
| - 'list': do_list,
|
| - 'urlize': do_urlize,
|
| - 'format': do_format,
|
| - 'trim': do_trim,
|
| - 'striptags': do_striptags,
|
| - 'select': do_select,
|
| - 'selectattr': do_selectattr,
|
| - 'slice': do_slice,
|
| - 'batch': do_batch,
|
| - 'sum': do_sum,
|
| - 'abs': abs,
|
| - 'round': do_round,
|
| - 'groupby': do_groupby,
|
| - 'safe': do_mark_safe,
|
| - 'xmlattr': do_xmlattr,
|
| - 'urlencode': do_urlencode
|
| -}
|
|
|