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