Mercurial > repos > shellac > sam_consensus_v3
comparison env/lib/python3.9/site-packages/jinja2/filters.py @ 0:4f3585e2f14b draft default tip
"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author | shellac |
---|---|
date | Mon, 22 Mar 2021 18:12:50 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4f3585e2f14b |
---|---|
1 # -*- coding: utf-8 -*- | |
2 """Built-in template filters used with the ``|`` operator.""" | |
3 import math | |
4 import random | |
5 import re | |
6 import warnings | |
7 from collections import namedtuple | |
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_unicode | |
14 | |
15 from ._compat import abc | |
16 from ._compat import imap | |
17 from ._compat import iteritems | |
18 from ._compat import string_types | |
19 from ._compat import text_type | |
20 from .exceptions import FilterArgumentError | |
21 from .runtime import Undefined | |
22 from .utils import htmlsafe_json_dumps | |
23 from .utils import pformat | |
24 from .utils import unicode_urlencode | |
25 from .utils import urlize | |
26 | |
27 _word_re = re.compile(r"\w+", re.UNICODE) | |
28 _word_beginning_split_re = re.compile(r"([-\s\(\{\[\<]+)", re.UNICODE) | |
29 | |
30 | |
31 def contextfilter(f): | |
32 """Decorator for marking context dependent filters. The current | |
33 :class:`Context` will be passed as first argument. | |
34 """ | |
35 f.contextfilter = True | |
36 return f | |
37 | |
38 | |
39 def evalcontextfilter(f): | |
40 """Decorator for marking eval-context dependent filters. An eval | |
41 context object is passed as first argument. For more information | |
42 about the eval context, see :ref:`eval-context`. | |
43 | |
44 .. versionadded:: 2.4 | |
45 """ | |
46 f.evalcontextfilter = True | |
47 return f | |
48 | |
49 | |
50 def environmentfilter(f): | |
51 """Decorator for marking environment dependent filters. The current | |
52 :class:`Environment` is passed to the filter as first argument. | |
53 """ | |
54 f.environmentfilter = True | |
55 return f | |
56 | |
57 | |
58 def ignore_case(value): | |
59 """For use as a postprocessor for :func:`make_attrgetter`. Converts strings | |
60 to lowercase and returns other types as-is.""" | |
61 return value.lower() if isinstance(value, string_types) else value | |
62 | |
63 | |
64 def make_attrgetter(environment, attribute, postprocess=None, default=None): | |
65 """Returns a callable that looks up the given attribute from a | |
66 passed object with the rules of the environment. Dots are allowed | |
67 to access attributes of attributes. Integer parts in paths are | |
68 looked up as integers. | |
69 """ | |
70 attribute = _prepare_attribute_parts(attribute) | |
71 | |
72 def attrgetter(item): | |
73 for part in attribute: | |
74 item = environment.getitem(item, part) | |
75 | |
76 if default and isinstance(item, Undefined): | |
77 item = default | |
78 | |
79 if postprocess is not None: | |
80 item = postprocess(item) | |
81 | |
82 return item | |
83 | |
84 return attrgetter | |
85 | |
86 | |
87 def make_multi_attrgetter(environment, attribute, postprocess=None): | |
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 attribute_parts = ( | |
99 attribute.split(",") if isinstance(attribute, string_types) else [attribute] | |
100 ) | |
101 attribute = [ | |
102 _prepare_attribute_parts(attribute_part) for attribute_part in attribute_parts | |
103 ] | |
104 | |
105 def attrgetter(item): | |
106 items = [None] * len(attribute) | |
107 for i, attribute_part in enumerate(attribute): | |
108 item_i = item | |
109 for part in attribute_part: | |
110 item_i = environment.getitem(item_i, part) | |
111 | |
112 if postprocess is not None: | |
113 item_i = postprocess(item_i) | |
114 | |
115 items[i] = item_i | |
116 return items | |
117 | |
118 return attrgetter | |
119 | |
120 | |
121 def _prepare_attribute_parts(attr): | |
122 if attr is None: | |
123 return [] | |
124 elif isinstance(attr, string_types): | |
125 return [int(x) if x.isdigit() else x for x in attr.split(".")] | |
126 else: | |
127 return [attr] | |
128 | |
129 | |
130 def do_forceescape(value): | |
131 """Enforce HTML escaping. This will probably double escape variables.""" | |
132 if hasattr(value, "__html__"): | |
133 value = value.__html__() | |
134 return escape(text_type(value)) | |
135 | |
136 | |
137 def do_urlencode(value): | |
138 """Quote data for use in a URL path or query using UTF-8. | |
139 | |
140 Basic wrapper around :func:`urllib.parse.quote` when given a | |
141 string, or :func:`urllib.parse.urlencode` for a dict or iterable. | |
142 | |
143 :param value: Data to quote. A string will be quoted directly. A | |
144 dict or iterable of ``(key, value)`` pairs will be joined as a | |
145 query string. | |
146 | |
147 When given a string, "/" is not quoted. HTTP servers treat "/" and | |
148 "%2F" equivalently in paths. If you need quoted slashes, use the | |
149 ``|replace("/", "%2F")`` filter. | |
150 | |
151 .. versionadded:: 2.7 | |
152 """ | |
153 if isinstance(value, string_types) or not isinstance(value, abc.Iterable): | |
154 return unicode_urlencode(value) | |
155 | |
156 if isinstance(value, dict): | |
157 items = iteritems(value) | |
158 else: | |
159 items = iter(value) | |
160 | |
161 return u"&".join( | |
162 "%s=%s" % (unicode_urlencode(k, for_qs=True), unicode_urlencode(v, for_qs=True)) | |
163 for k, v in items | |
164 ) | |
165 | |
166 | |
167 @evalcontextfilter | |
168 def do_replace(eval_ctx, s, old, new, count=None): | |
169 """Return a copy of the value with all occurrences of a substring | |
170 replaced with a new one. The first argument is the substring | |
171 that should be replaced, the second is the replacement string. | |
172 If the optional third argument ``count`` is given, only the first | |
173 ``count`` occurrences are replaced: | |
174 | |
175 .. sourcecode:: jinja | |
176 | |
177 {{ "Hello World"|replace("Hello", "Goodbye") }} | |
178 -> Goodbye World | |
179 | |
180 {{ "aaaaargh"|replace("a", "d'oh, ", 2) }} | |
181 -> d'oh, d'oh, aaargh | |
182 """ | |
183 if count is None: | |
184 count = -1 | |
185 if not eval_ctx.autoescape: | |
186 return text_type(s).replace(text_type(old), text_type(new), count) | |
187 if ( | |
188 hasattr(old, "__html__") | |
189 or hasattr(new, "__html__") | |
190 and not hasattr(s, "__html__") | |
191 ): | |
192 s = escape(s) | |
193 else: | |
194 s = soft_unicode(s) | |
195 return s.replace(soft_unicode(old), soft_unicode(new), count) | |
196 | |
197 | |
198 def do_upper(s): | |
199 """Convert a value to uppercase.""" | |
200 return soft_unicode(s).upper() | |
201 | |
202 | |
203 def do_lower(s): | |
204 """Convert a value to lowercase.""" | |
205 return soft_unicode(s).lower() | |
206 | |
207 | |
208 @evalcontextfilter | |
209 def do_xmlattr(_eval_ctx, d, autospace=True): | |
210 """Create an SGML/XML attribute string based on the items in a dict. | |
211 All values that are neither `none` nor `undefined` are automatically | |
212 escaped: | |
213 | |
214 .. sourcecode:: html+jinja | |
215 | |
216 <ul{{ {'class': 'my_list', 'missing': none, | |
217 'id': 'list-%d'|format(variable)}|xmlattr }}> | |
218 ... | |
219 </ul> | |
220 | |
221 Results in something like this: | |
222 | |
223 .. sourcecode:: html | |
224 | |
225 <ul class="my_list" id="list-42"> | |
226 ... | |
227 </ul> | |
228 | |
229 As you can see it automatically prepends a space in front of the item | |
230 if the filter returned something unless the second parameter is false. | |
231 """ | |
232 rv = u" ".join( | |
233 u'%s="%s"' % (escape(key), escape(value)) | |
234 for key, value in iteritems(d) | |
235 if value is not None and not isinstance(value, Undefined) | |
236 ) | |
237 if autospace and rv: | |
238 rv = u" " + rv | |
239 if _eval_ctx.autoescape: | |
240 rv = Markup(rv) | |
241 return rv | |
242 | |
243 | |
244 def do_capitalize(s): | |
245 """Capitalize a value. The first character will be uppercase, all others | |
246 lowercase. | |
247 """ | |
248 return soft_unicode(s).capitalize() | |
249 | |
250 | |
251 def do_title(s): | |
252 """Return a titlecased version of the value. I.e. words will start with | |
253 uppercase letters, all remaining characters are lowercase. | |
254 """ | |
255 return "".join( | |
256 [ | |
257 item[0].upper() + item[1:].lower() | |
258 for item in _word_beginning_split_re.split(soft_unicode(s)) | |
259 if item | |
260 ] | |
261 ) | |
262 | |
263 | |
264 def do_dictsort(value, case_sensitive=False, by="key", reverse=False): | |
265 """Sort a dict and yield (key, value) pairs. Because python dicts are | |
266 unsorted you may want to use this function to order them by either | |
267 key or value: | |
268 | |
269 .. sourcecode:: jinja | |
270 | |
271 {% for key, value in mydict|dictsort %} | |
272 sort the dict by key, case insensitive | |
273 | |
274 {% for key, value in mydict|dictsort(reverse=true) %} | |
275 sort the dict by key, case insensitive, reverse order | |
276 | |
277 {% for key, value in mydict|dictsort(true) %} | |
278 sort the dict by key, case sensitive | |
279 | |
280 {% for key, value in mydict|dictsort(false, 'value') %} | |
281 sort the dict by value, case insensitive | |
282 """ | |
283 if by == "key": | |
284 pos = 0 | |
285 elif by == "value": | |
286 pos = 1 | |
287 else: | |
288 raise FilterArgumentError('You can only sort by either "key" or "value"') | |
289 | |
290 def sort_func(item): | |
291 value = item[pos] | |
292 | |
293 if not case_sensitive: | |
294 value = ignore_case(value) | |
295 | |
296 return value | |
297 | |
298 return sorted(value.items(), key=sort_func, reverse=reverse) | |
299 | |
300 | |
301 @environmentfilter | |
302 def do_sort(environment, value, reverse=False, case_sensitive=False, attribute=None): | |
303 """Sort an iterable using Python's :func:`sorted`. | |
304 | |
305 .. sourcecode:: jinja | |
306 | |
307 {% for city in cities|sort %} | |
308 ... | |
309 {% endfor %} | |
310 | |
311 :param reverse: Sort descending instead of ascending. | |
312 :param case_sensitive: When sorting strings, sort upper and lower | |
313 case separately. | |
314 :param attribute: When sorting objects or dicts, an attribute or | |
315 key to sort by. Can use dot notation like ``"address.city"``. | |
316 Can be a list of attributes like ``"age,name"``. | |
317 | |
318 The sort is stable, it does not change the relative order of | |
319 elements that compare equal. This makes it is possible to chain | |
320 sorts on different attributes and ordering. | |
321 | |
322 .. sourcecode:: jinja | |
323 | |
324 {% for user in users|sort(attribute="name") | |
325 |sort(reverse=true, attribute="age") %} | |
326 ... | |
327 {% endfor %} | |
328 | |
329 As a shortcut to chaining when the direction is the same for all | |
330 attributes, pass a comma separate list of attributes. | |
331 | |
332 .. sourcecode:: jinja | |
333 | |
334 {% for user users|sort(attribute="age,name") %} | |
335 ... | |
336 {% endfor %} | |
337 | |
338 .. versionchanged:: 2.11.0 | |
339 The ``attribute`` parameter can be a comma separated list of | |
340 attributes, e.g. ``"age,name"``. | |
341 | |
342 .. versionchanged:: 2.6 | |
343 The ``attribute`` parameter was added. | |
344 """ | |
345 key_func = make_multi_attrgetter( | |
346 environment, attribute, postprocess=ignore_case if not case_sensitive else None | |
347 ) | |
348 return sorted(value, key=key_func, reverse=reverse) | |
349 | |
350 | |
351 @environmentfilter | |
352 def do_unique(environment, value, case_sensitive=False, attribute=None): | |
353 """Returns a list of unique items from the given iterable. | |
354 | |
355 .. sourcecode:: jinja | |
356 | |
357 {{ ['foo', 'bar', 'foobar', 'FooBar']|unique|list }} | |
358 -> ['foo', 'bar', 'foobar'] | |
359 | |
360 The unique items are yielded in the same order as their first occurrence in | |
361 the iterable passed to the filter. | |
362 | |
363 :param case_sensitive: Treat upper and lower case strings as distinct. | |
364 :param attribute: Filter objects with unique values for this attribute. | |
365 """ | |
366 getter = make_attrgetter( | |
367 environment, attribute, postprocess=ignore_case if not case_sensitive else None | |
368 ) | |
369 seen = set() | |
370 | |
371 for item in value: | |
372 key = getter(item) | |
373 | |
374 if key not in seen: | |
375 seen.add(key) | |
376 yield item | |
377 | |
378 | |
379 def _min_or_max(environment, value, func, case_sensitive, attribute): | |
380 it = iter(value) | |
381 | |
382 try: | |
383 first = next(it) | |
384 except StopIteration: | |
385 return environment.undefined("No aggregated item, sequence was empty.") | |
386 | |
387 key_func = make_attrgetter( | |
388 environment, attribute, postprocess=ignore_case if not case_sensitive else None | |
389 ) | |
390 return func(chain([first], it), key=key_func) | |
391 | |
392 | |
393 @environmentfilter | |
394 def do_min(environment, value, case_sensitive=False, attribute=None): | |
395 """Return the smallest item from the sequence. | |
396 | |
397 .. sourcecode:: jinja | |
398 | |
399 {{ [1, 2, 3]|min }} | |
400 -> 1 | |
401 | |
402 :param case_sensitive: Treat upper and lower case strings as distinct. | |
403 :param attribute: Get the object with the min value of this attribute. | |
404 """ | |
405 return _min_or_max(environment, value, min, case_sensitive, attribute) | |
406 | |
407 | |
408 @environmentfilter | |
409 def do_max(environment, value, case_sensitive=False, attribute=None): | |
410 """Return the largest item from the sequence. | |
411 | |
412 .. sourcecode:: jinja | |
413 | |
414 {{ [1, 2, 3]|max }} | |
415 -> 3 | |
416 | |
417 :param case_sensitive: Treat upper and lower case strings as distinct. | |
418 :param attribute: Get the object with the max value of this attribute. | |
419 """ | |
420 return _min_or_max(environment, value, max, case_sensitive, attribute) | |
421 | |
422 | |
423 def do_default(value, default_value=u"", boolean=False): | |
424 """If the value is undefined it will return the passed default value, | |
425 otherwise the value of the variable: | |
426 | |
427 .. sourcecode:: jinja | |
428 | |
429 {{ my_variable|default('my_variable is not defined') }} | |
430 | |
431 This will output the value of ``my_variable`` if the variable was | |
432 defined, otherwise ``'my_variable is not defined'``. If you want | |
433 to use default with variables that evaluate to false you have to | |
434 set the second parameter to `true`: | |
435 | |
436 .. sourcecode:: jinja | |
437 | |
438 {{ ''|default('the string was empty', true) }} | |
439 | |
440 .. versionchanged:: 2.11 | |
441 It's now possible to configure the :class:`~jinja2.Environment` with | |
442 :class:`~jinja2.ChainableUndefined` to make the `default` filter work | |
443 on nested elements and attributes that may contain undefined values | |
444 in the chain without getting an :exc:`~jinja2.UndefinedError`. | |
445 """ | |
446 if isinstance(value, Undefined) or (boolean and not value): | |
447 return default_value | |
448 return value | |
449 | |
450 | |
451 @evalcontextfilter | |
452 def do_join(eval_ctx, value, d=u"", attribute=None): | |
453 """Return a string which is the concatenation of the strings in the | |
454 sequence. The separator between elements is an empty string per | |
455 default, you can define it with the optional parameter: | |
456 | |
457 .. sourcecode:: jinja | |
458 | |
459 {{ [1, 2, 3]|join('|') }} | |
460 -> 1|2|3 | |
461 | |
462 {{ [1, 2, 3]|join }} | |
463 -> 123 | |
464 | |
465 It is also possible to join certain attributes of an object: | |
466 | |
467 .. sourcecode:: jinja | |
468 | |
469 {{ users|join(', ', attribute='username') }} | |
470 | |
471 .. versionadded:: 2.6 | |
472 The `attribute` parameter was added. | |
473 """ | |
474 if attribute is not None: | |
475 value = imap(make_attrgetter(eval_ctx.environment, attribute), value) | |
476 | |
477 # no automatic escaping? joining is a lot easier then | |
478 if not eval_ctx.autoescape: | |
479 return text_type(d).join(imap(text_type, value)) | |
480 | |
481 # if the delimiter doesn't have an html representation we check | |
482 # if any of the items has. If yes we do a coercion to Markup | |
483 if not hasattr(d, "__html__"): | |
484 value = list(value) | |
485 do_escape = False | |
486 for idx, item in enumerate(value): | |
487 if hasattr(item, "__html__"): | |
488 do_escape = True | |
489 else: | |
490 value[idx] = text_type(item) | |
491 if do_escape: | |
492 d = escape(d) | |
493 else: | |
494 d = text_type(d) | |
495 return d.join(value) | |
496 | |
497 # no html involved, to normal joining | |
498 return soft_unicode(d).join(imap(soft_unicode, value)) | |
499 | |
500 | |
501 def do_center(value, width=80): | |
502 """Centers the value in a field of a given width.""" | |
503 return text_type(value).center(width) | |
504 | |
505 | |
506 @environmentfilter | |
507 def do_first(environment, seq): | |
508 """Return the first item of a sequence.""" | |
509 try: | |
510 return next(iter(seq)) | |
511 except StopIteration: | |
512 return environment.undefined("No first item, sequence was empty.") | |
513 | |
514 | |
515 @environmentfilter | |
516 def do_last(environment, seq): | |
517 """ | |
518 Return the last item of a sequence. | |
519 | |
520 Note: Does not work with generators. You may want to explicitly | |
521 convert it to a list: | |
522 | |
523 .. sourcecode:: jinja | |
524 | |
525 {{ data | selectattr('name', '==', 'Jinja') | list | last }} | |
526 """ | |
527 try: | |
528 return next(iter(reversed(seq))) | |
529 except StopIteration: | |
530 return environment.undefined("No last item, sequence was empty.") | |
531 | |
532 | |
533 @contextfilter | |
534 def do_random(context, seq): | |
535 """Return a random item from the sequence.""" | |
536 try: | |
537 return random.choice(seq) | |
538 except IndexError: | |
539 return context.environment.undefined("No random item, sequence was empty.") | |
540 | |
541 | |
542 def do_filesizeformat(value, binary=False): | |
543 """Format the value like a 'human-readable' file size (i.e. 13 kB, | |
544 4.1 MB, 102 Bytes, etc). Per default decimal prefixes are used (Mega, | |
545 Giga, etc.), if the second parameter is set to `True` the binary | |
546 prefixes are used (Mebi, Gibi). | |
547 """ | |
548 bytes = float(value) | |
549 base = binary and 1024 or 1000 | |
550 prefixes = [ | |
551 (binary and "KiB" or "kB"), | |
552 (binary and "MiB" or "MB"), | |
553 (binary and "GiB" or "GB"), | |
554 (binary and "TiB" or "TB"), | |
555 (binary and "PiB" or "PB"), | |
556 (binary and "EiB" or "EB"), | |
557 (binary and "ZiB" or "ZB"), | |
558 (binary and "YiB" or "YB"), | |
559 ] | |
560 if bytes == 1: | |
561 return "1 Byte" | |
562 elif bytes < base: | |
563 return "%d Bytes" % bytes | |
564 else: | |
565 for i, prefix in enumerate(prefixes): | |
566 unit = base ** (i + 2) | |
567 if bytes < unit: | |
568 return "%.1f %s" % ((base * bytes / unit), prefix) | |
569 return "%.1f %s" % ((base * bytes / unit), prefix) | |
570 | |
571 | |
572 def do_pprint(value, verbose=False): | |
573 """Pretty print a variable. Useful for debugging. | |
574 | |
575 With Jinja 1.2 onwards you can pass it a parameter. If this parameter | |
576 is truthy the output will be more verbose (this requires `pretty`) | |
577 """ | |
578 return pformat(value, verbose=verbose) | |
579 | |
580 | |
581 @evalcontextfilter | |
582 def do_urlize( | |
583 eval_ctx, value, trim_url_limit=None, nofollow=False, target=None, rel=None | |
584 ): | |
585 """Converts URLs in plain text into clickable links. | |
586 | |
587 If you pass the filter an additional integer it will shorten the urls | |
588 to that number. Also a third argument exists that makes the urls | |
589 "nofollow": | |
590 | |
591 .. sourcecode:: jinja | |
592 | |
593 {{ mytext|urlize(40, true) }} | |
594 links are shortened to 40 chars and defined with rel="nofollow" | |
595 | |
596 If *target* is specified, the ``target`` attribute will be added to the | |
597 ``<a>`` tag: | |
598 | |
599 .. sourcecode:: jinja | |
600 | |
601 {{ mytext|urlize(40, target='_blank') }} | |
602 | |
603 .. versionchanged:: 2.8+ | |
604 The *target* parameter was added. | |
605 """ | |
606 policies = eval_ctx.environment.policies | |
607 rel = set((rel or "").split() or []) | |
608 if nofollow: | |
609 rel.add("nofollow") | |
610 rel.update((policies["urlize.rel"] or "").split()) | |
611 if target is None: | |
612 target = policies["urlize.target"] | |
613 rel = " ".join(sorted(rel)) or None | |
614 rv = urlize(value, trim_url_limit, rel=rel, target=target) | |
615 if eval_ctx.autoescape: | |
616 rv = Markup(rv) | |
617 return rv | |
618 | |
619 | |
620 def do_indent(s, width=4, first=False, blank=False, indentfirst=None): | |
621 """Return a copy of the string with each line indented by 4 spaces. The | |
622 first line and blank lines are not indented by default. | |
623 | |
624 :param width: Number of spaces to indent by. | |
625 :param first: Don't skip indenting the first line. | |
626 :param blank: Don't skip indenting empty lines. | |
627 | |
628 .. versionchanged:: 2.10 | |
629 Blank lines are not indented by default. | |
630 | |
631 Rename the ``indentfirst`` argument to ``first``. | |
632 """ | |
633 if indentfirst is not None: | |
634 warnings.warn( | |
635 "The 'indentfirst' argument is renamed to 'first' and will" | |
636 " be removed in version 3.0.", | |
637 DeprecationWarning, | |
638 stacklevel=2, | |
639 ) | |
640 first = indentfirst | |
641 | |
642 indention = u" " * width | |
643 newline = u"\n" | |
644 | |
645 if isinstance(s, Markup): | |
646 indention = Markup(indention) | |
647 newline = Markup(newline) | |
648 | |
649 s += newline # this quirk is necessary for splitlines method | |
650 | |
651 if blank: | |
652 rv = (newline + indention).join(s.splitlines()) | |
653 else: | |
654 lines = s.splitlines() | |
655 rv = lines.pop(0) | |
656 | |
657 if lines: | |
658 rv += newline + newline.join( | |
659 indention + line if line else line for line in lines | |
660 ) | |
661 | |
662 if first: | |
663 rv = indention + rv | |
664 | |
665 return rv | |
666 | |
667 | |
668 @environmentfilter | |
669 def do_truncate(env, s, length=255, killwords=False, end="...", leeway=None): | |
670 """Return a truncated copy of the string. The length is specified | |
671 with the first parameter which defaults to ``255``. If the second | |
672 parameter is ``true`` the filter will cut the text at length. Otherwise | |
673 it will discard the last word. If the text was in fact | |
674 truncated it will append an ellipsis sign (``"..."``). If you want a | |
675 different ellipsis sign than ``"..."`` you can specify it using the | |
676 third parameter. Strings that only exceed the length by the tolerance | |
677 margin given in the fourth parameter will not be truncated. | |
678 | |
679 .. sourcecode:: jinja | |
680 | |
681 {{ "foo bar baz qux"|truncate(9) }} | |
682 -> "foo..." | |
683 {{ "foo bar baz qux"|truncate(9, True) }} | |
684 -> "foo ba..." | |
685 {{ "foo bar baz qux"|truncate(11) }} | |
686 -> "foo bar baz qux" | |
687 {{ "foo bar baz qux"|truncate(11, False, '...', 0) }} | |
688 -> "foo bar..." | |
689 | |
690 The default leeway on newer Jinja versions is 5 and was 0 before but | |
691 can be reconfigured globally. | |
692 """ | |
693 if leeway is None: | |
694 leeway = env.policies["truncate.leeway"] | |
695 assert length >= len(end), "expected length >= %s, got %s" % (len(end), length) | |
696 assert leeway >= 0, "expected leeway >= 0, got %s" % leeway | |
697 if len(s) <= length + leeway: | |
698 return s | |
699 if killwords: | |
700 return s[: length - len(end)] + end | |
701 result = s[: length - len(end)].rsplit(" ", 1)[0] | |
702 return result + end | |
703 | |
704 | |
705 @environmentfilter | |
706 def do_wordwrap( | |
707 environment, | |
708 s, | |
709 width=79, | |
710 break_long_words=True, | |
711 wrapstring=None, | |
712 break_on_hyphens=True, | |
713 ): | |
714 """Wrap a string to the given width. Existing newlines are treated | |
715 as paragraphs to be wrapped separately. | |
716 | |
717 :param s: Original text to wrap. | |
718 :param width: Maximum length of wrapped lines. | |
719 :param break_long_words: If a word is longer than ``width``, break | |
720 it across lines. | |
721 :param break_on_hyphens: If a word contains hyphens, it may be split | |
722 across lines. | |
723 :param wrapstring: String to join each wrapped line. Defaults to | |
724 :attr:`Environment.newline_sequence`. | |
725 | |
726 .. versionchanged:: 2.11 | |
727 Existing newlines are treated as paragraphs wrapped separately. | |
728 | |
729 .. versionchanged:: 2.11 | |
730 Added the ``break_on_hyphens`` parameter. | |
731 | |
732 .. versionchanged:: 2.7 | |
733 Added the ``wrapstring`` parameter. | |
734 """ | |
735 | |
736 import textwrap | |
737 | |
738 if not wrapstring: | |
739 wrapstring = environment.newline_sequence | |
740 | |
741 # textwrap.wrap doesn't consider existing newlines when wrapping. | |
742 # If the string has a newline before width, wrap will still insert | |
743 # a newline at width, resulting in a short line. Instead, split and | |
744 # wrap each paragraph individually. | |
745 return wrapstring.join( | |
746 [ | |
747 wrapstring.join( | |
748 textwrap.wrap( | |
749 line, | |
750 width=width, | |
751 expand_tabs=False, | |
752 replace_whitespace=False, | |
753 break_long_words=break_long_words, | |
754 break_on_hyphens=break_on_hyphens, | |
755 ) | |
756 ) | |
757 for line in s.splitlines() | |
758 ] | |
759 ) | |
760 | |
761 | |
762 def do_wordcount(s): | |
763 """Count the words in that string.""" | |
764 return len(_word_re.findall(soft_unicode(s))) | |
765 | |
766 | |
767 def do_int(value, default=0, base=10): | |
768 """Convert the value into an integer. If the | |
769 conversion doesn't work it will return ``0``. You can | |
770 override this default using the first parameter. You | |
771 can also override the default base (10) in the second | |
772 parameter, which handles input with prefixes such as | |
773 0b, 0o and 0x for bases 2, 8 and 16 respectively. | |
774 The base is ignored for decimal numbers and non-string values. | |
775 """ | |
776 try: | |
777 if isinstance(value, string_types): | |
778 return int(value, base) | |
779 return int(value) | |
780 except (TypeError, ValueError): | |
781 # this quirk is necessary so that "42.23"|int gives 42. | |
782 try: | |
783 return int(float(value)) | |
784 except (TypeError, ValueError): | |
785 return default | |
786 | |
787 | |
788 def do_float(value, default=0.0): | |
789 """Convert the value into a floating point number. If the | |
790 conversion doesn't work it will return ``0.0``. You can | |
791 override this default using the first parameter. | |
792 """ | |
793 try: | |
794 return float(value) | |
795 except (TypeError, ValueError): | |
796 return default | |
797 | |
798 | |
799 def do_format(value, *args, **kwargs): | |
800 """Apply the given values to a `printf-style`_ format string, like | |
801 ``string % values``. | |
802 | |
803 .. sourcecode:: jinja | |
804 | |
805 {{ "%s, %s!"|format(greeting, name) }} | |
806 Hello, World! | |
807 | |
808 In most cases it should be more convenient and efficient to use the | |
809 ``%`` operator or :meth:`str.format`. | |
810 | |
811 .. code-block:: text | |
812 | |
813 {{ "%s, %s!" % (greeting, name) }} | |
814 {{ "{}, {}!".format(greeting, name) }} | |
815 | |
816 .. _printf-style: https://docs.python.org/library/stdtypes.html | |
817 #printf-style-string-formatting | |
818 """ | |
819 if args and kwargs: | |
820 raise FilterArgumentError( | |
821 "can't handle positional and keyword arguments at the same time" | |
822 ) | |
823 return soft_unicode(value) % (kwargs or args) | |
824 | |
825 | |
826 def do_trim(value, chars=None): | |
827 """Strip leading and trailing characters, by default whitespace.""" | |
828 return soft_unicode(value).strip(chars) | |
829 | |
830 | |
831 def do_striptags(value): | |
832 """Strip SGML/XML tags and replace adjacent whitespace by one space.""" | |
833 if hasattr(value, "__html__"): | |
834 value = value.__html__() | |
835 return Markup(text_type(value)).striptags() | |
836 | |
837 | |
838 def do_slice(value, slices, fill_with=None): | |
839 """Slice an iterator and return a list of lists containing | |
840 those items. Useful if you want to create a div containing | |
841 three ul tags that represent columns: | |
842 | |
843 .. sourcecode:: html+jinja | |
844 | |
845 <div class="columnwrapper"> | |
846 {%- for column in items|slice(3) %} | |
847 <ul class="column-{{ loop.index }}"> | |
848 {%- for item in column %} | |
849 <li>{{ item }}</li> | |
850 {%- endfor %} | |
851 </ul> | |
852 {%- endfor %} | |
853 </div> | |
854 | |
855 If you pass it a second argument it's used to fill missing | |
856 values on the last iteration. | |
857 """ | |
858 seq = list(value) | |
859 length = len(seq) | |
860 items_per_slice = length // slices | |
861 slices_with_extra = length % slices | |
862 offset = 0 | |
863 for slice_number in range(slices): | |
864 start = offset + slice_number * items_per_slice | |
865 if slice_number < slices_with_extra: | |
866 offset += 1 | |
867 end = offset + (slice_number + 1) * items_per_slice | |
868 tmp = seq[start:end] | |
869 if fill_with is not None and slice_number >= slices_with_extra: | |
870 tmp.append(fill_with) | |
871 yield tmp | |
872 | |
873 | |
874 def do_batch(value, linecount, fill_with=None): | |
875 """ | |
876 A filter that batches items. It works pretty much like `slice` | |
877 just the other way round. It returns a list of lists with the | |
878 given number of items. If you provide a second parameter this | |
879 is used to fill up missing items. See this example: | |
880 | |
881 .. sourcecode:: html+jinja | |
882 | |
883 <table> | |
884 {%- for row in items|batch(3, ' ') %} | |
885 <tr> | |
886 {%- for column in row %} | |
887 <td>{{ column }}</td> | |
888 {%- endfor %} | |
889 </tr> | |
890 {%- endfor %} | |
891 </table> | |
892 """ | |
893 tmp = [] | |
894 for item in value: | |
895 if len(tmp) == linecount: | |
896 yield tmp | |
897 tmp = [] | |
898 tmp.append(item) | |
899 if tmp: | |
900 if fill_with is not None and len(tmp) < linecount: | |
901 tmp += [fill_with] * (linecount - len(tmp)) | |
902 yield tmp | |
903 | |
904 | |
905 def do_round(value, precision=0, method="common"): | |
906 """Round the number to a given precision. The first | |
907 parameter specifies the precision (default is ``0``), the | |
908 second the rounding method: | |
909 | |
910 - ``'common'`` rounds either up or down | |
911 - ``'ceil'`` always rounds up | |
912 - ``'floor'`` always rounds down | |
913 | |
914 If you don't specify a method ``'common'`` is used. | |
915 | |
916 .. sourcecode:: jinja | |
917 | |
918 {{ 42.55|round }} | |
919 -> 43.0 | |
920 {{ 42.55|round(1, 'floor') }} | |
921 -> 42.5 | |
922 | |
923 Note that even if rounded to 0 precision, a float is returned. If | |
924 you need a real integer, pipe it through `int`: | |
925 | |
926 .. sourcecode:: jinja | |
927 | |
928 {{ 42.55|round|int }} | |
929 -> 43 | |
930 """ | |
931 if method not in {"common", "ceil", "floor"}: | |
932 raise FilterArgumentError("method must be common, ceil or floor") | |
933 if method == "common": | |
934 return round(value, precision) | |
935 func = getattr(math, method) | |
936 return func(value * (10 ** precision)) / (10 ** precision) | |
937 | |
938 | |
939 # Use a regular tuple repr here. This is what we did in the past and we | |
940 # really want to hide this custom type as much as possible. In particular | |
941 # we do not want to accidentally expose an auto generated repr in case | |
942 # people start to print this out in comments or something similar for | |
943 # debugging. | |
944 _GroupTuple = namedtuple("_GroupTuple", ["grouper", "list"]) | |
945 _GroupTuple.__repr__ = tuple.__repr__ | |
946 _GroupTuple.__str__ = tuple.__str__ | |
947 | |
948 | |
949 @environmentfilter | |
950 def do_groupby(environment, value, attribute): | |
951 """Group a sequence of objects by an attribute using Python's | |
952 :func:`itertools.groupby`. The attribute can use dot notation for | |
953 nested access, like ``"address.city"``. Unlike Python's ``groupby``, | |
954 the values are sorted first so only one group is returned for each | |
955 unique value. | |
956 | |
957 For example, a list of ``User`` objects with a ``city`` attribute | |
958 can be rendered in groups. In this example, ``grouper`` refers to | |
959 the ``city`` value of the group. | |
960 | |
961 .. sourcecode:: html+jinja | |
962 | |
963 <ul>{% for city, items in users|groupby("city") %} | |
964 <li>{{ city }} | |
965 <ul>{% for user in items %} | |
966 <li>{{ user.name }} | |
967 {% endfor %}</ul> | |
968 </li> | |
969 {% endfor %}</ul> | |
970 | |
971 ``groupby`` yields namedtuples of ``(grouper, list)``, which | |
972 can be used instead of the tuple unpacking above. ``grouper`` is the | |
973 value of the attribute, and ``list`` is the items with that value. | |
974 | |
975 .. sourcecode:: html+jinja | |
976 | |
977 <ul>{% for group in users|groupby("city") %} | |
978 <li>{{ group.grouper }}: {{ group.list|join(", ") }} | |
979 {% endfor %}</ul> | |
980 | |
981 .. versionchanged:: 2.6 | |
982 The attribute supports dot notation for nested access. | |
983 """ | |
984 expr = make_attrgetter(environment, attribute) | |
985 return [ | |
986 _GroupTuple(key, list(values)) | |
987 for key, values in groupby(sorted(value, key=expr), expr) | |
988 ] | |
989 | |
990 | |
991 @environmentfilter | |
992 def do_sum(environment, iterable, attribute=None, start=0): | |
993 """Returns the sum of a sequence of numbers plus the value of parameter | |
994 'start' (which defaults to 0). When the sequence is empty it returns | |
995 start. | |
996 | |
997 It is also possible to sum up only certain attributes: | |
998 | |
999 .. sourcecode:: jinja | |
1000 | |
1001 Total: {{ items|sum(attribute='price') }} | |
1002 | |
1003 .. versionchanged:: 2.6 | |
1004 The `attribute` parameter was added to allow suming up over | |
1005 attributes. Also the `start` parameter was moved on to the right. | |
1006 """ | |
1007 if attribute is not None: | |
1008 iterable = imap(make_attrgetter(environment, attribute), iterable) | |
1009 return sum(iterable, start) | |
1010 | |
1011 | |
1012 def do_list(value): | |
1013 """Convert the value into a list. If it was a string the returned list | |
1014 will be a list of characters. | |
1015 """ | |
1016 return list(value) | |
1017 | |
1018 | |
1019 def do_mark_safe(value): | |
1020 """Mark the value as safe which means that in an environment with automatic | |
1021 escaping enabled this variable will not be escaped. | |
1022 """ | |
1023 return Markup(value) | |
1024 | |
1025 | |
1026 def do_mark_unsafe(value): | |
1027 """Mark a value as unsafe. This is the reverse operation for :func:`safe`.""" | |
1028 return text_type(value) | |
1029 | |
1030 | |
1031 def do_reverse(value): | |
1032 """Reverse the object or return an iterator that iterates over it the other | |
1033 way round. | |
1034 """ | |
1035 if isinstance(value, string_types): | |
1036 return value[::-1] | |
1037 try: | |
1038 return reversed(value) | |
1039 except TypeError: | |
1040 try: | |
1041 rv = list(value) | |
1042 rv.reverse() | |
1043 return rv | |
1044 except TypeError: | |
1045 raise FilterArgumentError("argument must be iterable") | |
1046 | |
1047 | |
1048 @environmentfilter | |
1049 def do_attr(environment, obj, name): | |
1050 """Get an attribute of an object. ``foo|attr("bar")`` works like | |
1051 ``foo.bar`` just that always an attribute is returned and items are not | |
1052 looked up. | |
1053 | |
1054 See :ref:`Notes on subscriptions <notes-on-subscriptions>` for more details. | |
1055 """ | |
1056 try: | |
1057 name = str(name) | |
1058 except UnicodeError: | |
1059 pass | |
1060 else: | |
1061 try: | |
1062 value = getattr(obj, name) | |
1063 except AttributeError: | |
1064 pass | |
1065 else: | |
1066 if environment.sandboxed and not environment.is_safe_attribute( | |
1067 obj, name, value | |
1068 ): | |
1069 return environment.unsafe_undefined(obj, name) | |
1070 return value | |
1071 return environment.undefined(obj=obj, name=name) | |
1072 | |
1073 | |
1074 @contextfilter | |
1075 def do_map(*args, **kwargs): | |
1076 """Applies a filter on a sequence of objects or looks up an attribute. | |
1077 This is useful when dealing with lists of objects but you are really | |
1078 only interested in a certain value of it. | |
1079 | |
1080 The basic usage is mapping on an attribute. Imagine you have a list | |
1081 of users but you are only interested in a list of usernames: | |
1082 | |
1083 .. sourcecode:: jinja | |
1084 | |
1085 Users on this page: {{ users|map(attribute='username')|join(', ') }} | |
1086 | |
1087 You can specify a ``default`` value to use if an object in the list | |
1088 does not have the given attribute. | |
1089 | |
1090 .. sourcecode:: jinja | |
1091 | |
1092 {{ users|map(attribute="username", default="Anonymous")|join(", ") }} | |
1093 | |
1094 Alternatively you can let it invoke a filter by passing the name of the | |
1095 filter and the arguments afterwards. A good example would be applying a | |
1096 text conversion filter on a sequence: | |
1097 | |
1098 .. sourcecode:: jinja | |
1099 | |
1100 Users on this page: {{ titles|map('lower')|join(', ') }} | |
1101 | |
1102 Similar to a generator comprehension such as: | |
1103 | |
1104 .. code-block:: python | |
1105 | |
1106 (u.username for u in users) | |
1107 (u.username or "Anonymous" for u in users) | |
1108 (do_lower(x) for x in titles) | |
1109 | |
1110 .. versionchanged:: 2.11.0 | |
1111 Added the ``default`` parameter. | |
1112 | |
1113 .. versionadded:: 2.7 | |
1114 """ | |
1115 seq, func = prepare_map(args, kwargs) | |
1116 if seq: | |
1117 for item in seq: | |
1118 yield func(item) | |
1119 | |
1120 | |
1121 @contextfilter | |
1122 def do_select(*args, **kwargs): | |
1123 """Filters a sequence of objects by applying a test to each object, | |
1124 and only selecting the objects with the test succeeding. | |
1125 | |
1126 If no test is specified, each object will be evaluated as a boolean. | |
1127 | |
1128 Example usage: | |
1129 | |
1130 .. sourcecode:: jinja | |
1131 | |
1132 {{ numbers|select("odd") }} | |
1133 {{ numbers|select("odd") }} | |
1134 {{ numbers|select("divisibleby", 3) }} | |
1135 {{ numbers|select("lessthan", 42) }} | |
1136 {{ strings|select("equalto", "mystring") }} | |
1137 | |
1138 Similar to a generator comprehension such as: | |
1139 | |
1140 .. code-block:: python | |
1141 | |
1142 (n for n in numbers if test_odd(n)) | |
1143 (n for n in numbers if test_divisibleby(n, 3)) | |
1144 | |
1145 .. versionadded:: 2.7 | |
1146 """ | |
1147 return select_or_reject(args, kwargs, lambda x: x, False) | |
1148 | |
1149 | |
1150 @contextfilter | |
1151 def do_reject(*args, **kwargs): | |
1152 """Filters a sequence of objects by applying a test to each object, | |
1153 and rejecting the objects with the test succeeding. | |
1154 | |
1155 If no test is specified, each object will be evaluated as a boolean. | |
1156 | |
1157 Example usage: | |
1158 | |
1159 .. sourcecode:: jinja | |
1160 | |
1161 {{ numbers|reject("odd") }} | |
1162 | |
1163 Similar to a generator comprehension such as: | |
1164 | |
1165 .. code-block:: python | |
1166 | |
1167 (n for n in numbers if not test_odd(n)) | |
1168 | |
1169 .. versionadded:: 2.7 | |
1170 """ | |
1171 return select_or_reject(args, kwargs, lambda x: not x, False) | |
1172 | |
1173 | |
1174 @contextfilter | |
1175 def do_selectattr(*args, **kwargs): | |
1176 """Filters a sequence of objects by applying a test to the specified | |
1177 attribute of each object, and only selecting the objects with the | |
1178 test succeeding. | |
1179 | |
1180 If no test is specified, the attribute's value will be evaluated as | |
1181 a boolean. | |
1182 | |
1183 Example usage: | |
1184 | |
1185 .. sourcecode:: jinja | |
1186 | |
1187 {{ users|selectattr("is_active") }} | |
1188 {{ users|selectattr("email", "none") }} | |
1189 | |
1190 Similar to a generator comprehension such as: | |
1191 | |
1192 .. code-block:: python | |
1193 | |
1194 (u for user in users if user.is_active) | |
1195 (u for user in users if test_none(user.email)) | |
1196 | |
1197 .. versionadded:: 2.7 | |
1198 """ | |
1199 return select_or_reject(args, kwargs, lambda x: x, True) | |
1200 | |
1201 | |
1202 @contextfilter | |
1203 def do_rejectattr(*args, **kwargs): | |
1204 """Filters a sequence of objects by applying a test to the specified | |
1205 attribute of each object, and rejecting the objects with the test | |
1206 succeeding. | |
1207 | |
1208 If no test is specified, the attribute's value will be evaluated as | |
1209 a boolean. | |
1210 | |
1211 .. sourcecode:: jinja | |
1212 | |
1213 {{ users|rejectattr("is_active") }} | |
1214 {{ users|rejectattr("email", "none") }} | |
1215 | |
1216 Similar to a generator comprehension such as: | |
1217 | |
1218 .. code-block:: python | |
1219 | |
1220 (u for user in users if not user.is_active) | |
1221 (u for user in users if not test_none(user.email)) | |
1222 | |
1223 .. versionadded:: 2.7 | |
1224 """ | |
1225 return select_or_reject(args, kwargs, lambda x: not x, True) | |
1226 | |
1227 | |
1228 @evalcontextfilter | |
1229 def do_tojson(eval_ctx, value, indent=None): | |
1230 """Dumps a structure to JSON so that it's safe to use in ``<script>`` | |
1231 tags. It accepts the same arguments and returns a JSON string. Note that | |
1232 this is available in templates through the ``|tojson`` filter which will | |
1233 also mark the result as safe. Due to how this function escapes certain | |
1234 characters this is safe even if used outside of ``<script>`` tags. | |
1235 | |
1236 The following characters are escaped in strings: | |
1237 | |
1238 - ``<`` | |
1239 - ``>`` | |
1240 - ``&`` | |
1241 - ``'`` | |
1242 | |
1243 This makes it safe to embed such strings in any place in HTML with the | |
1244 notable exception of double quoted attributes. In that case single | |
1245 quote your attributes or HTML escape it in addition. | |
1246 | |
1247 The indent parameter can be used to enable pretty printing. Set it to | |
1248 the number of spaces that the structures should be indented with. | |
1249 | |
1250 Note that this filter is for use in HTML contexts only. | |
1251 | |
1252 .. versionadded:: 2.9 | |
1253 """ | |
1254 policies = eval_ctx.environment.policies | |
1255 dumper = policies["json.dumps_function"] | |
1256 options = policies["json.dumps_kwargs"] | |
1257 if indent is not None: | |
1258 options = dict(options) | |
1259 options["indent"] = indent | |
1260 return htmlsafe_json_dumps(value, dumper=dumper, **options) | |
1261 | |
1262 | |
1263 def prepare_map(args, kwargs): | |
1264 context = args[0] | |
1265 seq = args[1] | |
1266 default = None | |
1267 | |
1268 if len(args) == 2 and "attribute" in kwargs: | |
1269 attribute = kwargs.pop("attribute") | |
1270 default = kwargs.pop("default", None) | |
1271 if kwargs: | |
1272 raise FilterArgumentError( | |
1273 "Unexpected keyword argument %r" % next(iter(kwargs)) | |
1274 ) | |
1275 func = make_attrgetter(context.environment, attribute, default=default) | |
1276 else: | |
1277 try: | |
1278 name = args[2] | |
1279 args = args[3:] | |
1280 except LookupError: | |
1281 raise FilterArgumentError("map requires a filter argument") | |
1282 | |
1283 def func(item): | |
1284 return context.environment.call_filter( | |
1285 name, item, args, kwargs, context=context | |
1286 ) | |
1287 | |
1288 return seq, func | |
1289 | |
1290 | |
1291 def prepare_select_or_reject(args, kwargs, modfunc, lookup_attr): | |
1292 context = args[0] | |
1293 seq = args[1] | |
1294 if lookup_attr: | |
1295 try: | |
1296 attr = args[2] | |
1297 except LookupError: | |
1298 raise FilterArgumentError("Missing parameter for attribute name") | |
1299 transfunc = make_attrgetter(context.environment, attr) | |
1300 off = 1 | |
1301 else: | |
1302 off = 0 | |
1303 | |
1304 def transfunc(x): | |
1305 return x | |
1306 | |
1307 try: | |
1308 name = args[2 + off] | |
1309 args = args[3 + off :] | |
1310 | |
1311 def func(item): | |
1312 return context.environment.call_test(name, item, args, kwargs) | |
1313 | |
1314 except LookupError: | |
1315 func = bool | |
1316 | |
1317 return seq, lambda item: modfunc(func(transfunc(item))) | |
1318 | |
1319 | |
1320 def select_or_reject(args, kwargs, modfunc, lookup_attr): | |
1321 seq, func = prepare_select_or_reject(args, kwargs, modfunc, lookup_attr) | |
1322 if seq: | |
1323 for item in seq: | |
1324 if func(item): | |
1325 yield item | |
1326 | |
1327 | |
1328 FILTERS = { | |
1329 "abs": abs, | |
1330 "attr": do_attr, | |
1331 "batch": do_batch, | |
1332 "capitalize": do_capitalize, | |
1333 "center": do_center, | |
1334 "count": len, | |
1335 "d": do_default, | |
1336 "default": do_default, | |
1337 "dictsort": do_dictsort, | |
1338 "e": escape, | |
1339 "escape": escape, | |
1340 "filesizeformat": do_filesizeformat, | |
1341 "first": do_first, | |
1342 "float": do_float, | |
1343 "forceescape": do_forceescape, | |
1344 "format": do_format, | |
1345 "groupby": do_groupby, | |
1346 "indent": do_indent, | |
1347 "int": do_int, | |
1348 "join": do_join, | |
1349 "last": do_last, | |
1350 "length": len, | |
1351 "list": do_list, | |
1352 "lower": do_lower, | |
1353 "map": do_map, | |
1354 "min": do_min, | |
1355 "max": do_max, | |
1356 "pprint": do_pprint, | |
1357 "random": do_random, | |
1358 "reject": do_reject, | |
1359 "rejectattr": do_rejectattr, | |
1360 "replace": do_replace, | |
1361 "reverse": do_reverse, | |
1362 "round": do_round, | |
1363 "safe": do_mark_safe, | |
1364 "select": do_select, | |
1365 "selectattr": do_selectattr, | |
1366 "slice": do_slice, | |
1367 "sort": do_sort, | |
1368 "string": soft_unicode, | |
1369 "striptags": do_striptags, | |
1370 "sum": do_sum, | |
1371 "title": do_title, | |
1372 "trim": do_trim, | |
1373 "truncate": do_truncate, | |
1374 "unique": do_unique, | |
1375 "upper": do_upper, | |
1376 "urlencode": do_urlencode, | |
1377 "urlize": do_urlize, | |
1378 "wordcount": do_wordcount, | |
1379 "wordwrap": do_wordwrap, | |
1380 "xmlattr": do_xmlattr, | |
1381 "tojson": do_tojson, | |
1382 } |