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
24 _striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)')
28 """Decorator for marking context dependent filters. The current context
29 argument will be passed as first argument.
31 if getattr(f, 'environmentfilter', False):
32 raise TypeError('filter already marked as environment filter')
33 f.contextfilter = True
37 def environmentfilter(f):
38 """Decorator for marking evironment dependent filters. The environment
39 used for the template is passed to the filter as first argument.
41 if getattr(f, 'contextfilter', False):
42 raise TypeError('filter already marked as context filter')
43 f.environmentfilter = True
47 def do_forceescape(value):
48 """Enforce HTML escaping. This will probably double escape variables."""
49 if hasattr(value, '__html__'):
50 value = value.__html__()
51 return escape(unicode(value))
55 def do_replace(environment, s, old, new, count=None):
56 """Return a copy of the value with all occurrences of a substring
57 replaced with a new one. The first argument is the substring
58 that should be replaced, the second is the replacement string.
59 If the optional third argument ``count`` is given, only the first
60 ``count`` occurrences are replaced:
64 {{ "Hello World"|replace("Hello", "Goodbye") }}
67 {{ "aaaaargh"|replace("a", "d'oh, ", 2) }}
72 if not environment.autoescape:
73 return unicode(s).replace(unicode(old), unicode(new), count)
74 if hasattr(old, '__html__') or hasattr(new, '__html__') and \
75 not hasattr(s, '__html__'):
79 return s.replace(old, new, count)
83 """Convert a value to uppercase."""
84 return soft_unicode(s).upper()
88 """Convert a value to lowercase."""
89 return soft_unicode(s).lower()
93 def do_xmlattr(_environment, d, autospace=True):
94 """Create an SGML/XML attribute string based on the items in a dict.
95 All values that are neither `none` nor `undefined` are automatically
98 .. sourcecode:: html+jinja
100 <ul{{ {'class': 'my_list', 'missing': None,
101 'id': 'list-%d'|format(variable)}|xmlattr }}>
105 Results in something like this:
109 <ul class="my_list" id="list-42">
113 As you can see it automatically prepends a space in front of the item
114 if the filter returned something unless the second parameter is `False`.
117 u'%s="%s"' % (escape(key), escape(value))
118 for key, value in dict(*args, **kwargs).iteritems()
119 if value is not None and not isinstance(value, Undefined)
123 if _environment.autoescape:
128 def do_capitalize(s):
129 """Capitalize a value. The first character will be uppercase, all others
132 return soft_unicode(s).capitalize()
136 """Return a titlecased version of the value. I.e. words will start with
137 uppercase letters, all remaining characters are lowercase.
139 return soft_unicode(s).title()
142 def do_dictsort(value, case_sensitive=False, by='key'):
143 """ Sort a dict and yield (key, value) pairs. Because python dicts are
144 unsorted you may want to use this function to order them by either
147 .. sourcecode:: jinja
149 {% for item in mydict|dictsort %}
150 sort the dict by key, case insensitive
152 {% for item in mydict|dicsort(true) %}
153 sort the dict by key, case sensitive
155 {% for item in mydict|dictsort(false, 'value') %}
156 sort the dict by key, case insensitive, sorted
157 normally and ordered by value.
164 raise FilterArgumentError('You can only sort by either '
168 if isinstance(value, basestring):
169 value = unicode(value)
170 if not case_sensitive:
171 value = value.lower()
174 return sorted(value.items(), key=sort_func)
177 def do_default(value, default_value=u'', boolean=False):
178 """If the value is undefined it will return the passed default value,
179 otherwise the value of the variable:
181 .. sourcecode:: jinja
183 {{ my_variable|default('my_variable is not defined') }}
185 This will output the value of ``my_variable`` if the variable was
186 defined, otherwise ``'my_variable is not defined'``. If you want
187 to use default with variables that evaluate to false you have to
188 set the second parameter to `true`:
190 .. sourcecode:: jinja
192 {{ ''|default('the string was empty', true) }}
194 if (boolean and not value) or isinstance(value, Undefined):
200 def do_join(environment, value, d=u''):
201 """Return a string which is the concatenation of the strings in the
202 sequence. The separator between elements is an empty string per
203 default, you can define ith with the optional parameter:
205 .. sourcecode:: jinja
207 {{ [1, 2, 3]|join('|') }}
213 # no automatic escaping? joining is a lot eaiser then
214 if not environment.autoescape:
215 return unicode(d).join(imap(unicode, value))
217 # if the delimiter doesn't have an html representation we check
218 # if any of the items has. If yes we do a coercion to Markup
219 if not hasattr(d, '__html__'):
222 for idx, item in enumerate(value):
223 if hasattr(item, '__html__'):
226 value[idx] = unicode(item)
233 # no html involved, to normal joining
234 return soft_unicode(d).join(imap(soft_unicode, value))
237 def do_center(value, width=80):
238 """Centers the value in a field of a given width."""
239 return unicode(value).center(width)
243 def do_first(environment, seq):
244 """Return the frist item of a sequence."""
246 return iter(seq).next()
247 except StopIteration:
248 return environment.undefined('No first item, sequence was empty.')
252 def do_last(environment, seq):
253 """Return the last item of a sequence."""
255 return iter(reversed(seq)).next()
256 except StopIteration:
257 return environment.undefined('No last item, sequence was empty.')
261 def do_random(environment, seq):
262 """Return a random item from the sequence."""
266 return environment.undefined('No random item, sequence was empty.')
269 def do_filesizeformat(value):
270 """Format the value like a 'human-readable' file size (i.e. 13 KB,
271 4.1 MB, 102 bytes, etc).
280 return "%d Byte%s" % (bytes, bytes != 1 and 's' or '')
281 elif bytes < 1024 * 1024:
282 return "%.1f KB" % (bytes / 1024)
283 elif bytes < 1024 * 1024 * 1024:
284 return "%.1f MB" % (bytes / (1024 * 1024))
285 return "%.1f GB" % (bytes / (1024 * 1024 * 1024))
288 def do_pprint(value, verbose=False):
289 """Pretty print a variable. Useful for debugging.
291 With Jinja 1.2 onwards you can pass it a parameter. If this parameter
292 is truthy the output will be more verbose (this requires `pretty`)
294 return pformat(value, verbose=verbose)
297 def do_urlize(value, trim_url_limit=None, nofollow=False):
298 """Converts URLs in plain text into clickable links.
300 If you pass the filter an additional integer it will shorten the urls
301 to that number. Also a third argument exists that makes the urls
304 .. sourcecode:: jinja
306 {{ mytext|urlize(40, True) }}
307 links are shortened to 40 chars and defined with rel="nofollow"
309 return urlize(soft_unicode(value), trim_url_limit, nofollow)
312 def do_indent(s, width=4, indentfirst=False):
313 """Return a copy of the passed string, each line indented by
314 4 spaces. The first line is not indented. If you want to
315 change the number of spaces or indent the first line too
316 you can pass additional parameters to the filter:
318 .. sourcecode:: jinja
320 {{ mytext|indent(2, True) }}
321 indent by two spaces and indent the first line too.
323 indention = ' ' * width
325 return u'\n'.join(indention + line for line in s.splitlines())
326 return s.replace('\n', '\n' + indention)
329 def do_truncate(s, length=255, killwords=False, end='...'):
330 """Return a truncated copy of the string. The length is specified
331 with the first parameter which defaults to ``255``. If the second
332 parameter is ``true`` the filter will cut the text at length. Otherwise
333 it will try to save the last word. If the text was in fact
334 truncated it will append an ellipsis sign (``"..."``). If you want a
335 different ellipsis sign than ``"..."`` you can specify it using the
338 .. sourcecode jinja::
340 {{ mytext|truncate(300, false, '»') }}
341 truncate mytext to 300 chars, don't split up words, use a
342 right pointing double arrow as ellipsis sign.
347 return s[:length] + end
357 return u' '.join(result)
360 def do_wordwrap(s, pos=79, hard=False):
362 Return a copy of the string passed to the filter wrapped after
363 ``79`` characters. You can override this default using the first
364 parameter. If you set the second parameter to `true` Jinja will
365 also split words apart (usually a bad idea because it makes
371 return u'\n'.join(s[idx:idx + pos] for idx in
372 xrange(0, len(s), pos))
374 # TODO: switch to wordwrap.wrap
375 # code from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/148061
376 return reduce(lambda line, word, pos=pos: u'%s%s%s' %
377 (line, u' \n'[(len(line)-line.rfind('\n') - 1 +
378 len(word.split('\n', 1)[0]) >= pos)],
383 """Count the words in that string."""
384 return len(s.split())
387 def do_int(value, default=0):
388 """Convert the value into an integer. If the
389 conversion doesn't work it will return ``0``. You can
390 override this default using the first parameter.
394 except (TypeError, ValueError):
395 # this quirk is necessary so that "42.23"|int gives 42.
397 return int(float(value))
398 except (TypeError, ValueError):
402 def do_float(value, default=0.0):
403 """Convert the value into a floating point number. If the
404 conversion doesn't work it will return ``0.0``. You can
405 override this default using the first parameter.
409 except (TypeError, ValueError):
413 def do_format(value, *args, **kwargs):
415 Apply python string formatting on an object:
417 .. sourcecode:: jinja
419 {{ "%s - %s"|format("Hello?", "Foo!") }}
423 kwargs.update(idx, arg in enumerate(args))
425 return soft_unicode(value) % args
429 """Strip leading and trailing whitespace."""
430 return soft_unicode(value).strip()
433 def do_striptags(value):
434 """Strip SGML/XML tags and replace adjacent whitespace by one space.
436 if hasattr(value, '__html__'):
437 value = value.__html__()
438 return u' '.join(_striptags_re.sub('', value).split())
441 def do_slice(value, slices, fill_with=None):
442 """Slice an iterator and return a list of lists containing
443 those items. Useful if you want to create a div containing
444 three div tags that represent columns:
446 .. sourcecode:: html+jinja
448 <div class="columwrapper">
449 {%- for column in items|slice(3) %}
450 <ul class="column-{{ loop.index }}">
451 {%- for item in column %}
458 If you pass it a second argument it's used to fill missing
459 values on the last iteration.
463 items_per_slice = length // slices
464 slices_with_extra = length % slices
466 for slice_number in xrange(slices):
467 start = offset + slice_number * items_per_slice
468 if slice_number < slices_with_extra:
470 end = offset + (slice_number + 1) * items_per_slice
472 if fill_with is not None and slice_number >= slices_with_extra:
473 tmp.append(fill_with)
477 def do_batch(value, linecount, fill_with=None):
479 A filter that batches items. It works pretty much like `slice`
480 just the other way round. It returns a list of lists with the
481 given number of items. If you provide a second parameter this
482 is used to fill missing items. See this example:
484 .. sourcecode:: html+jinja
487 {%- for row in items|batch(3, ' ') %}
489 {%- for column in row %}
490 <tr>{{ column }}</td>
499 if len(tmp) == linecount:
504 if fill_with is not None and len(tmp) < linecount:
505 tmp += [fill_with] * (linecount - len(tmp))
509 def do_round(value, precision=0, method='common'):
510 """Round the number to a given precision. The first
511 parameter specifies the precision (default is ``0``), the
512 second the rounding method:
514 - ``'common'`` rounds either up or down
515 - ``'ceil'`` always rounds up
516 - ``'floor'`` always rounds down
518 If you don't specify a method ``'common'`` is used.
520 .. sourcecode:: jinja
524 {{ 42.55|round(1, 'floor') }}
527 if not method in ('common', 'ceil', 'floor'):
528 raise FilterArgumentError('method must be common, ceil or floor')
530 raise FilterArgumentError('precision must be a postive integer '
532 if method == 'common':
533 return round(value, precision)
534 func = getattr(math, method)
536 return func(value * 10 * precision) / (10 * precision)
541 def do_sort(value, reverse=False):
542 """Sort a sequence. Per default it sorts ascending, if you pass it
543 `True` as first argument it will reverse the sorting.
545 return sorted(value, reverse=reverse)
549 def do_groupby(environment, value, attribute):
550 """Group a sequence of objects by a common attribute.
552 If you for example have a list of dicts or objects that represent persons
553 with `gender`, `first_name` and `last_name` attributes and you want to
554 group all users by genders you can do something like the following
557 .. sourcecode:: html+jinja
560 {% for group in persons|groupby('gender') %}
561 <li>{{ group.grouper }}<ul>
562 {% for person in group.list %}
563 <li>{{ person.first_name }} {{ person.last_name }}</li>
564 {% endfor %}</ul></li>
568 Additionally it's possible to use tuple unpacking for the grouper and
571 .. sourcecode:: html+jinja
574 {% for grouper, list in persons|groupby('gender') %}
579 As you can see the item we're grouping by is stored in the `grouper`
580 attribute and the `list` contains all the objects that have this grouper
583 expr = lambda x: environment.subscribe(x, attribute)
584 return sorted(map(_GroupTuple, groupby(sorted(value, key=expr), expr)))
587 class _GroupTuple(tuple):
589 grouper = property(itemgetter(0))
590 list = property(itemgetter(1))
592 def __new__(cls, (key, value)):
593 return tuple.__new__(cls, (key, list(value)))
597 """Convert the value into a list. If it was a string the returned list
598 will be a list of characters.
603 def do_mark_safe(value):
604 """Mark the value as safe which means that in an environment with automatic
605 escaping enabled this variable will not be escaped.
610 def do_reverse(value):
611 """Reverse the object or return an iterator the iterates over it the other
614 if isinstance(value, basestring):
617 return reversed(value)
624 raise FilterArgumentError('argument must be iterable')
628 'replace': do_replace,
633 'forceescape': do_forceescape,
634 'capitalize': do_capitalize,
636 'default': do_default,
640 'dictsort': do_dictsort,
642 'reverse': do_reverse,
646 'capitalize': do_capitalize,
650 'filesizeformat': do_filesizeformat,
652 'truncate': do_truncate,
653 'wordwrap': do_wordwrap,
654 'wordcount': do_wordcount,
657 'string': soft_unicode,
662 'striptags': do_striptags,
669 'groupby': do_groupby,
670 'safe': do_mark_safe,
671 'xmlattr': do_xmlattr