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