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