1 # -*- coding: utf-8 -*-
8 :copyright: 2008 by Armin Ronacher, Christoph Hack.
9 :license: BSD, see LICENSE for more details.
14 from random import choice
15 from operator import itemgetter
16 from itertools import imap, groupby
17 from jinja2.utils import Markup, escape, pformat, urlize, soft_unicode
18 from jinja2.runtime import Undefined
19 from jinja2.exceptions import FilterArgumentError, SecurityError
22 _word_re = re.compile(r'\w+')
26 """Decorator for marking context dependent filters. The current
27 :class:`Context` will be passed as first argument.
29 if getattr(f, 'environmentfilter', False):
30 raise TypeError('filter already marked as environment filter')
31 f.contextfilter = True
35 def environmentfilter(f):
36 """Decorator for marking evironment dependent filters. The current
37 :class:`Environment` is passed to the filter as first argument.
39 if getattr(f, 'contextfilter', False):
40 raise TypeError('filter already marked as context filter')
41 f.environmentfilter = True
45 def do_forceescape(value):
46 """Enforce HTML escaping. This will probably double escape variables."""
47 if hasattr(value, '__html__'):
48 value = value.__html__()
49 return escape(unicode(value))
53 def do_replace(environment, s, old, new, count=None):
54 """Return a copy of the value with all occurrences of a substring
55 replaced with a new one. The first argument is the substring
56 that should be replaced, the second is the replacement string.
57 If the optional third argument ``count`` is given, only the first
58 ``count`` occurrences are replaced:
62 {{ "Hello World"|replace("Hello", "Goodbye") }}
65 {{ "aaaaargh"|replace("a", "d'oh, ", 2) }}
70 if not environment.autoescape:
71 return unicode(s).replace(unicode(old), unicode(new), count)
72 if hasattr(old, '__html__') or hasattr(new, '__html__') and \
73 not hasattr(s, '__html__'):
77 return s.replace(soft_unicode(old), soft_unicode(new), count)
81 """Convert a value to uppercase."""
82 return soft_unicode(s).upper()
86 """Convert a value to lowercase."""
87 return soft_unicode(s).lower()
91 def do_xmlattr(_environment, d, autospace=True):
92 """Create an SGML/XML attribute string based on the items in a dict.
93 All values that are neither `none` nor `undefined` are automatically
96 .. sourcecode:: html+jinja
98 <ul{{ {'class': 'my_list', 'missing': none,
99 'id': 'list-%d'|format(variable)}|xmlattr }}>
103 Results in something like this:
107 <ul class="my_list" id="list-42">
111 As you can see it automatically prepends a space in front of the item
112 if the filter returned something unless the second parameter is false.
115 u'%s="%s"' % (escape(key), escape(value))
116 for key, value in d.iteritems()
117 if value is not None and not isinstance(value, Undefined)
121 if _environment.autoescape:
126 def do_capitalize(s):
127 """Capitalize a value. The first character will be uppercase, all others
130 return soft_unicode(s).capitalize()
134 """Return a titlecased version of the value. I.e. words will start with
135 uppercase letters, all remaining characters are lowercase.
137 return soft_unicode(s).title()
140 def do_dictsort(value, case_sensitive=False, by='key'):
141 """ Sort a dict and yield (key, value) pairs. Because python dicts are
142 unsorted you may want to use this function to order them by either
145 .. sourcecode:: jinja
147 {% for item in mydict|dictsort %}
148 sort the dict by key, case insensitive
150 {% for item in mydict|dicsort(true) %}
151 sort the dict by key, case sensitive
153 {% for item in mydict|dictsort(false, 'value') %}
154 sort the dict by key, case insensitive, sorted
155 normally and ordered by value.
162 raise FilterArgumentError('You can only sort by either '
166 if isinstance(value, basestring):
167 value = unicode(value)
168 if not case_sensitive:
169 value = value.lower()
172 return sorted(value.items(), key=sort_func)
175 def do_default(value, default_value=u'', boolean=False):
176 """If the value is undefined it will return the passed default value,
177 otherwise the value of the variable:
179 .. sourcecode:: jinja
181 {{ my_variable|default('my_variable is not defined') }}
183 This will output the value of ``my_variable`` if the variable was
184 defined, otherwise ``'my_variable is not defined'``. If you want
185 to use default with variables that evaluate to false you have to
186 set the second parameter to `true`:
188 .. sourcecode:: jinja
190 {{ ''|default('the string was empty', true) }}
192 if (boolean and not value) or isinstance(value, Undefined):
198 def do_join(environment, value, d=u''):
199 """Return a string which is the concatenation of the strings in the
200 sequence. The separator between elements is an empty string per
201 default, you can define it with the optional parameter:
203 .. sourcecode:: jinja
205 {{ [1, 2, 3]|join('|') }}
211 # no automatic escaping? joining is a lot eaiser then
212 if not environment.autoescape:
213 return unicode(d).join(imap(unicode, value))
215 # if the delimiter doesn't have an html representation we check
216 # if any of the items has. If yes we do a coercion to Markup
217 if not hasattr(d, '__html__'):
220 for idx, item in enumerate(value):
221 if hasattr(item, '__html__'):
224 value[idx] = unicode(item)
231 # no html involved, to normal joining
232 return soft_unicode(d).join(imap(soft_unicode, value))
235 def do_center(value, width=80):
236 """Centers the value in a field of a given width."""
237 return unicode(value).center(width)
241 def do_first(environment, seq):
242 """Return the first item of a sequence."""
244 return iter(seq).next()
245 except StopIteration:
246 return environment.undefined('No first item, sequence was empty.')
250 def do_last(environment, seq):
251 """Return the last item of a sequence."""
253 return iter(reversed(seq)).next()
254 except StopIteration:
255 return environment.undefined('No last item, sequence was empty.')
259 def do_random(environment, seq):
260 """Return a random item from the sequence."""
264 return environment.undefined('No random item, sequence was empty.')
267 def do_filesizeformat(value):
268 """Format the value like a 'human-readable' file size (i.e. 13 KB,
269 4.1 MB, 102 bytes, etc).
278 return "%d Byte%s" % (bytes, bytes != 1 and 's' or '')
279 elif bytes < 1024 * 1024:
280 return "%.1f KB" % (bytes / 1024)
281 elif bytes < 1024 * 1024 * 1024:
282 return "%.1f MB" % (bytes / (1024 * 1024))
283 return "%.1f GB" % (bytes / (1024 * 1024 * 1024))
286 def do_pprint(value, verbose=False):
287 """Pretty print a variable. Useful for debugging.
289 With Jinja 1.2 onwards you can pass it a parameter. If this parameter
290 is truthy the output will be more verbose (this requires `pretty`)
292 return pformat(value, verbose=verbose)
296 def do_urlize(environment, value, trim_url_limit=None, nofollow=False):
297 """Converts URLs in plain text into clickable links.
299 If you pass the filter an additional integer it will shorten the urls
300 to that number. Also a third argument exists that makes the urls
303 .. sourcecode:: jinja
305 {{ mytext|urlize(40, true) }}
306 links are shortened to 40 chars and defined with rel="nofollow"
308 rv = urlize(soft_unicode(value), trim_url_limit, nofollow)
309 if environment.autoescape:
314 def do_indent(s, width=4, indentfirst=False):
315 """Return a copy of the passed string, each line indented by
316 4 spaces. The first line is not indented. If you want to
317 change the number of spaces or indent the first line too
318 you can pass additional parameters to the filter:
320 .. sourcecode:: jinja
322 {{ mytext|indent(2, true) }}
323 indent by two spaces and indent the first line too.
325 indention = ' ' * width
327 return u'\n'.join(indention + line for line in s.splitlines())
328 return s.replace('\n', '\n' + indention)
331 def do_truncate(s, length=255, killwords=False, end='...'):
332 """Return a truncated copy of the string. The length is specified
333 with the first parameter which defaults to ``255``. If the second
334 parameter is ``true`` the filter will cut the text at length. Otherwise
335 it will try to save the last word. If the text was in fact
336 truncated it will append an ellipsis sign (``"..."``). If you want a
337 different ellipsis sign than ``"..."`` you can specify it using the
340 .. sourcecode jinja::
342 {{ mytext|truncate(300, false, '»') }}
343 truncate mytext to 300 chars, don't split up words, use a
344 right pointing double arrow as ellipsis sign.
349 return s[:length] + end
359 return u' '.join(result)
362 def do_wordwrap(s, width=79, break_long_words=True):
364 Return a copy of the string passed to the filter wrapped after
365 ``79`` characters. You can override this default using the first
366 parameter. If you set the second parameter to `false` Jinja will not
367 split words apart if they are longer than `width`.
369 return u'\n'.join(textwrap.wrap(s, width=width, expand_tabs=False,
370 replace_whitespace=False,
371 break_long_words=break_long_words))
375 """Count the words in that string."""
376 return len(_word_re.findall(s))
379 def do_int(value, default=0):
380 """Convert the value into an integer. If the
381 conversion doesn't work it will return ``0``. You can
382 override this default using the first parameter.
386 except (TypeError, ValueError):
387 # this quirk is necessary so that "42.23"|int gives 42.
389 return int(float(value))
390 except (TypeError, ValueError):
394 def do_float(value, default=0.0):
395 """Convert the value into a floating point number. If the
396 conversion doesn't work it will return ``0.0``. You can
397 override this default using the first parameter.
401 except (TypeError, ValueError):
405 def do_format(value, *args, **kwargs):
407 Apply python string formatting on an object:
409 .. sourcecode:: jinja
411 {{ "%s - %s"|format("Hello?", "Foo!") }}
415 raise FilterArgumentError('can\'t handle positional and keyword '
416 'arguments at the same time')
417 return soft_unicode(value) % (kwargs or args)
421 """Strip leading and trailing whitespace."""
422 return soft_unicode(value).strip()
425 def do_striptags(value):
426 """Strip SGML/XML tags and replace adjacent whitespace by one space.
428 if hasattr(value, '__html__'):
429 value = value.__html__()
430 return Markup(unicode(value)).striptags()
433 def do_slice(value, slices, fill_with=None):
434 """Slice an iterator and return a list of lists containing
435 those items. Useful if you want to create a div containing
436 three div tags that represent columns:
438 .. sourcecode:: html+jinja
440 <div class="columwrapper">
441 {%- for column in items|slice(3) %}
442 <ul class="column-{{ loop.index }}">
443 {%- for item in column %}
450 If you pass it a second argument it's used to fill missing
451 values on the last iteration.
455 items_per_slice = length // slices
456 slices_with_extra = length % slices
458 for slice_number in xrange(slices):
459 start = offset + slice_number * items_per_slice
460 if slice_number < slices_with_extra:
462 end = offset + (slice_number + 1) * items_per_slice
464 if fill_with is not None and slice_number >= slices_with_extra:
465 tmp.append(fill_with)
469 def do_batch(value, linecount, fill_with=None):
471 A filter that batches items. It works pretty much like `slice`
472 just the other way round. It returns a list of lists with the
473 given number of items. If you provide a second parameter this
474 is used to fill missing items. See this example:
476 .. sourcecode:: html+jinja
479 {%- for row in items|batch(3, ' ') %}
481 {%- for column in row %}
482 <tr>{{ column }}</td>
491 if len(tmp) == linecount:
496 if fill_with is not None and len(tmp) < linecount:
497 tmp += [fill_with] * (linecount - len(tmp))
501 def do_round(value, precision=0, method='common'):
502 """Round the number to a given precision. The first
503 parameter specifies the precision (default is ``0``), the
504 second the rounding method:
506 - ``'common'`` rounds either up or down
507 - ``'ceil'`` always rounds up
508 - ``'floor'`` always rounds down
510 If you don't specify a method ``'common'`` is used.
512 .. sourcecode:: jinja
516 {{ 42.55|round(1, 'floor') }}
519 if not method in ('common', 'ceil', 'floor'):
520 raise FilterArgumentError('method must be common, ceil or floor')
522 raise FilterArgumentError('precision must be a postive integer '
524 if method == 'common':
525 return round(value, precision)
526 func = getattr(math, method)
528 return func(value * 10 * precision) / (10 * precision)
533 def do_sort(value, reverse=False):
534 """Sort a sequence. Per default it sorts ascending, if you pass it
535 true as first argument it will reverse the sorting.
537 return sorted(value, reverse=reverse)
541 def do_groupby(environment, value, attribute):
542 """Group a sequence of objects by a common attribute.
544 If you for example have a list of dicts or objects that represent persons
545 with `gender`, `first_name` and `last_name` attributes and you want to
546 group all users by genders you can do something like the following
549 .. sourcecode:: html+jinja
552 {% for group in persons|groupby('gender') %}
553 <li>{{ group.grouper }}<ul>
554 {% for person in group.list %}
555 <li>{{ person.first_name }} {{ person.last_name }}</li>
556 {% endfor %}</ul></li>
560 Additionally it's possible to use tuple unpacking for the grouper and
563 .. sourcecode:: html+jinja
566 {% for grouper, list in persons|groupby('gender') %}
571 As you can see the item we're grouping by is stored in the `grouper`
572 attribute and the `list` contains all the objects that have this grouper
575 expr = lambda x: environment.getitem(x, attribute)
576 return sorted(map(_GroupTuple, groupby(sorted(value, key=expr), expr)))
579 class _GroupTuple(tuple):
581 grouper = property(itemgetter(0))
582 list = property(itemgetter(1))
584 def __new__(cls, (key, value)):
585 return tuple.__new__(cls, (key, list(value)))
589 """Convert the value into a list. If it was a string the returned list
590 will be a list of characters.
595 def do_mark_safe(value):
596 """Mark the value as safe which means that in an environment with automatic
597 escaping enabled this variable will not be escaped.
602 def do_mark_unsafe(value):
603 """Mark a value as unsafe. This is the reverse operation for :func:`safe`."""
604 return unicode(value)
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 def do_attr(environment, obj, name):
626 """Get an attribute of an object. ``foo|attr("bar")`` works like
627 ``foo["bar"]`` just that always an attribute is returned and items are not
630 See :ref:`Notes on subscribing <notes-on-subscribing>` for more details.
633 value = getattr(obj, name)
634 except AttributeError:
635 return environment.undefined(obj=obj, name=name)
636 if environment.sandboxed and not \
637 environment.is_safe_attribute(obj, name, value):
638 return environment.unsafe_undefined(obj, name)
644 'replace': do_replace,
649 'forceescape': do_forceescape,
650 'capitalize': do_capitalize,
652 'default': do_default,
656 'dictsort': do_dictsort,
658 'reverse': do_reverse,
662 'capitalize': do_capitalize,
666 'filesizeformat': do_filesizeformat,
668 'truncate': do_truncate,
669 'wordwrap': do_wordwrap,
670 'wordcount': do_wordcount,
673 'string': soft_unicode,
678 'striptags': do_striptags,
685 'groupby': do_groupby,
686 'safe': do_mark_safe,
687 'xmlattr': do_xmlattr