Home | History | Annotate | Download | only in jinja2
      1 # -*- coding: utf-8 -*-
      2 """
      3     jinja2.filters
      4     ~~~~~~~~~~~~~~
      5 
      6     Bundled jinja filters.
      7 
      8     :copyright: (c) 2010 by the Jinja Team.
      9     :license: BSD, see LICENSE for more details.
     10 """
     11 import re
     12 import math
     13 
     14 from random import choice
     15 from operator import itemgetter
     16 from itertools import groupby
     17 from jinja2.utils import Markup, escape, pformat, urlize, soft_unicode, \
     18      unicode_urlencode
     19 from jinja2.runtime import Undefined
     20 from jinja2.exceptions import FilterArgumentError
     21 from jinja2._compat import next, imap, string_types, text_type, iteritems
     22 
     23 
     24 _word_re = re.compile(r'\w+(?u)')
     25 
     26 
     27 def contextfilter(f):
     28     """Decorator for marking context dependent filters. The current
     29     :class:`Context` will be passed as first argument.
     30     """
     31     f.contextfilter = True
     32     return f
     33 
     34 
     35 def evalcontextfilter(f):
     36     """Decorator for marking eval-context dependent filters.  An eval
     37     context object is passed as first argument.  For more information
     38     about the eval context, see :ref:`eval-context`.
     39 
     40     .. versionadded:: 2.4
     41     """
     42     f.evalcontextfilter = True
     43     return f
     44 
     45 
     46 def environmentfilter(f):
     47     """Decorator for marking evironment dependent filters.  The current
     48     :class:`Environment` is passed to the filter as first argument.
     49     """
     50     f.environmentfilter = True
     51     return f
     52 
     53 
     54 def make_attrgetter(environment, attribute):
     55     """Returns a callable that looks up the given attribute from a
     56     passed object with the rules of the environment.  Dots are allowed
     57     to access attributes of attributes.  Integer parts in paths are
     58     looked up as integers.
     59     """
     60     if not isinstance(attribute, string_types) \
     61        or ('.' not in attribute and not attribute.isdigit()):
     62         return lambda x: environment.getitem(x, attribute)
     63     attribute = attribute.split('.')
     64     def attrgetter(item):
     65         for part in attribute:
     66             if part.isdigit():
     67                 part = int(part)
     68             item = environment.getitem(item, part)
     69         return item
     70     return attrgetter
     71 
     72 
     73 def do_forceescape(value):
     74     """Enforce HTML escaping.  This will probably double escape variables."""
     75     if hasattr(value, '__html__'):
     76         value = value.__html__()
     77     return escape(text_type(value))
     78 
     79 
     80 def do_urlencode(value):
     81     """Escape strings for use in URLs (uses UTF-8 encoding).  It accepts both
     82     dictionaries and regular strings as well as pairwise iterables.
     83 
     84     .. versionadded:: 2.7
     85     """
     86     itemiter = None
     87     if isinstance(value, dict):
     88         itemiter = iteritems(value)
     89     elif not isinstance(value, string_types):
     90         try:
     91             itemiter = iter(value)
     92         except TypeError:
     93             pass
     94     if itemiter is None:
     95         return unicode_urlencode(value)
     96     return u'&'.join(unicode_urlencode(k) + '=' +
     97                      unicode_urlencode(v) for k, v in itemiter)
     98 
     99 
    100 @evalcontextfilter
    101 def do_replace(eval_ctx, s, old, new, count=None):
    102     """Return a copy of the value with all occurrences of a substring
    103     replaced with a new one. The first argument is the substring
    104     that should be replaced, the second is the replacement string.
    105     If the optional third argument ``count`` is given, only the first
    106     ``count`` occurrences are replaced:
    107 
    108     .. sourcecode:: jinja
    109 
    110         {{ "Hello World"|replace("Hello", "Goodbye") }}
    111             -> Goodbye World
    112 
    113         {{ "aaaaargh"|replace("a", "d'oh, ", 2) }}
    114             -> d'oh, d'oh, aaargh
    115     """
    116     if count is None:
    117         count = -1
    118     if not eval_ctx.autoescape:
    119         return text_type(s).replace(text_type(old), text_type(new), count)
    120     if hasattr(old, '__html__') or hasattr(new, '__html__') and \
    121        not hasattr(s, '__html__'):
    122         s = escape(s)
    123     else:
    124         s = soft_unicode(s)
    125     return s.replace(soft_unicode(old), soft_unicode(new), count)
    126 
    127 
    128 def do_upper(s):
    129     """Convert a value to uppercase."""
    130     return soft_unicode(s).upper()
    131 
    132 
    133 def do_lower(s):
    134     """Convert a value to lowercase."""
    135     return soft_unicode(s).lower()
    136 
    137 
    138 @evalcontextfilter
    139 def do_xmlattr(_eval_ctx, d, autospace=True):
    140     """Create an SGML/XML attribute string based on the items in a dict.
    141     All values that are neither `none` nor `undefined` are automatically
    142     escaped:
    143 
    144     .. sourcecode:: html+jinja
    145 
    146         <ul{{ {'class': 'my_list', 'missing': none,
    147                 'id': 'list-%d'|format(variable)}|xmlattr }}>
    148         ...
    149         </ul>
    150 
    151     Results in something like this:
    152 
    153     .. sourcecode:: html
    154 
    155         <ul class="my_list" id="list-42">
    156         ...
    157         </ul>
    158 
    159     As you can see it automatically prepends a space in front of the item
    160     if the filter returned something unless the second parameter is false.
    161     """
    162     rv = u' '.join(
    163         u'%s="%s"' % (escape(key), escape(value))
    164         for key, value in iteritems(d)
    165         if value is not None and not isinstance(value, Undefined)
    166     )
    167     if autospace and rv:
    168         rv = u' ' + rv
    169     if _eval_ctx.autoescape:
    170         rv = Markup(rv)
    171     return rv
    172 
    173 
    174 def do_capitalize(s):
    175     """Capitalize a value. The first character will be uppercase, all others
    176     lowercase.
    177     """
    178     return soft_unicode(s).capitalize()
    179 
    180 
    181 def do_title(s):
    182     """Return a titlecased version of the value. I.e. words will start with
    183     uppercase letters, all remaining characters are lowercase.
    184     """
    185     rv = []
    186     for item in re.compile(r'([-\s]+)(?u)').split(s):
    187         if not item:
    188             continue
    189         rv.append(item[0].upper() + item[1:].lower())
    190     return ''.join(rv)
    191 
    192 
    193 def do_dictsort(value, case_sensitive=False, by='key'):
    194     """Sort a dict and yield (key, value) pairs. Because python dicts are
    195     unsorted you may want to use this function to order them by either
    196     key or value:
    197 
    198     .. sourcecode:: jinja
    199 
    200         {% for item in mydict|dictsort %}
    201             sort the dict by key, case insensitive
    202 
    203         {% for item in mydict|dictsort(true) %}
    204             sort the dict by key, case sensitive
    205 
    206         {% for item in mydict|dictsort(false, 'value') %}
    207             sort the dict by key, case insensitive, sorted
    208             normally and ordered by value.
    209     """
    210     if by == 'key':
    211         pos = 0
    212     elif by == 'value':
    213         pos = 1
    214     else:
    215         raise FilterArgumentError('You can only sort by either '
    216                                   '"key" or "value"')
    217     def sort_func(item):
    218         value = item[pos]
    219         if isinstance(value, string_types) and not case_sensitive:
    220             value = value.lower()
    221         return value
    222 
    223     return sorted(value.items(), key=sort_func)
    224 
    225 
    226 @environmentfilter
    227 def do_sort(environment, value, reverse=False, case_sensitive=False,
    228             attribute=None):
    229     """Sort an iterable.  Per default it sorts ascending, if you pass it
    230     true as first argument it will reverse the sorting.
    231 
    232     If the iterable is made of strings the third parameter can be used to
    233     control the case sensitiveness of the comparison which is disabled by
    234     default.
    235 
    236     .. sourcecode:: jinja
    237 
    238         {% for item in iterable|sort %}
    239             ...
    240         {% endfor %}
    241 
    242     It is also possible to sort by an attribute (for example to sort
    243     by the date of an object) by specifying the `attribute` parameter:
    244 
    245     .. sourcecode:: jinja
    246 
    247         {% for item in iterable|sort(attribute='date') %}
    248             ...
    249         {% endfor %}
    250 
    251     .. versionchanged:: 2.6
    252        The `attribute` parameter was added.
    253     """
    254     if not case_sensitive:
    255         def sort_func(item):
    256             if isinstance(item, string_types):
    257                 item = item.lower()
    258             return item
    259     else:
    260         sort_func = None
    261     if attribute is not None:
    262         getter = make_attrgetter(environment, attribute)
    263         def sort_func(item, processor=sort_func or (lambda x: x)):
    264             return processor(getter(item))
    265     return sorted(value, key=sort_func, reverse=reverse)
    266 
    267 
    268 def do_default(value, default_value=u'', boolean=False):
    269     """If the value is undefined it will return the passed default value,
    270     otherwise the value of the variable:
    271 
    272     .. sourcecode:: jinja
    273 
    274         {{ my_variable|default('my_variable is not defined') }}
    275 
    276     This will output the value of ``my_variable`` if the variable was
    277     defined, otherwise ``'my_variable is not defined'``. If you want
    278     to use default with variables that evaluate to false you have to
    279     set the second parameter to `true`:
    280 
    281     .. sourcecode:: jinja
    282 
    283         {{ ''|default('the string was empty', true) }}
    284     """
    285     if isinstance(value, Undefined) or (boolean and not value):
    286         return default_value
    287     return value
    288 
    289 
    290 @evalcontextfilter
    291 def do_join(eval_ctx, value, d=u'', attribute=None):
    292     """Return a string which is the concatenation of the strings in the
    293     sequence. The separator between elements is an empty string per
    294     default, you can define it with the optional parameter:
    295 
    296     .. sourcecode:: jinja
    297 
    298         {{ [1, 2, 3]|join('|') }}
    299             -> 1|2|3
    300 
    301         {{ [1, 2, 3]|join }}
    302             -> 123
    303 
    304     It is also possible to join certain attributes of an object:
    305 
    306     .. sourcecode:: jinja
    307 
    308         {{ users|join(', ', attribute='username') }}
    309 
    310     .. versionadded:: 2.6
    311        The `attribute` parameter was added.
    312     """
    313     if attribute is not None:
    314         value = imap(make_attrgetter(eval_ctx.environment, attribute), value)
    315 
    316     # no automatic escaping?  joining is a lot eaiser then
    317     if not eval_ctx.autoescape:
    318         return text_type(d).join(imap(text_type, value))
    319 
    320     # if the delimiter doesn't have an html representation we check
    321     # if any of the items has.  If yes we do a coercion to Markup
    322     if not hasattr(d, '__html__'):
    323         value = list(value)
    324         do_escape = False
    325         for idx, item in enumerate(value):
    326             if hasattr(item, '__html__'):
    327                 do_escape = True
    328             else:
    329                 value[idx] = text_type(item)
    330         if do_escape:
    331             d = escape(d)
    332         else:
    333             d = text_type(d)
    334         return d.join(value)
    335 
    336     # no html involved, to normal joining
    337     return soft_unicode(d).join(imap(soft_unicode, value))
    338 
    339 
    340 def do_center(value, width=80):
    341     """Centers the value in a field of a given width."""
    342     return text_type(value).center(width)
    343 
    344 
    345 @environmentfilter
    346 def do_first(environment, seq):
    347     """Return the first item of a sequence."""
    348     try:
    349         return next(iter(seq))
    350     except StopIteration:
    351         return environment.undefined('No first item, sequence was empty.')
    352 
    353 
    354 @environmentfilter
    355 def do_last(environment, seq):
    356     """Return the last item of a sequence."""
    357     try:
    358         return next(iter(reversed(seq)))
    359     except StopIteration:
    360         return environment.undefined('No last item, sequence was empty.')
    361 
    362 
    363 @environmentfilter
    364 def do_random(environment, seq):
    365     """Return a random item from the sequence."""
    366     try:
    367         return choice(seq)
    368     except IndexError:
    369         return environment.undefined('No random item, sequence was empty.')
    370 
    371 
    372 def do_filesizeformat(value, binary=False):
    373     """Format the value like a 'human-readable' file size (i.e. 13 kB,
    374     4.1 MB, 102 Bytes, etc).  Per default decimal prefixes are used (Mega,
    375     Giga, etc.), if the second parameter is set to `True` the binary
    376     prefixes are used (Mebi, Gibi).
    377     """
    378     bytes = float(value)
    379     base = binary and 1024 or 1000
    380     prefixes = [
    381         (binary and 'KiB' or 'kB'),
    382         (binary and 'MiB' or 'MB'),
    383         (binary and 'GiB' or 'GB'),
    384         (binary and 'TiB' or 'TB'),
    385         (binary and 'PiB' or 'PB'),
    386         (binary and 'EiB' or 'EB'),
    387         (binary and 'ZiB' or 'ZB'),
    388         (binary and 'YiB' or 'YB')
    389     ]
    390     if bytes == 1:
    391         return '1 Byte'
    392     elif bytes < base:
    393         return '%d Bytes' % bytes
    394     else:
    395         for i, prefix in enumerate(prefixes):
    396             unit = base ** (i + 2)
    397             if bytes < unit:
    398                 return '%.1f %s' % ((base * bytes / unit), prefix)
    399         return '%.1f %s' % ((base * bytes / unit), prefix)
    400 
    401 
    402 def do_pprint(value, verbose=False):
    403     """Pretty print a variable. Useful for debugging.
    404 
    405     With Jinja 1.2 onwards you can pass it a parameter.  If this parameter
    406     is truthy the output will be more verbose (this requires `pretty`)
    407     """
    408     return pformat(value, verbose=verbose)
    409 
    410 
    411 @evalcontextfilter
    412 def do_urlize(eval_ctx, value, trim_url_limit=None, nofollow=False):
    413     """Converts URLs in plain text into clickable links.
    414 
    415     If you pass the filter an additional integer it will shorten the urls
    416     to that number. Also a third argument exists that makes the urls
    417     "nofollow":
    418 
    419     .. sourcecode:: jinja
    420 
    421         {{ mytext|urlize(40, true) }}
    422             links are shortened to 40 chars and defined with rel="nofollow"
    423     """
    424     rv = urlize(value, trim_url_limit, nofollow)
    425     if eval_ctx.autoescape:
    426         rv = Markup(rv)
    427     return rv
    428 
    429 
    430 def do_indent(s, width=4, indentfirst=False):
    431     """Return a copy of the passed string, each line indented by
    432     4 spaces. The first line is not indented. If you want to
    433     change the number of spaces or indent the first line too
    434     you can pass additional parameters to the filter:
    435 
    436     .. sourcecode:: jinja
    437 
    438         {{ mytext|indent(2, true) }}
    439             indent by two spaces and indent the first line too.
    440     """
    441     indention = u' ' * width
    442     rv = (u'\n' + indention).join(s.splitlines())
    443     if indentfirst:
    444         rv = indention + rv
    445     return rv
    446 
    447 
    448 def do_truncate(s, length=255, killwords=False, end='...'):
    449     """Return a truncated copy of the string. The length is specified
    450     with the first parameter which defaults to ``255``. If the second
    451     parameter is ``true`` the filter will cut the text at length. Otherwise
    452     it will discard the last word. If the text was in fact
    453     truncated it will append an ellipsis sign (``"..."``). If you want a
    454     different ellipsis sign than ``"..."`` you can specify it using the
    455     third parameter.
    456 
    457     .. sourcecode:: jinja
    458 
    459         {{ "foo bar"|truncate(5) }}
    460             -> "foo ..."
    461         {{ "foo bar"|truncate(5, True) }}
    462             -> "foo b..."
    463     """
    464     if len(s) <= length:
    465         return s
    466     elif killwords:
    467         return s[:length] + end
    468     words = s.split(' ')
    469     result = []
    470     m = 0
    471     for word in words:
    472         m += len(word) + 1
    473         if m > length:
    474             break
    475         result.append(word)
    476     result.append(end)
    477     return u' '.join(result)
    478 
    479 @environmentfilter
    480 def do_wordwrap(environment, s, width=79, break_long_words=True,
    481                 wrapstring=None):
    482     """
    483     Return a copy of the string passed to the filter wrapped after
    484     ``79`` characters.  You can override this default using the first
    485     parameter.  If you set the second parameter to `false` Jinja will not
    486     split words apart if they are longer than `width`. By default, the newlines
    487     will be the default newlines for the environment, but this can be changed
    488     using the wrapstring keyword argument.
    489 
    490     .. versionadded:: 2.7
    491        Added support for the `wrapstring` parameter.
    492     """
    493     if not wrapstring:
    494         wrapstring = environment.newline_sequence
    495     import textwrap
    496     return wrapstring.join(textwrap.wrap(s, width=width, expand_tabs=False,
    497                                    replace_whitespace=False,
    498                                    break_long_words=break_long_words))
    499 
    500 
    501 def do_wordcount(s):
    502     """Count the words in that string."""
    503     return len(_word_re.findall(s))
    504 
    505 
    506 def do_int(value, default=0):
    507     """Convert the value into an integer. If the
    508     conversion doesn't work it will return ``0``. You can
    509     override this default using the first parameter.
    510     """
    511     try:
    512         return int(value)
    513     except (TypeError, ValueError):
    514         # this quirk is necessary so that "42.23"|int gives 42.
    515         try:
    516             return int(float(value))
    517         except (TypeError, ValueError):
    518             return default
    519 
    520 
    521 def do_float(value, default=0.0):
    522     """Convert the value into a floating point number. If the
    523     conversion doesn't work it will return ``0.0``. You can
    524     override this default using the first parameter.
    525     """
    526     try:
    527         return float(value)
    528     except (TypeError, ValueError):
    529         return default
    530 
    531 
    532 def do_format(value, *args, **kwargs):
    533     """
    534     Apply python string formatting on an object:
    535 
    536     .. sourcecode:: jinja
    537 
    538         {{ "%s - %s"|format("Hello?", "Foo!") }}
    539             -> Hello? - Foo!
    540     """
    541     if args and kwargs:
    542         raise FilterArgumentError('can\'t handle positional and keyword '
    543                                   'arguments at the same time')
    544     return soft_unicode(value) % (kwargs or args)
    545 
    546 
    547 def do_trim(value):
    548     """Strip leading and trailing whitespace."""
    549     return soft_unicode(value).strip()
    550 
    551 
    552 def do_striptags(value):
    553     """Strip SGML/XML tags and replace adjacent whitespace by one space.
    554     """
    555     if hasattr(value, '__html__'):
    556         value = value.__html__()
    557     return Markup(text_type(value)).striptags()
    558 
    559 
    560 def do_slice(value, slices, fill_with=None):
    561     """Slice an iterator and return a list of lists containing
    562     those items. Useful if you want to create a div containing
    563     three ul tags that represent columns:
    564 
    565     .. sourcecode:: html+jinja
    566 
    567         <div class="columwrapper">
    568           {%- for column in items|slice(3) %}
    569             <ul class="column-{{ loop.index }}">
    570             {%- for item in column %}
    571               <li>{{ item }}</li>
    572             {%- endfor %}
    573             </ul>
    574           {%- endfor %}
    575         </div>
    576 
    577     If you pass it a second argument it's used to fill missing
    578     values on the last iteration.
    579     """
    580     seq = list(value)
    581     length = len(seq)
    582     items_per_slice = length // slices
    583     slices_with_extra = length % slices
    584     offset = 0
    585     for slice_number in range(slices):
    586         start = offset + slice_number * items_per_slice
    587         if slice_number < slices_with_extra:
    588             offset += 1
    589         end = offset + (slice_number + 1) * items_per_slice
    590         tmp = seq[start:end]
    591         if fill_with is not None and slice_number >= slices_with_extra:
    592             tmp.append(fill_with)
    593         yield tmp
    594 
    595 
    596 def do_batch(value, linecount, fill_with=None):
    597     """
    598     A filter that batches items. It works pretty much like `slice`
    599     just the other way round. It returns a list of lists with the
    600     given number of items. If you provide a second parameter this
    601     is used to fill up missing items. See this example:
    602 
    603     .. sourcecode:: html+jinja
    604 
    605         <table>
    606         {%- for row in items|batch(3, '&nbsp;') %}
    607           <tr>
    608           {%- for column in row %}
    609             <td>{{ column }}</td>
    610           {%- endfor %}
    611           </tr>
    612         {%- endfor %}
    613         </table>
    614     """
    615     result = []
    616     tmp = []
    617     for item in value:
    618         if len(tmp) == linecount:
    619             yield tmp
    620             tmp = []
    621         tmp.append(item)
    622     if tmp:
    623         if fill_with is not None and len(tmp) < linecount:
    624             tmp += [fill_with] * (linecount - len(tmp))
    625         yield tmp
    626 
    627 
    628 def do_round(value, precision=0, method='common'):
    629     """Round the number to a given precision. The first
    630     parameter specifies the precision (default is ``0``), the
    631     second the rounding method:
    632 
    633     - ``'common'`` rounds either up or down
    634     - ``'ceil'`` always rounds up
    635     - ``'floor'`` always rounds down
    636 
    637     If you don't specify a method ``'common'`` is used.
    638 
    639     .. sourcecode:: jinja
    640 
    641         {{ 42.55|round }}
    642             -> 43.0
    643         {{ 42.55|round(1, 'floor') }}
    644             -> 42.5
    645 
    646     Note that even if rounded to 0 precision, a float is returned.  If
    647     you need a real integer, pipe it through `int`:
    648 
    649     .. sourcecode:: jinja
    650 
    651         {{ 42.55|round|int }}
    652             -> 43
    653     """
    654     if not method in ('common', 'ceil', 'floor'):
    655         raise FilterArgumentError('method must be common, ceil or floor')
    656     if method == 'common':
    657         return round(value, precision)
    658     func = getattr(math, method)
    659     return func(value * (10 ** precision)) / (10 ** precision)
    660 
    661 
    662 @environmentfilter
    663 def do_groupby(environment, value, attribute):
    664     """Group a sequence of objects by a common attribute.
    665 
    666     If you for example have a list of dicts or objects that represent persons
    667     with `gender`, `first_name` and `last_name` attributes and you want to
    668     group all users by genders you can do something like the following
    669     snippet:
    670 
    671     .. sourcecode:: html+jinja
    672 
    673         <ul>
    674         {% for group in persons|groupby('gender') %}
    675             <li>{{ group.grouper }}<ul>
    676             {% for person in group.list %}
    677                 <li>{{ person.first_name }} {{ person.last_name }}</li>
    678             {% endfor %}</ul></li>
    679         {% endfor %}
    680         </ul>
    681 
    682     Additionally it's possible to use tuple unpacking for the grouper and
    683     list:
    684 
    685     .. sourcecode:: html+jinja
    686 
    687         <ul>
    688         {% for grouper, list in persons|groupby('gender') %}
    689             ...
    690         {% endfor %}
    691         </ul>
    692 
    693     As you can see the item we're grouping by is stored in the `grouper`
    694     attribute and the `list` contains all the objects that have this grouper
    695     in common.
    696 
    697     .. versionchanged:: 2.6
    698        It's now possible to use dotted notation to group by the child
    699        attribute of another attribute.
    700     """
    701     expr = make_attrgetter(environment, attribute)
    702     return sorted(map(_GroupTuple, groupby(sorted(value, key=expr), expr)))
    703 
    704 
    705 class _GroupTuple(tuple):
    706     __slots__ = ()
    707     grouper = property(itemgetter(0))
    708     list = property(itemgetter(1))
    709 
    710     def __new__(cls, xxx_todo_changeme):
    711         (key, value) = xxx_todo_changeme
    712         return tuple.__new__(cls, (key, list(value)))
    713 
    714 
    715 @environmentfilter
    716 def do_sum(environment, iterable, attribute=None, start=0):
    717     """Returns the sum of a sequence of numbers plus the value of parameter
    718     'start' (which defaults to 0).  When the sequence is empty it returns
    719     start.
    720 
    721     It is also possible to sum up only certain attributes:
    722 
    723     .. sourcecode:: jinja
    724 
    725         Total: {{ items|sum(attribute='price') }}
    726 
    727     .. versionchanged:: 2.6
    728        The `attribute` parameter was added to allow suming up over
    729        attributes.  Also the `start` parameter was moved on to the right.
    730     """
    731     if attribute is not None:
    732         iterable = imap(make_attrgetter(environment, attribute), iterable)
    733     return sum(iterable, start)
    734 
    735 
    736 def do_list(value):
    737     """Convert the value into a list.  If it was a string the returned list
    738     will be a list of characters.
    739     """
    740     return list(value)
    741 
    742 
    743 def do_mark_safe(value):
    744     """Mark the value as safe which means that in an environment with automatic
    745     escaping enabled this variable will not be escaped.
    746     """
    747     return Markup(value)
    748 
    749 
    750 def do_mark_unsafe(value):
    751     """Mark a value as unsafe.  This is the reverse operation for :func:`safe`."""
    752     return text_type(value)
    753 
    754 
    755 def do_reverse(value):
    756     """Reverse the object or return an iterator the iterates over it the other
    757     way round.
    758     """
    759     if isinstance(value, string_types):
    760         return value[::-1]
    761     try:
    762         return reversed(value)
    763     except TypeError:
    764         try:
    765             rv = list(value)
    766             rv.reverse()
    767             return rv
    768         except TypeError:
    769             raise FilterArgumentError('argument must be iterable')
    770 
    771 
    772 @environmentfilter
    773 def do_attr(environment, obj, name):
    774     """Get an attribute of an object.  ``foo|attr("bar")`` works like
    775     ``foo["bar"]`` just that always an attribute is returned and items are not
    776     looked up.
    777 
    778     See :ref:`Notes on subscriptions <notes-on-subscriptions>` for more details.
    779     """
    780     try:
    781         name = str(name)
    782     except UnicodeError:
    783         pass
    784     else:
    785         try:
    786             value = getattr(obj, name)
    787         except AttributeError:
    788             pass
    789         else:
    790             if environment.sandboxed and not \
    791                environment.is_safe_attribute(obj, name, value):
    792                 return environment.unsafe_undefined(obj, name)
    793             return value
    794     return environment.undefined(obj=obj, name=name)
    795 
    796 
    797 @contextfilter
    798 def do_map(*args, **kwargs):
    799     """Applies a filter on a sequence of objects or looks up an attribute.
    800     This is useful when dealing with lists of objects but you are really
    801     only interested in a certain value of it.
    802 
    803     The basic usage is mapping on an attribute.  Imagine you have a list
    804     of users but you are only interested in a list of usernames:
    805 
    806     .. sourcecode:: jinja
    807 
    808         Users on this page: {{ users|map(attribute='username')|join(', ') }}
    809 
    810     Alternatively you can let it invoke a filter by passing the name of the
    811     filter and the arguments afterwards.  A good example would be applying a
    812     text conversion filter on a sequence:
    813 
    814     .. sourcecode:: jinja
    815 
    816         Users on this page: {{ titles|map('lower')|join(', ') }}
    817 
    818     .. versionadded:: 2.7
    819     """
    820     context = args[0]
    821     seq = args[1]
    822 
    823     if len(args) == 2 and 'attribute' in kwargs:
    824         attribute = kwargs.pop('attribute')
    825         if kwargs:
    826             raise FilterArgumentError('Unexpected keyword argument %r' %
    827                 next(iter(kwargs)))
    828         func = make_attrgetter(context.environment, attribute)
    829     else:
    830         try:
    831             name = args[2]
    832             args = args[3:]
    833         except LookupError:
    834             raise FilterArgumentError('map requires a filter argument')
    835         func = lambda item: context.environment.call_filter(
    836             name, item, args, kwargs, context=context)
    837 
    838     if seq:
    839         for item in seq:
    840             yield func(item)
    841 
    842 
    843 @contextfilter
    844 def do_select(*args, **kwargs):
    845     """Filters a sequence of objects by appying a test to either the object
    846     or the attribute and only selecting the ones with the test succeeding.
    847 
    848     Example usage:
    849 
    850     .. sourcecode:: jinja
    851 
    852         {{ numbers|select("odd") }}
    853 
    854     .. versionadded:: 2.7
    855     """
    856     return _select_or_reject(args, kwargs, lambda x: x, False)
    857 
    858 
    859 @contextfilter
    860 def do_reject(*args, **kwargs):
    861     """Filters a sequence of objects by appying a test to either the object
    862     or the attribute and rejecting the ones with the test succeeding.
    863 
    864     Example usage:
    865 
    866     .. sourcecode:: jinja
    867 
    868         {{ numbers|reject("odd") }}
    869 
    870     .. versionadded:: 2.7
    871     """
    872     return _select_or_reject(args, kwargs, lambda x: not x, False)
    873 
    874 
    875 @contextfilter
    876 def do_selectattr(*args, **kwargs):
    877     """Filters a sequence of objects by appying a test to either the object
    878     or the attribute and only selecting the ones with the test succeeding.
    879 
    880     Example usage:
    881 
    882     .. sourcecode:: jinja
    883 
    884         {{ users|selectattr("is_active") }}
    885         {{ users|selectattr("email", "none") }}
    886 
    887     .. versionadded:: 2.7
    888     """
    889     return _select_or_reject(args, kwargs, lambda x: x, True)
    890 
    891 
    892 @contextfilter
    893 def do_rejectattr(*args, **kwargs):
    894     """Filters a sequence of objects by appying a test to either the object
    895     or the attribute and rejecting the ones with the test succeeding.
    896 
    897     .. sourcecode:: jinja
    898 
    899         {{ users|rejectattr("is_active") }}
    900         {{ users|rejectattr("email", "none") }}
    901 
    902     .. versionadded:: 2.7
    903     """
    904     return _select_or_reject(args, kwargs, lambda x: not x, True)
    905 
    906 
    907 def _select_or_reject(args, kwargs, modfunc, lookup_attr):
    908     context = args[0]
    909     seq = args[1]
    910     if lookup_attr:
    911         try:
    912             attr = args[2]
    913         except LookupError:
    914             raise FilterArgumentError('Missing parameter for attribute name')
    915         transfunc = make_attrgetter(context.environment, attr)
    916         off = 1
    917     else:
    918         off = 0
    919         transfunc = lambda x: x
    920 
    921     try:
    922         name = args[2 + off]
    923         args = args[3 + off:]
    924         func = lambda item: context.environment.call_test(
    925             name, item, args, kwargs)
    926     except LookupError:
    927         func = bool
    928 
    929     if seq:
    930         for item in seq:
    931             if modfunc(func(transfunc(item))):
    932                 yield item
    933 
    934 
    935 FILTERS = {
    936     'attr':                 do_attr,
    937     'replace':              do_replace,
    938     'upper':                do_upper,
    939     'lower':                do_lower,
    940     'escape':               escape,
    941     'e':                    escape,
    942     'forceescape':          do_forceescape,
    943     'capitalize':           do_capitalize,
    944     'title':                do_title,
    945     'default':              do_default,
    946     'd':                    do_default,
    947     'join':                 do_join,
    948     'count':                len,
    949     'dictsort':             do_dictsort,
    950     'sort':                 do_sort,
    951     'length':               len,
    952     'reverse':              do_reverse,
    953     'center':               do_center,
    954     'indent':               do_indent,
    955     'title':                do_title,
    956     'capitalize':           do_capitalize,
    957     'first':                do_first,
    958     'last':                 do_last,
    959     'map':                  do_map,
    960     'random':               do_random,
    961     'reject':               do_reject,
    962     'rejectattr':           do_rejectattr,
    963     'filesizeformat':       do_filesizeformat,
    964     'pprint':               do_pprint,
    965     'truncate':             do_truncate,
    966     'wordwrap':             do_wordwrap,
    967     'wordcount':            do_wordcount,
    968     'int':                  do_int,
    969     'float':                do_float,
    970     'string':               soft_unicode,
    971     'list':                 do_list,
    972     'urlize':               do_urlize,
    973     'format':               do_format,
    974     'trim':                 do_trim,
    975     'striptags':            do_striptags,
    976     'select':               do_select,
    977     'selectattr':           do_selectattr,
    978     'slice':                do_slice,
    979     'batch':                do_batch,
    980     'sum':                  do_sum,
    981     'abs':                  abs,
    982     'round':                do_round,
    983     'groupby':              do_groupby,
    984     'safe':                 do_mark_safe,
    985     'xmlattr':              do_xmlattr,
    986     'urlencode':            do_urlencode
    987 }
    988