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