1 # -*- coding: utf-8 -*-
8 :copyright: (c) 2010 by the Jinja Team.
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, \
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))
74 def do_urlescape(value):
75 """Escape strings for use in URLs (uses UTF-8 encoding). It accepts both
76 dictionaries and regular strings as well as pairwise iterables.
81 if isinstance(value, dict):
82 itemiter = value.iteritems()
83 elif not isinstance(value, basestring):
85 itemiter = iter(value)
89 return unicode_urlescape(value)
90 return u'&'.join(unicode_urlescape(k) + '=' +
91 unicode_urlescape(v) for k, v in itemiter)
95 def do_replace(eval_ctx, s, old, new, count=None):
96 """Return a copy of the value with all occurrences of a substring
97 replaced with a new one. The first argument is the substring
98 that should be replaced, the second is the replacement string.
99 If the optional third argument ``count`` is given, only the first
100 ``count`` occurrences are replaced:
102 .. sourcecode:: jinja
104 {{ "Hello World"|replace("Hello", "Goodbye") }}
107 {{ "aaaaargh"|replace("a", "d'oh, ", 2) }}
108 -> d'oh, d'oh, aaargh
112 if not eval_ctx.autoescape:
113 return unicode(s).replace(unicode(old), unicode(new), count)
114 if hasattr(old, '__html__') or hasattr(new, '__html__') and \
115 not hasattr(s, '__html__'):
119 return s.replace(soft_unicode(old), soft_unicode(new), count)
123 """Convert a value to uppercase."""
124 return soft_unicode(s).upper()
128 """Convert a value to lowercase."""
129 return soft_unicode(s).lower()
133 def do_xmlattr(_eval_ctx, d, autospace=True):
134 """Create an SGML/XML attribute string based on the items in a dict.
135 All values that are neither `none` nor `undefined` are automatically
138 .. sourcecode:: html+jinja
140 <ul{{ {'class': 'my_list', 'missing': none,
141 'id': 'list-%d'|format(variable)}|xmlattr }}>
145 Results in something like this:
149 <ul class="my_list" id="list-42">
153 As you can see it automatically prepends a space in front of the item
154 if the filter returned something unless the second parameter is false.
157 u'%s="%s"' % (escape(key), escape(value))
158 for key, value in d.iteritems()
159 if value is not None and not isinstance(value, Undefined)
163 if _eval_ctx.autoescape:
168 def do_capitalize(s):
169 """Capitalize a value. The first character will be uppercase, all others
172 return soft_unicode(s).capitalize()
176 """Return a titlecased version of the value. I.e. words will start with
177 uppercase letters, all remaining characters are lowercase.
179 return soft_unicode(s).title()
182 def do_dictsort(value, case_sensitive=False, by='key'):
183 """Sort a dict and yield (key, value) pairs. Because python dicts are
184 unsorted you may want to use this function to order them by either
187 .. sourcecode:: jinja
189 {% for item in mydict|dictsort %}
190 sort the dict by key, case insensitive
192 {% for item in mydict|dicsort(true) %}
193 sort the dict by key, case sensitive
195 {% for item in mydict|dictsort(false, 'value') %}
196 sort the dict by key, case insensitive, sorted
197 normally and ordered by value.
204 raise FilterArgumentError('You can only sort by either '
208 if isinstance(value, basestring) and not case_sensitive:
209 value = value.lower()
212 return sorted(value.items(), key=sort_func)
216 def do_sort(environment, value, reverse=False, case_sensitive=False,
218 """Sort an iterable. Per default it sorts ascending, if you pass it
219 true as first argument it will reverse the sorting.
221 If the iterable is made of strings the third parameter can be used to
222 control the case sensitiveness of the comparison which is disabled by
225 .. sourcecode:: jinja
227 {% for item in iterable|sort %}
231 It is also possible to sort by an attribute (for example to sort
232 by the date of an object) by specifying the `attribute` parameter:
234 .. sourcecode:: jinja
236 {% for item in iterable|sort(attribute='date') %}
240 .. versionchanged:: 2.6
241 The `attribute` parameter was added.
243 if not case_sensitive:
245 if isinstance(item, basestring):
250 if attribute is not None:
251 getter = make_attrgetter(environment, attribute)
252 def sort_func(item, processor=sort_func or (lambda x: x)):
253 return processor(getter(item))
254 return sorted(value, key=sort_func, reverse=reverse)
257 def do_default(value, default_value=u'', boolean=False):
258 """If the value is undefined it will return the passed default value,
259 otherwise the value of the variable:
261 .. sourcecode:: jinja
263 {{ my_variable|default('my_variable is not defined') }}
265 This will output the value of ``my_variable`` if the variable was
266 defined, otherwise ``'my_variable is not defined'``. If you want
267 to use default with variables that evaluate to false you have to
268 set the second parameter to `true`:
270 .. sourcecode:: jinja
272 {{ ''|default('the string was empty', true) }}
274 if (boolean and not value) or isinstance(value, Undefined):
280 def do_join(eval_ctx, value, d=u'', attribute=None):
281 """Return a string which is the concatenation of the strings in the
282 sequence. The separator between elements is an empty string per
283 default, you can define it with the optional parameter:
285 .. sourcecode:: jinja
287 {{ [1, 2, 3]|join('|') }}
293 It is also possible to join certain attributes of an object:
295 .. sourcecode:: jinja
297 {{ users|join(', ', attribute='username') }}
299 .. versionadded:: 2.6
300 The `attribute` parameter was added.
302 if attribute is not None:
303 value = imap(make_attrgetter(eval_ctx.environment, attribute), value)
305 # no automatic escaping? joining is a lot eaiser then
306 if not eval_ctx.autoescape:
307 return unicode(d).join(imap(unicode, value))
309 # if the delimiter doesn't have an html representation we check
310 # if any of the items has. If yes we do a coercion to Markup
311 if not hasattr(d, '__html__'):
314 for idx, item in enumerate(value):
315 if hasattr(item, '__html__'):
318 value[idx] = unicode(item)
325 # no html involved, to normal joining
326 return soft_unicode(d).join(imap(soft_unicode, value))
329 def do_center(value, width=80):
330 """Centers the value in a field of a given width."""
331 return unicode(value).center(width)
335 def do_first(environment, seq):
336 """Return the first item of a sequence."""
338 return iter(seq).next()
339 except StopIteration:
340 return environment.undefined('No first item, sequence was empty.')
344 def do_last(environment, seq):
345 """Return the last item of a sequence."""
347 return iter(reversed(seq)).next()
348 except StopIteration:
349 return environment.undefined('No last item, sequence was empty.')
353 def do_random(environment, seq):
354 """Return a random item from the sequence."""
358 return environment.undefined('No random item, sequence was empty.')
361 def do_filesizeformat(value, binary=False):
362 """Format the value like a 'human-readable' file size (i.e. 13 kB,
363 4.1 MB, 102 Bytes, etc). Per default decimal prefixes are used (Mega,
364 Giga, etc.), if the second parameter is set to `True` the binary
365 prefixes are used (Mebi, Gibi).
368 base = binary and 1024 or 1000
370 (binary and 'KiB' or 'kB'),
371 (binary and 'MiB' or 'MB'),
372 (binary and 'GiB' or 'GB'),
373 (binary and 'TiB' or 'TB'),
374 (binary and 'PiB' or 'PB'),
375 (binary and 'EiB' or 'EB'),
376 (binary and 'ZiB' or 'ZB'),
377 (binary and 'YiB' or 'YB')
382 return '%d Bytes' % bytes
384 for i, prefix in enumerate(prefixes):
385 unit = base ** (i + 2)
387 return '%.1f %s' % ((base * bytes / unit), prefix)
388 return '%.1f %s' % ((base * bytes / unit), prefix)
391 def do_pprint(value, verbose=False):
392 """Pretty print a variable. Useful for debugging.
394 With Jinja 1.2 onwards you can pass it a parameter. If this parameter
395 is truthy the output will be more verbose (this requires `pretty`)
397 return pformat(value, verbose=verbose)
401 def do_urlize(eval_ctx, value, trim_url_limit=None, nofollow=False):
402 """Converts URLs in plain text into clickable links.
404 If you pass the filter an additional integer it will shorten the urls
405 to that number. Also a third argument exists that makes the urls
408 .. sourcecode:: jinja
410 {{ mytext|urlize(40, true) }}
411 links are shortened to 40 chars and defined with rel="nofollow"
413 rv = urlize(value, trim_url_limit, nofollow)
414 if eval_ctx.autoescape:
419 def do_indent(s, width=4, indentfirst=False):
420 """Return a copy of the passed string, each line indented by
421 4 spaces. The first line is not indented. If you want to
422 change the number of spaces or indent the first line too
423 you can pass additional parameters to the filter:
425 .. sourcecode:: jinja
427 {{ mytext|indent(2, true) }}
428 indent by two spaces and indent the first line too.
430 indention = u' ' * width
431 rv = (u'\n' + indention).join(s.splitlines())
437 def do_truncate(s, length=255, killwords=False, end='...'):
438 """Return a truncated copy of the string. The length is specified
439 with the first parameter which defaults to ``255``. If the second
440 parameter is ``true`` the filter will cut the text at length. Otherwise
441 it will try to save the last word. If the text was in fact
442 truncated it will append an ellipsis sign (``"..."``). If you want a
443 different ellipsis sign than ``"..."`` you can specify it using the
446 .. sourcecode jinja::
448 {{ mytext|truncate(300, false, '»') }}
449 truncate mytext to 300 chars, don't split up words, use a
450 right pointing double arrow as ellipsis sign.
455 return s[:length] + end
465 return u' '.join(result)
468 def do_wordwrap(environment, s, width=79, break_long_words=True):
470 Return a copy of the string passed to the filter wrapped after
471 ``79`` characters. You can override this default using the first
472 parameter. If you set the second parameter to `false` Jinja will not
473 split words apart if they are longer than `width`.
476 return environment.newline_sequence.join(textwrap.wrap(s, width=width, expand_tabs=False,
477 replace_whitespace=False,
478 break_long_words=break_long_words))
482 """Count the words in that string."""
483 return len(_word_re.findall(s))
486 def do_int(value, default=0):
487 """Convert the value into an integer. If the
488 conversion doesn't work it will return ``0``. You can
489 override this default using the first parameter.
493 except (TypeError, ValueError):
494 # this quirk is necessary so that "42.23"|int gives 42.
496 return int(float(value))
497 except (TypeError, ValueError):
501 def do_float(value, default=0.0):
502 """Convert the value into a floating point number. If the
503 conversion doesn't work it will return ``0.0``. You can
504 override this default using the first parameter.
508 except (TypeError, ValueError):
512 def do_format(value, *args, **kwargs):
514 Apply python string formatting on an object:
516 .. sourcecode:: jinja
518 {{ "%s - %s"|format("Hello?", "Foo!") }}
522 raise FilterArgumentError('can\'t handle positional and keyword '
523 'arguments at the same time')
524 return soft_unicode(value) % (kwargs or args)
528 """Strip leading and trailing whitespace."""
529 return soft_unicode(value).strip()
532 def do_striptags(value):
533 """Strip SGML/XML tags and replace adjacent whitespace by one space.
535 if hasattr(value, '__html__'):
536 value = value.__html__()
537 return Markup(unicode(value)).striptags()
540 def do_slice(value, slices, fill_with=None):
541 """Slice an iterator and return a list of lists containing
542 those items. Useful if you want to create a div containing
543 three ul tags that represent columns:
545 .. sourcecode:: html+jinja
547 <div class="columwrapper">
548 {%- for column in items|slice(3) %}
549 <ul class="column-{{ loop.index }}">
550 {%- for item in column %}
557 If you pass it a second argument it's used to fill missing
558 values on the last iteration.
562 items_per_slice = length // slices
563 slices_with_extra = length % slices
565 for slice_number in xrange(slices):
566 start = offset + slice_number * items_per_slice
567 if slice_number < slices_with_extra:
569 end = offset + (slice_number + 1) * items_per_slice
571 if fill_with is not None and slice_number >= slices_with_extra:
572 tmp.append(fill_with)
576 def do_batch(value, linecount, fill_with=None):
578 A filter that batches items. It works pretty much like `slice`
579 just the other way round. It returns a list of lists with the
580 given number of items. If you provide a second parameter this
581 is used to fill missing items. See this example:
583 .. sourcecode:: html+jinja
586 {%- for row in items|batch(3, ' ') %}
588 {%- for column in row %}
589 <td>{{ column }}</td>
598 if len(tmp) == linecount:
603 if fill_with is not None and len(tmp) < linecount:
604 tmp += [fill_with] * (linecount - len(tmp))
608 def do_round(value, precision=0, method='common'):
609 """Round the number to a given precision. The first
610 parameter specifies the precision (default is ``0``), the
611 second the rounding method:
613 - ``'common'`` rounds either up or down
614 - ``'ceil'`` always rounds up
615 - ``'floor'`` always rounds down
617 If you don't specify a method ``'common'`` is used.
619 .. sourcecode:: jinja
623 {{ 42.55|round(1, 'floor') }}
626 Note that even if rounded to 0 precision, a float is returned. If
627 you need a real integer, pipe it through `int`:
629 .. sourcecode:: jinja
631 {{ 42.55|round|int }}
634 if not method in ('common', 'ceil', 'floor'):
635 raise FilterArgumentError('method must be common, ceil or floor')
636 if method == 'common':
637 return round(value, precision)
638 func = getattr(math, method)
639 return func(value * (10 ** precision)) / (10 ** precision)
643 def do_groupby(environment, value, attribute):
644 """Group a sequence of objects by a common attribute.
646 If you for example have a list of dicts or objects that represent persons
647 with `gender`, `first_name` and `last_name` attributes and you want to
648 group all users by genders you can do something like the following
651 .. sourcecode:: html+jinja
654 {% for group in persons|groupby('gender') %}
655 <li>{{ group.grouper }}<ul>
656 {% for person in group.list %}
657 <li>{{ person.first_name }} {{ person.last_name }}</li>
658 {% endfor %}</ul></li>
662 Additionally it's possible to use tuple unpacking for the grouper and
665 .. sourcecode:: html+jinja
668 {% for grouper, list in persons|groupby('gender') %}
673 As you can see the item we're grouping by is stored in the `grouper`
674 attribute and the `list` contains all the objects that have this grouper
677 .. versionchanged:: 2.6
678 It's now possible to use dotted notation to group by the child
679 attribute of another attribute.
681 expr = make_attrgetter(environment, attribute)
682 return sorted(map(_GroupTuple, groupby(sorted(value, key=expr), expr)))
685 class _GroupTuple(tuple):
687 grouper = property(itemgetter(0))
688 list = property(itemgetter(1))
690 def __new__(cls, (key, value)):
691 return tuple.__new__(cls, (key, list(value)))
695 def do_sum(environment, iterable, attribute=None, start=0):
696 """Returns the sum of a sequence of numbers plus the value of parameter
697 'start' (which defaults to 0). When the sequence is empty it returns
700 It is also possible to sum up only certain attributes:
702 .. sourcecode:: jinja
704 Total: {{ items|sum(attribute='price') }}
706 .. versionchanged:: 2.6
707 The `attribute` parameter was added to allow suming up over
708 attributes. Also the `start` parameter was moved on to the right.
710 if attribute is not None:
711 iterable = imap(make_attrgetter(environment, attribute), iterable)
712 return sum(iterable, start)
716 """Convert the value into a list. If it was a string the returned list
717 will be a list of characters.
722 def do_mark_safe(value):
723 """Mark the value as safe which means that in an environment with automatic
724 escaping enabled this variable will not be escaped.
729 def do_mark_unsafe(value):
730 """Mark a value as unsafe. This is the reverse operation for :func:`safe`."""
731 return unicode(value)
734 def do_reverse(value):
735 """Reverse the object or return an iterator the iterates over it the other
738 if isinstance(value, basestring):
741 return reversed(value)
748 raise FilterArgumentError('argument must be iterable')
752 def do_attr(environment, obj, name):
753 """Get an attribute of an object. ``foo|attr("bar")`` works like
754 ``foo["bar"]`` just that always an attribute is returned and items are not
757 See :ref:`Notes on subscriptions <notes-on-subscriptions>` for more details.
765 value = getattr(obj, name)
766 except AttributeError:
769 if environment.sandboxed and not \
770 environment.is_safe_attribute(obj, name, value):
771 return environment.unsafe_undefined(obj, name)
773 return environment.undefined(obj=obj, name=name)
778 'replace': do_replace,
783 'forceescape': do_forceescape,
784 'capitalize': do_capitalize,
786 'default': do_default,
790 'dictsort': do_dictsort,
793 'reverse': do_reverse,
797 'capitalize': do_capitalize,
801 'filesizeformat': do_filesizeformat,
803 'truncate': do_truncate,
804 'wordwrap': do_wordwrap,
805 'wordcount': do_wordcount,
808 'string': soft_unicode,
813 'striptags': do_striptags,
819 'groupby': do_groupby,
820 'safe': do_mark_safe,
821 'xmlattr': do_xmlattr,
822 'urlescape': do_urlescape