| OLD | NEW |
| 1 # -*- coding: utf-8 -*- | 1 # -*- coding: utf-8 -*- |
| 2 """ | 2 """ |
| 3 jinja2.filters | 3 jinja2.filters |
| 4 ~~~~~~~~~~~~~~ | 4 ~~~~~~~~~~~~~~ |
| 5 | 5 |
| 6 Bundled jinja filters. | 6 Bundled jinja filters. |
| 7 | 7 |
| 8 :copyright: (c) 2010 by the Jinja Team. | 8 :copyright: (c) 2010 by the Jinja Team. |
| 9 :license: BSD, see LICENSE for more details. | 9 :license: BSD, see LICENSE for more details. |
| 10 """ | 10 """ |
| 11 import re | 11 import re |
| 12 import math | 12 import math |
| 13 |
| 13 from random import choice | 14 from random import choice |
| 14 from operator import itemgetter | 15 from operator import itemgetter |
| 15 from itertools import imap, groupby | 16 from itertools import groupby |
| 16 from jinja2.utils import Markup, escape, pformat, urlize, soft_unicode | 17 from jinja2.utils import Markup, escape, pformat, urlize, soft_unicode, \ |
| 18 unicode_urlencode |
| 17 from jinja2.runtime import Undefined | 19 from jinja2.runtime import Undefined |
| 18 from jinja2.exceptions import FilterArgumentError, SecurityError | 20 from jinja2.exceptions import FilterArgumentError |
| 21 from jinja2._compat import next, imap, string_types, text_type, iteritems |
| 19 | 22 |
| 20 | 23 |
| 21 _word_re = re.compile(r'\w+(?u)') | 24 _word_re = re.compile(r'\w+(?u)') |
| 22 | 25 |
| 23 | 26 |
| 24 def contextfilter(f): | 27 def contextfilter(f): |
| 25 """Decorator for marking context dependent filters. The current | 28 """Decorator for marking context dependent filters. The current |
| 26 :class:`Context` will be passed as first argument. | 29 :class:`Context` will be passed as first argument. |
| 27 """ | 30 """ |
| 28 f.contextfilter = True | 31 f.contextfilter = True |
| (...skipping 15 matching lines...) Expand all Loading... |
| 44 """Decorator for marking evironment dependent filters. The current | 47 """Decorator for marking evironment dependent filters. The current |
| 45 :class:`Environment` is passed to the filter as first argument. | 48 :class:`Environment` is passed to the filter as first argument. |
| 46 """ | 49 """ |
| 47 f.environmentfilter = True | 50 f.environmentfilter = True |
| 48 return f | 51 return f |
| 49 | 52 |
| 50 | 53 |
| 51 def make_attrgetter(environment, attribute): | 54 def make_attrgetter(environment, attribute): |
| 52 """Returns a callable that looks up the given attribute from a | 55 """Returns a callable that looks up the given attribute from a |
| 53 passed object with the rules of the environment. Dots are allowed | 56 passed object with the rules of the environment. Dots are allowed |
| 54 to access attributes of attributes. | 57 to access attributes of attributes. Integer parts in paths are |
| 58 looked up as integers. |
| 55 """ | 59 """ |
| 56 if not isinstance(attribute, basestring) or '.' not in attribute: | 60 if not isinstance(attribute, string_types) \ |
| 61 or ('.' not in attribute and not attribute.isdigit()): |
| 57 return lambda x: environment.getitem(x, attribute) | 62 return lambda x: environment.getitem(x, attribute) |
| 58 attribute = attribute.split('.') | 63 attribute = attribute.split('.') |
| 59 def attrgetter(item): | 64 def attrgetter(item): |
| 60 for part in attribute: | 65 for part in attribute: |
| 66 if part.isdigit(): |
| 67 part = int(part) |
| 61 item = environment.getitem(item, part) | 68 item = environment.getitem(item, part) |
| 62 return item | 69 return item |
| 63 return attrgetter | 70 return attrgetter |
| 64 | 71 |
| 65 | 72 |
| 66 def do_forceescape(value): | 73 def do_forceescape(value): |
| 67 """Enforce HTML escaping. This will probably double escape variables.""" | 74 """Enforce HTML escaping. This will probably double escape variables.""" |
| 68 if hasattr(value, '__html__'): | 75 if hasattr(value, '__html__'): |
| 69 value = value.__html__() | 76 value = value.__html__() |
| 70 return escape(unicode(value)) | 77 return escape(text_type(value)) |
| 78 |
| 79 |
| 80 def do_urlencode(value): |
| 81 """Escape strings for use in URLs (uses UTF-8 encoding). It accepts both |
| 82 dictionaries and regular strings as well as pairwise iterables. |
| 83 |
| 84 .. versionadded:: 2.7 |
| 85 """ |
| 86 itemiter = None |
| 87 if isinstance(value, dict): |
| 88 itemiter = iteritems(value) |
| 89 elif not isinstance(value, string_types): |
| 90 try: |
| 91 itemiter = iter(value) |
| 92 except TypeError: |
| 93 pass |
| 94 if itemiter is None: |
| 95 return unicode_urlencode(value) |
| 96 return u'&'.join(unicode_urlencode(k) + '=' + |
| 97 unicode_urlencode(v) for k, v in itemiter) |
| 71 | 98 |
| 72 | 99 |
| 73 @evalcontextfilter | 100 @evalcontextfilter |
| 74 def do_replace(eval_ctx, s, old, new, count=None): | 101 def do_replace(eval_ctx, s, old, new, count=None): |
| 75 """Return a copy of the value with all occurrences of a substring | 102 """Return a copy of the value with all occurrences of a substring |
| 76 replaced with a new one. The first argument is the substring | 103 replaced with a new one. The first argument is the substring |
| 77 that should be replaced, the second is the replacement string. | 104 that should be replaced, the second is the replacement string. |
| 78 If the optional third argument ``count`` is given, only the first | 105 If the optional third argument ``count`` is given, only the first |
| 79 ``count`` occurrences are replaced: | 106 ``count`` occurrences are replaced: |
| 80 | 107 |
| 81 .. sourcecode:: jinja | 108 .. sourcecode:: jinja |
| 82 | 109 |
| 83 {{ "Hello World"|replace("Hello", "Goodbye") }} | 110 {{ "Hello World"|replace("Hello", "Goodbye") }} |
| 84 -> Goodbye World | 111 -> Goodbye World |
| 85 | 112 |
| 86 {{ "aaaaargh"|replace("a", "d'oh, ", 2) }} | 113 {{ "aaaaargh"|replace("a", "d'oh, ", 2) }} |
| 87 -> d'oh, d'oh, aaargh | 114 -> d'oh, d'oh, aaargh |
| 88 """ | 115 """ |
| 89 if count is None: | 116 if count is None: |
| 90 count = -1 | 117 count = -1 |
| 91 if not eval_ctx.autoescape: | 118 if not eval_ctx.autoescape: |
| 92 return unicode(s).replace(unicode(old), unicode(new), count) | 119 return text_type(s).replace(text_type(old), text_type(new), count) |
| 93 if hasattr(old, '__html__') or hasattr(new, '__html__') and \ | 120 if hasattr(old, '__html__') or hasattr(new, '__html__') and \ |
| 94 not hasattr(s, '__html__'): | 121 not hasattr(s, '__html__'): |
| 95 s = escape(s) | 122 s = escape(s) |
| 96 else: | 123 else: |
| 97 s = soft_unicode(s) | 124 s = soft_unicode(s) |
| 98 return s.replace(soft_unicode(old), soft_unicode(new), count) | 125 return s.replace(soft_unicode(old), soft_unicode(new), count) |
| 99 | 126 |
| 100 | 127 |
| 101 def do_upper(s): | 128 def do_upper(s): |
| 102 """Convert a value to uppercase.""" | 129 """Convert a value to uppercase.""" |
| (...skipping 24 matching lines...) Expand all Loading... |
| 127 | 154 |
| 128 <ul class="my_list" id="list-42"> | 155 <ul class="my_list" id="list-42"> |
| 129 ... | 156 ... |
| 130 </ul> | 157 </ul> |
| 131 | 158 |
| 132 As you can see it automatically prepends a space in front of the item | 159 As you can see it automatically prepends a space in front of the item |
| 133 if the filter returned something unless the second parameter is false. | 160 if the filter returned something unless the second parameter is false. |
| 134 """ | 161 """ |
| 135 rv = u' '.join( | 162 rv = u' '.join( |
| 136 u'%s="%s"' % (escape(key), escape(value)) | 163 u'%s="%s"' % (escape(key), escape(value)) |
| 137 for key, value in d.iteritems() | 164 for key, value in iteritems(d) |
| 138 if value is not None and not isinstance(value, Undefined) | 165 if value is not None and not isinstance(value, Undefined) |
| 139 ) | 166 ) |
| 140 if autospace and rv: | 167 if autospace and rv: |
| 141 rv = u' ' + rv | 168 rv = u' ' + rv |
| 142 if _eval_ctx.autoescape: | 169 if _eval_ctx.autoescape: |
| 143 rv = Markup(rv) | 170 rv = Markup(rv) |
| 144 return rv | 171 return rv |
| 145 | 172 |
| 146 | 173 |
| 147 def do_capitalize(s): | 174 def do_capitalize(s): |
| 148 """Capitalize a value. The first character will be uppercase, all others | 175 """Capitalize a value. The first character will be uppercase, all others |
| 149 lowercase. | 176 lowercase. |
| 150 """ | 177 """ |
| 151 return soft_unicode(s).capitalize() | 178 return soft_unicode(s).capitalize() |
| 152 | 179 |
| 153 | 180 |
| 154 def do_title(s): | 181 def do_title(s): |
| 155 """Return a titlecased version of the value. I.e. words will start with | 182 """Return a titlecased version of the value. I.e. words will start with |
| 156 uppercase letters, all remaining characters are lowercase. | 183 uppercase letters, all remaining characters are lowercase. |
| 157 """ | 184 """ |
| 158 return soft_unicode(s).title() | 185 rv = [] |
| 186 for item in re.compile(r'([-\s]+)(?u)').split(s): |
| 187 if not item: |
| 188 continue |
| 189 rv.append(item[0].upper() + item[1:].lower()) |
| 190 return ''.join(rv) |
| 159 | 191 |
| 160 | 192 |
| 161 def do_dictsort(value, case_sensitive=False, by='key'): | 193 def do_dictsort(value, case_sensitive=False, by='key'): |
| 162 """Sort a dict and yield (key, value) pairs. Because python dicts are | 194 """Sort a dict and yield (key, value) pairs. Because python dicts are |
| 163 unsorted you may want to use this function to order them by either | 195 unsorted you may want to use this function to order them by either |
| 164 key or value: | 196 key or value: |
| 165 | 197 |
| 166 .. sourcecode:: jinja | 198 .. sourcecode:: jinja |
| 167 | 199 |
| 168 {% for item in mydict|dictsort %} | 200 {% for item in mydict|dictsort %} |
| 169 sort the dict by key, case insensitive | 201 sort the dict by key, case insensitive |
| 170 | 202 |
| 171 {% for item in mydict|dicsort(true) %} | 203 {% for item in mydict|dictsort(true) %} |
| 172 sort the dict by key, case sensitive | 204 sort the dict by key, case sensitive |
| 173 | 205 |
| 174 {% for item in mydict|dictsort(false, 'value') %} | 206 {% for item in mydict|dictsort(false, 'value') %} |
| 175 sort the dict by key, case insensitive, sorted | 207 sort the dict by key, case insensitive, sorted |
| 176 normally and ordered by value. | 208 normally and ordered by value. |
| 177 """ | 209 """ |
| 178 if by == 'key': | 210 if by == 'key': |
| 179 pos = 0 | 211 pos = 0 |
| 180 elif by == 'value': | 212 elif by == 'value': |
| 181 pos = 1 | 213 pos = 1 |
| 182 else: | 214 else: |
| 183 raise FilterArgumentError('You can only sort by either ' | 215 raise FilterArgumentError('You can only sort by either ' |
| 184 '"key" or "value"') | 216 '"key" or "value"') |
| 185 def sort_func(item): | 217 def sort_func(item): |
| 186 value = item[pos] | 218 value = item[pos] |
| 187 if isinstance(value, basestring) and not case_sensitive: | 219 if isinstance(value, string_types) and not case_sensitive: |
| 188 value = value.lower() | 220 value = value.lower() |
| 189 return value | 221 return value |
| 190 | 222 |
| 191 return sorted(value.items(), key=sort_func) | 223 return sorted(value.items(), key=sort_func) |
| 192 | 224 |
| 193 | 225 |
| 194 @environmentfilter | 226 @environmentfilter |
| 195 def do_sort(environment, value, reverse=False, case_sensitive=False, | 227 def do_sort(environment, value, reverse=False, case_sensitive=False, |
| 196 attribute=None): | 228 attribute=None): |
| 197 """Sort an iterable. Per default it sorts ascending, if you pass it | 229 """Sort an iterable. Per default it sorts ascending, if you pass it |
| (...skipping 16 matching lines...) Expand all Loading... |
| 214 | 246 |
| 215 {% for item in iterable|sort(attribute='date') %} | 247 {% for item in iterable|sort(attribute='date') %} |
| 216 ... | 248 ... |
| 217 {% endfor %} | 249 {% endfor %} |
| 218 | 250 |
| 219 .. versionchanged:: 2.6 | 251 .. versionchanged:: 2.6 |
| 220 The `attribute` parameter was added. | 252 The `attribute` parameter was added. |
| 221 """ | 253 """ |
| 222 if not case_sensitive: | 254 if not case_sensitive: |
| 223 def sort_func(item): | 255 def sort_func(item): |
| 224 if isinstance(item, basestring): | 256 if isinstance(item, string_types): |
| 225 item = item.lower() | 257 item = item.lower() |
| 226 return item | 258 return item |
| 227 else: | 259 else: |
| 228 sort_func = None | 260 sort_func = None |
| 229 if attribute is not None: | 261 if attribute is not None: |
| 230 getter = make_attrgetter(environment, attribute) | 262 getter = make_attrgetter(environment, attribute) |
| 231 def sort_func(item, processor=sort_func or (lambda x: x)): | 263 def sort_func(item, processor=sort_func or (lambda x: x)): |
| 232 return processor(getter(item)) | 264 return processor(getter(item)) |
| 233 return sorted(value, key=sort_func, reverse=reverse) | 265 return sorted(value, key=sort_func, reverse=reverse) |
| 234 | 266 |
| 235 | 267 |
| 236 def do_default(value, default_value=u'', boolean=False): | 268 def do_default(value, default_value=u'', boolean=False): |
| 237 """If the value is undefined it will return the passed default value, | 269 """If the value is undefined it will return the passed default value, |
| 238 otherwise the value of the variable: | 270 otherwise the value of the variable: |
| 239 | 271 |
| 240 .. sourcecode:: jinja | 272 .. sourcecode:: jinja |
| 241 | 273 |
| 242 {{ my_variable|default('my_variable is not defined') }} | 274 {{ my_variable|default('my_variable is not defined') }} |
| 243 | 275 |
| 244 This will output the value of ``my_variable`` if the variable was | 276 This will output the value of ``my_variable`` if the variable was |
| 245 defined, otherwise ``'my_variable is not defined'``. If you want | 277 defined, otherwise ``'my_variable is not defined'``. If you want |
| 246 to use default with variables that evaluate to false you have to | 278 to use default with variables that evaluate to false you have to |
| 247 set the second parameter to `true`: | 279 set the second parameter to `true`: |
| 248 | 280 |
| 249 .. sourcecode:: jinja | 281 .. sourcecode:: jinja |
| 250 | 282 |
| 251 {{ ''|default('the string was empty', true) }} | 283 {{ ''|default('the string was empty', true) }} |
| 252 """ | 284 """ |
| 253 if (boolean and not value) or isinstance(value, Undefined): | 285 if isinstance(value, Undefined) or (boolean and not value): |
| 254 return default_value | 286 return default_value |
| 255 return value | 287 return value |
| 256 | 288 |
| 257 | 289 |
| 258 @evalcontextfilter | 290 @evalcontextfilter |
| 259 def do_join(eval_ctx, value, d=u'', attribute=None): | 291 def do_join(eval_ctx, value, d=u'', attribute=None): |
| 260 """Return a string which is the concatenation of the strings in the | 292 """Return a string which is the concatenation of the strings in the |
| 261 sequence. The separator between elements is an empty string per | 293 sequence. The separator between elements is an empty string per |
| 262 default, you can define it with the optional parameter: | 294 default, you can define it with the optional parameter: |
| 263 | 295 |
| (...skipping 12 matching lines...) Expand all Loading... |
| 276 {{ users|join(', ', attribute='username') }} | 308 {{ users|join(', ', attribute='username') }} |
| 277 | 309 |
| 278 .. versionadded:: 2.6 | 310 .. versionadded:: 2.6 |
| 279 The `attribute` parameter was added. | 311 The `attribute` parameter was added. |
| 280 """ | 312 """ |
| 281 if attribute is not None: | 313 if attribute is not None: |
| 282 value = imap(make_attrgetter(eval_ctx.environment, attribute), value) | 314 value = imap(make_attrgetter(eval_ctx.environment, attribute), value) |
| 283 | 315 |
| 284 # no automatic escaping? joining is a lot eaiser then | 316 # no automatic escaping? joining is a lot eaiser then |
| 285 if not eval_ctx.autoescape: | 317 if not eval_ctx.autoescape: |
| 286 return unicode(d).join(imap(unicode, value)) | 318 return text_type(d).join(imap(text_type, value)) |
| 287 | 319 |
| 288 # if the delimiter doesn't have an html representation we check | 320 # if the delimiter doesn't have an html representation we check |
| 289 # if any of the items has. If yes we do a coercion to Markup | 321 # if any of the items has. If yes we do a coercion to Markup |
| 290 if not hasattr(d, '__html__'): | 322 if not hasattr(d, '__html__'): |
| 291 value = list(value) | 323 value = list(value) |
| 292 do_escape = False | 324 do_escape = False |
| 293 for idx, item in enumerate(value): | 325 for idx, item in enumerate(value): |
| 294 if hasattr(item, '__html__'): | 326 if hasattr(item, '__html__'): |
| 295 do_escape = True | 327 do_escape = True |
| 296 else: | 328 else: |
| 297 value[idx] = unicode(item) | 329 value[idx] = text_type(item) |
| 298 if do_escape: | 330 if do_escape: |
| 299 d = escape(d) | 331 d = escape(d) |
| 300 else: | 332 else: |
| 301 d = unicode(d) | 333 d = text_type(d) |
| 302 return d.join(value) | 334 return d.join(value) |
| 303 | 335 |
| 304 # no html involved, to normal joining | 336 # no html involved, to normal joining |
| 305 return soft_unicode(d).join(imap(soft_unicode, value)) | 337 return soft_unicode(d).join(imap(soft_unicode, value)) |
| 306 | 338 |
| 307 | 339 |
| 308 def do_center(value, width=80): | 340 def do_center(value, width=80): |
| 309 """Centers the value in a field of a given width.""" | 341 """Centers the value in a field of a given width.""" |
| 310 return unicode(value).center(width) | 342 return text_type(value).center(width) |
| 311 | 343 |
| 312 | 344 |
| 313 @environmentfilter | 345 @environmentfilter |
| 314 def do_first(environment, seq): | 346 def do_first(environment, seq): |
| 315 """Return the first item of a sequence.""" | 347 """Return the first item of a sequence.""" |
| 316 try: | 348 try: |
| 317 return iter(seq).next() | 349 return next(iter(seq)) |
| 318 except StopIteration: | 350 except StopIteration: |
| 319 return environment.undefined('No first item, sequence was empty.') | 351 return environment.undefined('No first item, sequence was empty.') |
| 320 | 352 |
| 321 | 353 |
| 322 @environmentfilter | 354 @environmentfilter |
| 323 def do_last(environment, seq): | 355 def do_last(environment, seq): |
| 324 """Return the last item of a sequence.""" | 356 """Return the last item of a sequence.""" |
| 325 try: | 357 try: |
| 326 return iter(reversed(seq)).next() | 358 return next(iter(reversed(seq))) |
| 327 except StopIteration: | 359 except StopIteration: |
| 328 return environment.undefined('No last item, sequence was empty.') | 360 return environment.undefined('No last item, sequence was empty.') |
| 329 | 361 |
| 330 | 362 |
| 331 @environmentfilter | 363 @environmentfilter |
| 332 def do_random(environment, seq): | 364 def do_random(environment, seq): |
| 333 """Return a random item from the sequence.""" | 365 """Return a random item from the sequence.""" |
| 334 try: | 366 try: |
| 335 return choice(seq) | 367 return choice(seq) |
| 336 except IndexError: | 368 except IndexError: |
| 337 return environment.undefined('No random item, sequence was empty.') | 369 return environment.undefined('No random item, sequence was empty.') |
| 338 | 370 |
| 339 | 371 |
| 340 def do_filesizeformat(value, binary=False): | 372 def do_filesizeformat(value, binary=False): |
| 341 """Format the value like a 'human-readable' file size (i.e. 13 kB, | 373 """Format the value like a 'human-readable' file size (i.e. 13 kB, |
| 342 4.1 MB, 102 Bytes, etc). Per default decimal prefixes are used (Mega, | 374 4.1 MB, 102 Bytes, etc). Per default decimal prefixes are used (Mega, |
| 343 Giga, etc.), if the second parameter is set to `True` the binary | 375 Giga, etc.), if the second parameter is set to `True` the binary |
| 344 prefixes are used (Mebi, Gibi). | 376 prefixes are used (Mebi, Gibi). |
| 345 """ | 377 """ |
| 346 bytes = float(value) | 378 bytes = float(value) |
| 347 base = binary and 1024 or 1000 | 379 base = binary and 1024 or 1000 |
| 348 prefixes = [ | 380 prefixes = [ |
| 349 (binary and "KiB" or "kB"), | 381 (binary and 'KiB' or 'kB'), |
| 350 (binary and "MiB" or "MB"), | 382 (binary and 'MiB' or 'MB'), |
| 351 (binary and "GiB" or "GB"), | 383 (binary and 'GiB' or 'GB'), |
| 352 (binary and "TiB" or "TB"), | 384 (binary and 'TiB' or 'TB'), |
| 353 (binary and "PiB" or "PB"), | 385 (binary and 'PiB' or 'PB'), |
| 354 (binary and "EiB" or "EB"), | 386 (binary and 'EiB' or 'EB'), |
| 355 (binary and "ZiB" or "ZB"), | 387 (binary and 'ZiB' or 'ZB'), |
| 356 (binary and "YiB" or "YB") | 388 (binary and 'YiB' or 'YB') |
| 357 ] | 389 ] |
| 358 if bytes == 1: | 390 if bytes == 1: |
| 359 return "1 Byte" | 391 return '1 Byte' |
| 360 elif bytes < base: | 392 elif bytes < base: |
| 361 return "%d Bytes" % bytes | 393 return '%d Bytes' % bytes |
| 362 else: | 394 else: |
| 363 for i, prefix in enumerate(prefixes): | 395 for i, prefix in enumerate(prefixes): |
| 364 unit = base * base ** (i + 1) | 396 unit = base ** (i + 2) |
| 365 if bytes < unit: | 397 if bytes < unit: |
| 366 return "%.1f %s" % ((bytes / unit), prefix) | 398 return '%.1f %s' % ((base * bytes / unit), prefix) |
| 367 return "%.1f %s" % ((bytes / unit), prefix) | 399 return '%.1f %s' % ((base * bytes / unit), prefix) |
| 368 | 400 |
| 369 | 401 |
| 370 def do_pprint(value, verbose=False): | 402 def do_pprint(value, verbose=False): |
| 371 """Pretty print a variable. Useful for debugging. | 403 """Pretty print a variable. Useful for debugging. |
| 372 | 404 |
| 373 With Jinja 1.2 onwards you can pass it a parameter. If this parameter | 405 With Jinja 1.2 onwards you can pass it a parameter. If this parameter |
| 374 is truthy the output will be more verbose (this requires `pretty`) | 406 is truthy the output will be more verbose (this requires `pretty`) |
| 375 """ | 407 """ |
| 376 return pformat(value, verbose=verbose) | 408 return pformat(value, verbose=verbose) |
| 377 | 409 |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 410 rv = (u'\n' + indention).join(s.splitlines()) | 442 rv = (u'\n' + indention).join(s.splitlines()) |
| 411 if indentfirst: | 443 if indentfirst: |
| 412 rv = indention + rv | 444 rv = indention + rv |
| 413 return rv | 445 return rv |
| 414 | 446 |
| 415 | 447 |
| 416 def do_truncate(s, length=255, killwords=False, end='...'): | 448 def do_truncate(s, length=255, killwords=False, end='...'): |
| 417 """Return a truncated copy of the string. The length is specified | 449 """Return a truncated copy of the string. The length is specified |
| 418 with the first parameter which defaults to ``255``. If the second | 450 with the first parameter which defaults to ``255``. If the second |
| 419 parameter is ``true`` the filter will cut the text at length. Otherwise | 451 parameter is ``true`` the filter will cut the text at length. Otherwise |
| 420 it will try to save the last word. If the text was in fact | 452 it will discard the last word. If the text was in fact |
| 421 truncated it will append an ellipsis sign (``"..."``). If you want a | 453 truncated it will append an ellipsis sign (``"..."``). If you want a |
| 422 different ellipsis sign than ``"..."`` you can specify it using the | 454 different ellipsis sign than ``"..."`` you can specify it using the |
| 423 third parameter. | 455 third parameter. |
| 424 | 456 |
| 425 .. sourcecode jinja:: | 457 .. sourcecode:: jinja |
| 426 | 458 |
| 427 {{ mytext|truncate(300, false, '»') }} | 459 {{ "foo bar"|truncate(5) }} |
| 428 truncate mytext to 300 chars, don't split up words, use a | 460 -> "foo ..." |
| 429 right pointing double arrow as ellipsis sign. | 461 {{ "foo bar"|truncate(5, True) }} |
| 462 -> "foo b..." |
| 430 """ | 463 """ |
| 431 if len(s) <= length: | 464 if len(s) <= length: |
| 432 return s | 465 return s |
| 433 elif killwords: | 466 elif killwords: |
| 434 return s[:length] + end | 467 return s[:length] + end |
| 435 words = s.split(' ') | 468 words = s.split(' ') |
| 436 result = [] | 469 result = [] |
| 437 m = 0 | 470 m = 0 |
| 438 for word in words: | 471 for word in words: |
| 439 m += len(word) + 1 | 472 m += len(word) + 1 |
| 440 if m > length: | 473 if m > length: |
| 441 break | 474 break |
| 442 result.append(word) | 475 result.append(word) |
| 443 result.append(end) | 476 result.append(end) |
| 444 return u' '.join(result) | 477 return u' '.join(result) |
| 445 | 478 |
| 446 @environmentfilter | 479 @environmentfilter |
| 447 def do_wordwrap(environment, s, width=79, break_long_words=True): | 480 def do_wordwrap(environment, s, width=79, break_long_words=True, |
| 481 wrapstring=None): |
| 448 """ | 482 """ |
| 449 Return a copy of the string passed to the filter wrapped after | 483 Return a copy of the string passed to the filter wrapped after |
| 450 ``79`` characters. You can override this default using the first | 484 ``79`` characters. You can override this default using the first |
| 451 parameter. If you set the second parameter to `false` Jinja will not | 485 parameter. If you set the second parameter to `false` Jinja will not |
| 452 split words apart if they are longer than `width`. | 486 split words apart if they are longer than `width`. By default, the newlines |
| 487 will be the default newlines for the environment, but this can be changed |
| 488 using the wrapstring keyword argument. |
| 489 |
| 490 .. versionadded:: 2.7 |
| 491 Added support for the `wrapstring` parameter. |
| 453 """ | 492 """ |
| 493 if not wrapstring: |
| 494 wrapstring = environment.newline_sequence |
| 454 import textwrap | 495 import textwrap |
| 455 return environment.newline_sequence.join(textwrap.wrap(s, width=width, expan
d_tabs=False, | 496 return wrapstring.join(textwrap.wrap(s, width=width, expand_tabs=False, |
| 456 replace_whitespace=False, | 497 replace_whitespace=False, |
| 457 break_long_words=break_long_words)) | 498 break_long_words=break_long_words)) |
| 458 | 499 |
| 459 | 500 |
| 460 def do_wordcount(s): | 501 def do_wordcount(s): |
| 461 """Count the words in that string.""" | 502 """Count the words in that string.""" |
| 462 return len(_word_re.findall(s)) | 503 return len(_word_re.findall(s)) |
| 463 | 504 |
| 464 | 505 |
| 465 def do_int(value, default=0): | 506 def do_int(value, default=0): |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 506 def do_trim(value): | 547 def do_trim(value): |
| 507 """Strip leading and trailing whitespace.""" | 548 """Strip leading and trailing whitespace.""" |
| 508 return soft_unicode(value).strip() | 549 return soft_unicode(value).strip() |
| 509 | 550 |
| 510 | 551 |
| 511 def do_striptags(value): | 552 def do_striptags(value): |
| 512 """Strip SGML/XML tags and replace adjacent whitespace by one space. | 553 """Strip SGML/XML tags and replace adjacent whitespace by one space. |
| 513 """ | 554 """ |
| 514 if hasattr(value, '__html__'): | 555 if hasattr(value, '__html__'): |
| 515 value = value.__html__() | 556 value = value.__html__() |
| 516 return Markup(unicode(value)).striptags() | 557 return Markup(text_type(value)).striptags() |
| 517 | 558 |
| 518 | 559 |
| 519 def do_slice(value, slices, fill_with=None): | 560 def do_slice(value, slices, fill_with=None): |
| 520 """Slice an iterator and return a list of lists containing | 561 """Slice an iterator and return a list of lists containing |
| 521 those items. Useful if you want to create a div containing | 562 those items. Useful if you want to create a div containing |
| 522 three ul tags that represent columns: | 563 three ul tags that represent columns: |
| 523 | 564 |
| 524 .. sourcecode:: html+jinja | 565 .. sourcecode:: html+jinja |
| 525 | 566 |
| 526 <div class="columwrapper"> | 567 <div class="columwrapper"> |
| 527 {%- for column in items|slice(3) %} | 568 {%- for column in items|slice(3) %} |
| 528 <ul class="column-{{ loop.index }}"> | 569 <ul class="column-{{ loop.index }}"> |
| 529 {%- for item in column %} | 570 {%- for item in column %} |
| 530 <li>{{ item }}</li> | 571 <li>{{ item }}</li> |
| 531 {%- endfor %} | 572 {%- endfor %} |
| 532 </ul> | 573 </ul> |
| 533 {%- endfor %} | 574 {%- endfor %} |
| 534 </div> | 575 </div> |
| 535 | 576 |
| 536 If you pass it a second argument it's used to fill missing | 577 If you pass it a second argument it's used to fill missing |
| 537 values on the last iteration. | 578 values on the last iteration. |
| 538 """ | 579 """ |
| 539 seq = list(value) | 580 seq = list(value) |
| 540 length = len(seq) | 581 length = len(seq) |
| 541 items_per_slice = length // slices | 582 items_per_slice = length // slices |
| 542 slices_with_extra = length % slices | 583 slices_with_extra = length % slices |
| 543 offset = 0 | 584 offset = 0 |
| 544 for slice_number in xrange(slices): | 585 for slice_number in range(slices): |
| 545 start = offset + slice_number * items_per_slice | 586 start = offset + slice_number * items_per_slice |
| 546 if slice_number < slices_with_extra: | 587 if slice_number < slices_with_extra: |
| 547 offset += 1 | 588 offset += 1 |
| 548 end = offset + (slice_number + 1) * items_per_slice | 589 end = offset + (slice_number + 1) * items_per_slice |
| 549 tmp = seq[start:end] | 590 tmp = seq[start:end] |
| 550 if fill_with is not None and slice_number >= slices_with_extra: | 591 if fill_with is not None and slice_number >= slices_with_extra: |
| 551 tmp.append(fill_with) | 592 tmp.append(fill_with) |
| 552 yield tmp | 593 yield tmp |
| 553 | 594 |
| 554 | 595 |
| 555 def do_batch(value, linecount, fill_with=None): | 596 def do_batch(value, linecount, fill_with=None): |
| 556 """ | 597 """ |
| 557 A filter that batches items. It works pretty much like `slice` | 598 A filter that batches items. It works pretty much like `slice` |
| 558 just the other way round. It returns a list of lists with the | 599 just the other way round. It returns a list of lists with the |
| 559 given number of items. If you provide a second parameter this | 600 given number of items. If you provide a second parameter this |
| 560 is used to fill missing items. See this example: | 601 is used to fill up missing items. See this example: |
| 561 | 602 |
| 562 .. sourcecode:: html+jinja | 603 .. sourcecode:: html+jinja |
| 563 | 604 |
| 564 <table> | 605 <table> |
| 565 {%- for row in items|batch(3, ' ') %} | 606 {%- for row in items|batch(3, ' ') %} |
| 566 <tr> | 607 <tr> |
| 567 {%- for column in row %} | 608 {%- for column in row %} |
| 568 <td>{{ column }}</td> | 609 <td>{{ column }}</td> |
| 569 {%- endfor %} | 610 {%- endfor %} |
| 570 </tr> | 611 </tr> |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 659 """ | 700 """ |
| 660 expr = make_attrgetter(environment, attribute) | 701 expr = make_attrgetter(environment, attribute) |
| 661 return sorted(map(_GroupTuple, groupby(sorted(value, key=expr), expr))) | 702 return sorted(map(_GroupTuple, groupby(sorted(value, key=expr), expr))) |
| 662 | 703 |
| 663 | 704 |
| 664 class _GroupTuple(tuple): | 705 class _GroupTuple(tuple): |
| 665 __slots__ = () | 706 __slots__ = () |
| 666 grouper = property(itemgetter(0)) | 707 grouper = property(itemgetter(0)) |
| 667 list = property(itemgetter(1)) | 708 list = property(itemgetter(1)) |
| 668 | 709 |
| 669 def __new__(cls, (key, value)): | 710 def __new__(cls, xxx_todo_changeme): |
| 711 (key, value) = xxx_todo_changeme |
| 670 return tuple.__new__(cls, (key, list(value))) | 712 return tuple.__new__(cls, (key, list(value))) |
| 671 | 713 |
| 672 | 714 |
| 673 @environmentfilter | 715 @environmentfilter |
| 674 def do_sum(environment, iterable, attribute=None, start=0): | 716 def do_sum(environment, iterable, attribute=None, start=0): |
| 675 """Returns the sum of a sequence of numbers plus the value of parameter | 717 """Returns the sum of a sequence of numbers plus the value of parameter |
| 676 'start' (which defaults to 0). When the sequence is empty it returns | 718 'start' (which defaults to 0). When the sequence is empty it returns |
| 677 start. | 719 start. |
| 678 | 720 |
| 679 It is also possible to sum up only certain attributes: | 721 It is also possible to sum up only certain attributes: |
| (...skipping 20 matching lines...) Expand all Loading... |
| 700 | 742 |
| 701 def do_mark_safe(value): | 743 def do_mark_safe(value): |
| 702 """Mark the value as safe which means that in an environment with automatic | 744 """Mark the value as safe which means that in an environment with automatic |
| 703 escaping enabled this variable will not be escaped. | 745 escaping enabled this variable will not be escaped. |
| 704 """ | 746 """ |
| 705 return Markup(value) | 747 return Markup(value) |
| 706 | 748 |
| 707 | 749 |
| 708 def do_mark_unsafe(value): | 750 def do_mark_unsafe(value): |
| 709 """Mark a value as unsafe. This is the reverse operation for :func:`safe`."
"" | 751 """Mark a value as unsafe. This is the reverse operation for :func:`safe`."
"" |
| 710 return unicode(value) | 752 return text_type(value) |
| 711 | 753 |
| 712 | 754 |
| 713 def do_reverse(value): | 755 def do_reverse(value): |
| 714 """Reverse the object or return an iterator the iterates over it the other | 756 """Reverse the object or return an iterator the iterates over it the other |
| 715 way round. | 757 way round. |
| 716 """ | 758 """ |
| 717 if isinstance(value, basestring): | 759 if isinstance(value, string_types): |
| 718 return value[::-1] | 760 return value[::-1] |
| 719 try: | 761 try: |
| 720 return reversed(value) | 762 return reversed(value) |
| 721 except TypeError: | 763 except TypeError: |
| 722 try: | 764 try: |
| 723 rv = list(value) | 765 rv = list(value) |
| 724 rv.reverse() | 766 rv.reverse() |
| 725 return rv | 767 return rv |
| 726 except TypeError: | 768 except TypeError: |
| 727 raise FilterArgumentError('argument must be iterable') | 769 raise FilterArgumentError('argument must be iterable') |
| (...skipping 17 matching lines...) Expand all Loading... |
| 745 except AttributeError: | 787 except AttributeError: |
| 746 pass | 788 pass |
| 747 else: | 789 else: |
| 748 if environment.sandboxed and not \ | 790 if environment.sandboxed and not \ |
| 749 environment.is_safe_attribute(obj, name, value): | 791 environment.is_safe_attribute(obj, name, value): |
| 750 return environment.unsafe_undefined(obj, name) | 792 return environment.unsafe_undefined(obj, name) |
| 751 return value | 793 return value |
| 752 return environment.undefined(obj=obj, name=name) | 794 return environment.undefined(obj=obj, name=name) |
| 753 | 795 |
| 754 | 796 |
| 797 @contextfilter |
| 798 def do_map(*args, **kwargs): |
| 799 """Applies a filter on a sequence of objects or looks up an attribute. |
| 800 This is useful when dealing with lists of objects but you are really |
| 801 only interested in a certain value of it. |
| 802 |
| 803 The basic usage is mapping on an attribute. Imagine you have a list |
| 804 of users but you are only interested in a list of usernames: |
| 805 |
| 806 .. sourcecode:: jinja |
| 807 |
| 808 Users on this page: {{ users|map(attribute='username')|join(', ') }} |
| 809 |
| 810 Alternatively you can let it invoke a filter by passing the name of the |
| 811 filter and the arguments afterwards. A good example would be applying a |
| 812 text conversion filter on a sequence: |
| 813 |
| 814 .. sourcecode:: jinja |
| 815 |
| 816 Users on this page: {{ titles|map('lower')|join(', ') }} |
| 817 |
| 818 .. versionadded:: 2.7 |
| 819 """ |
| 820 context = args[0] |
| 821 seq = args[1] |
| 822 |
| 823 if len(args) == 2 and 'attribute' in kwargs: |
| 824 attribute = kwargs.pop('attribute') |
| 825 if kwargs: |
| 826 raise FilterArgumentError('Unexpected keyword argument %r' % |
| 827 next(iter(kwargs))) |
| 828 func = make_attrgetter(context.environment, attribute) |
| 829 else: |
| 830 try: |
| 831 name = args[2] |
| 832 args = args[3:] |
| 833 except LookupError: |
| 834 raise FilterArgumentError('map requires a filter argument') |
| 835 func = lambda item: context.environment.call_filter( |
| 836 name, item, args, kwargs, context=context) |
| 837 |
| 838 if seq: |
| 839 for item in seq: |
| 840 yield func(item) |
| 841 |
| 842 |
| 843 @contextfilter |
| 844 def do_select(*args, **kwargs): |
| 845 """Filters a sequence of objects by appying a test to either the object |
| 846 or the attribute and only selecting the ones with the test succeeding. |
| 847 |
| 848 Example usage: |
| 849 |
| 850 .. sourcecode:: jinja |
| 851 |
| 852 {{ numbers|select("odd") }} |
| 853 |
| 854 .. versionadded:: 2.7 |
| 855 """ |
| 856 return _select_or_reject(args, kwargs, lambda x: x, False) |
| 857 |
| 858 |
| 859 @contextfilter |
| 860 def do_reject(*args, **kwargs): |
| 861 """Filters a sequence of objects by appying a test to either the object |
| 862 or the attribute and rejecting the ones with the test succeeding. |
| 863 |
| 864 Example usage: |
| 865 |
| 866 .. sourcecode:: jinja |
| 867 |
| 868 {{ numbers|reject("odd") }} |
| 869 |
| 870 .. versionadded:: 2.7 |
| 871 """ |
| 872 return _select_or_reject(args, kwargs, lambda x: not x, False) |
| 873 |
| 874 |
| 875 @contextfilter |
| 876 def do_selectattr(*args, **kwargs): |
| 877 """Filters a sequence of objects by appying a test to either the object |
| 878 or the attribute and only selecting the ones with the test succeeding. |
| 879 |
| 880 Example usage: |
| 881 |
| 882 .. sourcecode:: jinja |
| 883 |
| 884 {{ users|selectattr("is_active") }} |
| 885 {{ users|selectattr("email", "none") }} |
| 886 |
| 887 .. versionadded:: 2.7 |
| 888 """ |
| 889 return _select_or_reject(args, kwargs, lambda x: x, True) |
| 890 |
| 891 |
| 892 @contextfilter |
| 893 def do_rejectattr(*args, **kwargs): |
| 894 """Filters a sequence of objects by appying a test to either the object |
| 895 or the attribute and rejecting the ones with the test succeeding. |
| 896 |
| 897 .. sourcecode:: jinja |
| 898 |
| 899 {{ users|rejectattr("is_active") }} |
| 900 {{ users|rejectattr("email", "none") }} |
| 901 |
| 902 .. versionadded:: 2.7 |
| 903 """ |
| 904 return _select_or_reject(args, kwargs, lambda x: not x, True) |
| 905 |
| 906 |
| 907 def _select_or_reject(args, kwargs, modfunc, lookup_attr): |
| 908 context = args[0] |
| 909 seq = args[1] |
| 910 if lookup_attr: |
| 911 try: |
| 912 attr = args[2] |
| 913 except LookupError: |
| 914 raise FilterArgumentError('Missing parameter for attribute name') |
| 915 transfunc = make_attrgetter(context.environment, attr) |
| 916 off = 1 |
| 917 else: |
| 918 off = 0 |
| 919 transfunc = lambda x: x |
| 920 |
| 921 try: |
| 922 name = args[2 + off] |
| 923 args = args[3 + off:] |
| 924 func = lambda item: context.environment.call_test( |
| 925 name, item, args, kwargs) |
| 926 except LookupError: |
| 927 func = bool |
| 928 |
| 929 if seq: |
| 930 for item in seq: |
| 931 if modfunc(func(transfunc(item))): |
| 932 yield item |
| 933 |
| 934 |
| 755 FILTERS = { | 935 FILTERS = { |
| 756 'attr': do_attr, | 936 'attr': do_attr, |
| 757 'replace': do_replace, | 937 'replace': do_replace, |
| 758 'upper': do_upper, | 938 'upper': do_upper, |
| 759 'lower': do_lower, | 939 'lower': do_lower, |
| 760 'escape': escape, | 940 'escape': escape, |
| 761 'e': escape, | 941 'e': escape, |
| 762 'forceescape': do_forceescape, | 942 'forceescape': do_forceescape, |
| 763 'capitalize': do_capitalize, | 943 'capitalize': do_capitalize, |
| 764 'title': do_title, | 944 'title': do_title, |
| 765 'default': do_default, | 945 'default': do_default, |
| 766 'd': do_default, | 946 'd': do_default, |
| 767 'join': do_join, | 947 'join': do_join, |
| 768 'count': len, | 948 'count': len, |
| 769 'dictsort': do_dictsort, | 949 'dictsort': do_dictsort, |
| 770 'sort': do_sort, | 950 'sort': do_sort, |
| 771 'length': len, | 951 'length': len, |
| 772 'reverse': do_reverse, | 952 'reverse': do_reverse, |
| 773 'center': do_center, | 953 'center': do_center, |
| 774 'indent': do_indent, | 954 'indent': do_indent, |
| 775 'title': do_title, | 955 'title': do_title, |
| 776 'capitalize': do_capitalize, | 956 'capitalize': do_capitalize, |
| 777 'first': do_first, | 957 'first': do_first, |
| 778 'last': do_last, | 958 'last': do_last, |
| 959 'map': do_map, |
| 779 'random': do_random, | 960 'random': do_random, |
| 961 'reject': do_reject, |
| 962 'rejectattr': do_rejectattr, |
| 780 'filesizeformat': do_filesizeformat, | 963 'filesizeformat': do_filesizeformat, |
| 781 'pprint': do_pprint, | 964 'pprint': do_pprint, |
| 782 'truncate': do_truncate, | 965 'truncate': do_truncate, |
| 783 'wordwrap': do_wordwrap, | 966 'wordwrap': do_wordwrap, |
| 784 'wordcount': do_wordcount, | 967 'wordcount': do_wordcount, |
| 785 'int': do_int, | 968 'int': do_int, |
| 786 'float': do_float, | 969 'float': do_float, |
| 787 'string': soft_unicode, | 970 'string': soft_unicode, |
| 788 'list': do_list, | 971 'list': do_list, |
| 789 'urlize': do_urlize, | 972 'urlize': do_urlize, |
| 790 'format': do_format, | 973 'format': do_format, |
| 791 'trim': do_trim, | 974 'trim': do_trim, |
| 792 'striptags': do_striptags, | 975 'striptags': do_striptags, |
| 976 'select': do_select, |
| 977 'selectattr': do_selectattr, |
| 793 'slice': do_slice, | 978 'slice': do_slice, |
| 794 'batch': do_batch, | 979 'batch': do_batch, |
| 795 'sum': do_sum, | 980 'sum': do_sum, |
| 796 'abs': abs, | 981 'abs': abs, |
| 797 'round': do_round, | 982 'round': do_round, |
| 798 'groupby': do_groupby, | 983 'groupby': do_groupby, |
| 799 'safe': do_mark_safe, | 984 'safe': do_mark_safe, |
| 800 'xmlattr': do_xmlattr | 985 'xmlattr': do_xmlattr, |
| 986 'urlencode': do_urlencode |
| 801 } | 987 } |
| OLD | NEW |