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