• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, '&nbsp;') %}
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