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"""
11import re
12import math
13from random import choice
14from operator import itemgetter
15from itertools import imap, groupby
16from jinja2.utils import Markup, escape, pformat, urlize, soft_unicode
17from jinja2.runtime import Undefined
18from jinja2.exceptions import FilterArgumentError, SecurityError
19
20
21_word_re = re.compile(r'\w+(?u)')
22
23
24def contextfilter(f):
25    """Decorator for marking context dependent filters. The current
26    :class:`Context` will be passed as first argument.
27    """
28    f.contextfilter = True
29    return f
30
31
32def evalcontextfilter(f):
33    """Decorator for marking eval-context dependent filters.  An eval
34    context object is passed as first argument.  For more information
35    about the eval context, see :ref:`eval-context`.
36
37    .. versionadded:: 2.4
38    """
39    f.evalcontextfilter = True
40    return f
41
42
43def environmentfilter(f):
44    """Decorator for marking evironment dependent filters.  The current
45    :class:`Environment` is passed to the filter as first argument.
46    """
47    f.environmentfilter = True
48    return f
49
50
51def do_forceescape(value):
52    """Enforce HTML escaping.  This will probably double escape variables."""
53    if hasattr(value, '__html__'):
54        value = value.__html__()
55    return escape(unicode(value))
56
57
58@evalcontextfilter
59def do_replace(eval_ctx, s, old, new, count=None):
60    """Return a copy of the value with all occurrences of a substring
61    replaced with a new one. The first argument is the substring
62    that should be replaced, the second is the replacement string.
63    If the optional third argument ``count`` is given, only the first
64    ``count`` occurrences are replaced:
65
66    .. sourcecode:: jinja
67
68        {{ "Hello World"|replace("Hello", "Goodbye") }}
69            -> Goodbye World
70
71        {{ "aaaaargh"|replace("a", "d'oh, ", 2) }}
72            -> d'oh, d'oh, aaargh
73    """
74    if count is None:
75        count = -1
76    if not eval_ctx.autoescape:
77        return unicode(s).replace(unicode(old), unicode(new), count)
78    if hasattr(old, '__html__') or hasattr(new, '__html__') and \
79       not hasattr(s, '__html__'):
80        s = escape(s)
81    else:
82        s = soft_unicode(s)
83    return s.replace(soft_unicode(old), soft_unicode(new), count)
84
85
86def do_upper(s):
87    """Convert a value to uppercase."""
88    return soft_unicode(s).upper()
89
90
91def do_lower(s):
92    """Convert a value to lowercase."""
93    return soft_unicode(s).lower()
94
95
96@evalcontextfilter
97def do_xmlattr(_eval_ctx, d, autospace=True):
98    """Create an SGML/XML attribute string based on the items in a dict.
99    All values that are neither `none` nor `undefined` are automatically
100    escaped:
101
102    .. sourcecode:: html+jinja
103
104        <ul{{ {'class': 'my_list', 'missing': none,
105                'id': 'list-%d'|format(variable)}|xmlattr }}>
106        ...
107        </ul>
108
109    Results in something like this:
110
111    .. sourcecode:: html
112
113        <ul class="my_list" id="list-42">
114        ...
115        </ul>
116
117    As you can see it automatically prepends a space in front of the item
118    if the filter returned something unless the second parameter is false.
119    """
120    rv = u' '.join(
121        u'%s="%s"' % (escape(key), escape(value))
122        for key, value in d.iteritems()
123        if value is not None and not isinstance(value, Undefined)
124    )
125    if autospace and rv:
126        rv = u' ' + rv
127    if _eval_ctx.autoescape:
128        rv = Markup(rv)
129    return rv
130
131
132def do_capitalize(s):
133    """Capitalize a value. The first character will be uppercase, all others
134    lowercase.
135    """
136    return soft_unicode(s).capitalize()
137
138
139def do_title(s):
140    """Return a titlecased version of the value. I.e. words will start with
141    uppercase letters, all remaining characters are lowercase.
142    """
143    return soft_unicode(s).title()
144
145
146def do_dictsort(value, case_sensitive=False, by='key'):
147    """Sort a dict and yield (key, value) pairs. Because python dicts are
148    unsorted you may want to use this function to order them by either
149    key or value:
150
151    .. sourcecode:: jinja
152
153        {% for item in mydict|dictsort %}
154            sort the dict by key, case insensitive
155
156        {% for item in mydict|dicsort(true) %}
157            sort the dict by key, case sensitive
158
159        {% for item in mydict|dictsort(false, 'value') %}
160            sort the dict by key, case insensitive, sorted
161            normally and ordered by value.
162    """
163    if by == 'key':
164        pos = 0
165    elif by == 'value':
166        pos = 1
167    else:
168        raise FilterArgumentError('You can only sort by either '
169                                  '"key" or "value"')
170    def sort_func(item):
171        value = item[pos]
172        if isinstance(value, basestring) and not case_sensitive:
173            value = value.lower()
174        return value
175
176    return sorted(value.items(), key=sort_func)
177
178
179def do_sort(value, case_sensitive=False):
180    """Sort an iterable.  If the iterable is made of strings the second
181    parameter can be used to control the case sensitiveness of the
182    comparison which is disabled by default.
183
184    .. sourcecode:: jinja
185
186        {% for item in iterable|sort %}
187            ...
188        {% endfor %}
189    """
190    if not case_sensitive:
191        def sort_func(item):
192            if isinstance(item, basestring):
193                item = item.lower()
194            return item
195    else:
196        sort_func = None
197    return sorted(seq, key=sort_func)
198
199
200def do_default(value, default_value=u'', boolean=False):
201    """If the value is undefined it will return the passed default value,
202    otherwise the value of the variable:
203
204    .. sourcecode:: jinja
205
206        {{ my_variable|default('my_variable is not defined') }}
207
208    This will output the value of ``my_variable`` if the variable was
209    defined, otherwise ``'my_variable is not defined'``. If you want
210    to use default with variables that evaluate to false you have to
211    set the second parameter to `true`:
212
213    .. sourcecode:: jinja
214
215        {{ ''|default('the string was empty', true) }}
216    """
217    if (boolean and not value) or isinstance(value, Undefined):
218        return default_value
219    return value
220
221
222@evalcontextfilter
223def do_join(eval_ctx, value, d=u''):
224    """Return a string which is the concatenation of the strings in the
225    sequence. The separator between elements is an empty string per
226    default, you can define it with the optional parameter:
227
228    .. sourcecode:: jinja
229
230        {{ [1, 2, 3]|join('|') }}
231            -> 1|2|3
232
233        {{ [1, 2, 3]|join }}
234            -> 123
235    """
236    # no automatic escaping?  joining is a lot eaiser then
237    if not eval_ctx.autoescape:
238        return unicode(d).join(imap(unicode, value))
239
240    # if the delimiter doesn't have an html representation we check
241    # if any of the items has.  If yes we do a coercion to Markup
242    if not hasattr(d, '__html__'):
243        value = list(value)
244        do_escape = False
245        for idx, item in enumerate(value):
246            if hasattr(item, '__html__'):
247                do_escape = True
248            else:
249                value[idx] = unicode(item)
250        if do_escape:
251            d = escape(d)
252        else:
253            d = unicode(d)
254        return d.join(value)
255
256    # no html involved, to normal joining
257    return soft_unicode(d).join(imap(soft_unicode, value))
258
259
260def do_center(value, width=80):
261    """Centers the value in a field of a given width."""
262    return unicode(value).center(width)
263
264
265@environmentfilter
266def do_first(environment, seq):
267    """Return the first item of a sequence."""
268    try:
269        return iter(seq).next()
270    except StopIteration:
271        return environment.undefined('No first item, sequence was empty.')
272
273
274@environmentfilter
275def do_last(environment, seq):
276    """Return the last item of a sequence."""
277    try:
278        return iter(reversed(seq)).next()
279    except StopIteration:
280        return environment.undefined('No last item, sequence was empty.')
281
282
283@environmentfilter
284def do_random(environment, seq):
285    """Return a random item from the sequence."""
286    try:
287        return choice(seq)
288    except IndexError:
289        return environment.undefined('No random item, sequence was empty.')
290
291
292def do_filesizeformat(value, binary=False):
293    """Format the value like a 'human-readable' file size (i.e. 13 KB,
294    4.1 MB, 102 bytes, etc).  Per default decimal prefixes are used (mega,
295    giga, etc.), if the second parameter is set to `True` the binary
296    prefixes are used (mebi, gibi).
297    """
298    bytes = float(value)
299    base = binary and 1024 or 1000
300    middle = binary and 'i' or ''
301    if bytes < base:
302        return "%d Byte%s" % (bytes, bytes != 1 and 's' or '')
303    elif bytes < base * base:
304        return "%.1f K%sB" % (bytes / base, middle)
305    elif bytes < base * base * base:
306        return "%.1f M%sB" % (bytes / (base * base), middle)
307    return "%.1f G%sB" % (bytes / (base * base * base), middle)
308
309
310def do_pprint(value, verbose=False):
311    """Pretty print a variable. Useful for debugging.
312
313    With Jinja 1.2 onwards you can pass it a parameter.  If this parameter
314    is truthy the output will be more verbose (this requires `pretty`)
315    """
316    return pformat(value, verbose=verbose)
317
318
319@evalcontextfilter
320def do_urlize(eval_ctx, value, trim_url_limit=None, nofollow=False):
321    """Converts URLs in plain text into clickable links.
322
323    If you pass the filter an additional integer it will shorten the urls
324    to that number. Also a third argument exists that makes the urls
325    "nofollow":
326
327    .. sourcecode:: jinja
328
329        {{ mytext|urlize(40, true) }}
330            links are shortened to 40 chars and defined with rel="nofollow"
331    """
332    rv = urlize(value, trim_url_limit, nofollow)
333    if eval_ctx.autoescape:
334        rv = Markup(rv)
335    return rv
336
337
338def do_indent(s, width=4, indentfirst=False):
339    """Return a copy of the passed string, each line indented by
340    4 spaces. The first line is not indented. If you want to
341    change the number of spaces or indent the first line too
342    you can pass additional parameters to the filter:
343
344    .. sourcecode:: jinja
345
346        {{ mytext|indent(2, true) }}
347            indent by two spaces and indent the first line too.
348    """
349    indention = u' ' * width
350    rv = (u'\n' + indention).join(s.splitlines())
351    if indentfirst:
352        rv = indention + rv
353    return rv
354
355
356def do_truncate(s, length=255, killwords=False, end='...'):
357    """Return a truncated copy of the string. The length is specified
358    with the first parameter which defaults to ``255``. If the second
359    parameter is ``true`` the filter will cut the text at length. Otherwise
360    it will try to save the last word. If the text was in fact
361    truncated it will append an ellipsis sign (``"..."``). If you want a
362    different ellipsis sign than ``"..."`` you can specify it using the
363    third parameter.
364
365    .. sourcecode jinja::
366
367        {{ mytext|truncate(300, false, '&raquo;') }}
368            truncate mytext to 300 chars, don't split up words, use a
369            right pointing double arrow as ellipsis sign.
370    """
371    if len(s) <= length:
372        return s
373    elif killwords:
374        return s[:length] + end
375    words = s.split(' ')
376    result = []
377    m = 0
378    for word in words:
379        m += len(word) + 1
380        if m > length:
381            break
382        result.append(word)
383    result.append(end)
384    return u' '.join(result)
385
386
387def do_wordwrap(s, width=79, break_long_words=True):
388    """
389    Return a copy of the string passed to the filter wrapped after
390    ``79`` characters.  You can override this default using the first
391    parameter.  If you set the second parameter to `false` Jinja will not
392    split words apart if they are longer than `width`.
393    """
394    import textwrap
395    return u'\n'.join(textwrap.wrap(s, width=width, expand_tabs=False,
396                                   replace_whitespace=False,
397                                   break_long_words=break_long_words))
398
399
400def do_wordcount(s):
401    """Count the words in that string."""
402    return len(_word_re.findall(s))
403
404
405def do_int(value, default=0):
406    """Convert the value into an integer. If the
407    conversion doesn't work it will return ``0``. You can
408    override this default using the first parameter.
409    """
410    try:
411        return int(value)
412    except (TypeError, ValueError):
413        # this quirk is necessary so that "42.23"|int gives 42.
414        try:
415            return int(float(value))
416        except (TypeError, ValueError):
417            return default
418
419
420def do_float(value, default=0.0):
421    """Convert the value into a floating point number. If the
422    conversion doesn't work it will return ``0.0``. You can
423    override this default using the first parameter.
424    """
425    try:
426        return float(value)
427    except (TypeError, ValueError):
428        return default
429
430
431def do_format(value, *args, **kwargs):
432    """
433    Apply python string formatting on an object:
434
435    .. sourcecode:: jinja
436
437        {{ "%s - %s"|format("Hello?", "Foo!") }}
438            -> Hello? - Foo!
439    """
440    if args and kwargs:
441        raise FilterArgumentError('can\'t handle positional and keyword '
442                                  'arguments at the same time')
443    return soft_unicode(value) % (kwargs or args)
444
445
446def do_trim(value):
447    """Strip leading and trailing whitespace."""
448    return soft_unicode(value).strip()
449
450
451def do_striptags(value):
452    """Strip SGML/XML tags and replace adjacent whitespace by one space.
453    """
454    if hasattr(value, '__html__'):
455        value = value.__html__()
456    return Markup(unicode(value)).striptags()
457
458
459def do_slice(value, slices, fill_with=None):
460    """Slice an iterator and return a list of lists containing
461    those items. Useful if you want to create a div containing
462    three ul tags that represent columns:
463
464    .. sourcecode:: html+jinja
465
466        <div class="columwrapper">
467          {%- for column in items|slice(3) %}
468            <ul class="column-{{ loop.index }}">
469            {%- for item in column %}
470              <li>{{ item }}</li>
471            {%- endfor %}
472            </ul>
473          {%- endfor %}
474        </div>
475
476    If you pass it a second argument it's used to fill missing
477    values on the last iteration.
478    """
479    seq = list(value)
480    length = len(seq)
481    items_per_slice = length // slices
482    slices_with_extra = length % slices
483    offset = 0
484    for slice_number in xrange(slices):
485        start = offset + slice_number * items_per_slice
486        if slice_number < slices_with_extra:
487            offset += 1
488        end = offset + (slice_number + 1) * items_per_slice
489        tmp = seq[start:end]
490        if fill_with is not None and slice_number >= slices_with_extra:
491            tmp.append(fill_with)
492        yield tmp
493
494
495def do_batch(value, linecount, fill_with=None):
496    """
497    A filter that batches items. It works pretty much like `slice`
498    just the other way round. It returns a list of lists with the
499    given number of items. If you provide a second parameter this
500    is used to fill missing items. See this example:
501
502    .. sourcecode:: html+jinja
503
504        <table>
505        {%- for row in items|batch(3, '&nbsp;') %}
506          <tr>
507          {%- for column in row %}
508            <td>{{ column }}</td>
509          {%- endfor %}
510          </tr>
511        {%- endfor %}
512        </table>
513    """
514    result = []
515    tmp = []
516    for item in value:
517        if len(tmp) == linecount:
518            yield tmp
519            tmp = []
520        tmp.append(item)
521    if tmp:
522        if fill_with is not None and len(tmp) < linecount:
523            tmp += [fill_with] * (linecount - len(tmp))
524        yield tmp
525
526
527def do_round(value, precision=0, method='common'):
528    """Round the number to a given precision. The first
529    parameter specifies the precision (default is ``0``), the
530    second the rounding method:
531
532    - ``'common'`` rounds either up or down
533    - ``'ceil'`` always rounds up
534    - ``'floor'`` always rounds down
535
536    If you don't specify a method ``'common'`` is used.
537
538    .. sourcecode:: jinja
539
540        {{ 42.55|round }}
541            -> 43.0
542        {{ 42.55|round(1, 'floor') }}
543            -> 42.5
544
545    Note that even if rounded to 0 precision, a float is returned.  If
546    you need a real integer, pipe it through `int`:
547
548    .. sourcecode:: jinja
549
550        {{ 42.55|round|int }}
551            -> 43
552    """
553    if not method in ('common', 'ceil', 'floor'):
554        raise FilterArgumentError('method must be common, ceil or floor')
555    if precision < 0:
556        raise FilterArgumentError('precision must be a postive integer '
557                                  'or zero.')
558    if method == 'common':
559        return round(value, precision)
560    func = getattr(math, method)
561    if precision:
562        return func(value * 10 * precision) / (10 * precision)
563    else:
564        return func(value)
565
566
567def do_sort(value, reverse=False):
568    """Sort a sequence. Per default it sorts ascending, if you pass it
569    true as first argument it will reverse the sorting.
570    """
571    return sorted(value, reverse=reverse)
572
573
574@environmentfilter
575def do_groupby(environment, value, attribute):
576    """Group a sequence of objects by a common attribute.
577
578    If you for example have a list of dicts or objects that represent persons
579    with `gender`, `first_name` and `last_name` attributes and you want to
580    group all users by genders you can do something like the following
581    snippet:
582
583    .. sourcecode:: html+jinja
584
585        <ul>
586        {% for group in persons|groupby('gender') %}
587            <li>{{ group.grouper }}<ul>
588            {% for person in group.list %}
589                <li>{{ person.first_name }} {{ person.last_name }}</li>
590            {% endfor %}</ul></li>
591        {% endfor %}
592        </ul>
593
594    Additionally it's possible to use tuple unpacking for the grouper and
595    list:
596
597    .. sourcecode:: html+jinja
598
599        <ul>
600        {% for grouper, list in persons|groupby('gender') %}
601            ...
602        {% endfor %}
603        </ul>
604
605    As you can see the item we're grouping by is stored in the `grouper`
606    attribute and the `list` contains all the objects that have this grouper
607    in common.
608    """
609    expr = lambda x: environment.getitem(x, attribute)
610    return sorted(map(_GroupTuple, groupby(sorted(value, key=expr), expr)))
611
612
613class _GroupTuple(tuple):
614    __slots__ = ()
615    grouper = property(itemgetter(0))
616    list = property(itemgetter(1))
617
618    def __new__(cls, (key, value)):
619        return tuple.__new__(cls, (key, list(value)))
620
621
622def do_list(value):
623    """Convert the value into a list.  If it was a string the returned list
624    will be a list of characters.
625    """
626    return list(value)
627
628
629def do_mark_safe(value):
630    """Mark the value as safe which means that in an environment with automatic
631    escaping enabled this variable will not be escaped.
632    """
633    return Markup(value)
634
635
636def do_mark_unsafe(value):
637    """Mark a value as unsafe.  This is the reverse operation for :func:`safe`."""
638    return unicode(value)
639
640
641def do_reverse(value):
642    """Reverse the object or return an iterator the iterates over it the other
643    way round.
644    """
645    if isinstance(value, basestring):
646        return value[::-1]
647    try:
648        return reversed(value)
649    except TypeError:
650        try:
651            rv = list(value)
652            rv.reverse()
653            return rv
654        except TypeError:
655            raise FilterArgumentError('argument must be iterable')
656
657
658@environmentfilter
659def do_attr(environment, obj, name):
660    """Get an attribute of an object.  ``foo|attr("bar")`` works like
661    ``foo["bar"]`` just that always an attribute is returned and items are not
662    looked up.
663
664    See :ref:`Notes on subscriptions <notes-on-subscriptions>` for more details.
665    """
666    try:
667        name = str(name)
668    except UnicodeError:
669        pass
670    else:
671        try:
672            value = getattr(obj, name)
673        except AttributeError:
674            pass
675        else:
676            if environment.sandboxed and not \
677               environment.is_safe_attribute(obj, name, value):
678                return environment.unsafe_undefined(obj, name)
679            return value
680    return environment.undefined(obj=obj, name=name)
681
682
683FILTERS = {
684    'attr':                 do_attr,
685    'replace':              do_replace,
686    'upper':                do_upper,
687    'lower':                do_lower,
688    'escape':               escape,
689    'e':                    escape,
690    'forceescape':          do_forceescape,
691    'capitalize':           do_capitalize,
692    'title':                do_title,
693    'default':              do_default,
694    'd':                    do_default,
695    'join':                 do_join,
696    'count':                len,
697    'dictsort':             do_dictsort,
698    'sort':                 do_sort,
699    'length':               len,
700    'reverse':              do_reverse,
701    'center':               do_center,
702    'indent':               do_indent,
703    'title':                do_title,
704    'capitalize':           do_capitalize,
705    'first':                do_first,
706    'last':                 do_last,
707    'random':               do_random,
708    'filesizeformat':       do_filesizeformat,
709    'pprint':               do_pprint,
710    'truncate':             do_truncate,
711    'wordwrap':             do_wordwrap,
712    'wordcount':            do_wordcount,
713    'int':                  do_int,
714    'float':                do_float,
715    'string':               soft_unicode,
716    'list':                 do_list,
717    'urlize':               do_urlize,
718    'format':               do_format,
719    'trim':                 do_trim,
720    'striptags':            do_striptags,
721    'slice':                do_slice,
722    'batch':                do_batch,
723    'sum':                  sum,
724    'abs':                  abs,
725    'round':                do_round,
726    'sort':                 do_sort,
727    'groupby':              do_groupby,
728    'safe':                 do_mark_safe,
729    'xmlattr':              do_xmlattr
730}
731