1"""Built-in template filters used with the ``|`` operator.""" 2import math 3import random 4import re 5import typing 6import typing as t 7from collections import abc 8from itertools import chain 9from itertools import groupby 10 11from markupsafe import escape 12from markupsafe import Markup 13from markupsafe import soft_str 14 15from .async_utils import async_variant 16from .async_utils import auto_aiter 17from .async_utils import auto_await 18from .async_utils import auto_to_list 19from .exceptions import FilterArgumentError 20from .runtime import Undefined 21from .utils import htmlsafe_json_dumps 22from .utils import pass_context 23from .utils import pass_environment 24from .utils import pass_eval_context 25from .utils import pformat 26from .utils import url_quote 27from .utils import urlize 28 29if t.TYPE_CHECKING: 30 import typing_extensions as te 31 from .environment import Environment 32 from .nodes import EvalContext 33 from .runtime import Context 34 from .sandbox import SandboxedEnvironment # noqa: F401 35 36 class HasHTML(te.Protocol): 37 def __html__(self) -> str: 38 pass 39 40 41F = t.TypeVar("F", bound=t.Callable[..., t.Any]) 42K = t.TypeVar("K") 43V = t.TypeVar("V") 44 45 46def ignore_case(value: V) -> V: 47 """For use as a postprocessor for :func:`make_attrgetter`. Converts strings 48 to lowercase and returns other types as-is.""" 49 if isinstance(value, str): 50 return t.cast(V, value.lower()) 51 52 return value 53 54 55def make_attrgetter( 56 environment: "Environment", 57 attribute: t.Optional[t.Union[str, int]], 58 postprocess: t.Optional[t.Callable[[t.Any], t.Any]] = None, 59 default: t.Optional[t.Any] = None, 60) -> t.Callable[[t.Any], t.Any]: 61 """Returns a callable that looks up the given attribute from a 62 passed object with the rules of the environment. Dots are allowed 63 to access attributes of attributes. Integer parts in paths are 64 looked up as integers. 65 """ 66 parts = _prepare_attribute_parts(attribute) 67 68 def attrgetter(item: t.Any) -> t.Any: 69 for part in parts: 70 item = environment.getitem(item, part) 71 72 if default is not None and isinstance(item, Undefined): 73 item = default 74 75 if postprocess is not None: 76 item = postprocess(item) 77 78 return item 79 80 return attrgetter 81 82 83def make_multi_attrgetter( 84 environment: "Environment", 85 attribute: t.Optional[t.Union[str, int]], 86 postprocess: t.Optional[t.Callable[[t.Any], t.Any]] = None, 87) -> t.Callable[[t.Any], t.List[t.Any]]: 88 """Returns a callable that looks up the given comma separated 89 attributes from a passed object with the rules of the environment. 90 Dots are allowed to access attributes of each attribute. Integer 91 parts in paths are looked up as integers. 92 93 The value returned by the returned callable is a list of extracted 94 attribute values. 95 96 Examples of attribute: "attr1,attr2", "attr1.inner1.0,attr2.inner2.0", etc. 97 """ 98 if isinstance(attribute, str): 99 split: t.Sequence[t.Union[str, int, None]] = attribute.split(",") 100 else: 101 split = [attribute] 102 103 parts = [_prepare_attribute_parts(item) for item in split] 104 105 def attrgetter(item: t.Any) -> t.List[t.Any]: 106 items = [None] * len(parts) 107 108 for i, attribute_part in enumerate(parts): 109 item_i = item 110 111 for part in attribute_part: 112 item_i = environment.getitem(item_i, part) 113 114 if postprocess is not None: 115 item_i = postprocess(item_i) 116 117 items[i] = item_i 118 119 return items 120 121 return attrgetter 122 123 124def _prepare_attribute_parts( 125 attr: t.Optional[t.Union[str, int]] 126) -> t.List[t.Union[str, int]]: 127 if attr is None: 128 return [] 129 130 if isinstance(attr, str): 131 return [int(x) if x.isdigit() else x for x in attr.split(".")] 132 133 return [attr] 134 135 136def do_forceescape(value: "t.Union[str, HasHTML]") -> Markup: 137 """Enforce HTML escaping. This will probably double escape variables.""" 138 if hasattr(value, "__html__"): 139 value = t.cast("HasHTML", value).__html__() 140 141 return escape(str(value)) 142 143 144def do_urlencode( 145 value: t.Union[str, t.Mapping[str, t.Any], t.Iterable[t.Tuple[str, t.Any]]] 146) -> str: 147 """Quote data for use in a URL path or query using UTF-8. 148 149 Basic wrapper around :func:`urllib.parse.quote` when given a 150 string, or :func:`urllib.parse.urlencode` for a dict or iterable. 151 152 :param value: Data to quote. A string will be quoted directly. A 153 dict or iterable of ``(key, value)`` pairs will be joined as a 154 query string. 155 156 When given a string, "/" is not quoted. HTTP servers treat "/" and 157 "%2F" equivalently in paths. If you need quoted slashes, use the 158 ``|replace("/", "%2F")`` filter. 159 160 .. versionadded:: 2.7 161 """ 162 if isinstance(value, str) or not isinstance(value, abc.Iterable): 163 return url_quote(value) 164 165 if isinstance(value, dict): 166 items: t.Iterable[t.Tuple[str, t.Any]] = value.items() 167 else: 168 items = value # type: ignore 169 170 return "&".join( 171 f"{url_quote(k, for_qs=True)}={url_quote(v, for_qs=True)}" for k, v in items 172 ) 173 174 175@pass_eval_context 176def do_replace( 177 eval_ctx: "EvalContext", s: str, old: str, new: str, count: t.Optional[int] = None 178) -> str: 179 """Return a copy of the value with all occurrences of a substring 180 replaced with a new one. The first argument is the substring 181 that should be replaced, the second is the replacement string. 182 If the optional third argument ``count`` is given, only the first 183 ``count`` occurrences are replaced: 184 185 .. sourcecode:: jinja 186 187 {{ "Hello World"|replace("Hello", "Goodbye") }} 188 -> Goodbye World 189 190 {{ "aaaaargh"|replace("a", "d'oh, ", 2) }} 191 -> d'oh, d'oh, aaargh 192 """ 193 if count is None: 194 count = -1 195 196 if not eval_ctx.autoescape: 197 return str(s).replace(str(old), str(new), count) 198 199 if ( 200 hasattr(old, "__html__") 201 or hasattr(new, "__html__") 202 and not hasattr(s, "__html__") 203 ): 204 s = escape(s) 205 else: 206 s = soft_str(s) 207 208 return s.replace(soft_str(old), soft_str(new), count) 209 210 211def do_upper(s: str) -> str: 212 """Convert a value to uppercase.""" 213 return soft_str(s).upper() 214 215 216def do_lower(s: str) -> str: 217 """Convert a value to lowercase.""" 218 return soft_str(s).lower() 219 220 221def do_items(value: t.Union[t.Mapping[K, V], Undefined]) -> t.Iterator[t.Tuple[K, V]]: 222 """Return an iterator over the ``(key, value)`` items of a mapping. 223 224 ``x|items`` is the same as ``x.items()``, except if ``x`` is 225 undefined an empty iterator is returned. 226 227 This filter is useful if you expect the template to be rendered with 228 an implementation of Jinja in another programming language that does 229 not have a ``.items()`` method on its mapping type. 230 231 .. code-block:: html+jinja 232 233 <dl> 234 {% for key, value in my_dict|items %} 235 <dt>{{ key }} 236 <dd>{{ value }} 237 {% endfor %} 238 </dl> 239 240 .. versionadded:: 3.1 241 """ 242 if isinstance(value, Undefined): 243 return 244 245 if not isinstance(value, abc.Mapping): 246 raise TypeError("Can only get item pairs from a mapping.") 247 248 yield from value.items() 249 250 251_space_re = re.compile(r"\s", flags=re.ASCII) 252 253 254@pass_eval_context 255def do_xmlattr( 256 eval_ctx: "EvalContext", d: t.Mapping[str, t.Any], autospace: bool = True 257) -> str: 258 """Create an SGML/XML attribute string based on the items in a dict. 259 260 If any key contains a space, this fails with a ``ValueError``. Values that 261 are neither ``none`` nor ``undefined`` are automatically escaped. 262 263 .. sourcecode:: html+jinja 264 265 <ul{{ {'class': 'my_list', 'missing': none, 266 'id': 'list-%d'|format(variable)}|xmlattr }}> 267 ... 268 </ul> 269 270 Results in something like this: 271 272 .. sourcecode:: html 273 274 <ul class="my_list" id="list-42"> 275 ... 276 </ul> 277 278 As you can see it automatically prepends a space in front of the item 279 if the filter returned something unless the second parameter is false. 280 281 .. versionchanged:: 3.1.3 282 Keys with spaces are not allowed. 283 """ 284 items = [] 285 286 for key, value in d.items(): 287 if value is None or isinstance(value, Undefined): 288 continue 289 290 if _space_re.search(key) is not None: 291 raise ValueError(f"Spaces are not allowed in attributes: '{key}'") 292 293 items.append(f'{escape(key)}="{escape(value)}"') 294 295 rv = " ".join(items) 296 297 if autospace and rv: 298 rv = " " + rv 299 300 if eval_ctx.autoescape: 301 rv = Markup(rv) 302 303 return rv 304 305 306def do_capitalize(s: str) -> str: 307 """Capitalize a value. The first character will be uppercase, all others 308 lowercase. 309 """ 310 return soft_str(s).capitalize() 311 312 313_word_beginning_split_re = re.compile(r"([-\s({\[<]+)") 314 315 316def do_title(s: str) -> str: 317 """Return a titlecased version of the value. I.e. words will start with 318 uppercase letters, all remaining characters are lowercase. 319 """ 320 return "".join( 321 [ 322 item[0].upper() + item[1:].lower() 323 for item in _word_beginning_split_re.split(soft_str(s)) 324 if item 325 ] 326 ) 327 328 329def do_dictsort( 330 value: t.Mapping[K, V], 331 case_sensitive: bool = False, 332 by: 'te.Literal["key", "value"]' = "key", 333 reverse: bool = False, 334) -> t.List[t.Tuple[K, V]]: 335 """Sort a dict and yield (key, value) pairs. Python dicts may not 336 be in the order you want to display them in, so sort them first. 337 338 .. sourcecode:: jinja 339 340 {% for key, value in mydict|dictsort %} 341 sort the dict by key, case insensitive 342 343 {% for key, value in mydict|dictsort(reverse=true) %} 344 sort the dict by key, case insensitive, reverse order 345 346 {% for key, value in mydict|dictsort(true) %} 347 sort the dict by key, case sensitive 348 349 {% for key, value in mydict|dictsort(false, 'value') %} 350 sort the dict by value, case insensitive 351 """ 352 if by == "key": 353 pos = 0 354 elif by == "value": 355 pos = 1 356 else: 357 raise FilterArgumentError('You can only sort by either "key" or "value"') 358 359 def sort_func(item: t.Tuple[t.Any, t.Any]) -> t.Any: 360 value = item[pos] 361 362 if not case_sensitive: 363 value = ignore_case(value) 364 365 return value 366 367 return sorted(value.items(), key=sort_func, reverse=reverse) 368 369 370@pass_environment 371def do_sort( 372 environment: "Environment", 373 value: "t.Iterable[V]", 374 reverse: bool = False, 375 case_sensitive: bool = False, 376 attribute: t.Optional[t.Union[str, int]] = None, 377) -> "t.List[V]": 378 """Sort an iterable using Python's :func:`sorted`. 379 380 .. sourcecode:: jinja 381 382 {% for city in cities|sort %} 383 ... 384 {% endfor %} 385 386 :param reverse: Sort descending instead of ascending. 387 :param case_sensitive: When sorting strings, sort upper and lower 388 case separately. 389 :param attribute: When sorting objects or dicts, an attribute or 390 key to sort by. Can use dot notation like ``"address.city"``. 391 Can be a list of attributes like ``"age,name"``. 392 393 The sort is stable, it does not change the relative order of 394 elements that compare equal. This makes it is possible to chain 395 sorts on different attributes and ordering. 396 397 .. sourcecode:: jinja 398 399 {% for user in users|sort(attribute="name") 400 |sort(reverse=true, attribute="age") %} 401 ... 402 {% endfor %} 403 404 As a shortcut to chaining when the direction is the same for all 405 attributes, pass a comma separate list of attributes. 406 407 .. sourcecode:: jinja 408 409 {% for user in users|sort(attribute="age,name") %} 410 ... 411 {% endfor %} 412 413 .. versionchanged:: 2.11.0 414 The ``attribute`` parameter can be a comma separated list of 415 attributes, e.g. ``"age,name"``. 416 417 .. versionchanged:: 2.6 418 The ``attribute`` parameter was added. 419 """ 420 key_func = make_multi_attrgetter( 421 environment, attribute, postprocess=ignore_case if not case_sensitive else None 422 ) 423 return sorted(value, key=key_func, reverse=reverse) 424 425 426@pass_environment 427def do_unique( 428 environment: "Environment", 429 value: "t.Iterable[V]", 430 case_sensitive: bool = False, 431 attribute: t.Optional[t.Union[str, int]] = None, 432) -> "t.Iterator[V]": 433 """Returns a list of unique items from the given iterable. 434 435 .. sourcecode:: jinja 436 437 {{ ['foo', 'bar', 'foobar', 'FooBar']|unique|list }} 438 -> ['foo', 'bar', 'foobar'] 439 440 The unique items are yielded in the same order as their first occurrence in 441 the iterable passed to the filter. 442 443 :param case_sensitive: Treat upper and lower case strings as distinct. 444 :param attribute: Filter objects with unique values for this attribute. 445 """ 446 getter = make_attrgetter( 447 environment, attribute, postprocess=ignore_case if not case_sensitive else None 448 ) 449 seen = set() 450 451 for item in value: 452 key = getter(item) 453 454 if key not in seen: 455 seen.add(key) 456 yield item 457 458 459def _min_or_max( 460 environment: "Environment", 461 value: "t.Iterable[V]", 462 func: "t.Callable[..., V]", 463 case_sensitive: bool, 464 attribute: t.Optional[t.Union[str, int]], 465) -> "t.Union[V, Undefined]": 466 it = iter(value) 467 468 try: 469 first = next(it) 470 except StopIteration: 471 return environment.undefined("No aggregated item, sequence was empty.") 472 473 key_func = make_attrgetter( 474 environment, attribute, postprocess=ignore_case if not case_sensitive else None 475 ) 476 return func(chain([first], it), key=key_func) 477 478 479@pass_environment 480def do_min( 481 environment: "Environment", 482 value: "t.Iterable[V]", 483 case_sensitive: bool = False, 484 attribute: t.Optional[t.Union[str, int]] = None, 485) -> "t.Union[V, Undefined]": 486 """Return the smallest item from the sequence. 487 488 .. sourcecode:: jinja 489 490 {{ [1, 2, 3]|min }} 491 -> 1 492 493 :param case_sensitive: Treat upper and lower case strings as distinct. 494 :param attribute: Get the object with the min value of this attribute. 495 """ 496 return _min_or_max(environment, value, min, case_sensitive, attribute) 497 498 499@pass_environment 500def do_max( 501 environment: "Environment", 502 value: "t.Iterable[V]", 503 case_sensitive: bool = False, 504 attribute: t.Optional[t.Union[str, int]] = None, 505) -> "t.Union[V, Undefined]": 506 """Return the largest item from the sequence. 507 508 .. sourcecode:: jinja 509 510 {{ [1, 2, 3]|max }} 511 -> 3 512 513 :param case_sensitive: Treat upper and lower case strings as distinct. 514 :param attribute: Get the object with the max value of this attribute. 515 """ 516 return _min_or_max(environment, value, max, case_sensitive, attribute) 517 518 519def do_default( 520 value: V, 521 default_value: V = "", # type: ignore 522 boolean: bool = False, 523) -> V: 524 """If the value is undefined it will return the passed default value, 525 otherwise the value of the variable: 526 527 .. sourcecode:: jinja 528 529 {{ my_variable|default('my_variable is not defined') }} 530 531 This will output the value of ``my_variable`` if the variable was 532 defined, otherwise ``'my_variable is not defined'``. If you want 533 to use default with variables that evaluate to false you have to 534 set the second parameter to `true`: 535 536 .. sourcecode:: jinja 537 538 {{ ''|default('the string was empty', true) }} 539 540 .. versionchanged:: 2.11 541 It's now possible to configure the :class:`~jinja2.Environment` with 542 :class:`~jinja2.ChainableUndefined` to make the `default` filter work 543 on nested elements and attributes that may contain undefined values 544 in the chain without getting an :exc:`~jinja2.UndefinedError`. 545 """ 546 if isinstance(value, Undefined) or (boolean and not value): 547 return default_value 548 549 return value 550 551 552@pass_eval_context 553def sync_do_join( 554 eval_ctx: "EvalContext", 555 value: t.Iterable, 556 d: str = "", 557 attribute: t.Optional[t.Union[str, int]] = None, 558) -> str: 559 """Return a string which is the concatenation of the strings in the 560 sequence. The separator between elements is an empty string per 561 default, you can define it with the optional parameter: 562 563 .. sourcecode:: jinja 564 565 {{ [1, 2, 3]|join('|') }} 566 -> 1|2|3 567 568 {{ [1, 2, 3]|join }} 569 -> 123 570 571 It is also possible to join certain attributes of an object: 572 573 .. sourcecode:: jinja 574 575 {{ users|join(', ', attribute='username') }} 576 577 .. versionadded:: 2.6 578 The `attribute` parameter was added. 579 """ 580 if attribute is not None: 581 value = map(make_attrgetter(eval_ctx.environment, attribute), value) 582 583 # no automatic escaping? joining is a lot easier then 584 if not eval_ctx.autoescape: 585 return str(d).join(map(str, value)) 586 587 # if the delimiter doesn't have an html representation we check 588 # if any of the items has. If yes we do a coercion to Markup 589 if not hasattr(d, "__html__"): 590 value = list(value) 591 do_escape = False 592 593 for idx, item in enumerate(value): 594 if hasattr(item, "__html__"): 595 do_escape = True 596 else: 597 value[idx] = str(item) 598 599 if do_escape: 600 d = escape(d) 601 else: 602 d = str(d) 603 604 return d.join(value) 605 606 # no html involved, to normal joining 607 return soft_str(d).join(map(soft_str, value)) 608 609 610@async_variant(sync_do_join) # type: ignore 611async def do_join( 612 eval_ctx: "EvalContext", 613 value: t.Union[t.AsyncIterable, t.Iterable], 614 d: str = "", 615 attribute: t.Optional[t.Union[str, int]] = None, 616) -> str: 617 return sync_do_join(eval_ctx, await auto_to_list(value), d, attribute) 618 619 620def do_center(value: str, width: int = 80) -> str: 621 """Centers the value in a field of a given width.""" 622 return soft_str(value).center(width) 623 624 625@pass_environment 626def sync_do_first( 627 environment: "Environment", seq: "t.Iterable[V]" 628) -> "t.Union[V, Undefined]": 629 """Return the first item of a sequence.""" 630 try: 631 return next(iter(seq)) 632 except StopIteration: 633 return environment.undefined("No first item, sequence was empty.") 634 635 636@async_variant(sync_do_first) # type: ignore 637async def do_first( 638 environment: "Environment", seq: "t.Union[t.AsyncIterable[V], t.Iterable[V]]" 639) -> "t.Union[V, Undefined]": 640 try: 641 return await auto_aiter(seq).__anext__() 642 except StopAsyncIteration: 643 return environment.undefined("No first item, sequence was empty.") 644 645 646@pass_environment 647def do_last( 648 environment: "Environment", seq: "t.Reversible[V]" 649) -> "t.Union[V, Undefined]": 650 """Return the last item of a sequence. 651 652 Note: Does not work with generators. You may want to explicitly 653 convert it to a list: 654 655 .. sourcecode:: jinja 656 657 {{ data | selectattr('name', '==', 'Jinja') | list | last }} 658 """ 659 try: 660 return next(iter(reversed(seq))) 661 except StopIteration: 662 return environment.undefined("No last item, sequence was empty.") 663 664 665# No async do_last, it may not be safe in async mode. 666 667 668@pass_context 669def do_random(context: "Context", seq: "t.Sequence[V]") -> "t.Union[V, Undefined]": 670 """Return a random item from the sequence.""" 671 try: 672 return random.choice(seq) 673 except IndexError: 674 return context.environment.undefined("No random item, sequence was empty.") 675 676 677def do_filesizeformat(value: t.Union[str, float, int], binary: bool = False) -> str: 678 """Format the value like a 'human-readable' file size (i.e. 13 kB, 679 4.1 MB, 102 Bytes, etc). Per default decimal prefixes are used (Mega, 680 Giga, etc.), if the second parameter is set to `True` the binary 681 prefixes are used (Mebi, Gibi). 682 """ 683 bytes = float(value) 684 base = 1024 if binary else 1000 685 prefixes = [ 686 ("KiB" if binary else "kB"), 687 ("MiB" if binary else "MB"), 688 ("GiB" if binary else "GB"), 689 ("TiB" if binary else "TB"), 690 ("PiB" if binary else "PB"), 691 ("EiB" if binary else "EB"), 692 ("ZiB" if binary else "ZB"), 693 ("YiB" if binary else "YB"), 694 ] 695 696 if bytes == 1: 697 return "1 Byte" 698 elif bytes < base: 699 return f"{int(bytes)} Bytes" 700 else: 701 for i, prefix in enumerate(prefixes): 702 unit = base ** (i + 2) 703 704 if bytes < unit: 705 return f"{base * bytes / unit:.1f} {prefix}" 706 707 return f"{base * bytes / unit:.1f} {prefix}" 708 709 710def do_pprint(value: t.Any) -> str: 711 """Pretty print a variable. Useful for debugging.""" 712 return pformat(value) 713 714 715_uri_scheme_re = re.compile(r"^([\w.+-]{2,}:(/){0,2})$") 716 717 718@pass_eval_context 719def do_urlize( 720 eval_ctx: "EvalContext", 721 value: str, 722 trim_url_limit: t.Optional[int] = None, 723 nofollow: bool = False, 724 target: t.Optional[str] = None, 725 rel: t.Optional[str] = None, 726 extra_schemes: t.Optional[t.Iterable[str]] = None, 727) -> str: 728 """Convert URLs in text into clickable links. 729 730 This may not recognize links in some situations. Usually, a more 731 comprehensive formatter, such as a Markdown library, is a better 732 choice. 733 734 Works on ``http://``, ``https://``, ``www.``, ``mailto:``, and email 735 addresses. Links with trailing punctuation (periods, commas, closing 736 parentheses) and leading punctuation (opening parentheses) are 737 recognized excluding the punctuation. Email addresses that include 738 header fields are not recognized (for example, 739 ``mailto:address@example.com?cc=copy@example.com``). 740 741 :param value: Original text containing URLs to link. 742 :param trim_url_limit: Shorten displayed URL values to this length. 743 :param nofollow: Add the ``rel=nofollow`` attribute to links. 744 :param target: Add the ``target`` attribute to links. 745 :param rel: Add the ``rel`` attribute to links. 746 :param extra_schemes: Recognize URLs that start with these schemes 747 in addition to the default behavior. Defaults to 748 ``env.policies["urlize.extra_schemes"]``, which defaults to no 749 extra schemes. 750 751 .. versionchanged:: 3.0 752 The ``extra_schemes`` parameter was added. 753 754 .. versionchanged:: 3.0 755 Generate ``https://`` links for URLs without a scheme. 756 757 .. versionchanged:: 3.0 758 The parsing rules were updated. Recognize email addresses with 759 or without the ``mailto:`` scheme. Validate IP addresses. Ignore 760 parentheses and brackets in more cases. 761 762 .. versionchanged:: 2.8 763 The ``target`` parameter was added. 764 """ 765 policies = eval_ctx.environment.policies 766 rel_parts = set((rel or "").split()) 767 768 if nofollow: 769 rel_parts.add("nofollow") 770 771 rel_parts.update((policies["urlize.rel"] or "").split()) 772 rel = " ".join(sorted(rel_parts)) or None 773 774 if target is None: 775 target = policies["urlize.target"] 776 777 if extra_schemes is None: 778 extra_schemes = policies["urlize.extra_schemes"] or () 779 780 for scheme in extra_schemes: 781 if _uri_scheme_re.fullmatch(scheme) is None: 782 raise FilterArgumentError(f"{scheme!r} is not a valid URI scheme prefix.") 783 784 rv = urlize( 785 value, 786 trim_url_limit=trim_url_limit, 787 rel=rel, 788 target=target, 789 extra_schemes=extra_schemes, 790 ) 791 792 if eval_ctx.autoescape: 793 rv = Markup(rv) 794 795 return rv 796 797 798def do_indent( 799 s: str, width: t.Union[int, str] = 4, first: bool = False, blank: bool = False 800) -> str: 801 """Return a copy of the string with each line indented by 4 spaces. The 802 first line and blank lines are not indented by default. 803 804 :param width: Number of spaces, or a string, to indent by. 805 :param first: Don't skip indenting the first line. 806 :param blank: Don't skip indenting empty lines. 807 808 .. versionchanged:: 3.0 809 ``width`` can be a string. 810 811 .. versionchanged:: 2.10 812 Blank lines are not indented by default. 813 814 Rename the ``indentfirst`` argument to ``first``. 815 """ 816 if isinstance(width, str): 817 indention = width 818 else: 819 indention = " " * width 820 821 newline = "\n" 822 823 if isinstance(s, Markup): 824 indention = Markup(indention) 825 newline = Markup(newline) 826 827 s += newline # this quirk is necessary for splitlines method 828 829 if blank: 830 rv = (newline + indention).join(s.splitlines()) 831 else: 832 lines = s.splitlines() 833 rv = lines.pop(0) 834 835 if lines: 836 rv += newline + newline.join( 837 indention + line if line else line for line in lines 838 ) 839 840 if first: 841 rv = indention + rv 842 843 return rv 844 845 846@pass_environment 847def do_truncate( 848 env: "Environment", 849 s: str, 850 length: int = 255, 851 killwords: bool = False, 852 end: str = "...", 853 leeway: t.Optional[int] = None, 854) -> str: 855 """Return a truncated copy of the string. The length is specified 856 with the first parameter which defaults to ``255``. If the second 857 parameter is ``true`` the filter will cut the text at length. Otherwise 858 it will discard the last word. If the text was in fact 859 truncated it will append an ellipsis sign (``"..."``). If you want a 860 different ellipsis sign than ``"..."`` you can specify it using the 861 third parameter. Strings that only exceed the length by the tolerance 862 margin given in the fourth parameter will not be truncated. 863 864 .. sourcecode:: jinja 865 866 {{ "foo bar baz qux"|truncate(9) }} 867 -> "foo..." 868 {{ "foo bar baz qux"|truncate(9, True) }} 869 -> "foo ba..." 870 {{ "foo bar baz qux"|truncate(11) }} 871 -> "foo bar baz qux" 872 {{ "foo bar baz qux"|truncate(11, False, '...', 0) }} 873 -> "foo bar..." 874 875 The default leeway on newer Jinja versions is 5 and was 0 before but 876 can be reconfigured globally. 877 """ 878 if leeway is None: 879 leeway = env.policies["truncate.leeway"] 880 881 assert length >= len(end), f"expected length >= {len(end)}, got {length}" 882 assert leeway >= 0, f"expected leeway >= 0, got {leeway}" 883 884 if len(s) <= length + leeway: 885 return s 886 887 if killwords: 888 return s[: length - len(end)] + end 889 890 result = s[: length - len(end)].rsplit(" ", 1)[0] 891 return result + end 892 893 894@pass_environment 895def do_wordwrap( 896 environment: "Environment", 897 s: str, 898 width: int = 79, 899 break_long_words: bool = True, 900 wrapstring: t.Optional[str] = None, 901 break_on_hyphens: bool = True, 902) -> str: 903 """Wrap a string to the given width. Existing newlines are treated 904 as paragraphs to be wrapped separately. 905 906 :param s: Original text to wrap. 907 :param width: Maximum length of wrapped lines. 908 :param break_long_words: If a word is longer than ``width``, break 909 it across lines. 910 :param break_on_hyphens: If a word contains hyphens, it may be split 911 across lines. 912 :param wrapstring: String to join each wrapped line. Defaults to 913 :attr:`Environment.newline_sequence`. 914 915 .. versionchanged:: 2.11 916 Existing newlines are treated as paragraphs wrapped separately. 917 918 .. versionchanged:: 2.11 919 Added the ``break_on_hyphens`` parameter. 920 921 .. versionchanged:: 2.7 922 Added the ``wrapstring`` parameter. 923 """ 924 import textwrap 925 926 if wrapstring is None: 927 wrapstring = environment.newline_sequence 928 929 # textwrap.wrap doesn't consider existing newlines when wrapping. 930 # If the string has a newline before width, wrap will still insert 931 # a newline at width, resulting in a short line. Instead, split and 932 # wrap each paragraph individually. 933 return wrapstring.join( 934 [ 935 wrapstring.join( 936 textwrap.wrap( 937 line, 938 width=width, 939 expand_tabs=False, 940 replace_whitespace=False, 941 break_long_words=break_long_words, 942 break_on_hyphens=break_on_hyphens, 943 ) 944 ) 945 for line in s.splitlines() 946 ] 947 ) 948 949 950_word_re = re.compile(r"\w+") 951 952 953def do_wordcount(s: str) -> int: 954 """Count the words in that string.""" 955 return len(_word_re.findall(soft_str(s))) 956 957 958def do_int(value: t.Any, default: int = 0, base: int = 10) -> int: 959 """Convert the value into an integer. If the 960 conversion doesn't work it will return ``0``. You can 961 override this default using the first parameter. You 962 can also override the default base (10) in the second 963 parameter, which handles input with prefixes such as 964 0b, 0o and 0x for bases 2, 8 and 16 respectively. 965 The base is ignored for decimal numbers and non-string values. 966 """ 967 try: 968 if isinstance(value, str): 969 return int(value, base) 970 971 return int(value) 972 except (TypeError, ValueError): 973 # this quirk is necessary so that "42.23"|int gives 42. 974 try: 975 return int(float(value)) 976 except (TypeError, ValueError): 977 return default 978 979 980def do_float(value: t.Any, default: float = 0.0) -> float: 981 """Convert the value into a floating point number. If the 982 conversion doesn't work it will return ``0.0``. You can 983 override this default using the first parameter. 984 """ 985 try: 986 return float(value) 987 except (TypeError, ValueError): 988 return default 989 990 991def do_format(value: str, *args: t.Any, **kwargs: t.Any) -> str: 992 """Apply the given values to a `printf-style`_ format string, like 993 ``string % values``. 994 995 .. sourcecode:: jinja 996 997 {{ "%s, %s!"|format(greeting, name) }} 998 Hello, World! 999 1000 In most cases it should be more convenient and efficient to use the 1001 ``%`` operator or :meth:`str.format`. 1002 1003 .. code-block:: text 1004 1005 {{ "%s, %s!" % (greeting, name) }} 1006 {{ "{}, {}!".format(greeting, name) }} 1007 1008 .. _printf-style: https://docs.python.org/library/stdtypes.html 1009 #printf-style-string-formatting 1010 """ 1011 if args and kwargs: 1012 raise FilterArgumentError( 1013 "can't handle positional and keyword arguments at the same time" 1014 ) 1015 1016 return soft_str(value) % (kwargs or args) 1017 1018 1019def do_trim(value: str, chars: t.Optional[str] = None) -> str: 1020 """Strip leading and trailing characters, by default whitespace.""" 1021 return soft_str(value).strip(chars) 1022 1023 1024def do_striptags(value: "t.Union[str, HasHTML]") -> str: 1025 """Strip SGML/XML tags and replace adjacent whitespace by one space.""" 1026 if hasattr(value, "__html__"): 1027 value = t.cast("HasHTML", value).__html__() 1028 1029 return Markup(str(value)).striptags() 1030 1031 1032def sync_do_slice( 1033 value: "t.Collection[V]", slices: int, fill_with: "t.Optional[V]" = None 1034) -> "t.Iterator[t.List[V]]": 1035 """Slice an iterator and return a list of lists containing 1036 those items. Useful if you want to create a div containing 1037 three ul tags that represent columns: 1038 1039 .. sourcecode:: html+jinja 1040 1041 <div class="columnwrapper"> 1042 {%- for column in items|slice(3) %} 1043 <ul class="column-{{ loop.index }}"> 1044 {%- for item in column %} 1045 <li>{{ item }}</li> 1046 {%- endfor %} 1047 </ul> 1048 {%- endfor %} 1049 </div> 1050 1051 If you pass it a second argument it's used to fill missing 1052 values on the last iteration. 1053 """ 1054 seq = list(value) 1055 length = len(seq) 1056 items_per_slice = length // slices 1057 slices_with_extra = length % slices 1058 offset = 0 1059 1060 for slice_number in range(slices): 1061 start = offset + slice_number * items_per_slice 1062 1063 if slice_number < slices_with_extra: 1064 offset += 1 1065 1066 end = offset + (slice_number + 1) * items_per_slice 1067 tmp = seq[start:end] 1068 1069 if fill_with is not None and slice_number >= slices_with_extra: 1070 tmp.append(fill_with) 1071 1072 yield tmp 1073 1074 1075@async_variant(sync_do_slice) # type: ignore 1076async def do_slice( 1077 value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", 1078 slices: int, 1079 fill_with: t.Optional[t.Any] = None, 1080) -> "t.Iterator[t.List[V]]": 1081 return sync_do_slice(await auto_to_list(value), slices, fill_with) 1082 1083 1084def do_batch( 1085 value: "t.Iterable[V]", linecount: int, fill_with: "t.Optional[V]" = None 1086) -> "t.Iterator[t.List[V]]": 1087 """ 1088 A filter that batches items. It works pretty much like `slice` 1089 just the other way round. It returns a list of lists with the 1090 given number of items. If you provide a second parameter this 1091 is used to fill up missing items. See this example: 1092 1093 .. sourcecode:: html+jinja 1094 1095 <table> 1096 {%- for row in items|batch(3, ' ') %} 1097 <tr> 1098 {%- for column in row %} 1099 <td>{{ column }}</td> 1100 {%- endfor %} 1101 </tr> 1102 {%- endfor %} 1103 </table> 1104 """ 1105 tmp: "t.List[V]" = [] 1106 1107 for item in value: 1108 if len(tmp) == linecount: 1109 yield tmp 1110 tmp = [] 1111 1112 tmp.append(item) 1113 1114 if tmp: 1115 if fill_with is not None and len(tmp) < linecount: 1116 tmp += [fill_with] * (linecount - len(tmp)) 1117 1118 yield tmp 1119 1120 1121def do_round( 1122 value: float, 1123 precision: int = 0, 1124 method: 'te.Literal["common", "ceil", "floor"]' = "common", 1125) -> float: 1126 """Round the number to a given precision. The first 1127 parameter specifies the precision (default is ``0``), the 1128 second the rounding method: 1129 1130 - ``'common'`` rounds either up or down 1131 - ``'ceil'`` always rounds up 1132 - ``'floor'`` always rounds down 1133 1134 If you don't specify a method ``'common'`` is used. 1135 1136 .. sourcecode:: jinja 1137 1138 {{ 42.55|round }} 1139 -> 43.0 1140 {{ 42.55|round(1, 'floor') }} 1141 -> 42.5 1142 1143 Note that even if rounded to 0 precision, a float is returned. If 1144 you need a real integer, pipe it through `int`: 1145 1146 .. sourcecode:: jinja 1147 1148 {{ 42.55|round|int }} 1149 -> 43 1150 """ 1151 if method not in {"common", "ceil", "floor"}: 1152 raise FilterArgumentError("method must be common, ceil or floor") 1153 1154 if method == "common": 1155 return round(value, precision) 1156 1157 func = getattr(math, method) 1158 return t.cast(float, func(value * (10**precision)) / (10**precision)) 1159 1160 1161class _GroupTuple(t.NamedTuple): 1162 grouper: t.Any 1163 list: t.List 1164 1165 # Use the regular tuple repr to hide this subclass if users print 1166 # out the value during debugging. 1167 def __repr__(self) -> str: 1168 return tuple.__repr__(self) 1169 1170 def __str__(self) -> str: 1171 return tuple.__str__(self) 1172 1173 1174@pass_environment 1175def sync_do_groupby( 1176 environment: "Environment", 1177 value: "t.Iterable[V]", 1178 attribute: t.Union[str, int], 1179 default: t.Optional[t.Any] = None, 1180 case_sensitive: bool = False, 1181) -> "t.List[_GroupTuple]": 1182 """Group a sequence of objects by an attribute using Python's 1183 :func:`itertools.groupby`. The attribute can use dot notation for 1184 nested access, like ``"address.city"``. Unlike Python's ``groupby``, 1185 the values are sorted first so only one group is returned for each 1186 unique value. 1187 1188 For example, a list of ``User`` objects with a ``city`` attribute 1189 can be rendered in groups. In this example, ``grouper`` refers to 1190 the ``city`` value of the group. 1191 1192 .. sourcecode:: html+jinja 1193 1194 <ul>{% for city, items in users|groupby("city") %} 1195 <li>{{ city }} 1196 <ul>{% for user in items %} 1197 <li>{{ user.name }} 1198 {% endfor %}</ul> 1199 </li> 1200 {% endfor %}</ul> 1201 1202 ``groupby`` yields namedtuples of ``(grouper, list)``, which 1203 can be used instead of the tuple unpacking above. ``grouper`` is the 1204 value of the attribute, and ``list`` is the items with that value. 1205 1206 .. sourcecode:: html+jinja 1207 1208 <ul>{% for group in users|groupby("city") %} 1209 <li>{{ group.grouper }}: {{ group.list|join(", ") }} 1210 {% endfor %}</ul> 1211 1212 You can specify a ``default`` value to use if an object in the list 1213 does not have the given attribute. 1214 1215 .. sourcecode:: jinja 1216 1217 <ul>{% for city, items in users|groupby("city", default="NY") %} 1218 <li>{{ city }}: {{ items|map(attribute="name")|join(", ") }}</li> 1219 {% endfor %}</ul> 1220 1221 Like the :func:`~jinja-filters.sort` filter, sorting and grouping is 1222 case-insensitive by default. The ``key`` for each group will have 1223 the case of the first item in that group of values. For example, if 1224 a list of users has cities ``["CA", "NY", "ca"]``, the "CA" group 1225 will have two values. This can be disabled by passing 1226 ``case_sensitive=True``. 1227 1228 .. versionchanged:: 3.1 1229 Added the ``case_sensitive`` parameter. Sorting and grouping is 1230 case-insensitive by default, matching other filters that do 1231 comparisons. 1232 1233 .. versionchanged:: 3.0 1234 Added the ``default`` parameter. 1235 1236 .. versionchanged:: 2.6 1237 The attribute supports dot notation for nested access. 1238 """ 1239 expr = make_attrgetter( 1240 environment, 1241 attribute, 1242 postprocess=ignore_case if not case_sensitive else None, 1243 default=default, 1244 ) 1245 out = [ 1246 _GroupTuple(key, list(values)) 1247 for key, values in groupby(sorted(value, key=expr), expr) 1248 ] 1249 1250 if not case_sensitive: 1251 # Return the real key from the first value instead of the lowercase key. 1252 output_expr = make_attrgetter(environment, attribute, default=default) 1253 out = [_GroupTuple(output_expr(values[0]), values) for _, values in out] 1254 1255 return out 1256 1257 1258@async_variant(sync_do_groupby) # type: ignore 1259async def do_groupby( 1260 environment: "Environment", 1261 value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", 1262 attribute: t.Union[str, int], 1263 default: t.Optional[t.Any] = None, 1264 case_sensitive: bool = False, 1265) -> "t.List[_GroupTuple]": 1266 expr = make_attrgetter( 1267 environment, 1268 attribute, 1269 postprocess=ignore_case if not case_sensitive else None, 1270 default=default, 1271 ) 1272 out = [ 1273 _GroupTuple(key, await auto_to_list(values)) 1274 for key, values in groupby(sorted(await auto_to_list(value), key=expr), expr) 1275 ] 1276 1277 if not case_sensitive: 1278 # Return the real key from the first value instead of the lowercase key. 1279 output_expr = make_attrgetter(environment, attribute, default=default) 1280 out = [_GroupTuple(output_expr(values[0]), values) for _, values in out] 1281 1282 return out 1283 1284 1285@pass_environment 1286def sync_do_sum( 1287 environment: "Environment", 1288 iterable: "t.Iterable[V]", 1289 attribute: t.Optional[t.Union[str, int]] = None, 1290 start: V = 0, # type: ignore 1291) -> V: 1292 """Returns the sum of a sequence of numbers plus the value of parameter 1293 'start' (which defaults to 0). When the sequence is empty it returns 1294 start. 1295 1296 It is also possible to sum up only certain attributes: 1297 1298 .. sourcecode:: jinja 1299 1300 Total: {{ items|sum(attribute='price') }} 1301 1302 .. versionchanged:: 2.6 1303 The ``attribute`` parameter was added to allow summing up over 1304 attributes. Also the ``start`` parameter was moved on to the right. 1305 """ 1306 if attribute is not None: 1307 iterable = map(make_attrgetter(environment, attribute), iterable) 1308 1309 return sum(iterable, start) # type: ignore[no-any-return, call-overload] 1310 1311 1312@async_variant(sync_do_sum) # type: ignore 1313async def do_sum( 1314 environment: "Environment", 1315 iterable: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", 1316 attribute: t.Optional[t.Union[str, int]] = None, 1317 start: V = 0, # type: ignore 1318) -> V: 1319 rv = start 1320 1321 if attribute is not None: 1322 func = make_attrgetter(environment, attribute) 1323 else: 1324 1325 def func(x: V) -> V: 1326 return x 1327 1328 async for item in auto_aiter(iterable): 1329 rv += func(item) 1330 1331 return rv 1332 1333 1334def sync_do_list(value: "t.Iterable[V]") -> "t.List[V]": 1335 """Convert the value into a list. If it was a string the returned list 1336 will be a list of characters. 1337 """ 1338 return list(value) 1339 1340 1341@async_variant(sync_do_list) # type: ignore 1342async def do_list(value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]") -> "t.List[V]": 1343 return await auto_to_list(value) 1344 1345 1346def do_mark_safe(value: str) -> Markup: 1347 """Mark the value as safe which means that in an environment with automatic 1348 escaping enabled this variable will not be escaped. 1349 """ 1350 return Markup(value) 1351 1352 1353def do_mark_unsafe(value: str) -> str: 1354 """Mark a value as unsafe. This is the reverse operation for :func:`safe`.""" 1355 return str(value) 1356 1357 1358@typing.overload 1359def do_reverse(value: str) -> str: 1360 ... 1361 1362 1363@typing.overload 1364def do_reverse(value: "t.Iterable[V]") -> "t.Iterable[V]": 1365 ... 1366 1367 1368def do_reverse(value: t.Union[str, t.Iterable[V]]) -> t.Union[str, t.Iterable[V]]: 1369 """Reverse the object or return an iterator that iterates over it the other 1370 way round. 1371 """ 1372 if isinstance(value, str): 1373 return value[::-1] 1374 1375 try: 1376 return reversed(value) # type: ignore 1377 except TypeError: 1378 try: 1379 rv = list(value) 1380 rv.reverse() 1381 return rv 1382 except TypeError as e: 1383 raise FilterArgumentError("argument must be iterable") from e 1384 1385 1386@pass_environment 1387def do_attr( 1388 environment: "Environment", obj: t.Any, name: str 1389) -> t.Union[Undefined, t.Any]: 1390 """Get an attribute of an object. ``foo|attr("bar")`` works like 1391 ``foo.bar`` just that always an attribute is returned and items are not 1392 looked up. 1393 1394 See :ref:`Notes on subscriptions <notes-on-subscriptions>` for more details. 1395 """ 1396 try: 1397 name = str(name) 1398 except UnicodeError: 1399 pass 1400 else: 1401 try: 1402 value = getattr(obj, name) 1403 except AttributeError: 1404 pass 1405 else: 1406 if environment.sandboxed: 1407 environment = t.cast("SandboxedEnvironment", environment) 1408 1409 if not environment.is_safe_attribute(obj, name, value): 1410 return environment.unsafe_undefined(obj, name) 1411 1412 return value 1413 1414 return environment.undefined(obj=obj, name=name) 1415 1416 1417@typing.overload 1418def sync_do_map( 1419 context: "Context", value: t.Iterable, name: str, *args: t.Any, **kwargs: t.Any 1420) -> t.Iterable: 1421 ... 1422 1423 1424@typing.overload 1425def sync_do_map( 1426 context: "Context", 1427 value: t.Iterable, 1428 *, 1429 attribute: str = ..., 1430 default: t.Optional[t.Any] = None, 1431) -> t.Iterable: 1432 ... 1433 1434 1435@pass_context 1436def sync_do_map( 1437 context: "Context", value: t.Iterable, *args: t.Any, **kwargs: t.Any 1438) -> t.Iterable: 1439 """Applies a filter on a sequence of objects or looks up an attribute. 1440 This is useful when dealing with lists of objects but you are really 1441 only interested in a certain value of it. 1442 1443 The basic usage is mapping on an attribute. Imagine you have a list 1444 of users but you are only interested in a list of usernames: 1445 1446 .. sourcecode:: jinja 1447 1448 Users on this page: {{ users|map(attribute='username')|join(', ') }} 1449 1450 You can specify a ``default`` value to use if an object in the list 1451 does not have the given attribute. 1452 1453 .. sourcecode:: jinja 1454 1455 {{ users|map(attribute="username", default="Anonymous")|join(", ") }} 1456 1457 Alternatively you can let it invoke a filter by passing the name of the 1458 filter and the arguments afterwards. A good example would be applying a 1459 text conversion filter on a sequence: 1460 1461 .. sourcecode:: jinja 1462 1463 Users on this page: {{ titles|map('lower')|join(', ') }} 1464 1465 Similar to a generator comprehension such as: 1466 1467 .. code-block:: python 1468 1469 (u.username for u in users) 1470 (getattr(u, "username", "Anonymous") for u in users) 1471 (do_lower(x) for x in titles) 1472 1473 .. versionchanged:: 2.11.0 1474 Added the ``default`` parameter. 1475 1476 .. versionadded:: 2.7 1477 """ 1478 if value: 1479 func = prepare_map(context, args, kwargs) 1480 1481 for item in value: 1482 yield func(item) 1483 1484 1485@typing.overload 1486def do_map( 1487 context: "Context", 1488 value: t.Union[t.AsyncIterable, t.Iterable], 1489 name: str, 1490 *args: t.Any, 1491 **kwargs: t.Any, 1492) -> t.Iterable: 1493 ... 1494 1495 1496@typing.overload 1497def do_map( 1498 context: "Context", 1499 value: t.Union[t.AsyncIterable, t.Iterable], 1500 *, 1501 attribute: str = ..., 1502 default: t.Optional[t.Any] = None, 1503) -> t.Iterable: 1504 ... 1505 1506 1507@async_variant(sync_do_map) # type: ignore 1508async def do_map( 1509 context: "Context", 1510 value: t.Union[t.AsyncIterable, t.Iterable], 1511 *args: t.Any, 1512 **kwargs: t.Any, 1513) -> t.AsyncIterable: 1514 if value: 1515 func = prepare_map(context, args, kwargs) 1516 1517 async for item in auto_aiter(value): 1518 yield await auto_await(func(item)) 1519 1520 1521@pass_context 1522def sync_do_select( 1523 context: "Context", value: "t.Iterable[V]", *args: t.Any, **kwargs: t.Any 1524) -> "t.Iterator[V]": 1525 """Filters a sequence of objects by applying a test to each object, 1526 and only selecting the objects with the test succeeding. 1527 1528 If no test is specified, each object will be evaluated as a boolean. 1529 1530 Example usage: 1531 1532 .. sourcecode:: jinja 1533 1534 {{ numbers|select("odd") }} 1535 {{ numbers|select("odd") }} 1536 {{ numbers|select("divisibleby", 3) }} 1537 {{ numbers|select("lessthan", 42) }} 1538 {{ strings|select("equalto", "mystring") }} 1539 1540 Similar to a generator comprehension such as: 1541 1542 .. code-block:: python 1543 1544 (n for n in numbers if test_odd(n)) 1545 (n for n in numbers if test_divisibleby(n, 3)) 1546 1547 .. versionadded:: 2.7 1548 """ 1549 return select_or_reject(context, value, args, kwargs, lambda x: x, False) 1550 1551 1552@async_variant(sync_do_select) # type: ignore 1553async def do_select( 1554 context: "Context", 1555 value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", 1556 *args: t.Any, 1557 **kwargs: t.Any, 1558) -> "t.AsyncIterator[V]": 1559 return async_select_or_reject(context, value, args, kwargs, lambda x: x, False) 1560 1561 1562@pass_context 1563def sync_do_reject( 1564 context: "Context", value: "t.Iterable[V]", *args: t.Any, **kwargs: t.Any 1565) -> "t.Iterator[V]": 1566 """Filters a sequence of objects by applying a test to each object, 1567 and rejecting the objects with the test succeeding. 1568 1569 If no test is specified, each object will be evaluated as a boolean. 1570 1571 Example usage: 1572 1573 .. sourcecode:: jinja 1574 1575 {{ numbers|reject("odd") }} 1576 1577 Similar to a generator comprehension such as: 1578 1579 .. code-block:: python 1580 1581 (n for n in numbers if not test_odd(n)) 1582 1583 .. versionadded:: 2.7 1584 """ 1585 return select_or_reject(context, value, args, kwargs, lambda x: not x, False) 1586 1587 1588@async_variant(sync_do_reject) # type: ignore 1589async def do_reject( 1590 context: "Context", 1591 value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", 1592 *args: t.Any, 1593 **kwargs: t.Any, 1594) -> "t.AsyncIterator[V]": 1595 return async_select_or_reject(context, value, args, kwargs, lambda x: not x, False) 1596 1597 1598@pass_context 1599def sync_do_selectattr( 1600 context: "Context", value: "t.Iterable[V]", *args: t.Any, **kwargs: t.Any 1601) -> "t.Iterator[V]": 1602 """Filters a sequence of objects by applying a test to the specified 1603 attribute of each object, and only selecting the objects with the 1604 test succeeding. 1605 1606 If no test is specified, the attribute's value will be evaluated as 1607 a boolean. 1608 1609 Example usage: 1610 1611 .. sourcecode:: jinja 1612 1613 {{ users|selectattr("is_active") }} 1614 {{ users|selectattr("email", "none") }} 1615 1616 Similar to a generator comprehension such as: 1617 1618 .. code-block:: python 1619 1620 (u for user in users if user.is_active) 1621 (u for user in users if test_none(user.email)) 1622 1623 .. versionadded:: 2.7 1624 """ 1625 return select_or_reject(context, value, args, kwargs, lambda x: x, True) 1626 1627 1628@async_variant(sync_do_selectattr) # type: ignore 1629async def do_selectattr( 1630 context: "Context", 1631 value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", 1632 *args: t.Any, 1633 **kwargs: t.Any, 1634) -> "t.AsyncIterator[V]": 1635 return async_select_or_reject(context, value, args, kwargs, lambda x: x, True) 1636 1637 1638@pass_context 1639def sync_do_rejectattr( 1640 context: "Context", value: "t.Iterable[V]", *args: t.Any, **kwargs: t.Any 1641) -> "t.Iterator[V]": 1642 """Filters a sequence of objects by applying a test to the specified 1643 attribute of each object, and rejecting the objects with the test 1644 succeeding. 1645 1646 If no test is specified, the attribute's value will be evaluated as 1647 a boolean. 1648 1649 .. sourcecode:: jinja 1650 1651 {{ users|rejectattr("is_active") }} 1652 {{ users|rejectattr("email", "none") }} 1653 1654 Similar to a generator comprehension such as: 1655 1656 .. code-block:: python 1657 1658 (u for user in users if not user.is_active) 1659 (u for user in users if not test_none(user.email)) 1660 1661 .. versionadded:: 2.7 1662 """ 1663 return select_or_reject(context, value, args, kwargs, lambda x: not x, True) 1664 1665 1666@async_variant(sync_do_rejectattr) # type: ignore 1667async def do_rejectattr( 1668 context: "Context", 1669 value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", 1670 *args: t.Any, 1671 **kwargs: t.Any, 1672) -> "t.AsyncIterator[V]": 1673 return async_select_or_reject(context, value, args, kwargs, lambda x: not x, True) 1674 1675 1676@pass_eval_context 1677def do_tojson( 1678 eval_ctx: "EvalContext", value: t.Any, indent: t.Optional[int] = None 1679) -> Markup: 1680 """Serialize an object to a string of JSON, and mark it safe to 1681 render in HTML. This filter is only for use in HTML documents. 1682 1683 The returned string is safe to render in HTML documents and 1684 ``<script>`` tags. The exception is in HTML attributes that are 1685 double quoted; either use single quotes or the ``|forceescape`` 1686 filter. 1687 1688 :param value: The object to serialize to JSON. 1689 :param indent: The ``indent`` parameter passed to ``dumps``, for 1690 pretty-printing the value. 1691 1692 .. versionadded:: 2.9 1693 """ 1694 policies = eval_ctx.environment.policies 1695 dumps = policies["json.dumps_function"] 1696 kwargs = policies["json.dumps_kwargs"] 1697 1698 if indent is not None: 1699 kwargs = kwargs.copy() 1700 kwargs["indent"] = indent 1701 1702 return htmlsafe_json_dumps(value, dumps=dumps, **kwargs) 1703 1704 1705def prepare_map( 1706 context: "Context", args: t.Tuple, kwargs: t.Dict[str, t.Any] 1707) -> t.Callable[[t.Any], t.Any]: 1708 if not args and "attribute" in kwargs: 1709 attribute = kwargs.pop("attribute") 1710 default = kwargs.pop("default", None) 1711 1712 if kwargs: 1713 raise FilterArgumentError( 1714 f"Unexpected keyword argument {next(iter(kwargs))!r}" 1715 ) 1716 1717 func = make_attrgetter(context.environment, attribute, default=default) 1718 else: 1719 try: 1720 name = args[0] 1721 args = args[1:] 1722 except LookupError: 1723 raise FilterArgumentError("map requires a filter argument") from None 1724 1725 def func(item: t.Any) -> t.Any: 1726 return context.environment.call_filter( 1727 name, item, args, kwargs, context=context 1728 ) 1729 1730 return func 1731 1732 1733def prepare_select_or_reject( 1734 context: "Context", 1735 args: t.Tuple, 1736 kwargs: t.Dict[str, t.Any], 1737 modfunc: t.Callable[[t.Any], t.Any], 1738 lookup_attr: bool, 1739) -> t.Callable[[t.Any], t.Any]: 1740 if lookup_attr: 1741 try: 1742 attr = args[0] 1743 except LookupError: 1744 raise FilterArgumentError("Missing parameter for attribute name") from None 1745 1746 transfunc = make_attrgetter(context.environment, attr) 1747 off = 1 1748 else: 1749 off = 0 1750 1751 def transfunc(x: V) -> V: 1752 return x 1753 1754 try: 1755 name = args[off] 1756 args = args[1 + off :] 1757 1758 def func(item: t.Any) -> t.Any: 1759 return context.environment.call_test(name, item, args, kwargs) 1760 1761 except LookupError: 1762 func = bool # type: ignore 1763 1764 return lambda item: modfunc(func(transfunc(item))) 1765 1766 1767def select_or_reject( 1768 context: "Context", 1769 value: "t.Iterable[V]", 1770 args: t.Tuple, 1771 kwargs: t.Dict[str, t.Any], 1772 modfunc: t.Callable[[t.Any], t.Any], 1773 lookup_attr: bool, 1774) -> "t.Iterator[V]": 1775 if value: 1776 func = prepare_select_or_reject(context, args, kwargs, modfunc, lookup_attr) 1777 1778 for item in value: 1779 if func(item): 1780 yield item 1781 1782 1783async def async_select_or_reject( 1784 context: "Context", 1785 value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", 1786 args: t.Tuple, 1787 kwargs: t.Dict[str, t.Any], 1788 modfunc: t.Callable[[t.Any], t.Any], 1789 lookup_attr: bool, 1790) -> "t.AsyncIterator[V]": 1791 if value: 1792 func = prepare_select_or_reject(context, args, kwargs, modfunc, lookup_attr) 1793 1794 async for item in auto_aiter(value): 1795 if func(item): 1796 yield item 1797 1798 1799FILTERS = { 1800 "abs": abs, 1801 "attr": do_attr, 1802 "batch": do_batch, 1803 "capitalize": do_capitalize, 1804 "center": do_center, 1805 "count": len, 1806 "d": do_default, 1807 "default": do_default, 1808 "dictsort": do_dictsort, 1809 "e": escape, 1810 "escape": escape, 1811 "filesizeformat": do_filesizeformat, 1812 "first": do_first, 1813 "float": do_float, 1814 "forceescape": do_forceescape, 1815 "format": do_format, 1816 "groupby": do_groupby, 1817 "indent": do_indent, 1818 "int": do_int, 1819 "join": do_join, 1820 "last": do_last, 1821 "length": len, 1822 "list": do_list, 1823 "lower": do_lower, 1824 "items": do_items, 1825 "map": do_map, 1826 "min": do_min, 1827 "max": do_max, 1828 "pprint": do_pprint, 1829 "random": do_random, 1830 "reject": do_reject, 1831 "rejectattr": do_rejectattr, 1832 "replace": do_replace, 1833 "reverse": do_reverse, 1834 "round": do_round, 1835 "safe": do_mark_safe, 1836 "select": do_select, 1837 "selectattr": do_selectattr, 1838 "slice": do_slice, 1839 "sort": do_sort, 1840 "string": soft_str, 1841 "striptags": do_striptags, 1842 "sum": do_sum, 1843 "title": do_title, 1844 "trim": do_trim, 1845 "truncate": do_truncate, 1846 "unique": do_unique, 1847 "upper": do_upper, 1848 "urlencode": do_urlencode, 1849 "urlize": do_urlize, 1850 "wordcount": do_wordcount, 1851 "wordwrap": do_wordwrap, 1852 "xmlattr": do_xmlattr, 1853 "tojson": do_tojson, 1854} 1855