]> jfr.im git - dlqueue.git/blob - venv/lib/python3.11/site-packages/jinja2/filters.py
init: venv aand flask
[dlqueue.git] / venv / lib / python3.11 / site-packages / jinja2 / filters.py
1 """Built-in template filters used with the ``|`` operator."""
2 import math
3 import random
4 import re
5 import typing
6 import typing as t
7 from collections import abc
8 from itertools import chain
9 from itertools import groupby
10
11 from markupsafe import escape
12 from markupsafe import Markup
13 from markupsafe import soft_str
14
15 from .async_utils import async_variant
16 from .async_utils import auto_aiter
17 from .async_utils import auto_await
18 from .async_utils import auto_to_list
19 from .exceptions import FilterArgumentError
20 from .runtime import Undefined
21 from .utils import htmlsafe_json_dumps
22 from .utils import pass_context
23 from .utils import pass_environment
24 from .utils import pass_eval_context
25 from .utils import pformat
26 from .utils import url_quote
27 from .utils import urlize
28
29 if t.TYPE_CHECKING:
30 import typing_extensions as te
31 from .environment import Environment
32 from .nodes import EvalContext
33 from .runtime import Context
34 from .sandbox import SandboxedEnvironment # noqa: F401
35
36 class HasHTML(te.Protocol):
37 def __html__(self) -> str:
38 pass
39
40
41 F = t.TypeVar("F", bound=t.Callable[..., t.Any])
42 K = t.TypeVar("K")
43 V = t.TypeVar("V")
44
45
46 def ignore_case(value: V) -> V:
47 """For use as a postprocessor for :func:`make_attrgetter`. Converts strings
48 to lowercase and returns other types as-is."""
49 if isinstance(value, str):
50 return t.cast(V, value.lower())
51
52 return value
53
54
55 def make_attrgetter(
56 environment: "Environment",
57 attribute: t.Optional[t.Union[str, int]],
58 postprocess: t.Optional[t.Callable[[t.Any], t.Any]] = None,
59 default: t.Optional[t.Any] = None,
60 ) -> t.Callable[[t.Any], t.Any]:
61 """Returns a callable that looks up the given attribute from a
62 passed object with the rules of the environment. Dots are allowed
63 to access attributes of attributes. Integer parts in paths are
64 looked up as integers.
65 """
66 parts = _prepare_attribute_parts(attribute)
67
68 def attrgetter(item: t.Any) -> t.Any:
69 for part in parts:
70 item = environment.getitem(item, part)
71
72 if default is not None and isinstance(item, Undefined):
73 item = default
74
75 if postprocess is not None:
76 item = postprocess(item)
77
78 return item
79
80 return attrgetter
81
82
83 def make_multi_attrgetter(
84 environment: "Environment",
85 attribute: t.Optional[t.Union[str, int]],
86 postprocess: t.Optional[t.Callable[[t.Any], t.Any]] = None,
87 ) -> t.Callable[[t.Any], t.List[t.Any]]:
88 """Returns a callable that looks up the given comma separated
89 attributes from a passed object with the rules of the environment.
90 Dots are allowed to access attributes of each attribute. Integer
91 parts in paths are looked up as integers.
92
93 The value returned by the returned callable is a list of extracted
94 attribute values.
95
96 Examples of attribute: "attr1,attr2", "attr1.inner1.0,attr2.inner2.0", etc.
97 """
98 if isinstance(attribute, str):
99 split: t.Sequence[t.Union[str, int, None]] = attribute.split(",")
100 else:
101 split = [attribute]
102
103 parts = [_prepare_attribute_parts(item) for item in split]
104
105 def attrgetter(item: t.Any) -> t.List[t.Any]:
106 items = [None] * len(parts)
107
108 for i, attribute_part in enumerate(parts):
109 item_i = item
110
111 for part in attribute_part:
112 item_i = environment.getitem(item_i, part)
113
114 if postprocess is not None:
115 item_i = postprocess(item_i)
116
117 items[i] = item_i
118
119 return items
120
121 return attrgetter
122
123
124 def _prepare_attribute_parts(
125 attr: t.Optional[t.Union[str, int]]
126 ) -> t.List[t.Union[str, int]]:
127 if attr is None:
128 return []
129
130 if isinstance(attr, str):
131 return [int(x) if x.isdigit() else x for x in attr.split(".")]
132
133 return [attr]
134
135
136 def do_forceescape(value: "t.Union[str, HasHTML]") -> Markup:
137 """Enforce HTML escaping. This will probably double escape variables."""
138 if hasattr(value, "__html__"):
139 value = t.cast("HasHTML", value).__html__()
140
141 return escape(str(value))
142
143
144 def do_urlencode(
145 value: t.Union[str, t.Mapping[str, t.Any], t.Iterable[t.Tuple[str, t.Any]]]
146 ) -> str:
147 """Quote data for use in a URL path or query using UTF-8.
148
149 Basic wrapper around :func:`urllib.parse.quote` when given a
150 string, or :func:`urllib.parse.urlencode` for a dict or iterable.
151
152 :param value: Data to quote. A string will be quoted directly. A
153 dict or iterable of ``(key, value)`` pairs will be joined as a
154 query string.
155
156 When given a string, "/" is not quoted. HTTP servers treat "/" and
157 "%2F" equivalently in paths. If you need quoted slashes, use the
158 ``|replace("/", "%2F")`` filter.
159
160 .. versionadded:: 2.7
161 """
162 if isinstance(value, str) or not isinstance(value, abc.Iterable):
163 return url_quote(value)
164
165 if isinstance(value, dict):
166 items: t.Iterable[t.Tuple[str, t.Any]] = value.items()
167 else:
168 items = value # type: ignore
169
170 return "&".join(
171 f"{url_quote(k, for_qs=True)}={url_quote(v, for_qs=True)}" for k, v in items
172 )
173
174
175 @pass_eval_context
176 def do_replace(
177 eval_ctx: "EvalContext", s: str, old: str, new: str, count: t.Optional[int] = None
178 ) -> str:
179 """Return a copy of the value with all occurrences of a substring
180 replaced with a new one. The first argument is the substring
181 that should be replaced, the second is the replacement string.
182 If the optional third argument ``count`` is given, only the first
183 ``count`` occurrences are replaced:
184
185 .. sourcecode:: jinja
186
187 {{ "Hello World"|replace("Hello", "Goodbye") }}
188 -> Goodbye World
189
190 {{ "aaaaargh"|replace("a", "d'oh, ", 2) }}
191 -> d'oh, d'oh, aaargh
192 """
193 if count is None:
194 count = -1
195
196 if not eval_ctx.autoescape:
197 return str(s).replace(str(old), str(new), count)
198
199 if (
200 hasattr(old, "__html__")
201 or hasattr(new, "__html__")
202 and not hasattr(s, "__html__")
203 ):
204 s = escape(s)
205 else:
206 s = soft_str(s)
207
208 return s.replace(soft_str(old), soft_str(new), count)
209
210
211 def do_upper(s: str) -> str:
212 """Convert a value to uppercase."""
213 return soft_str(s).upper()
214
215
216 def do_lower(s: str) -> str:
217 """Convert a value to lowercase."""
218 return soft_str(s).lower()
219
220
221 def do_items(value: t.Union[t.Mapping[K, V], Undefined]) -> t.Iterator[t.Tuple[K, V]]:
222 """Return an iterator over the ``(key, value)`` items of a mapping.
223
224 ``x|items`` is the same as ``x.items()``, except if ``x`` is
225 undefined an empty iterator is returned.
226
227 This filter is useful if you expect the template to be rendered with
228 an implementation of Jinja in another programming language that does
229 not have a ``.items()`` method on its mapping type.
230
231 .. code-block:: html+jinja
232
233 <dl>
234 {% for key, value in my_dict|items %}
235 <dt>{{ key }}
236 <dd>{{ value }}
237 {% endfor %}
238 </dl>
239
240 .. versionadded:: 3.1
241 """
242 if isinstance(value, Undefined):
243 return
244
245 if not isinstance(value, abc.Mapping):
246 raise TypeError("Can only get item pairs from a mapping.")
247
248 yield from value.items()
249
250
251 @pass_eval_context
252 def 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:
258
259 .. sourcecode:: html+jinja
260
261 <ul{{ {'class': 'my_list', 'missing': none,
262 'id': 'list-%d'|format(variable)}|xmlattr }}>
263 ...
264 </ul>
265
266 Results in something like this:
267
268 .. sourcecode:: html
269
270 <ul class="my_list" id="list-42">
271 ...
272 </ul>
273
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 )
282
283 if autospace and rv:
284 rv = " " + rv
285
286 if eval_ctx.autoescape:
287 rv = Markup(rv)
288
289 return rv
290
291
292 def 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()
297
298
299 _word_beginning_split_re = re.compile(r"([-\s({\[<]+)")
300
301
302 def 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 )
313
314
315 def 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.
323
324 .. sourcecode:: jinja
325
326 {% for key, value in mydict|dictsort %}
327 sort the dict by key, case insensitive
328
329 {% for key, value in mydict|dictsort(reverse=true) %}
330 sort the dict by key, case insensitive, reverse order
331
332 {% for key, value in mydict|dictsort(true) %}
333 sort the dict by key, case sensitive
334
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"')
344
345 def sort_func(item: t.Tuple[t.Any, t.Any]) -> t.Any:
346 value = item[pos]
347
348 if not case_sensitive:
349 value = ignore_case(value)
350
351 return value
352
353 return sorted(value.items(), key=sort_func, reverse=reverse)
354
355
356 @pass_environment
357 def 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`.
365
366 .. sourcecode:: jinja
367
368 {% for city in cities|sort %}
369 ...
370 {% endfor %}
371
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"``.
378
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.
382
383 .. sourcecode:: jinja
384
385 {% for user in users|sort(attribute="name")
386 |sort(reverse=true, attribute="age") %}
387 ...
388 {% endfor %}
389
390 As a shortcut to chaining when the direction is the same for all
391 attributes, pass a comma separate list of attributes.
392
393 .. sourcecode:: jinja
394
395 {% for user in users|sort(attribute="age,name") %}
396 ...
397 {% endfor %}
398
399 .. versionchanged:: 2.11.0
400 The ``attribute`` parameter can be a comma separated list of
401 attributes, e.g. ``"age,name"``.
402
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)
410
411
412 @pass_environment
413 def 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.
420
421 .. sourcecode:: jinja
422
423 {{ ['foo', 'bar', 'foobar', 'FooBar']|unique|list }}
424 -> ['foo', 'bar', 'foobar']
425
426 The unique items are yielded in the same order as their first occurrence in
427 the iterable passed to the filter.
428
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()
436
437 for item in value:
438 key = getter(item)
439
440 if key not in seen:
441 seen.add(key)
442 yield item
443
444
445 def _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)
453
454 try:
455 first = next(it)
456 except StopIteration:
457 return environment.undefined("No aggregated item, sequence was empty.")
458
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)
463
464
465 @pass_environment
466 def 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.
473
474 .. sourcecode:: jinja
475
476 {{ [1, 2, 3]|min }}
477 -> 1
478
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)
483
484
485 @pass_environment
486 def 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.
493
494 .. sourcecode:: jinja
495
496 {{ [1, 2, 3]|max }}
497 -> 3
498
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)
503
504
505 def 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:
512
513 .. sourcecode:: jinja
514
515 {{ my_variable|default('my_variable is not defined') }}
516
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`:
521
522 .. sourcecode:: jinja
523
524 {{ ''|default('the string was empty', true) }}
525
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
534
535 return value
536
537
538 @pass_eval_context
539 def 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:
548
549 .. sourcecode:: jinja
550
551 {{ [1, 2, 3]|join('|') }}
552 -> 1|2|3
553
554 {{ [1, 2, 3]|join }}
555 -> 123
556
557 It is also possible to join certain attributes of an object:
558
559 .. sourcecode:: jinja
560
561 {{ users|join(', ', attribute='username') }}
562
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)
568
569 # no automatic escaping? joining is a lot easier then
570 if not eval_ctx.autoescape:
571 return str(d).join(map(str, value))
572
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
578
579 for idx, item in enumerate(value):
580 if hasattr(item, "__html__"):
581 do_escape = True
582 else:
583 value[idx] = str(item)
584
585 if do_escape:
586 d = escape(d)
587 else:
588 d = str(d)
589
590 return d.join(value)
591
592 # no html involved, to normal joining
593 return soft_str(d).join(map(soft_str, value))
594
595
596 @async_variant(sync_do_join) # type: ignore
597 async 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)
604
605
606 def 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)
609
610
611 @pass_environment
612 def 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.")
620
621
622 @async_variant(sync_do_first) # type: ignore
623 async 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.")
630
631
632 @pass_environment
633 def do_last(
634 environment: "Environment", seq: "t.Reversible[V]"
635 ) -> "t.Union[V, Undefined]":
636 """Return the last item of a sequence.
637
638 Note: Does not work with generators. You may want to explicitly
639 convert it to a list:
640
641 .. sourcecode:: jinja
642
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.")
649
650
651 # No async do_last, it may not be safe in async mode.
652
653
654 @pass_context
655 def 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.")
661
662
663 def 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 ]
681
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)
689
690 if bytes < unit:
691 return f"{base * bytes / unit:.1f} {prefix}"
692
693 return f"{base * bytes / unit:.1f} {prefix}"
694
695
696 def do_pprint(value: t.Any) -> str:
697 """Pretty print a variable. Useful for debugging."""
698 return pformat(value)
699
700
701 _uri_scheme_re = re.compile(r"^([\w.+-]{2,}:(/){0,2})$")
702
703
704 @pass_eval_context
705 def 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.
715
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.
719
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``).
726
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.
736
737 .. versionchanged:: 3.0
738 The ``extra_schemes`` parameter was added.
739
740 .. versionchanged:: 3.0
741 Generate ``https://`` links for URLs without a scheme.
742
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.
747
748 .. versionchanged:: 2.8
749 The ``target`` parameter was added.
750 """
751 policies = eval_ctx.environment.policies
752 rel_parts = set((rel or "").split())
753
754 if nofollow:
755 rel_parts.add("nofollow")
756
757 rel_parts.update((policies["urlize.rel"] or "").split())
758 rel = " ".join(sorted(rel_parts)) or None
759
760 if target is None:
761 target = policies["urlize.target"]
762
763 if extra_schemes is None:
764 extra_schemes = policies["urlize.extra_schemes"] or ()
765
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.")
769
770 rv = urlize(
771 value,
772 trim_url_limit=trim_url_limit,
773 rel=rel,
774 target=target,
775 extra_schemes=extra_schemes,
776 )
777
778 if eval_ctx.autoescape:
779 rv = Markup(rv)
780
781 return rv
782
783
784 def 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.
789
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.
793
794 .. versionchanged:: 3.0
795 ``width`` can be a string.
796
797 .. versionchanged:: 2.10
798 Blank lines are not indented by default.
799
800 Rename the ``indentfirst`` argument to ``first``.
801 """
802 if isinstance(width, str):
803 indention = width
804 else:
805 indention = " " * width
806
807 newline = "\n"
808
809 if isinstance(s, Markup):
810 indention = Markup(indention)
811 newline = Markup(newline)
812
813 s += newline # this quirk is necessary for splitlines method
814
815 if blank:
816 rv = (newline + indention).join(s.splitlines())
817 else:
818 lines = s.splitlines()
819 rv = lines.pop(0)
820
821 if lines:
822 rv += newline + newline.join(
823 indention + line if line else line for line in lines
824 )
825
826 if first:
827 rv = indention + rv
828
829 return rv
830
831
832 @pass_environment
833 def 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.
849
850 .. sourcecode:: jinja
851
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..."
860
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"]
866
867 assert length >= len(end), f"expected length >= {len(end)}, got {length}"
868 assert leeway >= 0, f"expected leeway >= 0, got {leeway}"
869
870 if len(s) <= length + leeway:
871 return s
872
873 if killwords:
874 return s[: length - len(end)] + end
875
876 result = s[: length - len(end)].rsplit(" ", 1)[0]
877 return result + end
878
879
880 @pass_environment
881 def 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.
891
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`.
900
901 .. versionchanged:: 2.11
902 Existing newlines are treated as paragraphs wrapped separately.
903
904 .. versionchanged:: 2.11
905 Added the ``break_on_hyphens`` parameter.
906
907 .. versionchanged:: 2.7
908 Added the ``wrapstring`` parameter.
909 """
910 import textwrap
911
912 if wrapstring is None:
913 wrapstring = environment.newline_sequence
914
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 )
934
935
936 _word_re = re.compile(r"\w+")
937
938
939 def do_wordcount(s: str) -> int:
940 """Count the words in that string."""
941 return len(_word_re.findall(soft_str(s)))
942
943
944 def 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)
956
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
964
965
966 def 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
975
976
977 def 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``.
980
981 .. sourcecode:: jinja
982
983 {{ "%s, %s!"|format(greeting, name) }}
984 Hello, World!
985
986 In most cases it should be more convenient and efficient to use the
987 ``%`` operator or :meth:`str.format`.
988
989 .. code-block:: text
990
991 {{ "%s, %s!" % (greeting, name) }}
992 {{ "{}, {}!".format(greeting, name) }}
993
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 )
1001
1002 return soft_str(value) % (kwargs or args)
1003
1004
1005 def 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)
1008
1009
1010 def 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__()
1014
1015 return Markup(str(value)).striptags()
1016
1017
1018 def 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:
1024
1025 .. sourcecode:: html+jinja
1026
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>
1036
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
1045
1046 for slice_number in range(slices):
1047 start = offset + slice_number * items_per_slice
1048
1049 if slice_number < slices_with_extra:
1050 offset += 1
1051
1052 end = offset + (slice_number + 1) * items_per_slice
1053 tmp = seq[start:end]
1054
1055 if fill_with is not None and slice_number >= slices_with_extra:
1056 tmp.append(fill_with)
1057
1058 yield tmp
1059
1060
1061 @async_variant(sync_do_slice) # type: ignore
1062 async 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)
1068
1069
1070 def 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:
1078
1079 .. sourcecode:: html+jinja
1080
1081 <table>
1082 {%- for row in items|batch(3, '&nbsp;') %}
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]" = []
1092
1093 for item in value:
1094 if len(tmp) == linecount:
1095 yield tmp
1096 tmp = []
1097
1098 tmp.append(item)
1099
1100 if tmp:
1101 if fill_with is not None and len(tmp) < linecount:
1102 tmp += [fill_with] * (linecount - len(tmp))
1103
1104 yield tmp
1105
1106
1107 def 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:
1115
1116 - ``'common'`` rounds either up or down
1117 - ``'ceil'`` always rounds up
1118 - ``'floor'`` always rounds down
1119
1120 If you don't specify a method ``'common'`` is used.
1121
1122 .. sourcecode:: jinja
1123
1124 {{ 42.55|round }}
1125 -> 43.0
1126 {{ 42.55|round(1, 'floor') }}
1127 -> 42.5
1128
1129 Note that even if rounded to 0 precision, a float is returned. If
1130 you need a real integer, pipe it through `int`:
1131
1132 .. sourcecode:: jinja
1133
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")
1139
1140 if method == "common":
1141 return round(value, precision)
1142
1143 func = getattr(math, method)
1144 return t.cast(float, func(value * (10**precision)) / (10**precision))
1145
1146
1147 class _GroupTuple(t.NamedTuple):
1148 grouper: t.Any
1149 list: t.List
1150
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)
1155
1156 def __str__(self) -> str:
1157 return tuple.__str__(self)
1158
1159
1160 @pass_environment
1161 def 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.
1173
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.
1177
1178 .. sourcecode:: html+jinja
1179
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>
1187
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.
1191
1192 .. sourcecode:: html+jinja
1193
1194 <ul>{% for group in users|groupby("city") %}
1195 <li>{{ group.grouper }}: {{ group.list|join(", ") }}
1196 {% endfor %}</ul>
1197
1198 You can specify a ``default`` value to use if an object in the list
1199 does not have the given attribute.
1200
1201 .. sourcecode:: jinja
1202
1203 <ul>{% for city, items in users|groupby("city", default="NY") %}
1204 <li>{{ city }}: {{ items|map(attribute="name")|join(", ") }}</li>
1205 {% endfor %}</ul>
1206
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``.
1213
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.
1218
1219 .. versionchanged:: 3.0
1220 Added the ``default`` parameter.
1221
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 ]
1235
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]
1240
1241 return out
1242
1243
1244 @async_variant(sync_do_groupby) # type: ignore
1245 async 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 ]
1262
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]
1267
1268 return out
1269
1270
1271 @pass_environment
1272 def 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.
1281
1282 It is also possible to sum up only certain attributes:
1283
1284 .. sourcecode:: jinja
1285
1286 Total: {{ items|sum(attribute='price') }}
1287
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)
1294
1295 return sum(iterable, start) # type: ignore[no-any-return, call-overload]
1296
1297
1298 @async_variant(sync_do_sum) # type: ignore
1299 async 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
1306
1307 if attribute is not None:
1308 func = make_attrgetter(environment, attribute)
1309 else:
1310
1311 def func(x: V) -> V:
1312 return x
1313
1314 async for item in auto_aiter(iterable):
1315 rv += func(item)
1316
1317 return rv
1318
1319
1320 def 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)
1325
1326
1327 @async_variant(sync_do_list) # type: ignore
1328 async def do_list(value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]") -> "t.List[V]":
1329 return await auto_to_list(value)
1330
1331
1332 def 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)
1337
1338
1339 def do_mark_unsafe(value: str) -> str:
1340 """Mark a value as unsafe. This is the reverse operation for :func:`safe`."""
1341 return str(value)
1342
1343
1344 @typing.overload
1345 def do_reverse(value: str) -> str:
1346 ...
1347
1348
1349 @typing.overload
1350 def do_reverse(value: "t.Iterable[V]") -> "t.Iterable[V]":
1351 ...
1352
1353
1354 def 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]
1360
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
1370
1371
1372 @pass_environment
1373 def 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.
1379
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)
1394
1395 if not environment.is_safe_attribute(obj, name, value):
1396 return environment.unsafe_undefined(obj, name)
1397
1398 return value
1399
1400 return environment.undefined(obj=obj, name=name)
1401
1402
1403 @typing.overload
1404 def sync_do_map(
1405 context: "Context", value: t.Iterable, name: str, *args: t.Any, **kwargs: t.Any
1406 ) -> t.Iterable:
1407 ...
1408
1409
1410 @typing.overload
1411 def 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 ...
1419
1420
1421 @pass_context
1422 def 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.
1428
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:
1431
1432 .. sourcecode:: jinja
1433
1434 Users on this page: {{ users|map(attribute='username')|join(', ') }}
1435
1436 You can specify a ``default`` value to use if an object in the list
1437 does not have the given attribute.
1438
1439 .. sourcecode:: jinja
1440
1441 {{ users|map(attribute="username", default="Anonymous")|join(", ") }}
1442
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:
1446
1447 .. sourcecode:: jinja
1448
1449 Users on this page: {{ titles|map('lower')|join(', ') }}
1450
1451 Similar to a generator comprehension such as:
1452
1453 .. code-block:: python
1454
1455 (u.username for u in users)
1456 (getattr(u, "username", "Anonymous") for u in users)
1457 (do_lower(x) for x in titles)
1458
1459 .. versionchanged:: 2.11.0
1460 Added the ``default`` parameter.
1461
1462 .. versionadded:: 2.7
1463 """
1464 if value:
1465 func = prepare_map(context, args, kwargs)
1466
1467 for item in value:
1468 yield func(item)
1469
1470
1471 @typing.overload
1472 def 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 ...
1480
1481
1482 @typing.overload
1483 def 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 ...
1491
1492
1493 @async_variant(sync_do_map) # type: ignore
1494 async 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)
1502
1503 async for item in auto_aiter(value):
1504 yield await auto_await(func(item))
1505
1506
1507 @pass_context
1508 def 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.
1513
1514 If no test is specified, each object will be evaluated as a boolean.
1515
1516 Example usage:
1517
1518 .. sourcecode:: jinja
1519
1520 {{ numbers|select("odd") }}
1521 {{ numbers|select("odd") }}
1522 {{ numbers|select("divisibleby", 3) }}
1523 {{ numbers|select("lessthan", 42) }}
1524 {{ strings|select("equalto", "mystring") }}
1525
1526 Similar to a generator comprehension such as:
1527
1528 .. code-block:: python
1529
1530 (n for n in numbers if test_odd(n))
1531 (n for n in numbers if test_divisibleby(n, 3))
1532
1533 .. versionadded:: 2.7
1534 """
1535 return select_or_reject(context, value, args, kwargs, lambda x: x, False)
1536
1537
1538 @async_variant(sync_do_select) # type: ignore
1539 async 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)
1546
1547
1548 @pass_context
1549 def 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.
1554
1555 If no test is specified, each object will be evaluated as a boolean.
1556
1557 Example usage:
1558
1559 .. sourcecode:: jinja
1560
1561 {{ numbers|reject("odd") }}
1562
1563 Similar to a generator comprehension such as:
1564
1565 .. code-block:: python
1566
1567 (n for n in numbers if not test_odd(n))
1568
1569 .. versionadded:: 2.7
1570 """
1571 return select_or_reject(context, value, args, kwargs, lambda x: not x, False)
1572
1573
1574 @async_variant(sync_do_reject) # type: ignore
1575 async 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)
1582
1583
1584 @pass_context
1585 def 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.
1591
1592 If no test is specified, the attribute's value will be evaluated as
1593 a boolean.
1594
1595 Example usage:
1596
1597 .. sourcecode:: jinja
1598
1599 {{ users|selectattr("is_active") }}
1600 {{ users|selectattr("email", "none") }}
1601
1602 Similar to a generator comprehension such as:
1603
1604 .. code-block:: python
1605
1606 (u for user in users if user.is_active)
1607 (u for user in users if test_none(user.email))
1608
1609 .. versionadded:: 2.7
1610 """
1611 return select_or_reject(context, value, args, kwargs, lambda x: x, True)
1612
1613
1614 @async_variant(sync_do_selectattr) # type: ignore
1615 async 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)
1622
1623
1624 @pass_context
1625 def 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.
1631
1632 If no test is specified, the attribute's value will be evaluated as
1633 a boolean.
1634
1635 .. sourcecode:: jinja
1636
1637 {{ users|rejectattr("is_active") }}
1638 {{ users|rejectattr("email", "none") }}
1639
1640 Similar to a generator comprehension such as:
1641
1642 .. code-block:: python
1643
1644 (u for user in users if not user.is_active)
1645 (u for user in users if not test_none(user.email))
1646
1647 .. versionadded:: 2.7
1648 """
1649 return select_or_reject(context, value, args, kwargs, lambda x: not x, True)
1650
1651
1652 @async_variant(sync_do_rejectattr) # type: ignore
1653 async 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)
1660
1661
1662 @pass_eval_context
1663 def 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.
1668
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.
1673
1674 :param value: The object to serialize to JSON.
1675 :param indent: The ``indent`` parameter passed to ``dumps``, for
1676 pretty-printing the value.
1677
1678 .. versionadded:: 2.9
1679 """
1680 policies = eval_ctx.environment.policies
1681 dumps = policies["json.dumps_function"]
1682 kwargs = policies["json.dumps_kwargs"]
1683
1684 if indent is not None:
1685 kwargs = kwargs.copy()
1686 kwargs["indent"] = indent
1687
1688 return htmlsafe_json_dumps(value, dumps=dumps, **kwargs)
1689
1690
1691 def 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)
1697
1698 if kwargs:
1699 raise FilterArgumentError(
1700 f"Unexpected keyword argument {next(iter(kwargs))!r}"
1701 )
1702
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
1710
1711 def func(item: t.Any) -> t.Any:
1712 return context.environment.call_filter(
1713 name, item, args, kwargs, context=context
1714 )
1715
1716 return func
1717
1718
1719 def 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
1731
1732 transfunc = make_attrgetter(context.environment, attr)
1733 off = 1
1734 else:
1735 off = 0
1736
1737 def transfunc(x: V) -> V:
1738 return x
1739
1740 try:
1741 name = args[off]
1742 args = args[1 + off :]
1743
1744 def func(item: t.Any) -> t.Any:
1745 return context.environment.call_test(name, item, args, kwargs)
1746
1747 except LookupError:
1748 func = bool # type: ignore
1749
1750 return lambda item: modfunc(func(transfunc(item)))
1751
1752
1753 def 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)
1763
1764 for item in value:
1765 if func(item):
1766 yield item
1767
1768
1769 async 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)
1779
1780 async for item in auto_aiter(value):
1781 if func(item):
1782 yield item
1783
1784
1785 FILTERS = {
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 }