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
15 from operator import itemgetter
17 itemgetter = lambda a: lambda b: b[a]
18 from urllib import urlencode, quote
19 from itertools import imap, groupby
20 from jinja2.utils import Markup, escape, pformat, urlize, soft_unicode
21 from jinja2.runtime import Undefined
22 from jinja2.exceptions import FilterArgumentError
25 _striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)')
29 """Decorator for marking context dependent filters. The current context
30 argument will be passed as first argument.
32 if getattr(f, 'environmentfilter', False):
33 raise TypeError('filter already marked as environment filter')
34 f.contextfilter = True
38 def environmentfilter(f):
39 """Decorator for marking evironment dependent filters. The environment
40 used for the template is passed to the filter as first argument.
42 if getattr(f, 'contextfilter', False):
43 raise TypeError('filter already marked as context filter')
44 f.environmentfilter = True
48 def do_forceescape(value):
49 """Enforce HTML escaping. This will probably double escape variables."""
50 if hasattr(value, '__html__'):
51 value = value.__html__()
52 return escape(unicode(value))
56 def do_replace(environment, s, old, new, count=None):
57 """Return a copy of the value with all occurrences of a substring
58 replaced with a new one. The first argument is the substring
59 that should be replaced, the second is the replacement string.
60 If the optional third argument ``count`` is given, only the first
61 ``count`` occurrences are replaced:
65 {{ "Hello World"|replace("Hello", "Goodbye") }}
68 {{ "aaaaargh"|replace("a", "d'oh, ", 2) }}
73 if not environment.autoescape:
74 return unicode(s).replace(unicode(old), unicode(new), count)
75 if hasattr(old, '__html__') or hasattr(new, '__html__') and \
76 not hasattr(s, '__html__'):
80 return s.replace(old, new, count)
84 """Convert a value to uppercase."""
85 return soft_unicode(s).upper()
89 """Convert a value to lowercase."""
90 return soft_unicode(s).lower()
94 def do_xmlattr(_environment, d, autospace=True):
95 """Create an SGML/XML attribute string based on the items in a dict.
96 All values that are neither `none` nor `undefined` are automatically
99 .. sourcecode:: html+jinja
101 <ul{{ {'class': 'my_list', 'missing': None,
102 'id': 'list-%d'|format(variable)}|xmlattr }}>
106 Results in something like this:
110 <ul class="my_list" id="list-42">
114 As you can see it automatically prepends a space in front of the item
115 if the filter returned something unless the second parameter is false.
118 u'%s="%s"' % (escape(key), escape(value))
119 for key, value in d.iteritems()
120 if value is not None and not isinstance(value, Undefined)
124 if _environment.autoescape:
129 def do_capitalize(s):
130 """Capitalize a value. The first character will be uppercase, all others
133 return soft_unicode(s).capitalize()
137 """Return a titlecased version of the value. I.e. words will start with
138 uppercase letters, all remaining characters are lowercase.
140 return soft_unicode(s).title()
143 def do_dictsort(value, case_sensitive=False, by='key'):
144 """ Sort a dict and yield (key, value) pairs. Because python dicts are
145 unsorted you may want to use this function to order them by either
148 .. sourcecode:: jinja
150 {% for item in mydict|dictsort %}
151 sort the dict by key, case insensitive
153 {% for item in mydict|dicsort(true) %}
154 sort the dict by key, case sensitive
156 {% for item in mydict|dictsort(false, 'value') %}
157 sort the dict by key, case insensitive, sorted
158 normally and ordered by value.
165 raise FilterArgumentError('You can only sort by either '
169 if isinstance(value, basestring):
170 value = unicode(value)
171 if not case_sensitive:
172 value = value.lower()
175 return sorted(value.items(), key=sort_func)
178 def do_default(value, default_value=u'', boolean=False):
179 """If the value is undefined it will return the passed default value,
180 otherwise the value of the variable:
182 .. sourcecode:: jinja
184 {{ my_variable|default('my_variable is not defined') }}
186 This will output the value of ``my_variable`` if the variable was
187 defined, otherwise ``'my_variable is not defined'``. If you want
188 to use default with variables that evaluate to false you have to
189 set the second parameter to `true`:
191 .. sourcecode:: jinja
193 {{ ''|default('the string was empty', true) }}
195 if (boolean and not value) or isinstance(value, Undefined):
201 def do_join(environment, value, d=u''):
202 """Return a string which is the concatenation of the strings in the
203 sequence. The separator between elements is an empty string per
204 default, you can define ith with the optional parameter:
206 .. sourcecode:: jinja
208 {{ [1, 2, 3]|join('|') }}
214 # no automatic escaping? joining is a lot eaiser then
215 if not environment.autoescape:
216 return unicode(d).join(imap(unicode, value))
218 # if the delimiter doesn't have an html representation we check
219 # if any of the items has. If yes we do a coercion to Markup
220 if not hasattr(d, '__html__'):
223 for idx, item in enumerate(value):
224 if hasattr(item, '__html__'):
227 value[idx] = unicode(item)
234 # no html involved, to normal joining
235 return soft_unicode(d).join(imap(soft_unicode, value))
238 def do_center(value, width=80):
239 """Centers the value in a field of a given width."""
240 return unicode(value).center(width)
244 def do_first(environment, seq):
245 """Return the frist item of a sequence."""
247 return iter(seq).next()
248 except StopIteration:
249 return environment.undefined('No first item, sequence was empty.')
253 def do_last(environment, seq):
254 """Return the last item of a sequence."""
256 return iter(reversed(seq)).next()
257 except StopIteration:
258 return environment.undefined('No last item, sequence was empty.')
262 def do_random(environment, seq):
263 """Return a random item from the sequence."""
267 return environment.undefined('No random item, sequence was empty.')
270 def do_filesizeformat(value):
271 """Format the value like a 'human-readable' file size (i.e. 13 KB,
272 4.1 MB, 102 bytes, etc).
281 return "%d Byte%s" % (bytes, bytes != 1 and 's' or '')
282 elif bytes < 1024 * 1024:
283 return "%.1f KB" % (bytes / 1024)
284 elif bytes < 1024 * 1024 * 1024:
285 return "%.1f MB" % (bytes / (1024 * 1024))
286 return "%.1f GB" % (bytes / (1024 * 1024 * 1024))
289 def do_pprint(value, verbose=False):
290 """Pretty print a variable. Useful for debugging.
292 With Jinja 1.2 onwards you can pass it a parameter. If this parameter
293 is truthy the output will be more verbose (this requires `pretty`)
295 return pformat(value, verbose=verbose)
298 def do_urlize(value, trim_url_limit=None, nofollow=False):
299 """Converts URLs in plain text into clickable links.
301 If you pass the filter an additional integer it will shorten the urls
302 to that number. Also a third argument exists that makes the urls
305 .. sourcecode:: jinja
307 {{ mytext|urlize(40, True) }}
308 links are shortened to 40 chars and defined with rel="nofollow"
310 return urlize(soft_unicode(value), trim_url_limit, nofollow)
313 def do_indent(s, width=4, indentfirst=False):
314 """Return a copy of the passed string, each line indented by
315 4 spaces. The first line is not indented. If you want to
316 change the number of spaces or indent the first line too
317 you can pass additional parameters to the filter:
319 .. sourcecode:: jinja
321 {{ mytext|indent(2, True) }}
322 indent by two spaces and indent the first line too.
324 indention = ' ' * width
326 return u'\n'.join(indention + line for line in s.splitlines())
327 return s.replace('\n', '\n' + indention)
330 def do_truncate(s, length=255, killwords=False, end='...'):
331 """Return a truncated copy of the string. The length is specified
332 with the first parameter which defaults to ``255``. If the second
333 parameter is ``true`` the filter will cut the text at length. Otherwise
334 it will try to save the last word. If the text was in fact
335 truncated it will append an ellipsis sign (``"..."``). If you want a
336 different ellipsis sign than ``"..."`` you can specify it using the
339 .. sourcecode jinja::
341 {{ mytext|truncate(300, false, '»') }}
342 truncate mytext to 300 chars, don't split up words, use a
343 right pointing double arrow as ellipsis sign.
348 return s[:length] + end
358 return u' '.join(result)
361 def do_wordwrap(s, pos=79, hard=False):
363 Return a copy of the string passed to the filter wrapped after
364 ``79`` characters. You can override this default using the first
365 parameter. If you set the second parameter to `true` Jinja will
366 also split words apart (usually a bad idea because it makes
372 return u'\n'.join(s[idx:idx + pos] for idx in
373 xrange(0, len(s), pos))
375 # TODO: switch to wordwrap.wrap
376 # code from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/148061
377 return reduce(lambda line, word, pos=pos: u'%s%s%s' %
378 (line, u' \n'[(len(line)-line.rfind('\n') - 1 +
379 len(word.split('\n', 1)[0]) >= pos)],
384 """Count the words in that string."""
385 return len(s.split())
388 def do_int(value, default=0):
389 """Convert the value into an integer. If the
390 conversion doesn't work it will return ``0``. You can
391 override this default using the first parameter.
395 except (TypeError, ValueError):
396 # this quirk is necessary so that "42.23"|int gives 42.
398 return int(float(value))
399 except (TypeError, ValueError):
403 def do_float(value, default=0.0):
404 """Convert the value into a floating point number. If the
405 conversion doesn't work it will return ``0.0``. You can
406 override this default using the first parameter.
410 except (TypeError, ValueError):
414 def do_format(value, *args, **kwargs):
416 Apply python string formatting on an object:
418 .. sourcecode:: jinja
420 {{ "%s - %s"|format("Hello?", "Foo!") }}
424 kwargs.update(idx, arg in enumerate(args))
426 return soft_unicode(value) % args
430 """Strip leading and trailing whitespace."""
431 return soft_unicode(value).strip()
434 def do_striptags(value):
435 """Strip SGML/XML tags and replace adjacent whitespace by one space.
437 if hasattr(value, '__html__'):
438 value = value.__html__()
439 return u' '.join(_striptags_re.sub('', value).split())
442 def do_slice(value, slices, fill_with=None):
443 """Slice an iterator and return a list of lists containing
444 those items. Useful if you want to create a div containing
445 three div tags that represent columns:
447 .. sourcecode:: html+jinja
449 <div class="columwrapper">
450 {%- for column in items|slice(3) %}
451 <ul class="column-{{ loop.index }}">
452 {%- for item in column %}
459 If you pass it a second argument it's used to fill missing
460 values on the last iteration.
464 items_per_slice = length // slices
465 slices_with_extra = length % slices
467 for slice_number in xrange(slices):
468 start = offset + slice_number * items_per_slice
469 if slice_number < slices_with_extra:
471 end = offset + (slice_number + 1) * items_per_slice
473 if fill_with is not None and slice_number >= slices_with_extra:
474 tmp.append(fill_with)
478 def do_batch(value, linecount, fill_with=None):
480 A filter that batches items. It works pretty much like `slice`
481 just the other way round. It returns a list of lists with the
482 given number of items. If you provide a second parameter this
483 is used to fill missing items. See this example:
485 .. sourcecode:: html+jinja
488 {%- for row in items|batch(3, ' ') %}
490 {%- for column in row %}
491 <tr>{{ column }}</td>
500 if len(tmp) == linecount:
505 if fill_with is not None and len(tmp) < linecount:
506 tmp += [fill_with] * (linecount - len(tmp))
510 def do_round(value, precision=0, method='common'):
511 """Round the number to a given precision. The first
512 parameter specifies the precision (default is ``0``), the
513 second the rounding method:
515 - ``'common'`` rounds either up or down
516 - ``'ceil'`` always rounds up
517 - ``'floor'`` always rounds down
519 If you don't specify a method ``'common'`` is used.
521 .. sourcecode:: jinja
525 {{ 42.55|round(1, 'floor') }}
528 if not method in ('common', 'ceil', 'floor'):
529 raise FilterArgumentError('method must be common, ceil or floor')
531 raise FilterArgumentError('precision must be a postive integer '
533 if method == 'common':
534 return round(value, precision)
535 func = getattr(math, method)
537 return func(value * 10 * precision) / (10 * precision)
542 def do_sort(value, reverse=False):
543 """Sort a sequence. Per default it sorts ascending, if you pass it
544 true as first argument it will reverse the sorting.
546 return sorted(value, reverse=reverse)
550 def do_groupby(environment, value, attribute):
551 """Group a sequence of objects by a common attribute.
553 If you for example have a list of dicts or objects that represent persons
554 with `gender`, `first_name` and `last_name` attributes and you want to
555 group all users by genders you can do something like the following
558 .. sourcecode:: html+jinja
561 {% for group in persons|groupby('gender') %}
562 <li>{{ group.grouper }}<ul>
563 {% for person in group.list %}
564 <li>{{ person.first_name }} {{ person.last_name }}</li>
565 {% endfor %}</ul></li>
569 Additionally it's possible to use tuple unpacking for the grouper and
572 .. sourcecode:: html+jinja
575 {% for grouper, list in persons|groupby('gender') %}
580 As you can see the item we're grouping by is stored in the `grouper`
581 attribute and the `list` contains all the objects that have this grouper
584 expr = lambda x: environment.subscribe(x, attribute)
585 return sorted(map(_GroupTuple, groupby(sorted(value, key=expr), expr)))
588 class _GroupTuple(tuple):
590 grouper = property(itemgetter(0))
591 list = property(itemgetter(1))
593 def __new__(cls, (key, value)):
594 return tuple.__new__(cls, (key, list(value)))
598 """Convert the value into a list. If it was a string the returned list
599 will be a list of characters.
604 def do_mark_safe(value):
605 """Mark the value as safe which means that in an environment with automatic
606 escaping enabled this variable will not be escaped.
611 def do_reverse(value):
612 """Reverse the object or return an iterator the iterates over it the other
615 if isinstance(value, basestring):
618 return reversed(value)
625 raise FilterArgumentError('argument must be iterable')
629 'replace': do_replace,
634 'forceescape': do_forceescape,
635 'capitalize': do_capitalize,
637 'default': do_default,
641 'dictsort': do_dictsort,
643 'reverse': do_reverse,
647 'capitalize': do_capitalize,
651 'filesizeformat': do_filesizeformat,
653 'truncate': do_truncate,
654 'wordwrap': do_wordwrap,
655 'wordcount': do_wordcount,
658 'string': soft_unicode,
663 'striptags': do_striptags,
670 'groupby': do_groupby,
671 'safe': do_mark_safe,
672 'xmlattr': do_xmlattr