1 # -*- coding: utf-8 -*-
8 :copyright: 2008 by Armin Ronacher, Christoph Hack.
9 :license: BSD, see LICENSE for more details.
13 from random import choice
14 from operator import itemgetter
15 from itertools import imap, groupby
16 from jinja2.utils import Markup, escape, pformat, urlize, soft_unicode
17 from jinja2.runtime import Undefined
18 from jinja2.exceptions import FilterArgumentError
21 _striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)')
25 """Decorator for marking context dependent filters. The current context
26 argument will be passed as first argument.
28 if getattr(f, 'environmentfilter', False):
29 raise TypeError('filter already marked as environment filter')
30 f.contextfilter = True
34 def environmentfilter(f):
35 """Decorator for marking evironment dependent filters. The environment
36 used for the template is passed to the filter as first argument.
38 if getattr(f, 'contextfilter', False):
39 raise TypeError('filter already marked as context filter')
40 f.environmentfilter = True
44 def do_forceescape(value):
45 """Enforce HTML escaping. This will probably double escape variables."""
46 if hasattr(value, '__html__'):
47 value = value.__html__()
48 return escape(unicode(value))
52 def do_replace(environment, s, old, new, count=None):
53 """Return a copy of the value with all occurrences of a substring
54 replaced with a new one. The first argument is the substring
55 that should be replaced, the second is the replacement string.
56 If the optional third argument ``count`` is given, only the first
57 ``count`` occurrences are replaced:
61 {{ "Hello World"|replace("Hello", "Goodbye") }}
64 {{ "aaaaargh"|replace("a", "d'oh, ", 2) }}
69 if not environment.autoescape:
70 return unicode(s).replace(unicode(old), unicode(new), count)
71 if hasattr(old, '__html__') or hasattr(new, '__html__') and \
72 not hasattr(s, '__html__'):
76 return s.replace(old, new, count)
80 """Convert a value to uppercase."""
81 return soft_unicode(s).upper()
85 """Convert a value to lowercase."""
86 return soft_unicode(s).lower()
90 def do_xmlattr(_environment, d, autospace=True):
91 """Create an SGML/XML attribute string based on the items in a dict.
92 All values that are neither `none` nor `undefined` are automatically
95 .. sourcecode:: html+jinja
97 <ul{{ {'class': 'my_list', 'missing': None,
98 'id': 'list-%d'|format(variable)}|xmlattr }}>
102 Results in something like this:
106 <ul class="my_list" id="list-42">
110 As you can see it automatically prepends a space in front of the item
111 if the filter returned something unless the second parameter is false.
114 u'%s="%s"' % (escape(key), escape(value))
115 for key, value in d.iteritems()
116 if value is not None and not isinstance(value, Undefined)
120 if _environment.autoescape:
125 def do_capitalize(s):
126 """Capitalize a value. The first character will be uppercase, all others
129 return soft_unicode(s).capitalize()
133 """Return a titlecased version of the value. I.e. words will start with
134 uppercase letters, all remaining characters are lowercase.
136 return soft_unicode(s).title()
139 def do_dictsort(value, case_sensitive=False, by='key'):
140 """ Sort a dict and yield (key, value) pairs. Because python dicts are
141 unsorted you may want to use this function to order them by either
144 .. sourcecode:: jinja
146 {% for item in mydict|dictsort %}
147 sort the dict by key, case insensitive
149 {% for item in mydict|dicsort(true) %}
150 sort the dict by key, case sensitive
152 {% for item in mydict|dictsort(false, 'value') %}
153 sort the dict by key, case insensitive, sorted
154 normally and ordered by value.
161 raise FilterArgumentError('You can only sort by either '
165 if isinstance(value, basestring):
166 value = unicode(value)
167 if not case_sensitive:
168 value = value.lower()
171 return sorted(value.items(), key=sort_func)
174 def do_default(value, default_value=u'', boolean=False):
175 """If the value is undefined it will return the passed default value,
176 otherwise the value of the variable:
178 .. sourcecode:: jinja
180 {{ my_variable|default('my_variable is not defined') }}
182 This will output the value of ``my_variable`` if the variable was
183 defined, otherwise ``'my_variable is not defined'``. If you want
184 to use default with variables that evaluate to false you have to
185 set the second parameter to `true`:
187 .. sourcecode:: jinja
189 {{ ''|default('the string was empty', true) }}
191 if (boolean and not value) or isinstance(value, Undefined):
197 def do_join(environment, value, d=u''):
198 """Return a string which is the concatenation of the strings in the
199 sequence. The separator between elements is an empty string per
200 default, you can define it with the optional parameter:
202 .. sourcecode:: jinja
204 {{ [1, 2, 3]|join('|') }}
210 # no automatic escaping? joining is a lot eaiser then
211 if not environment.autoescape:
212 return unicode(d).join(imap(unicode, value))
214 # if the delimiter doesn't have an html representation we check
215 # if any of the items has. If yes we do a coercion to Markup
216 if not hasattr(d, '__html__'):
219 for idx, item in enumerate(value):
220 if hasattr(item, '__html__'):
223 value[idx] = unicode(item)
230 # no html involved, to normal joining
231 return soft_unicode(d).join(imap(soft_unicode, value))
234 def do_center(value, width=80):
235 """Centers the value in a field of a given width."""
236 return unicode(value).center(width)
240 def do_first(environment, seq):
241 """Return the first item of a sequence."""
243 return iter(seq).next()
244 except StopIteration:
245 return environment.undefined('No first item, sequence was empty.')
249 def do_last(environment, seq):
250 """Return the last item of a sequence."""
252 return iter(reversed(seq)).next()
253 except StopIteration:
254 return environment.undefined('No last item, sequence was empty.')
258 def do_random(environment, seq):
259 """Return a random item from the sequence."""
263 return environment.undefined('No random item, sequence was empty.')
266 def do_filesizeformat(value):
267 """Format the value like a 'human-readable' file size (i.e. 13 KB,
268 4.1 MB, 102 bytes, etc).
277 return "%d Byte%s" % (bytes, bytes != 1 and 's' or '')
278 elif bytes < 1024 * 1024:
279 return "%.1f KB" % (bytes / 1024)
280 elif bytes < 1024 * 1024 * 1024:
281 return "%.1f MB" % (bytes / (1024 * 1024))
282 return "%.1f GB" % (bytes / (1024 * 1024 * 1024))
285 def do_pprint(value, verbose=False):
286 """Pretty print a variable. Useful for debugging.
288 With Jinja 1.2 onwards you can pass it a parameter. If this parameter
289 is truthy the output will be more verbose (this requires `pretty`)
291 return pformat(value, verbose=verbose)
294 def do_urlize(value, trim_url_limit=None, nofollow=False):
295 """Converts URLs in plain text into clickable links.
297 If you pass the filter an additional integer it will shorten the urls
298 to that number. Also a third argument exists that makes the urls
301 .. sourcecode:: jinja
303 {{ mytext|urlize(40, True) }}
304 links are shortened to 40 chars and defined with rel="nofollow"
306 return urlize(soft_unicode(value), trim_url_limit, nofollow)
309 def do_indent(s, width=4, indentfirst=False):
310 """Return a copy of the passed string, each line indented by
311 4 spaces. The first line is not indented. If you want to
312 change the number of spaces or indent the first line too
313 you can pass additional parameters to the filter:
315 .. sourcecode:: jinja
317 {{ mytext|indent(2, True) }}
318 indent by two spaces and indent the first line too.
320 indention = ' ' * width
322 return u'\n'.join(indention + line for line in s.splitlines())
323 return s.replace('\n', '\n' + indention)
326 def do_truncate(s, length=255, killwords=False, end='...'):
327 """Return a truncated copy of the string. The length is specified
328 with the first parameter which defaults to ``255``. If the second
329 parameter is ``true`` the filter will cut the text at length. Otherwise
330 it will try to save the last word. If the text was in fact
331 truncated it will append an ellipsis sign (``"..."``). If you want a
332 different ellipsis sign than ``"..."`` you can specify it using the
335 .. sourcecode jinja::
337 {{ mytext|truncate(300, false, '»') }}
338 truncate mytext to 300 chars, don't split up words, use a
339 right pointing double arrow as ellipsis sign.
344 return s[:length] + end
354 return u' '.join(result)
357 def do_wordwrap(s, pos=79, hard=False):
359 Return a copy of the string passed to the filter wrapped after
360 ``79`` characters. You can override this default using the first
361 parameter. If you set the second parameter to `true` Jinja will
362 also split words apart (usually a bad idea because it makes
368 return u'\n'.join(s[idx:idx + pos] for idx in
369 xrange(0, len(s), pos))
371 # TODO: switch to wordwrap.wrap
372 # code from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/148061
373 return reduce(lambda line, word, pos=pos: u'%s%s%s' %
374 (line, u' \n'[(len(line)-line.rfind('\n') - 1 +
375 len(word.split('\n', 1)[0]) >= pos)],
380 """Count the words in that string."""
381 return len(s.split())
384 def do_int(value, default=0):
385 """Convert the value into an integer. If the
386 conversion doesn't work it will return ``0``. You can
387 override this default using the first parameter.
391 except (TypeError, ValueError):
392 # this quirk is necessary so that "42.23"|int gives 42.
394 return int(float(value))
395 except (TypeError, ValueError):
399 def do_float(value, default=0.0):
400 """Convert the value into a floating point number. If the
401 conversion doesn't work it will return ``0.0``. You can
402 override this default using the first parameter.
406 except (TypeError, ValueError):
410 def do_format(value, *args, **kwargs):
412 Apply python string formatting on an object:
414 .. sourcecode:: jinja
416 {{ "%s - %s"|format("Hello?", "Foo!") }}
420 kwargs.update(idx, arg in enumerate(args))
422 return soft_unicode(value) % args
426 """Strip leading and trailing whitespace."""
427 return soft_unicode(value).strip()
430 def do_striptags(value):
431 """Strip SGML/XML tags and replace adjacent whitespace by one space.
433 if hasattr(value, '__html__'):
434 value = value.__html__()
435 return u' '.join(_striptags_re.sub('', value).split())
438 def do_slice(value, slices, fill_with=None):
439 """Slice an iterator and return a list of lists containing
440 those items. Useful if you want to create a div containing
441 three div tags that represent columns:
443 .. sourcecode:: html+jinja
445 <div class="columwrapper">
446 {%- for column in items|slice(3) %}
447 <ul class="column-{{ loop.index }}">
448 {%- for item in column %}
455 If you pass it a second argument it's used to fill missing
456 values on the last iteration.
460 items_per_slice = length // slices
461 slices_with_extra = length % slices
463 for slice_number in xrange(slices):
464 start = offset + slice_number * items_per_slice
465 if slice_number < slices_with_extra:
467 end = offset + (slice_number + 1) * items_per_slice
469 if fill_with is not None and slice_number >= slices_with_extra:
470 tmp.append(fill_with)
474 def do_batch(value, linecount, fill_with=None):
476 A filter that batches items. It works pretty much like `slice`
477 just the other way round. It returns a list of lists with the
478 given number of items. If you provide a second parameter this
479 is used to fill missing items. See this example:
481 .. sourcecode:: html+jinja
484 {%- for row in items|batch(3, ' ') %}
486 {%- for column in row %}
487 <tr>{{ column }}</td>
496 if len(tmp) == linecount:
501 if fill_with is not None and len(tmp) < linecount:
502 tmp += [fill_with] * (linecount - len(tmp))
506 def do_round(value, precision=0, method='common'):
507 """Round the number to a given precision. The first
508 parameter specifies the precision (default is ``0``), the
509 second the rounding method:
511 - ``'common'`` rounds either up or down
512 - ``'ceil'`` always rounds up
513 - ``'floor'`` always rounds down
515 If you don't specify a method ``'common'`` is used.
517 .. sourcecode:: jinja
521 {{ 42.55|round(1, 'floor') }}
524 if not method in ('common', 'ceil', 'floor'):
525 raise FilterArgumentError('method must be common, ceil or floor')
527 raise FilterArgumentError('precision must be a postive integer '
529 if method == 'common':
530 return round(value, precision)
531 func = getattr(math, method)
533 return func(value * 10 * precision) / (10 * precision)
538 def do_sort(value, reverse=False):
539 """Sort a sequence. Per default it sorts ascending, if you pass it
540 true as first argument it will reverse the sorting.
542 return sorted(value, reverse=reverse)
546 def do_groupby(environment, value, attribute):
547 """Group a sequence of objects by a common attribute.
549 If you for example have a list of dicts or objects that represent persons
550 with `gender`, `first_name` and `last_name` attributes and you want to
551 group all users by genders you can do something like the following
554 .. sourcecode:: html+jinja
557 {% for group in persons|groupby('gender') %}
558 <li>{{ group.grouper }}<ul>
559 {% for person in group.list %}
560 <li>{{ person.first_name }} {{ person.last_name }}</li>
561 {% endfor %}</ul></li>
565 Additionally it's possible to use tuple unpacking for the grouper and
568 .. sourcecode:: html+jinja
571 {% for grouper, list in persons|groupby('gender') %}
576 As you can see the item we're grouping by is stored in the `grouper`
577 attribute and the `list` contains all the objects that have this grouper
580 expr = lambda x: environment.subscribe(x, attribute)
581 return sorted(map(_GroupTuple, groupby(sorted(value, key=expr), expr)))
584 class _GroupTuple(tuple):
586 grouper = property(itemgetter(0))
587 list = property(itemgetter(1))
589 def __new__(cls, (key, value)):
590 return tuple.__new__(cls, (key, list(value)))
594 """Convert the value into a list. If it was a string the returned list
595 will be a list of characters.
600 def do_mark_safe(value):
601 """Mark the value as safe which means that in an environment with automatic
602 escaping enabled this variable will not be escaped.
607 def do_reverse(value):
608 """Reverse the object or return an iterator the iterates over it the other
611 if isinstance(value, basestring):
614 return reversed(value)
621 raise FilterArgumentError('argument must be iterable')
625 'replace': do_replace,
630 'forceescape': do_forceescape,
631 'capitalize': do_capitalize,
633 'default': do_default,
637 'dictsort': do_dictsort,
639 'reverse': do_reverse,
643 'capitalize': do_capitalize,
647 'filesizeformat': do_filesizeformat,
649 'truncate': do_truncate,
650 'wordwrap': do_wordwrap,
651 'wordcount': do_wordcount,
654 'string': soft_unicode,
659 'striptags': do_striptags,
666 'groupby': do_groupby,
667 'safe': do_mark_safe,
668 'xmlattr': do_xmlattr