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 for use in URLs."""
75 return urllib.quote(value.encode('utf8'))
78 def do_replace(eval_ctx, s, old, new, count=None):
79 """Return a copy of the value with all occurrences of a substring
80 replaced with a new one. The first argument is the substring
81 that should be replaced, the second is the replacement string.
82 If the optional third argument ``count`` is given, only the first
83 ``count`` occurrences are replaced:
87 {{ "Hello World"|replace("Hello", "Goodbye") }}
90 {{ "aaaaargh"|replace("a", "d'oh, ", 2) }}
95 if not eval_ctx.autoescape:
96 return unicode(s).replace(unicode(old), unicode(new), count)
97 if hasattr(old, '__html__') or hasattr(new, '__html__') and \
98 not hasattr(s, '__html__'):
102 return s.replace(soft_unicode(old), soft_unicode(new), count)
106 """Convert a value to uppercase."""
107 return soft_unicode(s).upper()
111 """Convert a value to lowercase."""
112 return soft_unicode(s).lower()
116 def do_xmlattr(_eval_ctx, d, autospace=True):
117 """Create an SGML/XML attribute string based on the items in a dict.
118 All values that are neither `none` nor `undefined` are automatically
121 .. sourcecode:: html+jinja
123 <ul{{ {'class': 'my_list', 'missing': none,
124 'id': 'list-%d'|format(variable)}|xmlattr }}>
128 Results in something like this:
132 <ul class="my_list" id="list-42">
136 As you can see it automatically prepends a space in front of the item
137 if the filter returned something unless the second parameter is false.
140 u'%s="%s"' % (escape(key), escape(value))
141 for key, value in d.iteritems()
142 if value is not None and not isinstance(value, Undefined)
146 if _eval_ctx.autoescape:
151 def do_capitalize(s):
152 """Capitalize a value. The first character will be uppercase, all others
155 return soft_unicode(s).capitalize()
159 """Return a titlecased version of the value. I.e. words will start with
160 uppercase letters, all remaining characters are lowercase.
162 return soft_unicode(s).title()
165 def do_dictsort(value, case_sensitive=False, by='key'):
166 """Sort a dict and yield (key, value) pairs. Because python dicts are
167 unsorted you may want to use this function to order them by either
170 .. sourcecode:: jinja
172 {% for item in mydict|dictsort %}
173 sort the dict by key, case insensitive
175 {% for item in mydict|dicsort(true) %}
176 sort the dict by key, case sensitive
178 {% for item in mydict|dictsort(false, 'value') %}
179 sort the dict by key, case insensitive, sorted
180 normally and ordered by value.
187 raise FilterArgumentError('You can only sort by either '
191 if isinstance(value, basestring) and not case_sensitive:
192 value = value.lower()
195 return sorted(value.items(), key=sort_func)
199 def do_sort(environment, value, reverse=False, case_sensitive=False,
201 """Sort an iterable. Per default it sorts ascending, if you pass it
202 true as first argument it will reverse the sorting.
204 If the iterable is made of strings the third parameter can be used to
205 control the case sensitiveness of the comparison which is disabled by
208 .. sourcecode:: jinja
210 {% for item in iterable|sort %}
214 It is also possible to sort by an attribute (for example to sort
215 by the date of an object) by specifying the `attribute` parameter:
217 .. sourcecode:: jinja
219 {% for item in iterable|sort(attribute='date') %}
223 .. versionchanged:: 2.6
224 The `attribute` parameter was added.
226 if not case_sensitive:
228 if isinstance(item, basestring):
233 if attribute is not None:
234 getter = make_attrgetter(environment, attribute)
235 def sort_func(item, processor=sort_func or (lambda x: x)):
236 return processor(getter(item))
237 return sorted(value, key=sort_func, reverse=reverse)
240 def do_default(value, default_value=u'', boolean=False):
241 """If the value is undefined it will return the passed default value,
242 otherwise the value of the variable:
244 .. sourcecode:: jinja
246 {{ my_variable|default('my_variable is not defined') }}
248 This will output the value of ``my_variable`` if the variable was
249 defined, otherwise ``'my_variable is not defined'``. If you want
250 to use default with variables that evaluate to false you have to
251 set the second parameter to `true`:
253 .. sourcecode:: jinja
255 {{ ''|default('the string was empty', true) }}
257 if (boolean and not value) or isinstance(value, Undefined):
263 def do_join(eval_ctx, value, d=u'', attribute=None):
264 """Return a string which is the concatenation of the strings in the
265 sequence. The separator between elements is an empty string per
266 default, you can define it with the optional parameter:
268 .. sourcecode:: jinja
270 {{ [1, 2, 3]|join('|') }}
276 It is also possible to join certain attributes of an object:
278 .. sourcecode:: jinja
280 {{ users|join(', ', attribute='username') }}
282 .. versionadded:: 2.6
283 The `attribute` parameter was added.
285 if attribute is not None:
286 value = imap(make_attrgetter(eval_ctx.environment, attribute), value)
288 # no automatic escaping? joining is a lot eaiser then
289 if not eval_ctx.autoescape:
290 return unicode(d).join(imap(unicode, value))
292 # if the delimiter doesn't have an html representation we check
293 # if any of the items has. If yes we do a coercion to Markup
294 if not hasattr(d, '__html__'):
297 for idx, item in enumerate(value):
298 if hasattr(item, '__html__'):
301 value[idx] = unicode(item)
308 # no html involved, to normal joining
309 return soft_unicode(d).join(imap(soft_unicode, value))
312 def do_center(value, width=80):
313 """Centers the value in a field of a given width."""
314 return unicode(value).center(width)
318 def do_first(environment, seq):
319 """Return the first item of a sequence."""
321 return iter(seq).next()
322 except StopIteration:
323 return environment.undefined('No first item, sequence was empty.')
327 def do_last(environment, seq):
328 """Return the last item of a sequence."""
330 return iter(reversed(seq)).next()
331 except StopIteration:
332 return environment.undefined('No last item, sequence was empty.')
336 def do_random(environment, seq):
337 """Return a random item from the sequence."""
341 return environment.undefined('No random item, sequence was empty.')
344 def do_filesizeformat(value, binary=False):
345 """Format the value like a 'human-readable' file size (i.e. 13 kB,
346 4.1 MB, 102 Bytes, etc). Per default decimal prefixes are used (Mega,
347 Giga, etc.), if the second parameter is set to `True` the binary
348 prefixes are used (Mebi, Gibi).
351 base = binary and 1024 or 1000
353 (binary and 'KiB' or 'kB'),
354 (binary and 'MiB' or 'MB'),
355 (binary and 'GiB' or 'GB'),
356 (binary and 'TiB' or 'TB'),
357 (binary and 'PiB' or 'PB'),
358 (binary and 'EiB' or 'EB'),
359 (binary and 'ZiB' or 'ZB'),
360 (binary and 'YiB' or 'YB')
365 return '%d Bytes' % bytes
367 for i, prefix in enumerate(prefixes):
368 unit = base ** (i + 2)
370 return '%.1f %s' % ((base * bytes / unit), prefix)
371 return '%.1f %s' % ((base * bytes / unit), prefix)
374 def do_pprint(value, verbose=False):
375 """Pretty print a variable. Useful for debugging.
377 With Jinja 1.2 onwards you can pass it a parameter. If this parameter
378 is truthy the output will be more verbose (this requires `pretty`)
380 return pformat(value, verbose=verbose)
384 def do_urlize(eval_ctx, value, trim_url_limit=None, nofollow=False):
385 """Converts URLs in plain text into clickable links.
387 If you pass the filter an additional integer it will shorten the urls
388 to that number. Also a third argument exists that makes the urls
391 .. sourcecode:: jinja
393 {{ mytext|urlize(40, true) }}
394 links are shortened to 40 chars and defined with rel="nofollow"
396 rv = urlize(value, trim_url_limit, nofollow)
397 if eval_ctx.autoescape:
402 def do_indent(s, width=4, indentfirst=False):
403 """Return a copy of the passed string, each line indented by
404 4 spaces. The first line is not indented. If you want to
405 change the number of spaces or indent the first line too
406 you can pass additional parameters to the filter:
408 .. sourcecode:: jinja
410 {{ mytext|indent(2, true) }}
411 indent by two spaces and indent the first line too.
413 indention = u' ' * width
414 rv = (u'\n' + indention).join(s.splitlines())
420 def do_truncate(s, length=255, killwords=False, end='...'):
421 """Return a truncated copy of the string. The length is specified
422 with the first parameter which defaults to ``255``. If the second
423 parameter is ``true`` the filter will cut the text at length. Otherwise
424 it will try to save the last word. If the text was in fact
425 truncated it will append an ellipsis sign (``"..."``). If you want a
426 different ellipsis sign than ``"..."`` you can specify it using the
429 .. sourcecode jinja::
431 {{ mytext|truncate(300, false, '»') }}
432 truncate mytext to 300 chars, don't split up words, use a
433 right pointing double arrow as ellipsis sign.
438 return s[:length] + end
448 return u' '.join(result)
451 def do_wordwrap(environment, s, width=79, break_long_words=True):
453 Return a copy of the string passed to the filter wrapped after
454 ``79`` characters. You can override this default using the first
455 parameter. If you set the second parameter to `false` Jinja will not
456 split words apart if they are longer than `width`.
459 return environment.newline_sequence.join(textwrap.wrap(s, width=width, expand_tabs=False,
460 replace_whitespace=False,
461 break_long_words=break_long_words))
465 """Count the words in that string."""
466 return len(_word_re.findall(s))
469 def do_int(value, default=0):
470 """Convert the value into an integer. If the
471 conversion doesn't work it will return ``0``. You can
472 override this default using the first parameter.
476 except (TypeError, ValueError):
477 # this quirk is necessary so that "42.23"|int gives 42.
479 return int(float(value))
480 except (TypeError, ValueError):
484 def do_float(value, default=0.0):
485 """Convert the value into a floating point number. If the
486 conversion doesn't work it will return ``0.0``. You can
487 override this default using the first parameter.
491 except (TypeError, ValueError):
495 def do_format(value, *args, **kwargs):
497 Apply python string formatting on an object:
499 .. sourcecode:: jinja
501 {{ "%s - %s"|format("Hello?", "Foo!") }}
505 raise FilterArgumentError('can\'t handle positional and keyword '
506 'arguments at the same time')
507 return soft_unicode(value) % (kwargs or args)
511 """Strip leading and trailing whitespace."""
512 return soft_unicode(value).strip()
515 def do_striptags(value):
516 """Strip SGML/XML tags and replace adjacent whitespace by one space.
518 if hasattr(value, '__html__'):
519 value = value.__html__()
520 return Markup(unicode(value)).striptags()
523 def do_slice(value, slices, fill_with=None):
524 """Slice an iterator and return a list of lists containing
525 those items. Useful if you want to create a div containing
526 three ul tags that represent columns:
528 .. sourcecode:: html+jinja
530 <div class="columwrapper">
531 {%- for column in items|slice(3) %}
532 <ul class="column-{{ loop.index }}">
533 {%- for item in column %}
540 If you pass it a second argument it's used to fill missing
541 values on the last iteration.
545 items_per_slice = length // slices
546 slices_with_extra = length % slices
548 for slice_number in xrange(slices):
549 start = offset + slice_number * items_per_slice
550 if slice_number < slices_with_extra:
552 end = offset + (slice_number + 1) * items_per_slice
554 if fill_with is not None and slice_number >= slices_with_extra:
555 tmp.append(fill_with)
559 def do_batch(value, linecount, fill_with=None):
561 A filter that batches items. It works pretty much like `slice`
562 just the other way round. It returns a list of lists with the
563 given number of items. If you provide a second parameter this
564 is used to fill missing items. See this example:
566 .. sourcecode:: html+jinja
569 {%- for row in items|batch(3, ' ') %}
571 {%- for column in row %}
572 <td>{{ column }}</td>
581 if len(tmp) == linecount:
586 if fill_with is not None and len(tmp) < linecount:
587 tmp += [fill_with] * (linecount - len(tmp))
591 def do_round(value, precision=0, method='common'):
592 """Round the number to a given precision. The first
593 parameter specifies the precision (default is ``0``), the
594 second the rounding method:
596 - ``'common'`` rounds either up or down
597 - ``'ceil'`` always rounds up
598 - ``'floor'`` always rounds down
600 If you don't specify a method ``'common'`` is used.
602 .. sourcecode:: jinja
606 {{ 42.55|round(1, 'floor') }}
609 Note that even if rounded to 0 precision, a float is returned. If
610 you need a real integer, pipe it through `int`:
612 .. sourcecode:: jinja
614 {{ 42.55|round|int }}
617 if not method in ('common', 'ceil', 'floor'):
618 raise FilterArgumentError('method must be common, ceil or floor')
619 if method == 'common':
620 return round(value, precision)
621 func = getattr(math, method)
622 return func(value * (10 ** precision)) / (10 ** precision)
626 def do_groupby(environment, value, attribute):
627 """Group a sequence of objects by a common attribute.
629 If you for example have a list of dicts or objects that represent persons
630 with `gender`, `first_name` and `last_name` attributes and you want to
631 group all users by genders you can do something like the following
634 .. sourcecode:: html+jinja
637 {% for group in persons|groupby('gender') %}
638 <li>{{ group.grouper }}<ul>
639 {% for person in group.list %}
640 <li>{{ person.first_name }} {{ person.last_name }}</li>
641 {% endfor %}</ul></li>
645 Additionally it's possible to use tuple unpacking for the grouper and
648 .. sourcecode:: html+jinja
651 {% for grouper, list in persons|groupby('gender') %}
656 As you can see the item we're grouping by is stored in the `grouper`
657 attribute and the `list` contains all the objects that have this grouper
660 .. versionchanged:: 2.6
661 It's now possible to use dotted notation to group by the child
662 attribute of another attribute.
664 expr = make_attrgetter(environment, attribute)
665 return sorted(map(_GroupTuple, groupby(sorted(value, key=expr), expr)))
668 class _GroupTuple(tuple):
670 grouper = property(itemgetter(0))
671 list = property(itemgetter(1))
673 def __new__(cls, (key, value)):
674 return tuple.__new__(cls, (key, list(value)))
678 def do_sum(environment, iterable, attribute=None, start=0):
679 """Returns the sum of a sequence of numbers plus the value of parameter
680 'start' (which defaults to 0). When the sequence is empty it returns
683 It is also possible to sum up only certain attributes:
685 .. sourcecode:: jinja
687 Total: {{ items|sum(attribute='price') }}
689 .. versionchanged:: 2.6
690 The `attribute` parameter was added to allow suming up over
691 attributes. Also the `start` parameter was moved on to the right.
693 if attribute is not None:
694 iterable = imap(make_attrgetter(environment, attribute), iterable)
695 return sum(iterable, start)
699 """Convert the value into a list. If it was a string the returned list
700 will be a list of characters.
705 def do_mark_safe(value):
706 """Mark the value as safe which means that in an environment with automatic
707 escaping enabled this variable will not be escaped.
712 def do_mark_unsafe(value):
713 """Mark a value as unsafe. This is the reverse operation for :func:`safe`."""
714 return unicode(value)
717 def do_reverse(value):
718 """Reverse the object or return an iterator the iterates over it the other
721 if isinstance(value, basestring):
724 return reversed(value)
731 raise FilterArgumentError('argument must be iterable')
735 def do_attr(environment, obj, name):
736 """Get an attribute of an object. ``foo|attr("bar")`` works like
737 ``foo["bar"]`` just that always an attribute is returned and items are not
740 See :ref:`Notes on subscriptions <notes-on-subscriptions>` for more details.
748 value = getattr(obj, name)
749 except AttributeError:
752 if environment.sandboxed and not \
753 environment.is_safe_attribute(obj, name, value):
754 return environment.unsafe_undefined(obj, name)
756 return environment.undefined(obj=obj, name=name)
761 'replace': do_replace,
766 'forceescape': do_forceescape,
767 'capitalize': do_capitalize,
769 'default': do_default,
773 'dictsort': do_dictsort,
776 'reverse': do_reverse,
780 'capitalize': do_capitalize,
784 'filesizeformat': do_filesizeformat,
786 'truncate': do_truncate,
787 'wordwrap': do_wordwrap,
788 'wordcount': do_wordcount,
791 'string': soft_unicode,
796 'striptags': do_striptags,
802 'groupby': do_groupby,
803 'safe': do_mark_safe,
804 'xmlattr': do_xmlattr,
805 'urlescape': do_urlescape,
806 'urle': do_urlescape,
807 'urlencode': do_urlescape,
808 'urlquote': do_urlescape