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