1 # -*- coding: utf-8 -*-
8 :copyright: (c) 2010 by the Jinja Team.
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
22 _word_re = re.compile(r'\w+(?u)')
26 """Decorator for marking context dependent filters. The current
27 :class:`Context` will be passed as first argument.
29 f.contextfilter = True
33 def evalcontextfilter(f):
34 """Decorator for marking eval-context dependent filters. An eval
35 context object is passed as first argument. For more information
36 about the eval context, see :ref:`eval-context`.
40 f.evalcontextfilter = True
44 def environmentfilter(f):
45 """Decorator for marking evironment dependent filters. The current
46 :class:`Environment` is passed to the filter as first argument.
48 f.environmentfilter = True
52 def make_attrgetter(environment, attribute):
53 """Returns a callable that looks up the given attribute from a
54 passed object with the rules of the environment. Dots are allowed
55 to access attributes of attributes.
57 if not isinstance(attribute, basestring) or '.' not in attribute:
58 return lambda x: environment.getitem(x, attribute)
59 attribute = attribute.split('.')
61 for part in attribute:
62 item = environment.getitem(item, part)
67 def do_forceescape(value):
68 """Enforce HTML escaping. This will probably double escape variables."""
69 if hasattr(value, '__html__'):
70 value = value.__html__()
71 return escape(unicode(value))
73 def do_urlescape(value):
74 """Escape strings for use in URLs (uses UTF-8 encoding)."""
76 return unicode(o).encode('utf8')
78 if isinstance(value, basestring):
79 return urllib.quote(utf8(value))
81 if hasattr(value, 'items'):
82 # convert dictionaries to list of 2-tuples
85 if hasattr(value, 'next'):
86 # convert generators to list
89 return urllib.urlencode([(utf8(k), utf8(v)) for (k, v) in value])
92 def do_replace(eval_ctx, s, old, new, count=None):
93 """Return a copy of the value with all occurrences of a substring
94 replaced with a new one. The first argument is the substring
95 that should be replaced, the second is the replacement string.
96 If the optional third argument ``count`` is given, only the first
97 ``count`` occurrences are replaced:
101 {{ "Hello World"|replace("Hello", "Goodbye") }}
104 {{ "aaaaargh"|replace("a", "d'oh, ", 2) }}
105 -> d'oh, d'oh, aaargh
109 if not eval_ctx.autoescape:
110 return unicode(s).replace(unicode(old), unicode(new), count)
111 if hasattr(old, '__html__') or hasattr(new, '__html__') and \
112 not hasattr(s, '__html__'):
116 return s.replace(soft_unicode(old), soft_unicode(new), count)
120 """Convert a value to uppercase."""
121 return soft_unicode(s).upper()
125 """Convert a value to lowercase."""
126 return soft_unicode(s).lower()
130 def do_xmlattr(_eval_ctx, d, autospace=True):
131 """Create an SGML/XML attribute string based on the items in a dict.
132 All values that are neither `none` nor `undefined` are automatically
135 .. sourcecode:: html+jinja
137 <ul{{ {'class': 'my_list', 'missing': none,
138 'id': 'list-%d'|format(variable)}|xmlattr }}>
142 Results in something like this:
146 <ul class="my_list" id="list-42">
150 As you can see it automatically prepends a space in front of the item
151 if the filter returned something unless the second parameter is false.
154 u'%s="%s"' % (escape(key), escape(value))
155 for key, value in d.iteritems()
156 if value is not None and not isinstance(value, Undefined)
160 if _eval_ctx.autoescape:
165 def do_capitalize(s):
166 """Capitalize a value. The first character will be uppercase, all others
169 return soft_unicode(s).capitalize()
173 """Return a titlecased version of the value. I.e. words will start with
174 uppercase letters, all remaining characters are lowercase.
176 return soft_unicode(s).title()
179 def do_dictsort(value, case_sensitive=False, by='key'):
180 """Sort a dict and yield (key, value) pairs. Because python dicts are
181 unsorted you may want to use this function to order them by either
184 .. sourcecode:: jinja
186 {% for item in mydict|dictsort %}
187 sort the dict by key, case insensitive
189 {% for item in mydict|dicsort(true) %}
190 sort the dict by key, case sensitive
192 {% for item in mydict|dictsort(false, 'value') %}
193 sort the dict by key, case insensitive, sorted
194 normally and ordered by value.
201 raise FilterArgumentError('You can only sort by either '
205 if isinstance(value, basestring) and not case_sensitive:
206 value = value.lower()
209 return sorted(value.items(), key=sort_func)
213 def do_sort(environment, value, reverse=False, case_sensitive=False,
215 """Sort an iterable. Per default it sorts ascending, if you pass it
216 true as first argument it will reverse the sorting.
218 If the iterable is made of strings the third parameter can be used to
219 control the case sensitiveness of the comparison which is disabled by
222 .. sourcecode:: jinja
224 {% for item in iterable|sort %}
228 It is also possible to sort by an attribute (for example to sort
229 by the date of an object) by specifying the `attribute` parameter:
231 .. sourcecode:: jinja
233 {% for item in iterable|sort(attribute='date') %}
237 .. versionchanged:: 2.6
238 The `attribute` parameter was added.
240 if not case_sensitive:
242 if isinstance(item, basestring):
247 if attribute is not None:
248 getter = make_attrgetter(environment, attribute)
249 def sort_func(item, processor=sort_func or (lambda x: x)):
250 return processor(getter(item))
251 return sorted(value, key=sort_func, reverse=reverse)
254 def do_default(value, default_value=u'', boolean=False):
255 """If the value is undefined it will return the passed default value,
256 otherwise the value of the variable:
258 .. sourcecode:: jinja
260 {{ my_variable|default('my_variable is not defined') }}
262 This will output the value of ``my_variable`` if the variable was
263 defined, otherwise ``'my_variable is not defined'``. If you want
264 to use default with variables that evaluate to false you have to
265 set the second parameter to `true`:
267 .. sourcecode:: jinja
269 {{ ''|default('the string was empty', true) }}
271 if (boolean and not value) or isinstance(value, Undefined):
277 def do_join(eval_ctx, value, d=u'', attribute=None):
278 """Return a string which is the concatenation of the strings in the
279 sequence. The separator between elements is an empty string per
280 default, you can define it with the optional parameter:
282 .. sourcecode:: jinja
284 {{ [1, 2, 3]|join('|') }}
290 It is also possible to join certain attributes of an object:
292 .. sourcecode:: jinja
294 {{ users|join(', ', attribute='username') }}
296 .. versionadded:: 2.6
297 The `attribute` parameter was added.
299 if attribute is not None:
300 value = imap(make_attrgetter(eval_ctx.environment, attribute), value)
302 # no automatic escaping? joining is a lot eaiser then
303 if not eval_ctx.autoescape:
304 return unicode(d).join(imap(unicode, value))
306 # if the delimiter doesn't have an html representation we check
307 # if any of the items has. If yes we do a coercion to Markup
308 if not hasattr(d, '__html__'):
311 for idx, item in enumerate(value):
312 if hasattr(item, '__html__'):
315 value[idx] = unicode(item)
322 # no html involved, to normal joining
323 return soft_unicode(d).join(imap(soft_unicode, value))
326 def do_center(value, width=80):
327 """Centers the value in a field of a given width."""
328 return unicode(value).center(width)
332 def do_first(environment, seq):
333 """Return the first item of a sequence."""
335 return iter(seq).next()
336 except StopIteration:
337 return environment.undefined('No first item, sequence was empty.')
341 def do_last(environment, seq):
342 """Return the last item of a sequence."""
344 return iter(reversed(seq)).next()
345 except StopIteration:
346 return environment.undefined('No last item, sequence was empty.')
350 def do_random(environment, seq):
351 """Return a random item from the sequence."""
355 return environment.undefined('No random item, sequence was empty.')
358 def do_filesizeformat(value, binary=False):
359 """Format the value like a 'human-readable' file size (i.e. 13 kB,
360 4.1 MB, 102 Bytes, etc). Per default decimal prefixes are used (Mega,
361 Giga, etc.), if the second parameter is set to `True` the binary
362 prefixes are used (Mebi, Gibi).
365 base = binary and 1024 or 1000
367 (binary and 'KiB' or 'kB'),
368 (binary and 'MiB' or 'MB'),
369 (binary and 'GiB' or 'GB'),
370 (binary and 'TiB' or 'TB'),
371 (binary and 'PiB' or 'PB'),
372 (binary and 'EiB' or 'EB'),
373 (binary and 'ZiB' or 'ZB'),
374 (binary and 'YiB' or 'YB')
379 return '%d Bytes' % bytes
381 for i, prefix in enumerate(prefixes):
382 unit = base ** (i + 2)
384 return '%.1f %s' % ((base * bytes / unit), prefix)
385 return '%.1f %s' % ((base * bytes / unit), prefix)
388 def do_pprint(value, verbose=False):
389 """Pretty print a variable. Useful for debugging.
391 With Jinja 1.2 onwards you can pass it a parameter. If this parameter
392 is truthy the output will be more verbose (this requires `pretty`)
394 return pformat(value, verbose=verbose)
398 def do_urlize(eval_ctx, value, trim_url_limit=None, nofollow=False):
399 """Converts URLs in plain text into clickable links.
401 If you pass the filter an additional integer it will shorten the urls
402 to that number. Also a third argument exists that makes the urls
405 .. sourcecode:: jinja
407 {{ mytext|urlize(40, true) }}
408 links are shortened to 40 chars and defined with rel="nofollow"
410 rv = urlize(value, trim_url_limit, nofollow)
411 if eval_ctx.autoescape:
416 def do_indent(s, width=4, indentfirst=False):
417 """Return a copy of the passed string, each line indented by
418 4 spaces. The first line is not indented. If you want to
419 change the number of spaces or indent the first line too
420 you can pass additional parameters to the filter:
422 .. sourcecode:: jinja
424 {{ mytext|indent(2, true) }}
425 indent by two spaces and indent the first line too.
427 indention = u' ' * width
428 rv = (u'\n' + indention).join(s.splitlines())
434 def do_truncate(s, length=255, killwords=False, end='...'):
435 """Return a truncated copy of the string. The length is specified
436 with the first parameter which defaults to ``255``. If the second
437 parameter is ``true`` the filter will cut the text at length. Otherwise
438 it will try to save the last word. If the text was in fact
439 truncated it will append an ellipsis sign (``"..."``). If you want a
440 different ellipsis sign than ``"..."`` you can specify it using the
443 .. sourcecode jinja::
445 {{ mytext|truncate(300, false, '»') }}
446 truncate mytext to 300 chars, don't split up words, use a
447 right pointing double arrow as ellipsis sign.
452 return s[:length] + end
462 return u' '.join(result)
465 def do_wordwrap(environment, s, width=79, break_long_words=True):
467 Return a copy of the string passed to the filter wrapped after
468 ``79`` characters. You can override this default using the first
469 parameter. If you set the second parameter to `false` Jinja will not
470 split words apart if they are longer than `width`.
473 return environment.newline_sequence.join(textwrap.wrap(s, width=width, expand_tabs=False,
474 replace_whitespace=False,
475 break_long_words=break_long_words))
479 """Count the words in that string."""
480 return len(_word_re.findall(s))
483 def do_int(value, default=0):
484 """Convert the value into an integer. If the
485 conversion doesn't work it will return ``0``. You can
486 override this default using the first parameter.
490 except (TypeError, ValueError):
491 # this quirk is necessary so that "42.23"|int gives 42.
493 return int(float(value))
494 except (TypeError, ValueError):
498 def do_float(value, default=0.0):
499 """Convert the value into a floating point number. If the
500 conversion doesn't work it will return ``0.0``. You can
501 override this default using the first parameter.
505 except (TypeError, ValueError):
509 def do_format(value, *args, **kwargs):
511 Apply python string formatting on an object:
513 .. sourcecode:: jinja
515 {{ "%s - %s"|format("Hello?", "Foo!") }}
519 raise FilterArgumentError('can\'t handle positional and keyword '
520 'arguments at the same time')
521 return soft_unicode(value) % (kwargs or args)
525 """Strip leading and trailing whitespace."""
526 return soft_unicode(value).strip()
529 def do_striptags(value):
530 """Strip SGML/XML tags and replace adjacent whitespace by one space.
532 if hasattr(value, '__html__'):
533 value = value.__html__()
534 return Markup(unicode(value)).striptags()
537 def do_slice(value, slices, fill_with=None):
538 """Slice an iterator and return a list of lists containing
539 those items. Useful if you want to create a div containing
540 three ul tags that represent columns:
542 .. sourcecode:: html+jinja
544 <div class="columwrapper">
545 {%- for column in items|slice(3) %}
546 <ul class="column-{{ loop.index }}">
547 {%- for item in column %}
554 If you pass it a second argument it's used to fill missing
555 values on the last iteration.
559 items_per_slice = length // slices
560 slices_with_extra = length % slices
562 for slice_number in xrange(slices):
563 start = offset + slice_number * items_per_slice
564 if slice_number < slices_with_extra:
566 end = offset + (slice_number + 1) * items_per_slice
568 if fill_with is not None and slice_number >= slices_with_extra:
569 tmp.append(fill_with)
573 def do_batch(value, linecount, fill_with=None):
575 A filter that batches items. It works pretty much like `slice`
576 just the other way round. It returns a list of lists with the
577 given number of items. If you provide a second parameter this
578 is used to fill missing items. See this example:
580 .. sourcecode:: html+jinja
583 {%- for row in items|batch(3, ' ') %}
585 {%- for column in row %}
586 <td>{{ column }}</td>
595 if len(tmp) == linecount:
600 if fill_with is not None and len(tmp) < linecount:
601 tmp += [fill_with] * (linecount - len(tmp))
605 def do_round(value, precision=0, method='common'):
606 """Round the number to a given precision. The first
607 parameter specifies the precision (default is ``0``), the
608 second the rounding method:
610 - ``'common'`` rounds either up or down
611 - ``'ceil'`` always rounds up
612 - ``'floor'`` always rounds down
614 If you don't specify a method ``'common'`` is used.
616 .. sourcecode:: jinja
620 {{ 42.55|round(1, 'floor') }}
623 Note that even if rounded to 0 precision, a float is returned. If
624 you need a real integer, pipe it through `int`:
626 .. sourcecode:: jinja
628 {{ 42.55|round|int }}
631 if not method in ('common', 'ceil', 'floor'):
632 raise FilterArgumentError('method must be common, ceil or floor')
633 if method == 'common':
634 return round(value, precision)
635 func = getattr(math, method)
636 return func(value * (10 ** precision)) / (10 ** precision)
640 def do_groupby(environment, value, attribute):
641 """Group a sequence of objects by a common attribute.
643 If you for example have a list of dicts or objects that represent persons
644 with `gender`, `first_name` and `last_name` attributes and you want to
645 group all users by genders you can do something like the following
648 .. sourcecode:: html+jinja
651 {% for group in persons|groupby('gender') %}
652 <li>{{ group.grouper }}<ul>
653 {% for person in group.list %}
654 <li>{{ person.first_name }} {{ person.last_name }}</li>
655 {% endfor %}</ul></li>
659 Additionally it's possible to use tuple unpacking for the grouper and
662 .. sourcecode:: html+jinja
665 {% for grouper, list in persons|groupby('gender') %}
670 As you can see the item we're grouping by is stored in the `grouper`
671 attribute and the `list` contains all the objects that have this grouper
674 .. versionchanged:: 2.6
675 It's now possible to use dotted notation to group by the child
676 attribute of another attribute.
678 expr = make_attrgetter(environment, attribute)
679 return sorted(map(_GroupTuple, groupby(sorted(value, key=expr), expr)))
682 class _GroupTuple(tuple):
684 grouper = property(itemgetter(0))
685 list = property(itemgetter(1))
687 def __new__(cls, (key, value)):
688 return tuple.__new__(cls, (key, list(value)))
692 def do_sum(environment, iterable, attribute=None, start=0):
693 """Returns the sum of a sequence of numbers plus the value of parameter
694 'start' (which defaults to 0). When the sequence is empty it returns
697 It is also possible to sum up only certain attributes:
699 .. sourcecode:: jinja
701 Total: {{ items|sum(attribute='price') }}
703 .. versionchanged:: 2.6
704 The `attribute` parameter was added to allow suming up over
705 attributes. Also the `start` parameter was moved on to the right.
707 if attribute is not None:
708 iterable = imap(make_attrgetter(environment, attribute), iterable)
709 return sum(iterable, start)
713 """Convert the value into a list. If it was a string the returned list
714 will be a list of characters.
719 def do_mark_safe(value):
720 """Mark the value as safe which means that in an environment with automatic
721 escaping enabled this variable will not be escaped.
726 def do_mark_unsafe(value):
727 """Mark a value as unsafe. This is the reverse operation for :func:`safe`."""
728 return unicode(value)
731 def do_reverse(value):
732 """Reverse the object or return an iterator the iterates over it the other
735 if isinstance(value, basestring):
738 return reversed(value)
745 raise FilterArgumentError('argument must be iterable')
749 def do_attr(environment, obj, name):
750 """Get an attribute of an object. ``foo|attr("bar")`` works like
751 ``foo["bar"]`` just that always an attribute is returned and items are not
754 See :ref:`Notes on subscriptions <notes-on-subscriptions>` for more details.
762 value = getattr(obj, name)
763 except AttributeError:
766 if environment.sandboxed and not \
767 environment.is_safe_attribute(obj, name, value):
768 return environment.unsafe_undefined(obj, name)
770 return environment.undefined(obj=obj, name=name)
775 'replace': do_replace,
780 'forceescape': do_forceescape,
781 'capitalize': do_capitalize,
783 'default': do_default,
787 'dictsort': do_dictsort,
790 'reverse': do_reverse,
794 'capitalize': do_capitalize,
798 'filesizeformat': do_filesizeformat,
800 'truncate': do_truncate,
801 'wordwrap': do_wordwrap,
802 'wordcount': do_wordcount,
805 'string': soft_unicode,
810 'striptags': do_striptags,
816 'groupby': do_groupby,
817 'safe': do_mark_safe,
818 'xmlattr': do_xmlattr,
819 'urlescape': do_urlescape