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