blob: 1c3ffcba27785f029628e6e407bdf62752a113d2 [file] [log] [blame]
Armin Ronacher92f572f2007-02-26 22:17:32 +01001# -*- coding: utf-8 -*-
2"""
3 jinja.filters
4 ~~~~~~~~~~~~~
5
6 Bundled jinja filters.
7
Christoph Hack2751d942008-04-09 12:14:55 +02008 :copyright: 2008 by Armin Ronacher, Christoph Hack.
Armin Ronacher92f572f2007-02-26 22:17:32 +01009 :license: BSD, see LICENSE for more details.
10"""
Georg Brandlaf31e4d2007-04-15 00:47:37 +020011import re
Armin Ronacherfed86c12007-02-27 10:31:14 +010012from random import choice
Armin Ronacher1dcdac52007-12-09 22:53:46 +010013try:
14 from operator import itemgetter
15except ImportError:
16 itemgetter = lambda a: lambda b: b[a]
Armin Ronacherfed86c12007-02-27 10:31:14 +010017from urllib import urlencode, quote
Armin Ronacher8edbe492008-04-10 20:43:43 +020018from jinja2.utils import escape
Armin Ronacherfed86c12007-02-27 10:31:14 +010019
20
Armin Ronachereaf493e2007-10-01 22:31:16 +020021_striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)')
Armin Ronacher9bcd4112007-05-29 14:17:24 +020022
23
Christoph Hack2751d942008-04-09 12:14:55 +020024def contextfilter(f):
Armin Ronacher8edbe492008-04-10 20:43:43 +020025 """Decorator for marking context dependent filters. The current context
Christoph Hack2751d942008-04-09 12:14:55 +020026 argument will be passed as first argument.
Armin Ronacher92f572f2007-02-26 22:17:32 +010027 """
Christoph Hack2751d942008-04-09 12:14:55 +020028 f.contextfilter = True
29 return f
Armin Ronacher9bcd4112007-05-29 14:17:24 +020030
31
Armin Ronacher92f572f2007-02-26 22:17:32 +010032def do_replace(s, old, new, count=None):
33 """
Armin Ronacher37a88512007-03-02 20:42:18 +010034 Return a copy of the value with all occurrences of a substring
35 replaced with a new one. The first argument is the substring
36 that should be replaced, the second is the replacement string.
37 If the optional third argument ``count`` is given, only the first
38 ``count`` occurrences are replaced:
Armin Ronacher92f572f2007-02-26 22:17:32 +010039
Armin Ronacher37a88512007-03-02 20:42:18 +010040 .. sourcecode:: jinja
41
42 {{ "Hello World"|replace("Hello", "Goodbye") }}
43 -> Goodbye World
44
45 {{ "aaaaargh"|replace("a", "d'oh, ", 2) }}
46 -> d'oh, d'oh, aaargh
Armin Ronacher92f572f2007-02-26 22:17:32 +010047 """
Armin Ronacher2b765132007-03-13 16:48:10 +010048 if not isinstance(old, basestring) or \
49 not isinstance(new, basestring):
Armin Ronacherae16fd02007-03-27 21:31:24 +020050 raise FilterArgumentError('the replace filter requires '
51 'string replacement arguments')
Armin Ronacher2da479f2007-04-02 10:50:18 +020052 if count is None:
53 return s.replace(old, new)
54 if not isinstance(count, (int, long)):
Armin Ronacherae16fd02007-03-27 21:31:24 +020055 raise FilterArgumentError('the count parameter of the '
56 'replace filter requires '
57 'an integer')
Armin Ronacher92f572f2007-02-26 22:17:32 +010058 return s.replace(old, new, count)
Armin Ronacher92f572f2007-02-26 22:17:32 +010059
60
61def do_upper(s):
Armin Ronacher8edbe492008-04-10 20:43:43 +020062 """Convert a value to uppercase."""
63 return unicode(s).upper()
Armin Ronacher92f572f2007-02-26 22:17:32 +010064
65
Christoph Hack2751d942008-04-09 12:14:55 +020066def do_lower(s):
Armin Ronacher8edbe492008-04-10 20:43:43 +020067 """Convert a value to lowercase."""
68 return unicode(s).lower()
Armin Ronacher92f572f2007-02-26 22:17:32 +010069
70
Christoph Hack2751d942008-04-09 12:14:55 +020071def do_escape(s, attribute=False):
Armin Ronacher92f572f2007-02-26 22:17:32 +010072 """
Armin Ronacher37a88512007-03-02 20:42:18 +010073 XML escape ``&``, ``<``, and ``>`` in a string of data. If the
74 optional parameter is `true` this filter will also convert
75 ``"`` to ``&quot;``. This filter is just used if the environment
76 was configured with disabled `auto_escape`.
Armin Ronacher92f572f2007-02-26 22:17:32 +010077
Armin Ronacher37a88512007-03-02 20:42:18 +010078 This method will have no effect it the value is already escaped.
Armin Ronacher92f572f2007-02-26 22:17:32 +010079 """
Christoph Hackf4fdc722008-04-08 16:48:30 +020080 return escape(unicode(s), attribute)
Armin Ronacher92f572f2007-02-26 22:17:32 +010081
82
Christoph Hack2751d942008-04-09 12:14:55 +020083def do_xmlattr(d, autospace=False):
Armin Ronacher450756b2007-04-15 15:13:59 +020084 """
85 Create an SGML/XML attribute string based on the items in a dict.
86 All values that are neither `none` nor `undefined` are automatically
87 escaped:
88
Armin Ronacher8ca55df2007-04-15 15:16:08 +020089 .. sourcecode:: html+jinja
Armin Ronacher450756b2007-04-15 15:13:59 +020090
Armin Ronacherc7ddd1d2007-04-28 15:56:27 +020091 <ul{{ {'class': 'my_list', 'missing': None,
Armin Ronacherd459e272007-04-15 15:31:05 +020092 'id': 'list-%d'|format(variable)}|xmlattr }}>
Armin Ronacher450756b2007-04-15 15:13:59 +020093 ...
94 </ul>
95
96 Results in something like this:
97
Armin Ronacher8ca55df2007-04-15 15:16:08 +020098 .. sourcecode:: html
99
Armin Ronacher450756b2007-04-15 15:13:59 +0200100 <ul class="my_list" id="list-42">
101 ...
102 </ul>
103
Armin Ronacher9bcd4112007-05-29 14:17:24 +0200104 As you can see it automatically prepends a space in front of the item
Armin Ronacherc7ddd1d2007-04-28 15:56:27 +0200105 if the filter returned something. You can disable this by passing
106 `false` as only argument to the filter.
107
Armin Ronacher450756b2007-04-15 15:13:59 +0200108 *New in Jinja 1.1*
109 """
Christoph Hack2751d942008-04-09 12:14:55 +0200110 if not hasattr(d, 'iteritems'):
111 raise TypeError('a dict is required')
112 result = []
113 for key, value in d.iteritems():
114 if value not in (None, env.undefined_singleton):
115 result.append(u'%s="%s"' % (
116 escape(env.to_unicode(key)),
117 escape(env.to_unicode(value), True)
118 ))
119 rv = u' '.join(result)
120 if autospace:
121 rv = ' ' + rv
122 return rv
Armin Ronacher450756b2007-04-15 15:13:59 +0200123
124
Armin Ronacher92f572f2007-02-26 22:17:32 +0100125def do_capitalize(s):
126 """
Armin Ronacher37a88512007-03-02 20:42:18 +0100127 Capitalize a value. The first character will be uppercase, all others
128 lowercase.
Armin Ronacher92f572f2007-02-26 22:17:32 +0100129 """
Christoph Hack2751d942008-04-09 12:14:55 +0200130 return unicode(s).capitalize()
Armin Ronacher92f572f2007-02-26 22:17:32 +0100131
132
133def do_title(s):
134 """
Armin Ronacher37a88512007-03-02 20:42:18 +0100135 Return a titlecased version of the value. I.e. words will start with
136 uppercase letters, all remaining characters are lowercase.
Armin Ronacher92f572f2007-02-26 22:17:32 +0100137 """
Christoph Hack2751d942008-04-09 12:14:55 +0200138 return unicode(s).title()
Armin Ronacher92f572f2007-02-26 22:17:32 +0100139
140
Armin Ronacher2b765132007-03-13 16:48:10 +0100141def do_dictsort(case_sensitive=False, by='key'):
142 """
143 Sort a dict and yield (key, value) pairs. Because python dicts are
144 unsorted you may want to use this function to order them by either
145 key or value:
146
147 .. sourcecode:: jinja
148
149 {% for item in mydict|dictsort %}
150 sort the dict by key, case insensitive
151
152 {% for item in mydict|dicsort(true) %}
153 sort the dict by key, case sensitive
154
155 {% for item in mydict|dictsort(false, 'value') %}
156 sort the dict by key, case insensitive, sorted
157 normally and ordered by value.
158 """
159 if by == 'key':
160 pos = 0
161 elif by == 'value':
162 pos = 1
163 else:
164 raise FilterArgumentError('You can only sort by either '
165 '"key" or "value"')
166 def sort_func(value, env):
167 if isinstance(value, basestring):
168 value = env.to_unicode(value)
169 if not case_sensitive:
170 value = value.lower()
171 return value
172
173 def wrapped(env, context, value):
174 items = value.items()
175 items.sort(lambda a, b: cmp(sort_func(a[pos], env),
176 sort_func(b[pos], env)))
177 return items
178 return wrapped
179
180
Christoph Hack2751d942008-04-09 12:14:55 +0200181def do_default(value, default_value=u'', boolean=False):
Armin Ronacher92f572f2007-02-26 22:17:32 +0100182 """
Armin Ronacher37a88512007-03-02 20:42:18 +0100183 If the value is undefined it will return the passed default value,
184 otherwise the value of the variable:
Armin Ronacher92f572f2007-02-26 22:17:32 +0100185
Armin Ronacher37a88512007-03-02 20:42:18 +0100186 .. sourcecode:: jinja
187
188 {{ my_variable|default('my_variable is not defined') }}
189
190 This will output the value of ``my_variable`` if the variable was
191 defined, otherwise ``'my_variable is not defined'``. If you want
192 to use default with variables that evaluate to false you have to
193 set the second parameter to `true`:
194
195 .. sourcecode:: jinja
196
197 {{ ''|default('the string was empty', true) }}
Armin Ronacher92f572f2007-02-26 22:17:32 +0100198 """
Christoph Hack2751d942008-04-09 12:14:55 +0200199 # XXX: undefined_sigleton
200 if (boolean and not value) or value in (env.undefined_singleton, None):
201 return default_value
202 return value
Armin Ronacher92f572f2007-02-26 22:17:32 +0100203
204
Christoph Hack2751d942008-04-09 12:14:55 +0200205def do_join(value, d=u''):
Armin Ronacher92f572f2007-02-26 22:17:32 +0100206 """
Armin Ronacher92f572f2007-02-26 22:17:32 +0100207 Return a string which is the concatenation of the strings in the
Armin Ronacher37a88512007-03-02 20:42:18 +0100208 sequence. The separator between elements is an empty string per
209 default, you can define ith with the optional parameter:
210
211 .. sourcecode:: jinja
212
213 {{ [1, 2, 3]|join('|') }}
214 -> 1|2|3
215
216 {{ [1, 2, 3]|join }}
217 -> 123
Armin Ronacher92f572f2007-02-26 22:17:32 +0100218 """
Christoph Hack2751d942008-04-09 12:14:55 +0200219 return unicode(d).join([unicode(x) for x in value])
Armin Ronacher92f572f2007-02-26 22:17:32 +0100220
221
222def do_count():
223 """
Armin Ronacher37a88512007-03-02 20:42:18 +0100224 Return the length of the value. In case if getting an integer or float
Armin Ronacher92f572f2007-02-26 22:17:32 +0100225 it will convert it into a string an return the length of the new
Armin Ronacher37a88512007-03-02 20:42:18 +0100226 string. If the object has no length it will of corse return 0.
Armin Ronacher92f572f2007-02-26 22:17:32 +0100227 """
Christoph Hack2751d942008-04-09 12:14:55 +0200228 try:
229 if type(value) in (int, float, long):
230 return len(str(value))
231 return len(value)
232 except TypeError:
233 return 0
Armin Ronacher92f572f2007-02-26 22:17:32 +0100234
235
Christoph Hack2751d942008-04-09 12:14:55 +0200236def do_reverse(l):
Armin Ronacher92f572f2007-02-26 22:17:32 +0100237 """
Armin Ronacher37a88512007-03-02 20:42:18 +0100238 Return a reversed list of the sequence filtered. You can use this
239 for example for reverse iteration:
Armin Ronacher92f572f2007-02-26 22:17:32 +0100240
Armin Ronacher37a88512007-03-02 20:42:18 +0100241 .. sourcecode:: jinja
242
Armin Ronacherdbc945d2007-03-05 18:03:32 +0100243 {% for item in seq|reverse %}
Armin Ronacher37a88512007-03-02 20:42:18 +0100244 {{ item|e }}
245 {% endfor %}
Armin Ronacher92f572f2007-02-26 22:17:32 +0100246 """
Christoph Hack2751d942008-04-09 12:14:55 +0200247 try:
248 return value[::-1]
249 except:
250 l = list(value)
251 l.reverse()
252 return l
Armin Ronacher92f572f2007-02-26 22:17:32 +0100253
254
Armin Ronacherfed86c12007-02-27 10:31:14 +0100255def do_center(value, width=80):
256 """
Armin Ronacherfed86c12007-02-27 10:31:14 +0100257 Centers the value in a field of a given width.
258 """
Christoph Hack2751d942008-04-09 12:14:55 +0200259 return unicode(value).center(width)
Armin Ronacherfed86c12007-02-27 10:31:14 +0100260
261
Christoph Hack2751d942008-04-09 12:14:55 +0200262def do_first(seq):
Armin Ronacherfed86c12007-02-27 10:31:14 +0100263 """
Armin Ronacher37a88512007-03-02 20:42:18 +0100264 Return the frist item of a sequence.
Armin Ronacherfed86c12007-02-27 10:31:14 +0100265 """
Christoph Hack2751d942008-04-09 12:14:55 +0200266 try:
267 return iter(seq).next()
268 except StopIteration:
269 return env.undefined_singleton
Armin Ronacherfed86c12007-02-27 10:31:14 +0100270
271
Christoph Hack2751d942008-04-09 12:14:55 +0200272def do_last(seq):
Armin Ronacherfed86c12007-02-27 10:31:14 +0100273 """
Armin Ronacherfed86c12007-02-27 10:31:14 +0100274 Return the last item of a sequence.
275 """
Christoph Hack2751d942008-04-09 12:14:55 +0200276 try:
277 return iter(reversed(seq)).next()
278 except StopIteration:
279 return env.undefined_singleton
Armin Ronacherfed86c12007-02-27 10:31:14 +0100280
281
282def do_random():
283 """
Armin Ronacherfed86c12007-02-27 10:31:14 +0100284 Return a random item from the sequence.
285 """
Christoph Hack2751d942008-04-09 12:14:55 +0200286 try:
287 return choice(seq)
288 except IndexError:
289 return env.undefined_singleton
Armin Ronacherfed86c12007-02-27 10:31:14 +0100290
291
Christoph Hack2751d942008-04-09 12:14:55 +0200292def do_urlencode(value):
Armin Ronacherfed86c12007-02-27 10:31:14 +0100293 """
Armin Ronacherfed86c12007-02-27 10:31:14 +0100294 urlencode a string or directory.
Armin Ronacher37a88512007-03-02 20:42:18 +0100295
296 .. sourcecode:: jinja
297
298 {{ {'foo': 'bar', 'blub': 'blah'}|urlencode }}
299 -> foo=bar&blub=blah
300
301 {{ 'Hello World' }}
302 -> Hello%20World
Armin Ronacherfed86c12007-02-27 10:31:14 +0100303 """
Christoph Hack2751d942008-04-09 12:14:55 +0200304 if isinstance(value, dict):
305 tmp = {}
306 for key, value in value.iteritems():
307 # XXX env.charset?
308 key = unicode(key).encode(env.charset)
309 value = unicode(value).encode(env.charset)
310 tmp[key] = value
311 return urlencode(tmp)
312 else:
313 # XXX: env.charset?
314 return quote(unicode(value).encode(env.charset))
Armin Ronacherfed86c12007-02-27 10:31:14 +0100315
316
Christoph Hack2751d942008-04-09 12:14:55 +0200317def do_jsonencode(value):
Armin Ronacherfed86c12007-02-27 10:31:14 +0100318 """
Armin Ronacherfed86c12007-02-27 10:31:14 +0100319 JSON dump a variable. just works if simplejson is installed.
Armin Ronacher37a88512007-03-02 20:42:18 +0100320
321 .. sourcecode:: jinja
322
323 {{ 'Hello World'|jsonencode }}
324 -> "Hello World"
Armin Ronacherfed86c12007-02-27 10:31:14 +0100325 """
326 global simplejson
327 try:
328 simplejson
329 except NameError:
330 import simplejson
Christoph Hack2751d942008-04-09 12:14:55 +0200331 return simplejson.dumps(value)
Armin Ronacherfed86c12007-02-27 10:31:14 +0100332
333
Armin Ronacher2b765132007-03-13 16:48:10 +0100334def do_filesizeformat():
335 """
336 Format the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB, 102
337 bytes, etc).
338 """
339 def wrapped(env, context, value):
340 # fail silently
341 try:
342 bytes = float(value)
343 except TypeError:
344 bytes = 0
345
346 if bytes < 1024:
347 return "%d Byte%s" % (bytes, bytes != 1 and 's' or '')
348 elif bytes < 1024 * 1024:
349 return "%.1f KB" % (bytes / 1024)
350 elif bytes < 1024 * 1024 * 1024:
351 return "%.1f MB" % (bytes / (1024 * 1024))
352 return "%.1f GB" % (bytes / (1024 * 1024 * 1024))
353 return wrapped
354
355
Christoph Hack2751d942008-04-09 12:14:55 +0200356def do_pprint(value, verbose=False):
Armin Ronacher2b765132007-03-13 16:48:10 +0100357 """
358 Pretty print a variable. Useful for debugging.
Armin Ronacherf2ce1262007-10-21 21:51:51 +0200359
360 With Jinja 1.2 onwards you can pass it a parameter. If this parameter
Armin Ronacher5f3f1362007-10-21 22:15:04 +0200361 is truthy the output will be more verbose (this requires `pretty`)
Armin Ronacher2b765132007-03-13 16:48:10 +0100362 """
Christoph Hack2751d942008-04-09 12:14:55 +0200363 return pformat(value, verbose=verbose)
Armin Ronacher2b765132007-03-13 16:48:10 +0100364
365
366def do_urlize(value, trim_url_limit=None, nofollow=False):
367 """
368 Converts URLs in plain text into clickable links.
369
370 If you pass the filter an additional integer it will shorten the urls
371 to that number. Also a third argument exists that makes the urls
372 "nofollow":
373
374 .. sourcecode:: jinja
375
376 {{ mytext|urlize(40, True) }}
377 links are shortened to 40 chars and defined with rel="nofollow"
378 """
Christoph Hack2751d942008-04-09 12:14:55 +0200379 return urlize(unicode(value), trim_url_limit, nofollow)
Armin Ronacher2b765132007-03-13 16:48:10 +0100380
381
382def do_indent(s, width=4, indentfirst=False):
383 """
384 {{ s|indent[ width[ indentfirst[ usetab]]] }}
385
386 Return a copy of the passed string, each line indented by
387 4 spaces. The first line is not indented. If you want to
388 change the number of spaces or indent the first line too
389 you can pass additional parameters to the filter:
390
391 .. sourcecode:: jinja
392
393 {{ mytext|indent(2, True) }}
394 indent by two spaces and indent the first line too.
395 """
396 indention = ' ' * width
397 if indentfirst:
398 return u'\n'.join([indention + line for line in s.splitlines()])
399 return s.replace('\n', '\n' + indention)
Armin Ronacher2b765132007-03-13 16:48:10 +0100400
401
402def do_truncate(s, length=255, killwords=False, end='...'):
403 """
Armin Ronacher2b765132007-03-13 16:48:10 +0100404 Return a truncated copy of the string. The length is specified
405 with the first parameter which defaults to ``255``. If the second
406 parameter is ``true`` the filter will cut the text at length. Otherwise
407 it will try to save the last word. If the text was in fact
408 truncated it will append an ellipsis sign (``"..."``). If you want a
409 different ellipsis sign than ``"..."`` you can specify it using the
410 third parameter.
411
412 .. sourcecode jinja::
413
414 {{ mytext|truncate(300, false, '&raquo;') }}
415 truncate mytext to 300 chars, don't split up words, use a
416 right pointing double arrow as ellipsis sign.
417 """
418 if len(s) <= length:
419 return s
420 elif killwords:
421 return s[:length] + end
422 words = s.split(' ')
423 result = []
424 m = 0
425 for word in words:
426 m += len(word) + 1
427 if m > length:
428 break
429 result.append(word)
430 result.append(end)
431 return u' '.join(result)
Armin Ronacher2b765132007-03-13 16:48:10 +0100432
433
434def do_wordwrap(s, pos=79, hard=False):
435 """
436 Return a copy of the string passed to the filter wrapped after
437 ``79`` characters. You can override this default using the first
438 parameter. If you set the second parameter to `true` Jinja will
439 also split words apart (usually a bad idea because it makes
440 reading hard).
441 """
442 if len(s) < pos:
443 return s
444 if hard:
445 return u'\n'.join([s[idx:idx + pos] for idx in
446 xrange(0, len(s), pos)])
447 # code from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/148061
448 return reduce(lambda line, word, pos=pos: u'%s%s%s' %
449 (line, u' \n'[(len(line)-line.rfind('\n') - 1 +
450 len(word.split('\n', 1)[0]) >= pos)],
451 word), s.split(' '))
Armin Ronacher2b765132007-03-13 16:48:10 +0100452
453
454def do_wordcount(s):
455 """
456 Count the words in that string.
457 """
458 return len([x for x in s.split() if x])
Armin Ronacher2b765132007-03-13 16:48:10 +0100459
460
461def do_textile(s):
462 """
463 Prase the string using textile.
464
465 requires the `PyTextile`_ library.
466
467 .. _PyTextile: http://dealmeida.net/projects/textile/
468 """
469 from textile import textile
Armin Ronacher8712dad2007-06-08 01:07:48 +0200470 return textile(s.encode('utf-8')).decode('utf-8')
Armin Ronacher2b765132007-03-13 16:48:10 +0100471
472
473def do_markdown(s):
474 """
475 Parse the string using markdown.
476
477 requires the `Python-markdown`_ library.
478
479 .. _Python-markdown: http://www.freewisdom.org/projects/python-markdown/
480 """
481 from markdown import markdown
Armin Ronacher8712dad2007-06-08 01:07:48 +0200482 return markdown(s.encode('utf-8')).decode('utf-8')
Armin Ronacher2b765132007-03-13 16:48:10 +0100483
484
485def do_rst(s):
486 """
487 Parse the string using the reStructuredText parser from the
488 docutils package.
489
490 requires `docutils`_.
491
Georg Brandlfde3ff22007-07-18 09:27:01 +0200492 .. _docutils: http://docutils.sourceforge.net/
Armin Ronacher2b765132007-03-13 16:48:10 +0100493 """
Armin Ronacher8712dad2007-06-08 01:07:48 +0200494 from docutils.core import publish_parts
495 parts = publish_parts(source=s, writer_name='html4css1')
496 return parts['fragment']
Armin Ronacher2b765132007-03-13 16:48:10 +0100497
Armin Ronacherab45b842007-03-18 20:47:50 +0100498def do_int(default=0):
Armin Ronacher2b765132007-03-13 16:48:10 +0100499 """
Armin Ronacherab45b842007-03-18 20:47:50 +0100500 Convert the value into an integer. If the
501 conversion doesn't work it will return ``0``. You can
502 override this default using the first parameter.
Armin Ronacher2b765132007-03-13 16:48:10 +0100503 """
504 def wrapped(env, context, value):
Armin Ronacherab45b842007-03-18 20:47:50 +0100505 try:
506 return int(value)
507 except (TypeError, ValueError):
508 try:
509 return int(float(value))
510 except (TypeError, ValueError):
511 return default
Armin Ronacher2b765132007-03-13 16:48:10 +0100512 return wrapped
513
514
Armin Ronacherab45b842007-03-18 20:47:50 +0100515def do_float(default=0.0):
Armin Ronacher2b765132007-03-13 16:48:10 +0100516 """
Armin Ronacherab45b842007-03-18 20:47:50 +0100517 Convert the value into a floating point number. If the
518 conversion doesn't work it will return ``0.0``. You can
519 override this default using the first parameter.
Armin Ronacher2b765132007-03-13 16:48:10 +0100520 """
521 def wrapped(env, context, value):
Armin Ronacherab45b842007-03-18 20:47:50 +0100522 try:
523 return float(value)
524 except (TypeError, ValueError):
525 return default
Armin Ronacher2b765132007-03-13 16:48:10 +0100526 return wrapped
527
528
529def do_string():
530 """
531 Convert the value into an string.
532 """
533 return lambda e, c, v: e.to_unicode(v)
534
535
Armin Ronacherf0f4deb2007-03-18 23:10:15 +0100536def do_format(*args):
537 """
538 Apply python string formatting on an object:
539
540 .. sourcecode:: jinja
541
542 {{ "%s - %s"|format("Hello?", "Foo!") }}
543 -> Hello? - Foo!
544
545 Note that you cannot use the mapping syntax (``%(name)s``)
Armin Ronacher49659872007-05-12 23:29:33 +0200546 like in python. Use `|dformat` for that.
Armin Ronacherf0f4deb2007-03-18 23:10:15 +0100547 """
548 def wrapped(env, context, value):
549 return env.to_unicode(value) % args
550 return wrapped
551
552
Armin Ronacher49659872007-05-12 23:29:33 +0200553def do_dformat(d):
554 """
555 Apply python mapping string formatting on an object:
556
557 .. sourcecode:: jinja
558
559 {{ "Hello %(username)s!"|dformat({'username': 'John Doe'}) }}
560 -> Hello John Doe!
561
562 This is useful when adding variables to translateable
563 string expressions.
564
565 *New in Jinja 1.1*
566 """
567 if not isinstance(d, dict):
568 raise FilterArgumentError('dict required')
569 def wrapped(env, context, value):
570 return env.to_unicode(value) % d
571 return wrapped
572
573
Armin Ronacher566295e2007-03-19 13:19:34 +0100574def do_trim(value):
575 """
576 Strip leading and trailing whitespace.
577 """
578 return value.strip()
Armin Ronacher566295e2007-03-19 13:19:34 +0100579
580
581def do_capture(name='captured', clean=False):
Armin Ronacherf0f4deb2007-03-18 23:10:15 +0100582 """
583 Store the value in a variable called ``captured`` or a variable
584 with the name provided. Useful for filter blocks:
585
586 .. sourcecode:: jinja
587
588 {% filter capture('foo') %}
Armin Ronacher566295e2007-03-19 13:19:34 +0100589 ...
590 {% endfilter %}
591 {{ foo }}
592
593 This will output "..." two times. One time from the filter block
594 and one time from the variable. If you don't want the filter to
595 output something you can use it in `clean` mode:
596
597 .. sourcecode:: jinja
598
599 {% filter capture('foo', True) %}
600 ...
Armin Ronacherf0f4deb2007-03-18 23:10:15 +0100601 {% endfilter %}
602 {{ foo }}
603 """
Armin Ronacher954153b2007-04-01 12:07:21 +0200604 if not isinstance(name, basestring):
Armin Ronacher566295e2007-03-19 13:19:34 +0100605 raise FilterArgumentError('You can only capture into variables')
Armin Ronacher0b666572007-03-18 23:15:23 +0100606 def wrapped(env, context, value):
607 context[name] = value
Armin Ronacher566295e2007-03-19 13:19:34 +0100608 if clean:
Armin Ronacherfb5bebc2007-04-27 18:24:19 +0200609 return TemplateData()
Armin Ronacher0b666572007-03-18 23:15:23 +0100610 return value
611 return wrapped
Armin Ronacherf0f4deb2007-03-18 23:10:15 +0100612
613
Armin Ronacher9bcd4112007-05-29 14:17:24 +0200614def do_striptags(value):
Georg Brandlaf31e4d2007-04-15 00:47:37 +0200615 """
616 Strip SGML/XML tags and replace adjacent whitespace by one space.
Armin Ronacher21580912007-04-17 17:13:10 +0200617
Georg Brandlaf31e4d2007-04-15 00:47:37 +0200618 *new in Jinja 1.1*
619 """
Armin Ronacher9bcd4112007-05-29 14:17:24 +0200620 return ' '.join(_striptags_re.sub('', value).split())
Georg Brandlaf31e4d2007-04-15 00:47:37 +0200621
622
Armin Ronacherd071f952007-04-13 22:32:11 +0200623def do_slice(slices, fill_with=None):
624 """
625 Slice an iterator and return a list of lists containing
626 those items. Useful if you want to create a div containing
627 three div tags that represent columns:
628
Armin Ronacher89376072007-04-13 22:34:35 +0200629 .. sourcecode:: html+jinja
Armin Ronacherd071f952007-04-13 22:32:11 +0200630
631 <div class="columwrapper">
632 {%- for column in items|slice(3) %}
633 <ul class="column-{{ loop.index }}">
634 {%- for item in column %}
635 <li>{{ item }}</li>
636 {%- endfor %}
637 </ul>
638 {%- endfor %}
639 </div>
640
Armin Ronachereec31382007-04-14 14:50:45 +0200641 If you pass it a second argument it's used to fill missing
642 values on the last iteration.
643
644 *new in Jinja 1.1*
Armin Ronacherd071f952007-04-13 22:32:11 +0200645 """
646 def wrapped(env, context, value):
647 result = []
648 seq = list(value)
649 length = len(seq)
650 items_per_slice = length // slices
651 slices_with_extra = length % slices
652 offset = 0
653 for slice_number in xrange(slices):
654 start = offset + slice_number * items_per_slice
655 if slice_number < slices_with_extra:
656 offset += 1
657 end = offset + (slice_number + 1) * items_per_slice
658 tmp = seq[start:end]
659 if fill_with is not None and slice_number >= slices_with_extra:
660 tmp.append(fill_with)
661 result.append(tmp)
662 return result
663 return wrapped
664
665
666def do_batch(linecount, fill_with=None):
667 """
668 A filter that batches items. It works pretty much like `slice`
669 just the other way round. It returns a list of lists with the
670 given number of items. If you provide a second parameter this
671 is used to fill missing items. See this example:
672
Armin Ronacher89376072007-04-13 22:34:35 +0200673 .. sourcecode:: html+jinja
Armin Ronacherd071f952007-04-13 22:32:11 +0200674
675 <table>
676 {%- for row in items|batch(3, '&nbsp;') %}
677 <tr>
678 {%- for column in row %}
679 <tr>{{ column }}</td>
680 {%- endfor %}
681 </tr>
682 {%- endfor %}
683 </table>
684
Armin Ronachereec31382007-04-14 14:50:45 +0200685 *new in Jinja 1.1*
Armin Ronacherd071f952007-04-13 22:32:11 +0200686 """
687 def wrapped(env, context, value):
688 result = []
689 tmp = []
690 for item in value:
691 if len(tmp) == linecount:
692 result.append(tmp)
693 tmp = []
694 tmp.append(item)
695 if tmp:
696 if fill_with is not None and len(tmp) < linecount:
697 tmp += [fill_with] * (linecount - len(tmp))
698 result.append(tmp)
699 return result
700 return wrapped
701
702
Armin Ronachereec31382007-04-14 14:50:45 +0200703def do_sum():
704 """
705 Sum up the given sequence of numbers.
Armin Ronachera38b3122007-04-15 00:49:13 +0200706
707 *new in Jinja 1.1*
Armin Ronachereec31382007-04-14 14:50:45 +0200708 """
709 def wrapped(env, context, value):
710 return sum(value)
711 return wrapped
712
713
714def do_abs():
715 """
716 Return the absolute value of a number.
Armin Ronachera38b3122007-04-15 00:49:13 +0200717
718 *new in Jinja 1.1*
Armin Ronachereec31382007-04-14 14:50:45 +0200719 """
720 def wrapped(env, context, value):
721 return abs(value)
722 return wrapped
723
724
725def do_round(precision=0, method='common'):
726 """
727 Round the number to a given precision. The first
728 parameter specifies the precision (default is ``0``), the
729 second the rounding method:
730
731 - ``'common'`` rounds either up or down
732 - ``'ceil'`` always rounds up
733 - ``'floor'`` always rounds down
734
735 If you don't specify a method ``'common'`` is used.
736
737 .. sourcecode:: jinja
738
739 {{ 42.55|round }}
740 -> 43
741 {{ 42.55|round(1, 'floor') }}
742 -> 42.5
Armin Ronachera38b3122007-04-15 00:49:13 +0200743
744 *new in Jinja 1.1*
Armin Ronachereec31382007-04-14 14:50:45 +0200745 """
746 if not method in ('common', 'ceil', 'floor'):
747 raise FilterArgumentError('method must be common, ceil or floor')
748 if precision < 0:
749 raise FilterArgumentError('precision must be a postive integer '
750 'or zero.')
751 def wrapped(env, context, value):
752 if method == 'common':
753 return round(value, precision)
754 import math
755 func = getattr(math, method)
Armin Ronacher21580912007-04-17 17:13:10 +0200756 if precision:
757 return func(value * 10 * precision) / (10 * precision)
758 else:
759 return func(value)
Armin Ronachereec31382007-04-14 14:50:45 +0200760 return wrapped
761
762
Armin Ronacher9bcd4112007-05-29 14:17:24 +0200763def do_sort(reverse=False):
764 """
765 Sort a sequence. Per default it sorts ascending, if you pass it
766 `True` as first argument it will reverse the sorting.
767
768 *new in Jinja 1.1*
769 """
770 def wrapped(env, context, value):
771 return sorted(value, reverse=reverse)
772 return wrapped
773
774
Armin Ronachere39a5d22007-06-23 21:11:53 +0200775def do_groupby(attribute):
776 """
777 Group a sequence of objects by a common attribute.
778
779 If you for example have a list of dicts or objects that represent persons
780 with `gender`, `first_name` and `last_name` attributes and you want to
781 group all users by genders you can do something like the following
782 snippet:
783
784 .. sourcecode:: html+jinja
785
786 <ul>
787 {% for group in persons|groupby('gender') %}
788 <li>{{ group.grouper }}<ul>
789 {% for person in group.list %}
790 <li>{{ person.first_name }} {{ person.last_name }}</li>
791 {% endfor %}</ul></li>
792 {% endfor %}
793 </ul>
794
795 As you can see the item we're grouping by is stored in the `grouper`
796 attribute and the `list` contains all the objects that have this grouper
797 in common.
Armin Ronacher69ddc582007-06-24 12:37:13 +0200798
799 *New in Jinja 1.2*
Armin Ronachere39a5d22007-06-23 21:11:53 +0200800 """
801 def wrapped(env, context, value):
802 expr = lambda x: env.get_attribute(x, attribute)
Armin Ronacher30ffab12007-07-10 20:51:47 +0200803 return sorted([{
Armin Ronachere39a5d22007-06-23 21:11:53 +0200804 'grouper': a,
805 'list': list(b)
Armin Ronacher30ffab12007-07-10 20:51:47 +0200806 } for a, b in groupby(sorted(value, key=expr), expr)],
807 key=itemgetter('grouper'))
Armin Ronachere39a5d22007-06-23 21:11:53 +0200808 return wrapped
809
810
Armin Ronacher1cc232c2007-09-07 17:52:41 +0200811def do_getattribute(attribute):
812 """
813 Get one attribute from an object. Normally you don't have to use this
814 filter because the attribute and subscript expressions try to either
815 get an attribute of an object or an item. In some situations it could
816 be that there is an item *and* an attribute with the same name. In that
817 situation only the item is returned, never the attribute.
818
819 .. sourcecode:: jinja
820
821 {{ foo.bar }} -> {{ foo|getattribute('bar') }}
822
823 *New in Jinja 1.2*
824 """
825 def wrapped(env, context, value):
826 try:
827 return get_attribute(value, attribute)
828 except (SecurityException, AttributeError):
829 return env.undefined_singleton
830 return wrapped
831
832
833def do_getitem(key):
834 """
835 This filter basically works like the normal subscript expression but
836 it doesn't fall back to attribute lookup. If an item does not exist for
837 an object undefined is returned.
838
839 .. sourcecode:: jinja
840
841 {{ foo.bar }} -> {{ foo|getitem('bar') }}
842
843 *New in Jinja 1.2*
844 """
845 def wrapped(env, context, value):
846 try:
847 return value[key]
848 except (TypeError, KeyError, IndexError, AttributeError):
849 return env.undefined_singleton
850 return wrapped
851
852
Armin Ronacher92f572f2007-02-26 22:17:32 +0100853FILTERS = {
854 'replace': do_replace,
855 'upper': do_upper,
856 'lower': do_lower,
857 'escape': do_escape,
858 'e': do_escape,
Armin Ronacher450756b2007-04-15 15:13:59 +0200859 'xmlattr': do_xmlattr,
Armin Ronacher92f572f2007-02-26 22:17:32 +0100860 'capitalize': do_capitalize,
861 'title': do_title,
862 'default': do_default,
863 'join': do_join,
864 'count': do_count,
Armin Ronacher2b765132007-03-13 16:48:10 +0100865 'dictsort': do_dictsort,
Armin Ronacher68c87252007-03-06 17:35:54 +0100866 'length': do_count,
Armin Ronacherdbc945d2007-03-05 18:03:32 +0100867 'reverse': do_reverse,
Armin Ronacherfed86c12007-02-27 10:31:14 +0100868 'center': do_center,
869 'title': do_title,
870 'capitalize': do_capitalize,
871 'first': do_first,
872 'last': do_last,
873 'random': do_random,
874 'urlencode': do_urlencode,
Armin Ronacher2b765132007-03-13 16:48:10 +0100875 'jsonencode': do_jsonencode,
876 'filesizeformat': do_filesizeformat,
877 'pprint': do_pprint,
878 'indent': do_indent,
879 'truncate': do_truncate,
880 'wordwrap': do_wordwrap,
881 'wordcount': do_wordcount,
882 'textile': do_textile,
883 'markdown': do_markdown,
884 'rst': do_rst,
885 'int': do_int,
886 'float': do_float,
Tassilo Schweyer934cb712007-03-14 18:04:07 +0100887 'string': do_string,
Armin Ronacherf0f4deb2007-03-18 23:10:15 +0100888 'urlize': do_urlize,
889 'format': do_format,
Armin Ronacher49659872007-05-12 23:29:33 +0200890 'dformat': do_dformat,
Armin Ronacher566295e2007-03-19 13:19:34 +0100891 'capture': do_capture,
Armin Ronacherd071f952007-04-13 22:32:11 +0200892 'trim': do_trim,
Georg Brandlaf31e4d2007-04-15 00:47:37 +0200893 'striptags': do_striptags,
Armin Ronacherd071f952007-04-13 22:32:11 +0200894 'slice': do_slice,
Armin Ronachereec31382007-04-14 14:50:45 +0200895 'batch': do_batch,
896 'sum': do_sum,
897 'abs': do_abs,
Armin Ronacher9bcd4112007-05-29 14:17:24 +0200898 'round': do_round,
Armin Ronachere39a5d22007-06-23 21:11:53 +0200899 'sort': do_sort,
Armin Ronacher1cc232c2007-09-07 17:52:41 +0200900 'groupby': do_groupby,
901 'getattribute': do_getattribute,
902 'getitem': do_getitem
Armin Ronacher92f572f2007-02-26 22:17:32 +0100903}