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