1# -*- coding: utf-8 -*- 2""" 3 jinja2.filters 4 ~~~~~~~~~~~~~~ 5 6 Bundled jinja filters. 7 8 :copyright: (c) 2017 by the Jinja Team. 9 :license: BSD, see LICENSE for more details. 10""" 11import re 12import math 13import random 14import warnings 15 16from itertools import groupby, chain 17from collections import namedtuple 18from jinja2.utils import Markup, escape, pformat, urlize, soft_unicode, \ 19 unicode_urlencode, htmlsafe_json_dumps 20from jinja2.runtime import Undefined 21from jinja2.exceptions import FilterArgumentError 22from jinja2._compat import imap, string_types, text_type, iteritems, PY2 23 24 25_word_re = re.compile(r'\w+', re.UNICODE) 26_word_beginning_split_re = re.compile(r'([-\s\(\{\[\<]+)', re.UNICODE) 27 28 29def contextfilter(f): 30 """Decorator for marking context dependent filters. The current 31 :class:`Context` will be passed as first argument. 32 """ 33 f.contextfilter = True 34 return f 35 36 37def evalcontextfilter(f): 38 """Decorator for marking eval-context dependent filters. An eval 39 context object is passed as first argument. For more information 40 about the eval context, see :ref:`eval-context`. 41 42 .. versionadded:: 2.4 43 """ 44 f.evalcontextfilter = True 45 return f 46 47 48def environmentfilter(f): 49 """Decorator for marking environment dependent filters. The current 50 :class:`Environment` is passed to the filter as first argument. 51 """ 52 f.environmentfilter = True 53 return f 54 55 56def ignore_case(value): 57 """For use as a postprocessor for :func:`make_attrgetter`. Converts strings 58 to lowercase and returns other types as-is.""" 59 return value.lower() if isinstance(value, string_types) else value 60 61 62def make_attrgetter(environment, attribute, postprocess=None): 63 """Returns a callable that looks up the given attribute from a 64 passed object with the rules of the environment. Dots are allowed 65 to access attributes of attributes. Integer parts in paths are 66 looked up as integers. 67 """ 68 if attribute is None: 69 attribute = [] 70 elif isinstance(attribute, string_types): 71 attribute = [int(x) if x.isdigit() else x for x in attribute.split('.')] 72 else: 73 attribute = [attribute] 74 75 def attrgetter(item): 76 for part in attribute: 77 item = environment.getitem(item, part) 78 79 if postprocess is not None: 80 item = postprocess(item) 81 82 return item 83 84 return attrgetter 85 86 87def do_forceescape(value): 88 """Enforce HTML escaping. This will probably double escape variables.""" 89 if hasattr(value, '__html__'): 90 value = value.__html__() 91 return escape(text_type(value)) 92 93 94def do_urlencode(value): 95 """Escape strings for use in URLs (uses UTF-8 encoding). It accepts both 96 dictionaries and regular strings as well as pairwise iterables. 97 98 .. versionadded:: 2.7 99 """ 100 itemiter = None 101 if isinstance(value, dict): 102 itemiter = iteritems(value) 103 elif not isinstance(value, string_types): 104 try: 105 itemiter = iter(value) 106 except TypeError: 107 pass 108 if itemiter is None: 109 return unicode_urlencode(value) 110 return u'&'.join(unicode_urlencode(k) + '=' + 111 unicode_urlencode(v, for_qs=True) 112 for k, v in itemiter) 113 114 115@evalcontextfilter 116def do_replace(eval_ctx, s, old, new, count=None): 117 """Return a copy of the value with all occurrences of a substring 118 replaced with a new one. The first argument is the substring 119 that should be replaced, the second is the replacement string. 120 If the optional third argument ``count`` is given, only the first 121 ``count`` occurrences are replaced: 122 123 .. sourcecode:: jinja 124 125 {{ "Hello World"|replace("Hello", "Goodbye") }} 126 -> Goodbye World 127 128 {{ "aaaaargh"|replace("a", "d'oh, ", 2) }} 129 -> d'oh, d'oh, aaargh 130 """ 131 if count is None: 132 count = -1 133 if not eval_ctx.autoescape: 134 return text_type(s).replace(text_type(old), text_type(new), count) 135 if hasattr(old, '__html__') or hasattr(new, '__html__') and \ 136 not hasattr(s, '__html__'): 137 s = escape(s) 138 else: 139 s = soft_unicode(s) 140 return s.replace(soft_unicode(old), soft_unicode(new), count) 141 142 143def do_upper(s): 144 """Convert a value to uppercase.""" 145 return soft_unicode(s).upper() 146 147 148def do_lower(s): 149 """Convert a value to lowercase.""" 150 return soft_unicode(s).lower() 151 152 153@evalcontextfilter 154def do_xmlattr(_eval_ctx, d, autospace=True): 155 """Create an SGML/XML attribute string based on the items in a dict. 156 All values that are neither `none` nor `undefined` are automatically 157 escaped: 158 159 .. sourcecode:: html+jinja 160 161 <ul{{ {'class': 'my_list', 'missing': none, 162 'id': 'list-%d'|format(variable)}|xmlattr }}> 163 ... 164 </ul> 165 166 Results in something like this: 167 168 .. sourcecode:: html 169 170 <ul class="my_list" id="list-42"> 171 ... 172 </ul> 173 174 As you can see it automatically prepends a space in front of the item 175 if the filter returned something unless the second parameter is false. 176 """ 177 rv = u' '.join( 178 u'%s="%s"' % (escape(key), escape(value)) 179 for key, value in iteritems(d) 180 if value is not None and not isinstance(value, Undefined) 181 ) 182 if autospace and rv: 183 rv = u' ' + rv 184 if _eval_ctx.autoescape: 185 rv = Markup(rv) 186 return rv 187 188 189def do_capitalize(s): 190 """Capitalize a value. The first character will be uppercase, all others 191 lowercase. 192 """ 193 return soft_unicode(s).capitalize() 194 195 196def do_title(s): 197 """Return a titlecased version of the value. I.e. words will start with 198 uppercase letters, all remaining characters are lowercase. 199 """ 200 return ''.join( 201 [item[0].upper() + item[1:].lower() 202 for item in _word_beginning_split_re.split(soft_unicode(s)) 203 if item]) 204 205 206def do_dictsort(value, case_sensitive=False, by='key', reverse=False): 207 """Sort a dict and yield (key, value) pairs. Because python dicts are 208 unsorted you may want to use this function to order them by either 209 key or value: 210 211 .. sourcecode:: jinja 212 213 {% for item in mydict|dictsort %} 214 sort the dict by key, case insensitive 215 216 {% for item in mydict|dictsort(reverse=true) %} 217 sort the dict by key, case insensitive, reverse order 218 219 {% for item in mydict|dictsort(true) %} 220 sort the dict by key, case sensitive 221 222 {% for item in mydict|dictsort(false, 'value') %} 223 sort the dict by value, case insensitive 224 """ 225 if by == 'key': 226 pos = 0 227 elif by == 'value': 228 pos = 1 229 else: 230 raise FilterArgumentError( 231 'You can only sort by either "key" or "value"' 232 ) 233 234 def sort_func(item): 235 value = item[pos] 236 237 if not case_sensitive: 238 value = ignore_case(value) 239 240 return value 241 242 return sorted(value.items(), key=sort_func, reverse=reverse) 243 244 245@environmentfilter 246def do_sort( 247 environment, value, reverse=False, case_sensitive=False, attribute=None 248): 249 """Sort an iterable. Per default it sorts ascending, if you pass it 250 true as first argument it will reverse the sorting. 251 252 If the iterable is made of strings the third parameter can be used to 253 control the case sensitiveness of the comparison which is disabled by 254 default. 255 256 .. sourcecode:: jinja 257 258 {% for item in iterable|sort %} 259 ... 260 {% endfor %} 261 262 It is also possible to sort by an attribute (for example to sort 263 by the date of an object) by specifying the `attribute` parameter: 264 265 .. sourcecode:: jinja 266 267 {% for item in iterable|sort(attribute='date') %} 268 ... 269 {% endfor %} 270 271 .. versionchanged:: 2.6 272 The `attribute` parameter was added. 273 """ 274 key_func = make_attrgetter( 275 environment, attribute, 276 postprocess=ignore_case if not case_sensitive else None 277 ) 278 return sorted(value, key=key_func, reverse=reverse) 279 280 281@environmentfilter 282def do_unique(environment, value, case_sensitive=False, attribute=None): 283 """Returns a list of unique items from the the given iterable. 284 285 .. sourcecode:: jinja 286 287 {{ ['foo', 'bar', 'foobar', 'FooBar']|unique }} 288 -> ['foo', 'bar', 'foobar'] 289 290 The unique items are yielded in the same order as their first occurrence in 291 the iterable passed to the filter. 292 293 :param case_sensitive: Treat upper and lower case strings as distinct. 294 :param attribute: Filter objects with unique values for this attribute. 295 """ 296 getter = make_attrgetter( 297 environment, attribute, 298 postprocess=ignore_case if not case_sensitive else None 299 ) 300 seen = set() 301 302 for item in value: 303 key = getter(item) 304 305 if key not in seen: 306 seen.add(key) 307 yield item 308 309 310def _min_or_max(environment, value, func, case_sensitive, attribute): 311 it = iter(value) 312 313 try: 314 first = next(it) 315 except StopIteration: 316 return environment.undefined('No aggregated item, sequence was empty.') 317 318 key_func = make_attrgetter( 319 environment, attribute, 320 ignore_case if not case_sensitive else None 321 ) 322 return func(chain([first], it), key=key_func) 323 324 325@environmentfilter 326def do_min(environment, value, case_sensitive=False, attribute=None): 327 """Return the smallest item from the sequence. 328 329 .. sourcecode:: jinja 330 331 {{ [1, 2, 3]|min }} 332 -> 1 333 334 :param case_sensitive: Treat upper and lower case strings as distinct. 335 :param attribute: Get the object with the max value of this attribute. 336 """ 337 return _min_or_max(environment, value, min, case_sensitive, attribute) 338 339 340@environmentfilter 341def do_max(environment, value, case_sensitive=False, attribute=None): 342 """Return the largest item from the sequence. 343 344 .. sourcecode:: jinja 345 346 {{ [1, 2, 3]|max }} 347 -> 3 348 349 :param case_sensitive: Treat upper and lower case strings as distinct. 350 :param attribute: Get the object with the max value of this attribute. 351 """ 352 return _min_or_max(environment, value, max, case_sensitive, attribute) 353 354 355def do_default(value, default_value=u'', boolean=False): 356 """If the value is undefined it will return the passed default value, 357 otherwise the value of the variable: 358 359 .. sourcecode:: jinja 360 361 {{ my_variable|default('my_variable is not defined') }} 362 363 This will output the value of ``my_variable`` if the variable was 364 defined, otherwise ``'my_variable is not defined'``. If you want 365 to use default with variables that evaluate to false you have to 366 set the second parameter to `true`: 367 368 .. sourcecode:: jinja 369 370 {{ ''|default('the string was empty', true) }} 371 """ 372 if isinstance(value, Undefined) or (boolean and not value): 373 return default_value 374 return value 375 376 377@evalcontextfilter 378def do_join(eval_ctx, value, d=u'', attribute=None): 379 """Return a string which is the concatenation of the strings in the 380 sequence. The separator between elements is an empty string per 381 default, you can define it with the optional parameter: 382 383 .. sourcecode:: jinja 384 385 {{ [1, 2, 3]|join('|') }} 386 -> 1|2|3 387 388 {{ [1, 2, 3]|join }} 389 -> 123 390 391 It is also possible to join certain attributes of an object: 392 393 .. sourcecode:: jinja 394 395 {{ users|join(', ', attribute='username') }} 396 397 .. versionadded:: 2.6 398 The `attribute` parameter was added. 399 """ 400 if attribute is not None: 401 value = imap(make_attrgetter(eval_ctx.environment, attribute), value) 402 403 # no automatic escaping? joining is a lot eaiser then 404 if not eval_ctx.autoescape: 405 return text_type(d).join(imap(text_type, value)) 406 407 # if the delimiter doesn't have an html representation we check 408 # if any of the items has. If yes we do a coercion to Markup 409 if not hasattr(d, '__html__'): 410 value = list(value) 411 do_escape = False 412 for idx, item in enumerate(value): 413 if hasattr(item, '__html__'): 414 do_escape = True 415 else: 416 value[idx] = text_type(item) 417 if do_escape: 418 d = escape(d) 419 else: 420 d = text_type(d) 421 return d.join(value) 422 423 # no html involved, to normal joining 424 return soft_unicode(d).join(imap(soft_unicode, value)) 425 426 427def do_center(value, width=80): 428 """Centers the value in a field of a given width.""" 429 return text_type(value).center(width) 430 431 432@environmentfilter 433def do_first(environment, seq): 434 """Return the first item of a sequence.""" 435 try: 436 return next(iter(seq)) 437 except StopIteration: 438 return environment.undefined('No first item, sequence was empty.') 439 440 441@environmentfilter 442def do_last(environment, seq): 443 """Return the last item of a sequence.""" 444 try: 445 return next(iter(reversed(seq))) 446 except StopIteration: 447 return environment.undefined('No last item, sequence was empty.') 448 449 450@contextfilter 451def do_random(context, seq): 452 """Return a random item from the sequence.""" 453 try: 454 return random.choice(seq) 455 except IndexError: 456 return context.environment.undefined('No random item, sequence was empty.') 457 458 459def do_filesizeformat(value, binary=False): 460 """Format the value like a 'human-readable' file size (i.e. 13 kB, 461 4.1 MB, 102 Bytes, etc). Per default decimal prefixes are used (Mega, 462 Giga, etc.), if the second parameter is set to `True` the binary 463 prefixes are used (Mebi, Gibi). 464 """ 465 bytes = float(value) 466 base = binary and 1024 or 1000 467 prefixes = [ 468 (binary and 'KiB' or 'kB'), 469 (binary and 'MiB' or 'MB'), 470 (binary and 'GiB' or 'GB'), 471 (binary and 'TiB' or 'TB'), 472 (binary and 'PiB' or 'PB'), 473 (binary and 'EiB' or 'EB'), 474 (binary and 'ZiB' or 'ZB'), 475 (binary and 'YiB' or 'YB') 476 ] 477 if bytes == 1: 478 return '1 Byte' 479 elif bytes < base: 480 return '%d Bytes' % bytes 481 else: 482 for i, prefix in enumerate(prefixes): 483 unit = base ** (i + 2) 484 if bytes < unit: 485 return '%.1f %s' % ((base * bytes / unit), prefix) 486 return '%.1f %s' % ((base * bytes / unit), prefix) 487 488 489def do_pprint(value, verbose=False): 490 """Pretty print a variable. Useful for debugging. 491 492 With Jinja 1.2 onwards you can pass it a parameter. If this parameter 493 is truthy the output will be more verbose (this requires `pretty`) 494 """ 495 return pformat(value, verbose=verbose) 496 497 498@evalcontextfilter 499def do_urlize(eval_ctx, value, trim_url_limit=None, nofollow=False, 500 target=None, rel=None): 501 """Converts URLs in plain text into clickable links. 502 503 If you pass the filter an additional integer it will shorten the urls 504 to that number. Also a third argument exists that makes the urls 505 "nofollow": 506 507 .. sourcecode:: jinja 508 509 {{ mytext|urlize(40, true) }} 510 links are shortened to 40 chars and defined with rel="nofollow" 511 512 If *target* is specified, the ``target`` attribute will be added to the 513 ``<a>`` tag: 514 515 .. sourcecode:: jinja 516 517 {{ mytext|urlize(40, target='_blank') }} 518 519 .. versionchanged:: 2.8+ 520 The *target* parameter was added. 521 """ 522 policies = eval_ctx.environment.policies 523 rel = set((rel or '').split() or []) 524 if nofollow: 525 rel.add('nofollow') 526 rel.update((policies['urlize.rel'] or '').split()) 527 if target is None: 528 target = policies['urlize.target'] 529 rel = ' '.join(sorted(rel)) or None 530 rv = urlize(value, trim_url_limit, rel=rel, target=target) 531 if eval_ctx.autoescape: 532 rv = Markup(rv) 533 return rv 534 535 536def do_indent( 537 s, width=4, first=False, blank=False, indentfirst=None 538): 539 """Return a copy of the string with each line indented by 4 spaces. The 540 first line and blank lines are not indented by default. 541 542 :param width: Number of spaces to indent by. 543 :param first: Don't skip indenting the first line. 544 :param blank: Don't skip indenting empty lines. 545 546 .. versionchanged:: 2.10 547 Blank lines are not indented by default. 548 549 Rename the ``indentfirst`` argument to ``first``. 550 """ 551 if indentfirst is not None: 552 warnings.warn(DeprecationWarning( 553 'The "indentfirst" argument is renamed to "first".' 554 ), stacklevel=2) 555 first = indentfirst 556 557 s += u'\n' # this quirk is necessary for splitlines method 558 indention = u' ' * width 559 560 if blank: 561 rv = (u'\n' + indention).join(s.splitlines()) 562 else: 563 lines = s.splitlines() 564 rv = lines.pop(0) 565 566 if lines: 567 rv += u'\n' + u'\n'.join( 568 indention + line if line else line for line in lines 569 ) 570 571 if first: 572 rv = indention + rv 573 574 return rv 575 576 577@environmentfilter 578def do_truncate(env, s, length=255, killwords=False, end='...', leeway=None): 579 """Return a truncated copy of the string. The length is specified 580 with the first parameter which defaults to ``255``. If the second 581 parameter is ``true`` the filter will cut the text at length. Otherwise 582 it will discard the last word. If the text was in fact 583 truncated it will append an ellipsis sign (``"..."``). If you want a 584 different ellipsis sign than ``"..."`` you can specify it using the 585 third parameter. Strings that only exceed the length by the tolerance 586 margin given in the fourth parameter will not be truncated. 587 588 .. sourcecode:: jinja 589 590 {{ "foo bar baz qux"|truncate(9) }} 591 -> "foo..." 592 {{ "foo bar baz qux"|truncate(9, True) }} 593 -> "foo ba..." 594 {{ "foo bar baz qux"|truncate(11) }} 595 -> "foo bar baz qux" 596 {{ "foo bar baz qux"|truncate(11, False, '...', 0) }} 597 -> "foo bar..." 598 599 The default leeway on newer Jinja2 versions is 5 and was 0 before but 600 can be reconfigured globally. 601 """ 602 if leeway is None: 603 leeway = env.policies['truncate.leeway'] 604 assert length >= len(end), 'expected length >= %s, got %s' % (len(end), length) 605 assert leeway >= 0, 'expected leeway >= 0, got %s' % leeway 606 if len(s) <= length + leeway: 607 return s 608 if killwords: 609 return s[:length - len(end)] + end 610 result = s[:length - len(end)].rsplit(' ', 1)[0] 611 return result + end 612 613 614@environmentfilter 615def do_wordwrap(environment, s, width=79, break_long_words=True, 616 wrapstring=None): 617 """ 618 Return a copy of the string passed to the filter wrapped after 619 ``79`` characters. You can override this default using the first 620 parameter. If you set the second parameter to `false` Jinja will not 621 split words apart if they are longer than `width`. By default, the newlines 622 will be the default newlines for the environment, but this can be changed 623 using the wrapstring keyword argument. 624 625 .. versionadded:: 2.7 626 Added support for the `wrapstring` parameter. 627 """ 628 if not wrapstring: 629 wrapstring = environment.newline_sequence 630 import textwrap 631 return wrapstring.join(textwrap.wrap(s, width=width, expand_tabs=False, 632 replace_whitespace=False, 633 break_long_words=break_long_words)) 634 635 636def do_wordcount(s): 637 """Count the words in that string.""" 638 return len(_word_re.findall(s)) 639 640 641def do_int(value, default=0, base=10): 642 """Convert the value into an integer. If the 643 conversion doesn't work it will return ``0``. You can 644 override this default using the first parameter. You 645 can also override the default base (10) in the second 646 parameter, which handles input with prefixes such as 647 0b, 0o and 0x for bases 2, 8 and 16 respectively. 648 The base is ignored for decimal numbers and non-string values. 649 """ 650 try: 651 if isinstance(value, string_types): 652 return int(value, base) 653 return int(value) 654 except (TypeError, ValueError): 655 # this quirk is necessary so that "42.23"|int gives 42. 656 try: 657 return int(float(value)) 658 except (TypeError, ValueError): 659 return default 660 661 662def do_float(value, default=0.0): 663 """Convert the value into a floating point number. If the 664 conversion doesn't work it will return ``0.0``. You can 665 override this default using the first parameter. 666 """ 667 try: 668 return float(value) 669 except (TypeError, ValueError): 670 return default 671 672 673def do_format(value, *args, **kwargs): 674 """ 675 Apply python string formatting on an object: 676 677 .. sourcecode:: jinja 678 679 {{ "%s - %s"|format("Hello?", "Foo!") }} 680 -> Hello? - Foo! 681 """ 682 if args and kwargs: 683 raise FilterArgumentError('can\'t handle positional and keyword ' 684 'arguments at the same time') 685 return soft_unicode(value) % (kwargs or args) 686 687 688def do_trim(value): 689 """Strip leading and trailing whitespace.""" 690 return soft_unicode(value).strip() 691 692 693def do_striptags(value): 694 """Strip SGML/XML tags and replace adjacent whitespace by one space. 695 """ 696 if hasattr(value, '__html__'): 697 value = value.__html__() 698 return Markup(text_type(value)).striptags() 699 700 701def do_slice(value, slices, fill_with=None): 702 """Slice an iterator and return a list of lists containing 703 those items. Useful if you want to create a div containing 704 three ul tags that represent columns: 705 706 .. sourcecode:: html+jinja 707 708 <div class="columwrapper"> 709 {%- for column in items|slice(3) %} 710 <ul class="column-{{ loop.index }}"> 711 {%- for item in column %} 712 <li>{{ item }}</li> 713 {%- endfor %} 714 </ul> 715 {%- endfor %} 716 </div> 717 718 If you pass it a second argument it's used to fill missing 719 values on the last iteration. 720 """ 721 seq = list(value) 722 length = len(seq) 723 items_per_slice = length // slices 724 slices_with_extra = length % slices 725 offset = 0 726 for slice_number in range(slices): 727 start = offset + slice_number * items_per_slice 728 if slice_number < slices_with_extra: 729 offset += 1 730 end = offset + (slice_number + 1) * items_per_slice 731 tmp = seq[start:end] 732 if fill_with is not None and slice_number >= slices_with_extra: 733 tmp.append(fill_with) 734 yield tmp 735 736 737def do_batch(value, linecount, fill_with=None): 738 """ 739 A filter that batches items. It works pretty much like `slice` 740 just the other way round. It returns a list of lists with the 741 given number of items. If you provide a second parameter this 742 is used to fill up missing items. See this example: 743 744 .. sourcecode:: html+jinja 745 746 <table> 747 {%- for row in items|batch(3, ' ') %} 748 <tr> 749 {%- for column in row %} 750 <td>{{ column }}</td> 751 {%- endfor %} 752 </tr> 753 {%- endfor %} 754 </table> 755 """ 756 tmp = [] 757 for item in value: 758 if len(tmp) == linecount: 759 yield tmp 760 tmp = [] 761 tmp.append(item) 762 if tmp: 763 if fill_with is not None and len(tmp) < linecount: 764 tmp += [fill_with] * (linecount - len(tmp)) 765 yield tmp 766 767 768def do_round(value, precision=0, method='common'): 769 """Round the number to a given precision. The first 770 parameter specifies the precision (default is ``0``), the 771 second the rounding method: 772 773 - ``'common'`` rounds either up or down 774 - ``'ceil'`` always rounds up 775 - ``'floor'`` always rounds down 776 777 If you don't specify a method ``'common'`` is used. 778 779 .. sourcecode:: jinja 780 781 {{ 42.55|round }} 782 -> 43.0 783 {{ 42.55|round(1, 'floor') }} 784 -> 42.5 785 786 Note that even if rounded to 0 precision, a float is returned. If 787 you need a real integer, pipe it through `int`: 788 789 .. sourcecode:: jinja 790 791 {{ 42.55|round|int }} 792 -> 43 793 """ 794 if not method in ('common', 'ceil', 'floor'): 795 raise FilterArgumentError('method must be common, ceil or floor') 796 if method == 'common': 797 return round(value, precision) 798 func = getattr(math, method) 799 return func(value * (10 ** precision)) / (10 ** precision) 800 801 802# Use a regular tuple repr here. This is what we did in the past and we 803# really want to hide this custom type as much as possible. In particular 804# we do not want to accidentally expose an auto generated repr in case 805# people start to print this out in comments or something similar for 806# debugging. 807_GroupTuple = namedtuple('_GroupTuple', ['grouper', 'list']) 808_GroupTuple.__repr__ = tuple.__repr__ 809_GroupTuple.__str__ = tuple.__str__ 810 811@environmentfilter 812def do_groupby(environment, value, attribute): 813 """Group a sequence of objects by a common attribute. 814 815 If you for example have a list of dicts or objects that represent persons 816 with `gender`, `first_name` and `last_name` attributes and you want to 817 group all users by genders you can do something like the following 818 snippet: 819 820 .. sourcecode:: html+jinja 821 822 <ul> 823 {% for group in persons|groupby('gender') %} 824 <li>{{ group.grouper }}<ul> 825 {% for person in group.list %} 826 <li>{{ person.first_name }} {{ person.last_name }}</li> 827 {% endfor %}</ul></li> 828 {% endfor %} 829 </ul> 830 831 Additionally it's possible to use tuple unpacking for the grouper and 832 list: 833 834 .. sourcecode:: html+jinja 835 836 <ul> 837 {% for grouper, list in persons|groupby('gender') %} 838 ... 839 {% endfor %} 840 </ul> 841 842 As you can see the item we're grouping by is stored in the `grouper` 843 attribute and the `list` contains all the objects that have this grouper 844 in common. 845 846 .. versionchanged:: 2.6 847 It's now possible to use dotted notation to group by the child 848 attribute of another attribute. 849 """ 850 expr = make_attrgetter(environment, attribute) 851 return [_GroupTuple(key, list(values)) for key, values 852 in groupby(sorted(value, key=expr), expr)] 853 854 855@environmentfilter 856def do_sum(environment, iterable, attribute=None, start=0): 857 """Returns the sum of a sequence of numbers plus the value of parameter 858 'start' (which defaults to 0). When the sequence is empty it returns 859 start. 860 861 It is also possible to sum up only certain attributes: 862 863 .. sourcecode:: jinja 864 865 Total: {{ items|sum(attribute='price') }} 866 867 .. versionchanged:: 2.6 868 The `attribute` parameter was added to allow suming up over 869 attributes. Also the `start` parameter was moved on to the right. 870 """ 871 if attribute is not None: 872 iterable = imap(make_attrgetter(environment, attribute), iterable) 873 return sum(iterable, start) 874 875 876def do_list(value): 877 """Convert the value into a list. If it was a string the returned list 878 will be a list of characters. 879 """ 880 return list(value) 881 882 883def do_mark_safe(value): 884 """Mark the value as safe which means that in an environment with automatic 885 escaping enabled this variable will not be escaped. 886 """ 887 return Markup(value) 888 889 890def do_mark_unsafe(value): 891 """Mark a value as unsafe. This is the reverse operation for :func:`safe`.""" 892 return text_type(value) 893 894 895def do_reverse(value): 896 """Reverse the object or return an iterator that iterates over it the other 897 way round. 898 """ 899 if isinstance(value, string_types): 900 return value[::-1] 901 try: 902 return reversed(value) 903 except TypeError: 904 try: 905 rv = list(value) 906 rv.reverse() 907 return rv 908 except TypeError: 909 raise FilterArgumentError('argument must be iterable') 910 911 912@environmentfilter 913def do_attr(environment, obj, name): 914 """Get an attribute of an object. ``foo|attr("bar")`` works like 915 ``foo.bar`` just that always an attribute is returned and items are not 916 looked up. 917 918 See :ref:`Notes on subscriptions <notes-on-subscriptions>` for more details. 919 """ 920 try: 921 name = str(name) 922 except UnicodeError: 923 pass 924 else: 925 try: 926 value = getattr(obj, name) 927 except AttributeError: 928 pass 929 else: 930 if environment.sandboxed and not \ 931 environment.is_safe_attribute(obj, name, value): 932 return environment.unsafe_undefined(obj, name) 933 return value 934 return environment.undefined(obj=obj, name=name) 935 936 937@contextfilter 938def do_map(*args, **kwargs): 939 """Applies a filter on a sequence of objects or looks up an attribute. 940 This is useful when dealing with lists of objects but you are really 941 only interested in a certain value of it. 942 943 The basic usage is mapping on an attribute. Imagine you have a list 944 of users but you are only interested in a list of usernames: 945 946 .. sourcecode:: jinja 947 948 Users on this page: {{ users|map(attribute='username')|join(', ') }} 949 950 Alternatively you can let it invoke a filter by passing the name of the 951 filter and the arguments afterwards. A good example would be applying a 952 text conversion filter on a sequence: 953 954 .. sourcecode:: jinja 955 956 Users on this page: {{ titles|map('lower')|join(', ') }} 957 958 .. versionadded:: 2.7 959 """ 960 seq, func = prepare_map(args, kwargs) 961 if seq: 962 for item in seq: 963 yield func(item) 964 965 966@contextfilter 967def do_select(*args, **kwargs): 968 """Filters a sequence of objects by applying a test to each object, 969 and only selecting the objects with the test succeeding. 970 971 If no test is specified, each object will be evaluated as a boolean. 972 973 Example usage: 974 975 .. sourcecode:: jinja 976 977 {{ numbers|select("odd") }} 978 {{ numbers|select("odd") }} 979 {{ numbers|select("divisibleby", 3) }} 980 {{ numbers|select("lessthan", 42) }} 981 {{ strings|select("equalto", "mystring") }} 982 983 .. versionadded:: 2.7 984 """ 985 return select_or_reject(args, kwargs, lambda x: x, False) 986 987 988@contextfilter 989def do_reject(*args, **kwargs): 990 """Filters a sequence of objects by applying a test to each object, 991 and rejecting the objects with the test succeeding. 992 993 If no test is specified, each object will be evaluated as a boolean. 994 995 Example usage: 996 997 .. sourcecode:: jinja 998 999 {{ numbers|reject("odd") }} 1000 1001 .. versionadded:: 2.7 1002 """ 1003 return select_or_reject(args, kwargs, lambda x: not x, False) 1004 1005 1006@contextfilter 1007def do_selectattr(*args, **kwargs): 1008 """Filters a sequence of objects by applying a test to the specified 1009 attribute of each object, and only selecting the objects with the 1010 test succeeding. 1011 1012 If no test is specified, the attribute's value will be evaluated as 1013 a boolean. 1014 1015 Example usage: 1016 1017 .. sourcecode:: jinja 1018 1019 {{ users|selectattr("is_active") }} 1020 {{ users|selectattr("email", "none") }} 1021 1022 .. versionadded:: 2.7 1023 """ 1024 return select_or_reject(args, kwargs, lambda x: x, True) 1025 1026 1027@contextfilter 1028def do_rejectattr(*args, **kwargs): 1029 """Filters a sequence of objects by applying a test to the specified 1030 attribute of each object, and rejecting the objects with the test 1031 succeeding. 1032 1033 If no test is specified, the attribute's value will be evaluated as 1034 a boolean. 1035 1036 .. sourcecode:: jinja 1037 1038 {{ users|rejectattr("is_active") }} 1039 {{ users|rejectattr("email", "none") }} 1040 1041 .. versionadded:: 2.7 1042 """ 1043 return select_or_reject(args, kwargs, lambda x: not x, True) 1044 1045 1046@evalcontextfilter 1047def do_tojson(eval_ctx, value, indent=None): 1048 """Dumps a structure to JSON so that it's safe to use in ``<script>`` 1049 tags. It accepts the same arguments and returns a JSON string. Note that 1050 this is available in templates through the ``|tojson`` filter which will 1051 also mark the result as safe. Due to how this function escapes certain 1052 characters this is safe even if used outside of ``<script>`` tags. 1053 1054 The following characters are escaped in strings: 1055 1056 - ``<`` 1057 - ``>`` 1058 - ``&`` 1059 - ``'`` 1060 1061 This makes it safe to embed such strings in any place in HTML with the 1062 notable exception of double quoted attributes. In that case single 1063 quote your attributes or HTML escape it in addition. 1064 1065 The indent parameter can be used to enable pretty printing. Set it to 1066 the number of spaces that the structures should be indented with. 1067 1068 Note that this filter is for use in HTML contexts only. 1069 1070 .. versionadded:: 2.9 1071 """ 1072 policies = eval_ctx.environment.policies 1073 dumper = policies['json.dumps_function'] 1074 options = policies['json.dumps_kwargs'] 1075 if indent is not None: 1076 options = dict(options) 1077 options['indent'] = indent 1078 return htmlsafe_json_dumps(value, dumper=dumper, **options) 1079 1080 1081def prepare_map(args, kwargs): 1082 context = args[0] 1083 seq = args[1] 1084 1085 if len(args) == 2 and 'attribute' in kwargs: 1086 attribute = kwargs.pop('attribute') 1087 if kwargs: 1088 raise FilterArgumentError('Unexpected keyword argument %r' % 1089 next(iter(kwargs))) 1090 func = make_attrgetter(context.environment, attribute) 1091 else: 1092 try: 1093 name = args[2] 1094 args = args[3:] 1095 except LookupError: 1096 raise FilterArgumentError('map requires a filter argument') 1097 func = lambda item: context.environment.call_filter( 1098 name, item, args, kwargs, context=context) 1099 1100 return seq, func 1101 1102 1103def prepare_select_or_reject(args, kwargs, modfunc, lookup_attr): 1104 context = args[0] 1105 seq = args[1] 1106 if lookup_attr: 1107 try: 1108 attr = args[2] 1109 except LookupError: 1110 raise FilterArgumentError('Missing parameter for attribute name') 1111 transfunc = make_attrgetter(context.environment, attr) 1112 off = 1 1113 else: 1114 off = 0 1115 transfunc = lambda x: x 1116 1117 try: 1118 name = args[2 + off] 1119 args = args[3 + off:] 1120 func = lambda item: context.environment.call_test( 1121 name, item, args, kwargs) 1122 except LookupError: 1123 func = bool 1124 1125 return seq, lambda item: modfunc(func(transfunc(item))) 1126 1127 1128def select_or_reject(args, kwargs, modfunc, lookup_attr): 1129 seq, func = prepare_select_or_reject(args, kwargs, modfunc, lookup_attr) 1130 if seq: 1131 for item in seq: 1132 if func(item): 1133 yield item 1134 1135 1136FILTERS = { 1137 'abs': abs, 1138 'attr': do_attr, 1139 'batch': do_batch, 1140 'capitalize': do_capitalize, 1141 'center': do_center, 1142 'count': len, 1143 'd': do_default, 1144 'default': do_default, 1145 'dictsort': do_dictsort, 1146 'e': escape, 1147 'escape': escape, 1148 'filesizeformat': do_filesizeformat, 1149 'first': do_first, 1150 'float': do_float, 1151 'forceescape': do_forceescape, 1152 'format': do_format, 1153 'groupby': do_groupby, 1154 'indent': do_indent, 1155 'int': do_int, 1156 'join': do_join, 1157 'last': do_last, 1158 'length': len, 1159 'list': do_list, 1160 'lower': do_lower, 1161 'map': do_map, 1162 'min': do_min, 1163 'max': do_max, 1164 'pprint': do_pprint, 1165 'random': do_random, 1166 'reject': do_reject, 1167 'rejectattr': do_rejectattr, 1168 'replace': do_replace, 1169 'reverse': do_reverse, 1170 'round': do_round, 1171 'safe': do_mark_safe, 1172 'select': do_select, 1173 'selectattr': do_selectattr, 1174 'slice': do_slice, 1175 'sort': do_sort, 1176 'string': soft_unicode, 1177 'striptags': do_striptags, 1178 'sum': do_sum, 1179 'title': do_title, 1180 'trim': do_trim, 1181 'truncate': do_truncate, 1182 'unique': do_unique, 1183 'upper': do_upper, 1184 'urlencode': do_urlencode, 1185 'urlize': do_urlize, 1186 'wordcount': do_wordcount, 1187 'wordwrap': do_wordwrap, 1188 'xmlattr': do_xmlattr, 1189 'tojson': do_tojson, 1190} 1191