Bundled jinja filters.
- :copyright: 2008 by Armin Ronacher, Christoph Hack.
+ :copyright: (c) 2009 by the Jinja Team.
:license: BSD, see LICENSE for more details.
"""
import re
import math
-import textwrap
from random import choice
from operator import itemgetter
from itertools import imap, groupby
from jinja2.exceptions import FilterArgumentError, SecurityError
-_word_re = re.compile(r'\w+')
+_word_re = re.compile(r'\w+(?u)')
def contextfilter(f):
def do_dictsort(value, case_sensitive=False, by='key'):
- """ Sort a dict and yield (key, value) pairs. Because python dicts are
+ """Sort a dict and yield (key, value) pairs. Because python dicts are
unsorted you may want to use this function to order them by either
key or value:
'"key" or "value"')
def sort_func(item):
value = item[pos]
- if isinstance(value, basestring):
- value = unicode(value)
- if not case_sensitive:
- value = value.lower()
+ if isinstance(value, basestring) and not case_sensitive:
+ value = value.lower()
return value
return sorted(value.items(), key=sort_func)
+def do_sort(value, case_sensitive=False):
+ """Sort an iterable. If the iterable is made of strings the second
+ parameter can be used to control the case sensitiveness of the
+ comparison which is disabled by default.
+
+ .. sourcecode:: jinja
+
+ {% for item in iterable|sort %}
+ ...
+ {% endfor %}
+ """
+ if not case_sensitive:
+ def sort_func(item):
+ if isinstance(item, basestring):
+ item = item.lower()
+ return item
+ else:
+ sort_func = None
+ return sorted(seq, key=sort_func)
+
+
def do_default(value, default_value=u'', boolean=False):
"""If the value is undefined it will return the passed default value,
otherwise the value of the variable:
return environment.undefined('No random item, sequence was empty.')
-def do_filesizeformat(value):
+def do_filesizeformat(value, binary=False):
"""Format the value like a 'human-readable' file size (i.e. 13 KB,
- 4.1 MB, 102 bytes, etc).
+ 4.1 MB, 102 bytes, etc). Per default decimal prefixes are used (mega,
+ giga, etc.), if the second parameter is set to `True` the binary
+ prefixes are used (mebi, gibi).
"""
- # fail silently
- try:
- bytes = float(value)
- except TypeError:
- bytes = 0
-
- if bytes < 1024:
+ bytes = float(value)
+ base = binary and 1024 or 1000
+ middle = binary and 'i' or ''
+ if bytes < base:
return "%d Byte%s" % (bytes, bytes != 1 and 's' or '')
- elif bytes < 1024 * 1024:
- return "%.1f KB" % (bytes / 1024)
- elif bytes < 1024 * 1024 * 1024:
- return "%.1f MB" % (bytes / (1024 * 1024))
- return "%.1f GB" % (bytes / (1024 * 1024 * 1024))
+ elif bytes < base * base:
+ return "%.1f K%sB" % (bytes / base, middle)
+ elif bytes < base * base * base:
+ return "%.1f M%sB" % (bytes / (base * base), middle)
+ return "%.1f G%sB" % (bytes / (base * base * base), middle)
def do_pprint(value, verbose=False):
{{ mytext|urlize(40, true) }}
links are shortened to 40 chars and defined with rel="nofollow"
"""
- rv = urlize(soft_unicode(value), trim_url_limit, nofollow)
+ rv = urlize(value, trim_url_limit, nofollow)
if environment.autoescape:
rv = Markup(rv)
return rv
{{ mytext|indent(2, true) }}
indent by two spaces and indent the first line too.
"""
- indention = ' ' * width
+ indention = u' ' * width
+ rv = (u'\n' + indention).join(s.splitlines())
if indentfirst:
- return u'\n'.join(indention + line for line in s.splitlines())
- return s.replace('\n', '\n' + indention)
+ rv = indention + rv
+ return rv
def do_truncate(s, length=255, killwords=False, end='...'):
parameter. If you set the second parameter to `false` Jinja will not
split words apart if they are longer than `width`.
"""
+ import textwrap
return u'\n'.join(textwrap.wrap(s, width=width, expand_tabs=False,
replace_whitespace=False,
break_long_words=break_long_words))
def do_slice(value, slices, fill_with=None):
"""Slice an iterator and return a list of lists containing
those items. Useful if you want to create a div containing
- three div tags that represent columns:
+ three ul tags that represent columns:
.. sourcecode:: html+jinja
{%- for row in items|batch(3, ' ') %}
<tr>
{%- for column in row %}
- <tr>{{ column }}</td>
+ <td>{{ column }}</td>
{%- endfor %}
</tr>
{%- endfor %}
attribute and the `list` contains all the objects that have this grouper
in common.
"""
- expr = lambda x: environment.subscribe(x, attribute)
+ expr = lambda x: environment.getitem(x, attribute)
return sorted(map(_GroupTuple, groupby(sorted(value, key=expr), expr)))
@environmentfilter
def do_attr(environment, obj, name):
"""Get an attribute of an object. ``foo|attr("bar")`` works like
- ``foo["bar"]`` just that always an attribute is returned. This is useful
- if data structures are passed to the template that have an item that hides
- an attribute with the same name. For example a dict ``{'items': []}``
- that obviously hides the item method of a dict.
+ ``foo["bar"]`` just that always an attribute is returned and items are not
+ looked up.
+
+ See :ref:`Notes on subscriptions <notes-on-subscriptions>` for more details.
"""
try:
- value = getattr(obj, name)
- except AttributeError:
- return environment.undefined(obj=obj, name=name)
- if environment.sandboxed and not \
- environment.is_safe_attribute(obj, name, value):
- return environment.undefined('access to attribute %r of %r '
- 'object is unsafe.' % (
- name, obj.__class__.__name__
- ), name=name, obj=obj, exc=SecurityError)
- return value
+ name = str(name)
+ except UnicodeError:
+ pass
+ else:
+ try:
+ value = getattr(obj, name)
+ except AttributeError:
+ pass
+ else:
+ if environment.sandboxed and not \
+ environment.is_safe_attribute(obj, name, value):
+ return environment.unsafe_undefined(obj, name)
+ return value
+ return environment.undefined(obj=obj, name=name)
FILTERS = {
'join': do_join,
'count': len,
'dictsort': do_dictsort,
+ 'sort': do_sort,
'length': len,
'reverse': do_reverse,
'center': do_center,