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