1 # -*- coding: utf-8 -*- 2 """ 3 jinja2.filters 4 ~~~~~~~~~~~~~~ 5 6 Bundled jinja filters. 7 8 :copyright: (c) 2010 by the Jinja Team. 9 :license: BSD, see LICENSE for more details. 10 """ 11 import re 12 import math 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 17 from jinja2.runtime import Undefined 18 from jinja2.exceptions import FilterArgumentError, SecurityError 19 20 21 _word_re = re.compile(r'\w+(?u)') 22 23 24 def contextfilter(f): 25 """Decorator for marking context dependent filters. The current 26 :class:`Context` will be passed as first argument. 27 """ 28 f.contextfilter = True 29 return f 30 31 32 def evalcontextfilter(f): 33 """Decorator for marking eval-context dependent filters. An eval 34 context object is passed as first argument. For more information 35 about the eval context, see :ref:`eval-context`. 36 37 .. versionadded:: 2.4 38 """ 39 f.evalcontextfilter = True 40 return f 41 42 43 def environmentfilter(f): 44 """Decorator for marking evironment dependent filters. The current 45 :class:`Environment` is passed to the filter as first argument. 46 """ 47 f.environmentfilter = True 48 return f 49 50 51 def make_attrgetter(environment, attribute): 52 """Returns a callable that looks up the given attribute from a 53 passed object with the rules of the environment. Dots are allowed 54 to access attributes of attributes. 55 """ 56 if not isinstance(attribute, basestring) or '.' not in attribute: 57 return lambda x: environment.getitem(x, attribute) 58 attribute = attribute.split('.') 59 def attrgetter(item): 60 for part in attribute: 61 item = environment.getitem(item, part) 62 return item 63 return attrgetter 64 65 66 def do_forceescape(value): 67 """Enforce HTML escaping. This will probably double escape variables.""" 68 if hasattr(value, '__html__'): 69 value = value.__html__() 70 return escape(unicode(value)) 71 72 73 @evalcontextfilter 74 def do_replace(eval_ctx, s, old, new, count=None): 75 """Return a copy of the value with all occurrences of a substring 76 replaced with a new one. The first argument is the substring 77 that should be replaced, the second is the replacement string. 78 If the optional third argument ``count`` is given, only the first 79 ``count`` occurrences are replaced: 80 81 .. sourcecode:: jinja 82 83 {{ "Hello World"|replace("Hello", "Goodbye") }} 84 -> Goodbye World 85 86 {{ "aaaaargh"|replace("a", "d'oh, ", 2) }} 87 -> d'oh, d'oh, aaargh 88 """ 89 if count is None: 90 count = -1 91 if not eval_ctx.autoescape: 92 return unicode(s).replace(unicode(old), unicode(new), count) 93 if hasattr(old, '__html__') or hasattr(new, '__html__') and \ 94 not hasattr(s, '__html__'): 95 s = escape(s) 96 else: 97 s = soft_unicode(s) 98 return s.replace(soft_unicode(old), soft_unicode(new), count) 99 100 101 def do_upper(s): 102 """Convert a value to uppercase.""" 103 return soft_unicode(s).upper() 104 105 106 def do_lower(s): 107 """Convert a value to lowercase.""" 108 return soft_unicode(s).lower() 109 110 111 @evalcontextfilter 112 def do_xmlattr(_eval_ctx, d, autospace=True): 113 """Create an SGML/XML attribute string based on the items in a dict. 114 All values that are neither `none` nor `undefined` are automatically 115 escaped: 116 117 .. sourcecode:: html+jinja 118 119 <ul{{ {'class': 'my_list', 'missing': none, 120 'id': 'list-%d'|format(variable)}|xmlattr }}> 121 ... 122 </ul> 123 124 Results in something like this: 125 126 .. sourcecode:: html 127 128 <ul class="my_list" id="list-42"> 129 ... 130 </ul> 131 132 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. 134 """ 135 rv = u' '.join( 136 u'%s="%s"' % (escape(key), escape(value)) 137 for key, value in d.iteritems() 138 if value is not None and not isinstance(value, Undefined) 139 ) 140 if autospace and rv: 141 rv = u' ' + rv 142 if _eval_ctx.autoescape: 143 rv = Markup(rv) 144 return rv 145 146 147 def do_capitalize(s): 148 """Capitalize a value. The first character will be uppercase, all others 149 lowercase. 150 """ 151 return soft_unicode(s).capitalize() 152 153 154 def do_title(s): 155 """Return a titlecased version of the value. I.e. words will start with 156 uppercase letters, all remaining characters are lowercase. 157 """ 158 return soft_unicode(s).title() 159 160 161 def do_dictsort(value, case_sensitive=False, by='key'): 162 """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 164 key or value: 165 166 .. sourcecode:: jinja 167 168 {% for item in mydict|dictsort %} 169 sort the dict by key, case insensitive 170 171 {% for item in mydict|dicsort(true) %} 172 sort the dict by key, case sensitive 173 174 {% for item in mydict|dictsort(false, 'value') %} 175 sort the dict by key, case insensitive, sorted 176 normally and ordered by value. 177 """ 178 if by == 'key': 179 pos = 0 180 elif by == 'value': 181 pos = 1 182 else: 183 raise FilterArgumentError('You can only sort by either ' 184 '"key" or "value"') 185 def sort_func(item): 186 value = item[pos] 187 if isinstance(value, basestring) and not case_sensitive: 188 value = value.lower() 189 return value 190 191 return sorted(value.items(), key=sort_func) 192 193 194 @environmentfilter 195 def do_sort(environment, value, reverse=False, case_sensitive=False, 196 attribute=None): 197 """Sort an iterable. Per default it sorts ascending, if you pass it 198 true as first argument it will reverse the sorting. 199 200 If the iterable is made of strings the third parameter can be used to 201 control the case sensitiveness of the comparison which is disabled by 202 default. 203 204 .. sourcecode:: jinja 205 206 {% for item in iterable|sort %} 207 ... 208 {% endfor %} 209 210 It is also possible to sort by an attribute (for example to sort 211 by the date of an object) by specifying the `attribute` parameter: 212 213 .. sourcecode:: jinja 214 215 {% for item in iterable|sort(attribute='date') %} 216 ... 217 {% endfor %} 218 219 .. versionchanged:: 2.6 220 The `attribute` parameter was added. 221 """ 222 if not case_sensitive: 223 def sort_func(item): 224 if isinstance(item, basestring): 225 item = item.lower() 226 return item 227 else: 228 sort_func = None 229 if attribute is not None: 230 getter = make_attrgetter(environment, attribute) 231 def sort_func(item, processor=sort_func or (lambda x: x)): 232 return processor(getter(item)) 233 return sorted(value, key=sort_func, reverse=reverse) 234 235 236 def do_default(value, default_value=u'', boolean=False): 237 """If the value is undefined it will return the passed default value, 238 otherwise the value of the variable: 239 240 .. sourcecode:: jinja 241 242 {{ my_variable|default('my_variable is not defined') }} 243 244 This will output the value of ``my_variable`` if the variable was 245 defined, otherwise ``'my_variable is not defined'``. If you want 246 to use default with variables that evaluate to false you have to 247 set the second parameter to `true`: 248 249 .. sourcecode:: jinja 250 251 {{ ''|default('the string was empty', true) }} 252 """ 253 if (boolean and not value) or isinstance(value, Undefined): 254 return default_value 255 return value 256 257 258 @evalcontextfilter 259 def do_join(eval_ctx, value, d=u'', attribute=None): 260 """Return a string which is the concatenation of the strings in the 261 sequence. The separator between elements is an empty string per 262 default, you can define it with the optional parameter: 263 264 .. sourcecode:: jinja 265 266 {{ [1, 2, 3]|join('|') }} 267 -> 1|2|3 268 269 {{ [1, 2, 3]|join }} 270 -> 123 271 272 It is also possible to join certain attributes of an object: 273 274 .. sourcecode:: jinja 275 276 {{ users|join(', ', attribute='username') }} 277 278 .. versionadded:: 2.6 279 The `attribute` parameter was added. 280 """ 281 if attribute is not None: 282 value = imap(make_attrgetter(eval_ctx.environment, attribute), value) 283 284 # no automatic escaping? joining is a lot eaiser then 285 if not eval_ctx.autoescape: 286 return unicode(d).join(imap(unicode, value)) 287 288 # 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 290 if not hasattr(d, '__html__'): 291 value = list(value) 292 do_escape = False 293 for idx, item in enumerate(value): 294 if hasattr(item, '__html__'): 295 do_escape = True 296 else: 297 value[idx] = unicode(item) 298 if do_escape: 299 d = escape(d) 300 else: 301 d = unicode(d) 302 return d.join(value) 303 304 # no html involved, to normal joining 305 return soft_unicode(d).join(imap(soft_unicode, value)) 306 307 308 def do_center(value, width=80): 309 """Centers the value in a field of a given width.""" 310 return unicode(value).center(width) 311 312 313 @environmentfilter 314 def do_first(environment, seq): 315 """Return the first item of a sequence.""" 316 try: 317 return iter(seq).next() 318 except StopIteration: 319 return environment.undefined('No first item, sequence was empty.') 320 321 322 @environmentfilter 323 def do_last(environment, seq): 324 """Return the last item of a sequence.""" 325 try: 326 return iter(reversed(seq)).next() 327 except StopIteration: 328 return environment.undefined('No last item, sequence was empty.') 329 330 331 @environmentfilter 332 def do_random(environment, seq): 333 """Return a random item from the sequence.""" 334 try: 335 return choice(seq) 336 except IndexError: 337 return environment.undefined('No random item, sequence was empty.') 338 339 340 def do_filesizeformat(value, binary=False): 341 """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, 343 Giga, etc.), if the second parameter is set to `True` the binary 344 prefixes are used (Mebi, Gibi). 345 """ 346 bytes = float(value) 347 base = binary and 1024 or 1000 348 prefixes = [ 349 (binary and "KiB" or "kB"), 350 (binary and "MiB" or "MB"), 351 (binary and "GiB" or "GB"), 352 (binary and "TiB" or "TB"), 353 (binary and "PiB" or "PB"), 354 (binary and "EiB" or "EB"), 355 (binary and "ZiB" or "ZB"), 356 (binary and "YiB" or "YB") 357 ] 358 if bytes == 1: 359 return "1 Byte" 360 elif bytes < base: 361 return "%d Bytes" % bytes 362 else: 363 for i, prefix in enumerate(prefixes): 364 unit = base * base ** (i + 1) 365 if bytes < unit: 366 return "%.1f %s" % ((bytes / unit), prefix) 367 return "%.1f %s" % ((bytes / unit), prefix) 368 369 370 def do_pprint(value, verbose=False): 371 """Pretty print a variable. Useful for debugging. 372 373 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`) 375 """ 376 return pformat(value, verbose=verbose) 377 378 379 @evalcontextfilter 380 def do_urlize(eval_ctx, value, trim_url_limit=None, nofollow=False): 381 """Converts URLs in plain text into clickable links. 382 383 If you pass the filter an additional integer it will shorten the urls 384 to that number. Also a third argument exists that makes the urls 385 "nofollow": 386 387 .. sourcecode:: jinja 388 389 {{ mytext|urlize(40, true) }} 390 links are shortened to 40 chars and defined with rel="nofollow" 391 """ 392 rv = urlize(value, trim_url_limit, nofollow) 393 if eval_ctx.autoescape: 394 rv = Markup(rv) 395 return rv 396 397 398 def do_indent(s, width=4, indentfirst=False): 399 """Return a copy of the passed string, each line indented by 400 4 spaces. The first line is not indented. If you want to 401 change the number of spaces or indent the first line too 402 you can pass additional parameters to the filter: 403 404 .. sourcecode:: jinja 405 406 {{ mytext|indent(2, true) }} 407 indent by two spaces and indent the first line too. 408 """ 409 indention = u' ' * width 410 rv = (u'\n' + indention).join(s.splitlines()) 411 if indentfirst: 412 rv = indention + rv 413 return rv 414 415 416 def do_truncate(s, length=255, killwords=False, end='...'): 417 """Return a truncated copy of the string. The length is specified 418 with the first parameter which defaults to ``255``. If the second 419 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 421 truncated it will append an ellipsis sign (``"..."``). If you want a 422 different ellipsis sign than ``"..."`` you can specify it using the 423 third parameter. 424 425 .. sourcecode jinja:: 426 427 {{ mytext|truncate(300, false, '»') }} 428 truncate mytext to 300 chars, don't split up words, use a 429 right pointing double arrow as ellipsis sign. 430 """ 431 if len(s) <= length: 432 return s 433 elif killwords: 434 return s[:length] + end 435 words = s.split(' ') 436 result = [] 437 m = 0 438 for word in words: 439 m += len(word) + 1 440 if m > length: 441 break 442 result.append(word) 443 result.append(end) 444 return u' '.join(result) 445 446 @environmentfilter 447 def do_wordwrap(environment, s, width=79, break_long_words=True): 448 """ 449 Return a copy of the string passed to the filter wrapped after 450 ``79`` characters. You can override this default using the first 451 parameter. If you set the second parameter to `false` Jinja will not 452 split words apart if they are longer than `width`. 453 """ 454 import textwrap 455 return environment.newline_sequence.join(textwrap.wrap(s, width=width, expand_tabs=False, 456 replace_whitespace=False, 457 break_long_words=break_long_words)) 458 459 460 def do_wordcount(s): 461 """Count the words in that string.""" 462 return len(_word_re.findall(s)) 463 464 465 def do_int(value, default=0): 466 """Convert the value into an integer. If the 467 conversion doesn't work it will return ``0``. You can 468 override this default using the first parameter. 469 """ 470 try: 471 return int(value) 472 except (TypeError, ValueError): 473 # this quirk is necessary so that "42.23"|int gives 42. 474 try: 475 return int(float(value)) 476 except (TypeError, ValueError): 477 return default 478 479 480 def do_float(value, default=0.0): 481 """Convert the value into a floating point number. If the 482 conversion doesn't work it will return ``0.0``. You can 483 override this default using the first parameter. 484 """ 485 try: 486 return float(value) 487 except (TypeError, ValueError): 488 return default 489 490 491 def do_format(value, *args, **kwargs): 492 """ 493 Apply python string formatting on an object: 494 495 .. sourcecode:: jinja 496 497 {{ "%s - %s"|format("Hello?", "Foo!") }} 498 -> Hello? - Foo! 499 """ 500 if args and kwargs: 501 raise FilterArgumentError('can\'t handle positional and keyword ' 502 'arguments at the same time') 503 return soft_unicode(value) % (kwargs or args) 504 505 506 def do_trim(value): 507 """Strip leading and trailing whitespace.""" 508 return soft_unicode(value).strip() 509 510 511 def do_striptags(value): 512 """Strip SGML/XML tags and replace adjacent whitespace by one space. 513 """ 514 if hasattr(value, '__html__'): 515 value = value.__html__() 516 return Markup(unicode(value)).striptags() 517 518 519 def do_slice(value, slices, fill_with=None): 520 """Slice an iterator and return a list of lists containing 521 those items. Useful if you want to create a div containing 522 three ul tags that represent columns: 523 524 .. sourcecode:: html+jinja 525 526 <div class="columwrapper"> 527 {%- for column in items|slice(3) %} 528 <ul class="column-{{ loop.index }}"> 529 {%- for item in column %} 530 <li>{{ item }}</li> 531 {%- endfor %} 532 </ul> 533 {%- endfor %} 534 </div> 535 536 If you pass it a second argument it's used to fill missing 537 values on the last iteration. 538 """ 539 seq = list(value) 540 length = len(seq) 541 items_per_slice = length // slices 542 slices_with_extra = length % slices 543 offset = 0 544 for slice_number in xrange(slices): 545 start = offset + slice_number * items_per_slice 546 if slice_number < slices_with_extra: 547 offset += 1 548 end = offset + (slice_number + 1) * items_per_slice 549 tmp = seq[start:end] 550 if fill_with is not None and slice_number >= slices_with_extra: 551 tmp.append(fill_with) 552 yield tmp 553 554 555 def do_batch(value, linecount, fill_with=None): 556 """ 557 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 559 given number of items. If you provide a second parameter this 560 is used to fill missing items. See this example: 561 562 .. sourcecode:: html+jinja 563 564 <table> 565 {%- for row in items|batch(3, ' ') %} 566 <tr> 567 {%- for column in row %} 568 <td>{{ column }}</td> 569 {%- endfor %} 570 </tr> 571 {%- endfor %} 572 </table> 573 """ 574 result = [] 575 tmp = [] 576 for item in value: 577 if len(tmp) == linecount: 578 yield tmp 579 tmp = [] 580 tmp.append(item) 581 if tmp: 582 if fill_with is not None and len(tmp) < linecount: 583 tmp += [fill_with] * (linecount - len(tmp)) 584 yield tmp 585 586 587 def do_round(value, precision=0, method='common'): 588 """Round the number to a given precision. The first 589 parameter specifies the precision (default is ``0``), the 590 second the rounding method: 591 592 - ``'common'`` rounds either up or down 593 - ``'ceil'`` always rounds up 594 - ``'floor'`` always rounds down 595 596 If you don't specify a method ``'common'`` is used. 597 598 .. sourcecode:: jinja 599 600 {{ 42.55|round }} 601 -> 43.0 602 {{ 42.55|round(1, 'floor') }} 603 -> 42.5 604 605 Note that even if rounded to 0 precision, a float is returned. If 606 you need a real integer, pipe it through `int`: 607 608 .. sourcecode:: jinja 609 610 {{ 42.55|round|int }} 611 -> 43 612 """ 613 if not method in ('common', 'ceil', 'floor'): 614 raise FilterArgumentError('method must be common, ceil or floor') 615 if method == 'common': 616 return round(value, precision) 617 func = getattr(math, method) 618 return func(value * (10 ** precision)) / (10 ** precision) 619 620 621 @environmentfilter 622 def do_groupby(environment, value, attribute): 623 """Group a sequence of objects by a common attribute. 624 625 If you for example have a list of dicts or objects that represent persons 626 with `gender`, `first_name` and `last_name` attributes and you want to 627 group all users by genders you can do something like the following 628 snippet: 629 630 .. sourcecode:: html+jinja 631 632 <ul> 633 {% for group in persons|groupby('gender') %} 634 <li>{{ group.grouper }}<ul> 635 {% for person in group.list %} 636 <li>{{ person.first_name }} {{ person.last_name }}</li> 637 {% endfor %}</ul></li> 638 {% endfor %} 639 </ul> 640 641 Additionally it's possible to use tuple unpacking for the grouper and 642 list: 643 644 .. sourcecode:: html+jinja 645 646 <ul> 647 {% for grouper, list in persons|groupby('gender') %} 648 ... 649 {% endfor %} 650 </ul> 651 652 As you can see the item we're grouping by is stored in the `grouper` 653 attribute and the `list` contains all the objects that have this grouper 654 in common. 655 656 .. versionchanged:: 2.6 657 It's now possible to use dotted notation to group by the child 658 attribute of another attribute. 659 """ 660 expr = make_attrgetter(environment, attribute) 661 return sorted(map(_GroupTuple, groupby(sorted(value, key=expr), expr))) 662 663 664 class _GroupTuple(tuple): 665 __slots__ = () 666 grouper = property(itemgetter(0)) 667 list = property(itemgetter(1)) 668 669 def __new__(cls, (key, value)): 670 return tuple.__new__(cls, (key, list(value))) 671 672 673 @environmentfilter 674 def do_sum(environment, iterable, attribute=None, start=0): 675 """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 677 start. 678 679 It is also possible to sum up only certain attributes: 680 681 .. sourcecode:: jinja 682 683 Total: {{ items|sum(attribute='price') }} 684 685 .. versionchanged:: 2.6 686 The `attribute` parameter was added to allow suming up over 687 attributes. Also the `start` parameter was moved on to the right. 688 """ 689 if attribute is not None: 690 iterable = imap(make_attrgetter(environment, attribute), iterable) 691 return sum(iterable, start) 692 693 694 def do_list(value): 695 """Convert the value into a list. If it was a string the returned list 696 will be a list of characters. 697 """ 698 return list(value) 699 700 701 def do_mark_safe(value): 702 """Mark the value as safe which means that in an environment with automatic 703 escaping enabled this variable will not be escaped. 704 """ 705 return Markup(value) 706 707 708 def do_mark_unsafe(value): 709 """Mark a value as unsafe. This is the reverse operation for :func:`safe`.""" 710 return unicode(value) 711 712 713 def do_reverse(value): 714 """Reverse the object or return an iterator the iterates over it the other 715 way round. 716 """ 717 if isinstance(value, basestring): 718 return value[::-1] 719 try: 720 return reversed(value) 721 except TypeError: 722 try: 723 rv = list(value) 724 rv.reverse() 725 return rv 726 except TypeError: 727 raise FilterArgumentError('argument must be iterable') 728 729 730 @environmentfilter 731 def do_attr(environment, obj, name): 732 """Get an attribute of an object. ``foo|attr("bar")`` works like 733 ``foo["bar"]`` just that always an attribute is returned and items are not 734 looked up. 735 736 See :ref:`Notes on subscriptions <notes-on-subscriptions>` for more details. 737 """ 738 try: 739 name = str(name) 740 except UnicodeError: 741 pass 742 else: 743 try: 744 value = getattr(obj, name) 745 except AttributeError: 746 pass 747 else: 748 if environment.sandboxed and not \ 749 environment.is_safe_attribute(obj, name, value): 750 return environment.unsafe_undefined(obj, name) 751 return value 752 return environment.undefined(obj=obj, name=name) 753 754 755 FILTERS = { 756 'attr': do_attr, 757 'replace': do_replace, 758 'upper': do_upper, 759 'lower': do_lower, 760 'escape': escape, 761 'e': escape, 762 'forceescape': do_forceescape, 763 'capitalize': do_capitalize, 764 'title': do_title, 765 'default': do_default, 766 'd': do_default, 767 'join': do_join, 768 'count': len, 769 'dictsort': do_dictsort, 770 'sort': do_sort, 771 'length': len, 772 'reverse': do_reverse, 773 'center': do_center, 774 'indent': do_indent, 775 'title': do_title, 776 'capitalize': do_capitalize, 777 'first': do_first, 778 'last': do_last, 779 'random': do_random, 780 'filesizeformat': do_filesizeformat, 781 'pprint': do_pprint, 782 'truncate': do_truncate, 783 'wordwrap': do_wordwrap, 784 'wordcount': do_wordcount, 785 'int': do_int, 786 'float': do_float, 787 'string': soft_unicode, 788 'list': do_list, 789 'urlize': do_urlize, 790 'format': do_format, 791 'trim': do_trim, 792 'striptags': do_striptags, 793 'slice': do_slice, 794 'batch': do_batch, 795 'sum': do_sum, 796 'abs': abs, 797 'round': do_round, 798 'groupby': do_groupby, 799 'safe': do_mark_safe, 800 'xmlattr': do_xmlattr 801 } 802