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