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