a clean restart
[jinja2.git] / jinja2 / filters.py
1 # -*- coding: utf-8 -*-
2 """
3     jinja.filters
4     ~~~~~~~~~~~~~
5
6     Bundled jinja filters.
7
8     :copyright: 2007 by Armin Ronacher.
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
19
20 _striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)')
21
22
23 def stringfilter(f):
24     """
25     Decorator for filters that just work on unicode objects.
26     """
27     def decorator(*args):
28         def wrapped(env, context, value):
29             nargs = list(args)
30             for idx, var in enumerate(nargs):
31                 if isinstance(var, str):
32                     nargs[idx] = env.to_unicode(var)
33             return f(env.to_unicode(value), *nargs)
34         return wrapped
35     try:
36         decorator.__doc__ = f.__doc__
37         decorator.__name__ = f.__name__
38     except:
39         pass
40     return decorator
41
42
43 def simplefilter(f):
44     """
45     Decorator for simplifying filters. Filter arguments are passed
46     to the decorated function without environment and context. The
47     source value is the first argument. (like stringfilter but
48     without unicode conversion)
49     """
50     def decorator(*args):
51         def wrapped(env, context, value):
52             return f(value, *args)
53         return wrapped
54     try:
55         decorator.__doc__ = f.__doc__
56         decorator.__name__ = f.__name__
57     except:
58         pass
59     return decorator
60
61
62 def do_replace(s, old, new, count=None):
63     """
64     Return a copy of the value with all occurrences of a substring
65     replaced with a new one. The first argument is the substring
66     that should be replaced, the second is the replacement string.
67     If the optional third argument ``count`` is given, only the first
68     ``count`` occurrences are replaced:
69
70     .. sourcecode:: jinja
71
72         {{ "Hello World"|replace("Hello", "Goodbye") }}
73             -> Goodbye World
74
75         {{ "aaaaargh"|replace("a", "d'oh, ", 2) }}
76             -> d'oh, d'oh, aaargh
77     """
78     if not isinstance(old, basestring) or \
79        not isinstance(new, basestring):
80         raise FilterArgumentError('the replace filter requires '
81                                   'string replacement arguments')
82     if count is None:
83         return s.replace(old, new)
84     if not isinstance(count, (int, long)):
85         raise FilterArgumentError('the count parameter of the '
86                                    'replace filter requires '
87                                    'an integer')
88     return s.replace(old, new, count)
89 do_replace = stringfilter(do_replace)
90
91
92 def do_upper(s):
93     """
94     Convert a value to uppercase.
95     """
96     return s.upper()
97 do_upper = stringfilter(do_upper)
98
99
100 def do_lower(s):
101     """
102     Convert a value to lowercase.
103     """
104     return s.lower()
105 do_lower = stringfilter(do_lower)
106
107
108 def do_escape(attribute=False):
109     """
110     XML escape ``&``, ``<``, and ``>`` in a string of data. If the
111     optional parameter is `true` this filter will also convert
112     ``"`` to ``&quot;``. This filter is just used if the environment
113     was configured with disabled `auto_escape`.
114
115     This method will have no effect it the value is already escaped.
116     """
117     #: because filters are cached we can make a local alias to
118     #: speed things up a bit
119     e = escape
120     def wrapped(env, context, s):
121         if isinstance(s, TemplateData):
122             return s
123         elif hasattr(s, '__html__'):
124             return s.__html__()
125         #: small speedup, do not convert to unicode if we already
126         #: have an unicode object.
127         if s.__class__ is not unicode:
128             s = env.to_unicode(s)
129         return e(s, attribute)
130     return wrapped
131
132
133 def do_xmlattr(autospace=False):
134     """
135     Create an SGML/XML attribute string based on the items in a dict.
136     All values that are neither `none` nor `undefined` are automatically
137     escaped:
138
139     .. sourcecode:: html+jinja
140
141         <ul{{ {'class': 'my_list', 'missing': None,
142                 'id': 'list-%d'|format(variable)}|xmlattr }}>
143         ...
144         </ul>
145
146     Results in something like this:
147
148     .. sourcecode:: html
149
150         <ul class="my_list" id="list-42">
151         ...
152         </ul>
153
154     As you can see it automatically prepends a space in front of the item
155     if the filter returned something. You can disable this by passing
156     `false` as only argument to the filter.
157
158     *New in Jinja 1.1*
159     """
160     e = escape
161     def wrapped(env, context, d):
162         if not hasattr(d, 'iteritems'):
163             raise TypeError('a dict is required')
164         result = []
165         for key, value in d.iteritems():
166             if value not in (None, env.undefined_singleton):
167                 result.append(u'%s="%s"' % (
168                     e(env.to_unicode(key)),
169                     e(env.to_unicode(value), True)
170                 ))
171         rv = u' '.join(result)
172         if autospace:
173             rv = ' ' + rv
174         return rv
175     return wrapped
176
177
178 def do_capitalize(s):
179     """
180     Capitalize a value. The first character will be uppercase, all others
181     lowercase.
182     """
183     return s.capitalize()
184 do_capitalize = stringfilter(do_capitalize)
185
186
187 def do_title(s):
188     """
189     Return a titlecased version of the value. I.e. words will start with
190     uppercase letters, all remaining characters are lowercase.
191     """
192     return s.title()
193 do_title = stringfilter(do_title)
194
195
196 def do_dictsort(case_sensitive=False, by='key'):
197     """
198     Sort a dict and yield (key, value) pairs. Because python dicts are
199     unsorted you may want to use this function to order them by either
200     key or value:
201
202     .. sourcecode:: jinja
203
204         {% for item in mydict|dictsort %}
205             sort the dict by key, case insensitive
206
207         {% for item in mydict|dicsort(true) %}
208             sort the dict by key, case sensitive
209
210         {% for item in mydict|dictsort(false, 'value') %}
211             sort the dict by key, case insensitive, sorted
212             normally and ordered by value.
213     """
214     if by == 'key':
215         pos = 0
216     elif by == 'value':
217         pos = 1
218     else:
219         raise FilterArgumentError('You can only sort by either '
220                                   '"key" or "value"')
221     def sort_func(value, env):
222         if isinstance(value, basestring):
223             value = env.to_unicode(value)
224             if not case_sensitive:
225                 value = value.lower()
226         return value
227
228     def wrapped(env, context, value):
229         items = value.items()
230         items.sort(lambda a, b: cmp(sort_func(a[pos], env),
231                                     sort_func(b[pos], env)))
232         return items
233     return wrapped
234
235
236 def do_default(default_value=u'', boolean=False):
237     """
238     If the value is undefined it will return the passed default value,
239     otherwise the value of the variable:
240
241     .. sourcecode:: jinja
242
243         {{ my_variable|default('my_variable is not defined') }}
244
245     This will output the value of ``my_variable`` if the variable was
246     defined, otherwise ``'my_variable is not defined'``. If you want
247     to use default with variables that evaluate to false you have to
248     set the second parameter to `true`:
249
250     .. sourcecode:: jinja
251
252         {{ ''|default('the string was empty', true) }}
253     """
254     def wrapped(env, context, value):
255         if (boolean and not value) or value in (env.undefined_singleton, None):
256             return default_value
257         return value
258     return wrapped
259
260
261 def do_join(d=u''):
262     """
263     Return a string which is the concatenation of the strings in the
264     sequence. The separator between elements is an empty string per
265     default, you can define ith with the optional parameter:
266
267     .. sourcecode:: jinja
268
269         {{ [1, 2, 3]|join('|') }}
270             -> 1|2|3
271
272         {{ [1, 2, 3]|join }}
273             -> 123
274     """
275     def wrapped(env, context, value):
276         return env.to_unicode(d).join([env.to_unicode(x) for x in value])
277     return wrapped
278
279
280 def do_count():
281     """
282     Return the length of the value. In case if getting an integer or float
283     it will convert it into a string an return the length of the new
284     string. If the object has no length it will of corse return 0.
285     """
286     def wrapped(env, context, value):
287         try:
288             if type(value) in (int, float, long):
289                 return len(str(value))
290             return len(value)
291         except TypeError:
292             return 0
293     return wrapped
294
295
296 def do_reverse():
297     """
298     Return a reversed list of the sequence filtered. You can use this
299     for example for reverse iteration:
300
301     .. sourcecode:: jinja
302
303         {% for item in seq|reverse %}
304             {{ item|e }}
305         {% endfor %}
306     """
307     def wrapped(env, context, value):
308         try:
309             return value[::-1]
310         except:
311             l = list(value)
312             l.reverse()
313             return l
314     return wrapped
315
316
317 def do_center(value, width=80):
318     """
319     Centers the value in a field of a given width.
320     """
321     return value.center(width)
322 do_center = stringfilter(do_center)
323
324
325 def do_first():
326     """
327     Return the frist item of a sequence.
328     """
329     def wrapped(env, context, seq):
330         try:
331             return iter(seq).next()
332         except StopIteration:
333             return env.undefined_singleton
334     return wrapped
335
336
337 def do_last():
338     """
339     Return the last item of a sequence.
340     """
341     def wrapped(env, context, seq):
342         try:
343             return iter(reversed(seq)).next()
344         except StopIteration:
345             return env.undefined_singleton
346     return wrapped
347
348
349 def do_random():
350     """
351     Return a random item from the sequence.
352     """
353     def wrapped(env, context, seq):
354         try:
355             return choice(seq)
356         except IndexError:
357             return env.undefined_singleton
358     return wrapped
359
360
361 def do_urlencode():
362     """
363     urlencode a string or directory.
364
365     .. sourcecode:: jinja
366
367         {{ {'foo': 'bar', 'blub': 'blah'}|urlencode }}
368             -> foo=bar&blub=blah
369
370         {{ 'Hello World' }}
371             -> Hello%20World
372     """
373     def wrapped(env, context, value):
374         if isinstance(value, dict):
375             tmp = {}
376             for key, value in value.iteritems():
377                 key = env.to_unicode(key).encode(env.charset)
378                 value = env.to_unicode(value).encode(env.charset)
379                 tmp[key] = value
380             return urlencode(tmp)
381         else:
382             return quote(env.to_unicode(value).encode(env.charset))
383     return wrapped
384
385
386 def do_jsonencode():
387     """
388     JSON dump a variable. just works if simplejson is installed.
389
390     .. sourcecode:: jinja
391
392         {{ 'Hello World'|jsonencode }}
393             -> "Hello World"
394     """
395     global simplejson
396     try:
397         simplejson
398     except NameError:
399         import simplejson
400     return lambda e, c, v: simplejson.dumps(v)
401
402
403 def do_filesizeformat():
404     """
405     Format the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB, 102
406     bytes, etc).
407     """
408     def wrapped(env, context, value):
409         # fail silently
410         try:
411             bytes = float(value)
412         except TypeError:
413             bytes = 0
414
415         if bytes < 1024:
416             return "%d Byte%s" % (bytes, bytes != 1 and 's' or '')
417         elif bytes < 1024 * 1024:
418             return "%.1f KB" % (bytes / 1024)
419         elif bytes < 1024 * 1024 * 1024:
420             return "%.1f MB" % (bytes / (1024 * 1024))
421         return "%.1f GB" % (bytes / (1024 * 1024 * 1024))
422     return wrapped
423
424
425 def do_pprint(verbose=False):
426     """
427     Pretty print a variable. Useful for debugging.
428
429     With Jinja 1.2 onwards you can pass it a parameter.  If this parameter
430     is truthy the output will be more verbose (this requires `pretty`)
431     """
432     def wrapped(env, context, value):
433         return pformat(value, verbose=verbose)
434     return wrapped
435
436
437 def do_urlize(value, trim_url_limit=None, nofollow=False):
438     """
439     Converts URLs in plain text into clickable links.
440
441     If you pass the filter an additional integer it will shorten the urls
442     to that number. Also a third argument exists that makes the urls
443     "nofollow":
444
445     .. sourcecode:: jinja
446
447         {{ mytext|urlize(40, True) }}
448             links are shortened to 40 chars and defined with rel="nofollow"
449     """
450     return urlize(value, trim_url_limit, nofollow)
451 do_urlize = stringfilter(do_urlize)
452
453
454 def do_indent(s, width=4, indentfirst=False):
455     """
456     {{ s|indent[ width[ indentfirst[ usetab]]] }}
457
458     Return a copy of the passed string, each line indented by
459     4 spaces. The first line is not indented. If you want to
460     change the number of spaces or indent the first line too
461     you can pass additional parameters to the filter:
462
463     .. sourcecode:: jinja
464
465         {{ mytext|indent(2, True) }}
466             indent by two spaces and indent the first line too.
467     """
468     indention = ' ' * width
469     if indentfirst:
470         return u'\n'.join([indention + line for line in s.splitlines()])
471     return s.replace('\n', '\n' + indention)
472 do_indent = stringfilter(do_indent)
473
474
475 def do_truncate(s, length=255, killwords=False, end='...'):
476     """
477     Return a truncated copy of the string. The length is specified
478     with the first parameter which defaults to ``255``. If the second
479     parameter is ``true`` the filter will cut the text at length. Otherwise
480     it will try to save the last word. If the text was in fact
481     truncated it will append an ellipsis sign (``"..."``). If you want a
482     different ellipsis sign than ``"..."`` you can specify it using the
483     third parameter.
484
485     .. sourcecode jinja::
486
487         {{ mytext|truncate(300, false, '&raquo;') }}
488             truncate mytext to 300 chars, don't split up words, use a
489             right pointing double arrow as ellipsis sign.
490     """
491     if len(s) <= length:
492         return s
493     elif killwords:
494         return s[:length] + end
495     words = s.split(' ')
496     result = []
497     m = 0
498     for word in words:
499         m += len(word) + 1
500         if m > length:
501             break
502         result.append(word)
503     result.append(end)
504     return u' '.join(result)
505 do_truncate = stringfilter(do_truncate)
506
507
508 def do_wordwrap(s, pos=79, hard=False):
509     """
510     Return a copy of the string passed to the filter wrapped after
511     ``79`` characters. You can override this default using the first
512     parameter. If you set the second parameter to `true` Jinja will
513     also split words apart (usually a bad idea because it makes
514     reading hard).
515     """
516     if len(s) < pos:
517         return s
518     if hard:
519         return u'\n'.join([s[idx:idx + pos] for idx in
520                           xrange(0, len(s), pos)])
521     # code from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/148061
522     return reduce(lambda line, word, pos=pos: u'%s%s%s' %
523                   (line, u' \n'[(len(line)-line.rfind('\n') - 1 +
524                                 len(word.split('\n', 1)[0]) >= pos)],
525                    word), s.split(' '))
526 do_wordwrap = stringfilter(do_wordwrap)
527
528
529 def do_wordcount(s):
530     """
531     Count the words in that string.
532     """
533     return len([x for x in s.split() if x])
534 do_wordcount = stringfilter(do_wordcount)
535
536
537 def do_textile(s):
538     """
539     Prase the string using textile.
540
541     requires the `PyTextile`_ library.
542
543     .. _PyTextile: http://dealmeida.net/projects/textile/
544     """
545     from textile import textile
546     return textile(s.encode('utf-8')).decode('utf-8')
547 do_textile = stringfilter(do_textile)
548
549
550 def do_markdown(s):
551     """
552     Parse the string using markdown.
553
554     requires the `Python-markdown`_ library.
555
556     .. _Python-markdown: http://www.freewisdom.org/projects/python-markdown/
557     """
558     from markdown import markdown
559     return markdown(s.encode('utf-8')).decode('utf-8')
560 do_markdown = stringfilter(do_markdown)
561
562
563 def do_rst(s):
564     """
565     Parse the string using the reStructuredText parser from the
566     docutils package.
567
568     requires `docutils`_.
569
570     .. _docutils: http://docutils.sourceforge.net/
571     """
572     from docutils.core import publish_parts
573     parts = publish_parts(source=s, writer_name='html4css1')
574     return parts['fragment']
575 do_rst = stringfilter(do_rst)
576
577
578 def do_int(default=0):
579     """
580     Convert the value into an integer. If the
581     conversion doesn't work it will return ``0``. You can
582     override this default using the first parameter.
583     """
584     def wrapped(env, context, value):
585         try:
586             return int(value)
587         except (TypeError, ValueError):
588             try:
589                 return int(float(value))
590             except (TypeError, ValueError):
591                 return default
592     return wrapped
593
594
595 def do_float(default=0.0):
596     """
597     Convert the value into a floating point number. If the
598     conversion doesn't work it will return ``0.0``. You can
599     override this default using the first parameter.
600     """
601     def wrapped(env, context, value):
602         try:
603             return float(value)
604         except (TypeError, ValueError):
605             return default
606     return wrapped
607
608
609 def do_string():
610     """
611     Convert the value into an string.
612     """
613     return lambda e, c, v: e.to_unicode(v)
614
615
616 def do_format(*args):
617     """
618     Apply python string formatting on an object:
619
620     .. sourcecode:: jinja
621
622         {{ "%s - %s"|format("Hello?", "Foo!") }}
623             -> Hello? - Foo!
624
625     Note that you cannot use the mapping syntax (``%(name)s``)
626     like in python. Use `|dformat` for that.
627     """
628     def wrapped(env, context, value):
629         return env.to_unicode(value) % args
630     return wrapped
631
632
633 def do_dformat(d):
634     """
635     Apply python mapping string formatting on an object:
636
637     .. sourcecode:: jinja
638
639         {{ "Hello %(username)s!"|dformat({'username': 'John Doe'}) }}
640             -> Hello John Doe!
641
642     This is useful when adding variables to translateable
643     string expressions.
644
645     *New in Jinja 1.1*
646     """
647     if not isinstance(d, dict):
648         raise FilterArgumentError('dict required')
649     def wrapped(env, context, value):
650         return env.to_unicode(value) % d
651     return wrapped
652
653
654 def do_trim(value):
655     """
656     Strip leading and trailing whitespace.
657     """
658     return value.strip()
659 do_trim = stringfilter(do_trim)
660
661
662 def do_capture(name='captured', clean=False):
663     """
664     Store the value in a variable called ``captured`` or a variable
665     with the name provided. Useful for filter blocks:
666
667     .. sourcecode:: jinja
668
669         {% filter capture('foo') %}
670             ...
671         {% endfilter %}
672         {{ foo }}
673
674     This will output "..." two times. One time from the filter block
675     and one time from the variable. If you don't want the filter to
676     output something you can use it in `clean` mode:
677
678     .. sourcecode:: jinja
679
680         {% filter capture('foo', True) %}
681             ...
682         {% endfilter %}
683         {{ foo }}
684     """
685     if not isinstance(name, basestring):
686         raise FilterArgumentError('You can only capture into variables')
687     def wrapped(env, context, value):
688         context[name] = value
689         if clean:
690             return TemplateData()
691         return value
692     return wrapped
693
694
695 def do_striptags(value):
696     """
697     Strip SGML/XML tags and replace adjacent whitespace by one space.
698
699     *new in Jinja 1.1*
700     """
701     return ' '.join(_striptags_re.sub('', value).split())
702 do_striptags = stringfilter(do_striptags)
703
704
705 def do_slice(slices, fill_with=None):
706     """
707     Slice an iterator and return a list of lists containing
708     those items. Useful if you want to create a div containing
709     three div tags that represent columns:
710
711     .. sourcecode:: html+jinja
712
713         <div class="columwrapper">
714           {%- for column in items|slice(3) %}
715             <ul class="column-{{ loop.index }}">
716             {%- for item in column %}
717               <li>{{ item }}</li>
718             {%- endfor %}
719             </ul>
720           {%- endfor %}
721         </div>
722
723     If you pass it a second argument it's used to fill missing
724     values on the last iteration.
725
726     *new in Jinja 1.1*
727     """
728     def wrapped(env, context, value):
729         result = []
730         seq = list(value)
731         length = len(seq)
732         items_per_slice = length // slices
733         slices_with_extra = length % slices
734         offset = 0
735         for slice_number in xrange(slices):
736             start = offset + slice_number * items_per_slice
737             if slice_number < slices_with_extra:
738                 offset += 1
739             end = offset + (slice_number + 1) * items_per_slice
740             tmp = seq[start:end]
741             if fill_with is not None and slice_number >= slices_with_extra:
742                 tmp.append(fill_with)
743             result.append(tmp)
744         return result
745     return wrapped
746
747
748 def do_batch(linecount, fill_with=None):
749     """
750     A filter that batches items. It works pretty much like `slice`
751     just the other way round. It returns a list of lists with the
752     given number of items. If you provide a second parameter this
753     is used to fill missing items. See this example:
754
755     .. sourcecode:: html+jinja
756
757         <table>
758         {%- for row in items|batch(3, '&nbsp;') %}
759           <tr>
760           {%- for column in row %}
761             <tr>{{ column }}</td>
762           {%- endfor %}
763           </tr>
764         {%- endfor %}
765         </table>
766
767     *new in Jinja 1.1*
768     """
769     def wrapped(env, context, value):
770         result = []
771         tmp = []
772         for item in value:
773             if len(tmp) == linecount:
774                 result.append(tmp)
775                 tmp = []
776             tmp.append(item)
777         if tmp:
778             if fill_with is not None and len(tmp) < linecount:
779                 tmp += [fill_with] * (linecount - len(tmp))
780             result.append(tmp)
781         return result
782     return wrapped
783
784
785 def do_sum():
786     """
787     Sum up the given sequence of numbers.
788
789     *new in Jinja 1.1*
790     """
791     def wrapped(env, context, value):
792         return sum(value)
793     return wrapped
794
795
796 def do_abs():
797     """
798     Return the absolute value of a number.
799
800     *new in Jinja 1.1*
801     """
802     def wrapped(env, context, value):
803         return abs(value)
804     return wrapped
805
806
807 def do_round(precision=0, method='common'):
808     """
809     Round the number to a given precision. The first
810     parameter specifies the precision (default is ``0``), the
811     second the rounding method:
812
813     - ``'common'`` rounds either up or down
814     - ``'ceil'`` always rounds up
815     - ``'floor'`` always rounds down
816
817     If you don't specify a method ``'common'`` is used.
818
819     .. sourcecode:: jinja
820
821         {{ 42.55|round }}
822             -> 43
823         {{ 42.55|round(1, 'floor') }}
824             -> 42.5
825
826     *new in Jinja 1.1*
827     """
828     if not method in ('common', 'ceil', 'floor'):
829         raise FilterArgumentError('method must be common, ceil or floor')
830     if precision < 0:
831         raise FilterArgumentError('precision must be a postive integer '
832                                   'or zero.')
833     def wrapped(env, context, value):
834         if method == 'common':
835             return round(value, precision)
836         import math
837         func = getattr(math, method)
838         if precision:
839             return func(value * 10 * precision) / (10 * precision)
840         else:
841             return func(value)
842     return wrapped
843
844
845 def do_sort(reverse=False):
846     """
847     Sort a sequence. Per default it sorts ascending, if you pass it
848     `True` as first argument it will reverse the sorting.
849
850     *new in Jinja 1.1*
851     """
852     def wrapped(env, context, value):
853         return sorted(value, reverse=reverse)
854     return wrapped
855
856
857 def do_groupby(attribute):
858     """
859     Group a sequence of objects by a common attribute.
860
861     If you for example have a list of dicts or objects that represent persons
862     with `gender`, `first_name` and `last_name` attributes and you want to
863     group all users by genders you can do something like the following
864     snippet:
865
866     .. sourcecode:: html+jinja
867
868         <ul>
869         {% for group in persons|groupby('gender') %}
870             <li>{{ group.grouper }}<ul>
871             {% for person in group.list %}
872                 <li>{{ person.first_name }} {{ person.last_name }}</li>
873             {% endfor %}</ul></li>
874         {% endfor %}
875         </ul>
876
877     As you can see the item we're grouping by is stored in the `grouper`
878     attribute and the `list` contains all the objects that have this grouper
879     in common.
880
881     *New in Jinja 1.2*
882     """
883     def wrapped(env, context, value):
884         expr = lambda x: env.get_attribute(x, attribute)
885         return sorted([{
886             'grouper':  a,
887             'list':     list(b)
888         } for a, b in groupby(sorted(value, key=expr), expr)],
889             key=itemgetter('grouper'))
890     return wrapped
891
892
893 def do_getattribute(attribute):
894     """
895     Get one attribute from an object. Normally you don't have to use this
896     filter because the attribute and subscript expressions try to either
897     get an attribute of an object or an item. In some situations it could
898     be that there is an item *and* an attribute with the same name. In that
899     situation only the item is returned, never the attribute.
900
901     .. sourcecode:: jinja
902
903         {{ foo.bar }} -> {{ foo|getattribute('bar') }}
904
905     *New in Jinja 1.2*
906     """
907     def wrapped(env, context, value):
908         try:
909             return get_attribute(value, attribute)
910         except (SecurityException, AttributeError):
911             return env.undefined_singleton
912     return wrapped
913
914
915 def do_getitem(key):
916     """
917     This filter basically works like the normal subscript expression but
918     it doesn't fall back to attribute lookup. If an item does not exist for
919     an object undefined is returned.
920
921     .. sourcecode:: jinja
922
923         {{ foo.bar }} -> {{ foo|getitem('bar') }}
924
925     *New in Jinja 1.2*
926     """
927     def wrapped(env, context, value):
928         try:
929             return value[key]
930         except (TypeError, KeyError, IndexError, AttributeError):
931             return env.undefined_singleton
932     return wrapped
933
934
935 FILTERS = {
936     'replace':              do_replace,
937     'upper':                do_upper,
938     'lower':                do_lower,
939     'escape':               do_escape,
940     'e':                    do_escape,
941     'xmlattr':              do_xmlattr,
942     'capitalize':           do_capitalize,
943     'title':                do_title,
944     'default':              do_default,
945     'join':                 do_join,
946     'count':                do_count,
947     'dictsort':             do_dictsort,
948     'length':               do_count,
949     'reverse':              do_reverse,
950     'center':               do_center,
951     'title':                do_title,
952     'capitalize':           do_capitalize,
953     'first':                do_first,
954     'last':                 do_last,
955     'random':               do_random,
956     'urlencode':            do_urlencode,
957     'jsonencode':           do_jsonencode,
958     'filesizeformat':       do_filesizeformat,
959     'pprint':               do_pprint,
960     'indent':               do_indent,
961     'truncate':             do_truncate,
962     'wordwrap':             do_wordwrap,
963     'wordcount':            do_wordcount,
964     'textile':              do_textile,
965     'markdown':             do_markdown,
966     'rst':                  do_rst,
967     'int':                  do_int,
968     'float':                do_float,
969     'string':               do_string,
970     'urlize':               do_urlize,
971     'format':               do_format,
972     'dformat':              do_dformat,
973     'capture':              do_capture,
974     'trim':                 do_trim,
975     'striptags':            do_striptags,
976     'slice':                do_slice,
977     'batch':                do_batch,
978     'sum':                  do_sum,
979     'abs':                  do_abs,
980     'round':                do_round,
981     'sort':                 do_sort,
982     'groupby':              do_groupby,
983     'getattribute':         do_getattribute,
984     'getitem':              do_getitem
985 }