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