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