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