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