Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/jinja2/filters.py: 21%

525 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

1"""Built-in template filters used with the ``|`` operator.""" 

2import math 

3import random 

4import re 

5import typing 

6import typing as t 

7from collections import abc 

8from itertools import chain 

9from itertools import groupby 

10 

11from markupsafe import escape 

12from markupsafe import Markup 

13from markupsafe import soft_str 

14 

15from .async_utils import async_variant 

16from .async_utils import auto_aiter 

17from .async_utils import auto_await 

18from .async_utils import auto_to_list 

19from .exceptions import FilterArgumentError 

20from .runtime import Undefined 

21from .utils import htmlsafe_json_dumps 

22from .utils import pass_context 

23from .utils import pass_environment 

24from .utils import pass_eval_context 

25from .utils import pformat 

26from .utils import url_quote 

27from .utils import urlize 

28 

29if t.TYPE_CHECKING: 29 ↛ 30line 29 didn't jump to line 30, because the condition on line 29 was never true

30 import typing_extensions as te 

31 from .environment import Environment 

32 from .nodes import EvalContext 

33 from .runtime import Context 

34 from .sandbox import SandboxedEnvironment # noqa: F401 

35 

36 class HasHTML(te.Protocol): 

37 def __html__(self) -> str: 

38 pass 

39 

40 

41F = t.TypeVar("F", bound=t.Callable[..., t.Any]) 

42K = t.TypeVar("K") 

43V = t.TypeVar("V") 

44 

45 

46def ignore_case(value: V) -> V: 

47 """For use as a postprocessor for :func:`make_attrgetter`. Converts strings 

48 to lowercase and returns other types as-is.""" 

49 if isinstance(value, str): 

50 return t.cast(V, value.lower()) 

51 

52 return value 

53 

54 

55def make_attrgetter( 

56 environment: "Environment", 

57 attribute: t.Optional[t.Union[str, int]], 

58 postprocess: t.Optional[t.Callable[[t.Any], t.Any]] = None, 

59 default: t.Optional[t.Any] = None, 

60) -> t.Callable[[t.Any], t.Any]: 

61 """Returns a callable that looks up the given attribute from a 

62 passed object with the rules of the environment. Dots are allowed 

63 to access attributes of attributes. Integer parts in paths are 

64 looked up as integers. 

65 """ 

66 parts = _prepare_attribute_parts(attribute) 

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 

83def make_multi_attrgetter( 

84 environment: "Environment", 

85 attribute: t.Optional[t.Union[str, int]], 

86 postprocess: t.Optional[t.Callable[[t.Any], t.Any]] = None, 

87) -> t.Callable[[t.Any], t.List[t.Any]]: 

88 """Returns a callable that looks up the given comma separated 

89 attributes from a passed object with the rules of the environment. 

90 Dots are allowed to access attributes of each attribute. Integer 

91 parts in paths are looked up as integers. 

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 

124def _prepare_attribute_parts( 

125 attr: t.Optional[t.Union[str, int]] 

126) -> t.List[t.Union[str, int]]: 

127 if attr is None: 

128 return [] 

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 

136def do_forceescape(value: "t.Union[str, HasHTML]") -> Markup: 

137 """Enforce HTML escaping. This will probably double escape variables.""" 

138 if hasattr(value, "__html__"): 

139 value = t.cast("HasHTML", value).__html__() 

140 

141 return escape(str(value)) 

142 

143 

144def do_urlencode( 

145 value: t.Union[str, t.Mapping[str, t.Any], t.Iterable[t.Tuple[str, t.Any]]] 

146) -> str: 

147 """Quote data for use in a URL path or query using UTF-8. 

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 

176def do_replace( 

177 eval_ctx: "EvalContext", s: str, old: str, new: str, count: t.Optional[int] = None 

178) -> str: 

179 """Return a copy of the value with all occurrences of a substring 

180 replaced with a new one. The first argument is the substring 

181 that should be replaced, the second is the replacement string. 

182 If the optional third argument ``count`` is given, only the first 

183 ``count`` occurrences are replaced: 

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 

211def do_upper(s: str) -> str: 

212 """Convert a value to uppercase.""" 

213 return soft_str(s).upper() 

214 

215 

216def do_lower(s: str) -> str: 

217 """Convert a value to lowercase.""" 

218 return soft_str(s).lower() 

219 

220 

221def do_items(value: t.Union[t.Mapping[K, V], Undefined]) -> t.Iterator[t.Tuple[K, V]]: 

222 """Return an iterator over the ``(key, value)`` items of a mapping. 

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 

252def do_xmlattr( 

253 eval_ctx: "EvalContext", d: t.Mapping[str, t.Any], autospace: bool = True 

254) -> str: 

255 """Create an SGML/XML attribute string based on the items in a dict. 

256 All values that are neither `none` nor `undefined` are automatically 

257 escaped: 

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 

292def do_capitalize(s: str) -> str: 

293 """Capitalize a value. The first character will be uppercase, all others 

294 lowercase. 

295 """ 

296 return soft_str(s).capitalize() 

297 

298 

299_word_beginning_split_re = re.compile(r"([-\s({\[<]+)") 

300 

301 

302def do_title(s: str) -> str: 

303 """Return a titlecased version of the value. I.e. words will start with 

304 uppercase letters, all remaining characters are lowercase. 

305 """ 

306 return "".join( 

307 [ 

308 item[0].upper() + item[1:].lower() 

309 for item in _word_beginning_split_re.split(soft_str(s)) 

310 if item 

311 ] 

312 ) 

313 

314 

315def do_dictsort( 

316 value: t.Mapping[K, V], 

317 case_sensitive: bool = False, 

318 by: 'te.Literal["key", "value"]' = "key", 

319 reverse: bool = False, 

320) -> t.List[t.Tuple[K, V]]: 

321 """Sort a dict and yield (key, value) pairs. Python dicts may not 

322 be in the order you want to display them in, so sort them first. 

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 

357def do_sort( 

358 environment: "Environment", 

359 value: "t.Iterable[V]", 

360 reverse: bool = False, 

361 case_sensitive: bool = False, 

362 attribute: t.Optional[t.Union[str, int]] = None, 

363) -> "t.List[V]": 

364 """Sort an iterable using Python's :func:`sorted`. 

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 

413def do_unique( 

414 environment: "Environment", 

415 value: "t.Iterable[V]", 

416 case_sensitive: bool = False, 

417 attribute: t.Optional[t.Union[str, int]] = None, 

418) -> "t.Iterator[V]": 

419 """Returns a list of unique items from the given iterable. 

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 

445def _min_or_max( 

446 environment: "Environment", 

447 value: "t.Iterable[V]", 

448 func: "t.Callable[..., V]", 

449 case_sensitive: bool, 

450 attribute: t.Optional[t.Union[str, int]], 

451) -> "t.Union[V, Undefined]": 

452 it = iter(value) 

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 

466def do_min( 

467 environment: "Environment", 

468 value: "t.Iterable[V]", 

469 case_sensitive: bool = False, 

470 attribute: t.Optional[t.Union[str, int]] = None, 

471) -> "t.Union[V, Undefined]": 

472 """Return the smallest item from the sequence. 

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 

486def do_max( 

487 environment: "Environment", 

488 value: "t.Iterable[V]", 

489 case_sensitive: bool = False, 

490 attribute: t.Optional[t.Union[str, int]] = None, 

491) -> "t.Union[V, Undefined]": 

492 """Return the largest item from the sequence. 

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 

505def do_default( 

506 value: V, 

507 default_value: V = "", # type: ignore 

508 boolean: bool = False, 

509) -> V: 

510 """If the value is undefined it will return the passed default value, 

511 otherwise the value of the variable: 

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 

539def sync_do_join( 

540 eval_ctx: "EvalContext", 

541 value: t.Iterable, 

542 d: str = "", 

543 attribute: t.Optional[t.Union[str, int]] = None, 

544) -> str: 

545 """Return a string which is the concatenation of the strings in the 

546 sequence. The separator between elements is an empty string per 

547 default, you can define it with the optional parameter: 

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 

597async def do_join( 

598 eval_ctx: "EvalContext", 

599 value: t.Union[t.AsyncIterable, t.Iterable], 

600 d: str = "", 

601 attribute: t.Optional[t.Union[str, int]] = None, 

602) -> str: 

603 return sync_do_join(eval_ctx, await auto_to_list(value), d, attribute) 

604 

605 

606def do_center(value: str, width: int = 80) -> str: 

607 """Centers the value in a field of a given width.""" 

608 return soft_str(value).center(width) 

609 

610 

611@pass_environment 

612def sync_do_first( 

613 environment: "Environment", seq: "t.Iterable[V]" 

614) -> "t.Union[V, Undefined]": 

615 """Return the first item of a sequence.""" 

616 try: 

617 return next(iter(seq)) 

618 except StopIteration: 

619 return environment.undefined("No first item, sequence was empty.") 

620 

621 

622@async_variant(sync_do_first) # type: ignore 

623async def do_first( 

624 environment: "Environment", seq: "t.Union[t.AsyncIterable[V], t.Iterable[V]]" 

625) -> "t.Union[V, Undefined]": 

626 try: 

627 return await auto_aiter(seq).__anext__() 

628 except StopAsyncIteration: 

629 return environment.undefined("No first item, sequence was empty.") 

630 

631 

632@pass_environment 

633def do_last( 

634 environment: "Environment", seq: "t.Reversible[V]" 

635) -> "t.Union[V, Undefined]": 

636 """Return the last item of a sequence. 

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 

655def do_random(context: "Context", seq: "t.Sequence[V]") -> "t.Union[V, Undefined]": 

656 """Return a random item from the sequence.""" 

657 try: 

658 return random.choice(seq) 

659 except IndexError: 

660 return context.environment.undefined("No random item, sequence was empty.") 

661 

662 

663def do_filesizeformat(value: t.Union[str, float, int], binary: bool = False) -> str: 

664 """Format the value like a 'human-readable' file size (i.e. 13 kB, 

665 4.1 MB, 102 Bytes, etc). Per default decimal prefixes are used (Mega, 

666 Giga, etc.), if the second parameter is set to `True` the binary 

667 prefixes are used (Mebi, Gibi). 

668 """ 

669 bytes = float(value) 

670 base = 1024 if binary else 1000 

671 prefixes = [ 

672 ("KiB" if binary else "kB"), 

673 ("MiB" if binary else "MB"), 

674 ("GiB" if binary else "GB"), 

675 ("TiB" if binary else "TB"), 

676 ("PiB" if binary else "PB"), 

677 ("EiB" if binary else "EB"), 

678 ("ZiB" if binary else "ZB"), 

679 ("YiB" if binary else "YB"), 

680 ] 

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 

696def 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 

705def do_urlize( 

706 eval_ctx: "EvalContext", 

707 value: str, 

708 trim_url_limit: t.Optional[int] = None, 

709 nofollow: bool = False, 

710 target: t.Optional[str] = None, 

711 rel: t.Optional[str] = None, 

712 extra_schemes: t.Optional[t.Iterable[str]] = None, 

713) -> str: 

714 """Convert URLs in text into clickable links. 

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 

784def do_indent( 

785 s: str, width: t.Union[int, str] = 4, first: bool = False, blank: bool = False 

786) -> str: 

787 """Return a copy of the string with each line indented by 4 spaces. The 

788 first line and blank lines are not indented by default. 

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 

833def do_truncate( 

834 env: "Environment", 

835 s: str, 

836 length: int = 255, 

837 killwords: bool = False, 

838 end: str = "...", 

839 leeway: t.Optional[int] = None, 

840) -> str: 

841 """Return a truncated copy of the string. The length is specified 

842 with the first parameter which defaults to ``255``. If the second 

843 parameter is ``true`` the filter will cut the text at length. Otherwise 

844 it will discard the last word. If the text was in fact 

845 truncated it will append an ellipsis sign (``"..."``). If you want a 

846 different ellipsis sign than ``"..."`` you can specify it using the 

847 third parameter. Strings that only exceed the length by the tolerance 

848 margin given in the fourth parameter will not be truncated. 

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 

881def do_wordwrap( 

882 environment: "Environment", 

883 s: str, 

884 width: int = 79, 

885 break_long_words: bool = True, 

886 wrapstring: t.Optional[str] = None, 

887 break_on_hyphens: bool = True, 

888) -> str: 

889 """Wrap a string to the given width. Existing newlines are treated 

890 as paragraphs to be wrapped separately. 

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 

939def do_wordcount(s: str) -> int: 

940 """Count the words in that string.""" 

941 return len(_word_re.findall(soft_str(s))) 

942 

943 

944def do_int(value: t.Any, default: int = 0, base: int = 10) -> int: 

945 """Convert the value into an integer. If the 

946 conversion doesn't work it will return ``0``. You can 

947 override this default using the first parameter. You 

948 can also override the default base (10) in the second 

949 parameter, which handles input with prefixes such as 

950 0b, 0o and 0x for bases 2, 8 and 16 respectively. 

951 The base is ignored for decimal numbers and non-string values. 

952 """ 

953 try: 

954 if isinstance(value, str): 

955 return int(value, base) 

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 

966def do_float(value: t.Any, default: float = 0.0) -> float: 

967 """Convert the value into a floating point number. If the 

968 conversion doesn't work it will return ``0.0``. You can 

969 override this default using the first parameter. 

970 """ 

971 try: 

972 return float(value) 

973 except (TypeError, ValueError): 

974 return default 

975 

976 

977def do_format(value: str, *args: t.Any, **kwargs: t.Any) -> str: 

978 """Apply the given values to a `printf-style`_ format string, like 

979 ``string % values``. 

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 

1005def do_trim(value: str, chars: t.Optional[str] = None) -> str: 

1006 """Strip leading and trailing characters, by default whitespace.""" 

1007 return soft_str(value).strip(chars) 

1008 

1009 

1010def do_striptags(value: "t.Union[str, HasHTML]") -> str: 

1011 """Strip SGML/XML tags and replace adjacent whitespace by one space.""" 

1012 if hasattr(value, "__html__"): 

1013 value = t.cast("HasHTML", value).__html__() 

1014 

1015 return Markup(str(value)).striptags() 

1016 

1017 

1018def sync_do_slice( 

1019 value: "t.Collection[V]", slices: int, fill_with: "t.Optional[V]" = None 

1020) -> "t.Iterator[t.List[V]]": 

1021 """Slice an iterator and return a list of lists containing 

1022 those items. Useful if you want to create a div containing 

1023 three ul tags that represent columns: 

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 

1062async def do_slice( 

1063 value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", 

1064 slices: int, 

1065 fill_with: t.Optional[t.Any] = None, 

1066) -> "t.Iterator[t.List[V]]": 

1067 return sync_do_slice(await auto_to_list(value), slices, fill_with) 

1068 

1069 

1070def do_batch( 

1071 value: "t.Iterable[V]", linecount: int, fill_with: "t.Optional[V]" = None 

1072) -> "t.Iterator[t.List[V]]": 

1073 """ 

1074 A filter that batches items. It works pretty much like `slice` 

1075 just the other way round. It returns a list of lists with the 

1076 given number of items. If you provide a second parameter this 

1077 is used to fill up missing items. See this example: 

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 

1107def do_round( 

1108 value: float, 

1109 precision: int = 0, 

1110 method: 'te.Literal["common", "ceil", "floor"]' = "common", 

1111) -> float: 

1112 """Round the number to a given precision. The first 

1113 parameter specifies the precision (default is ``0``), the 

1114 second the rounding method: 

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 

1147class _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 

1161def sync_do_groupby( 

1162 environment: "Environment", 

1163 value: "t.Iterable[V]", 

1164 attribute: t.Union[str, int], 

1165 default: t.Optional[t.Any] = None, 

1166 case_sensitive: bool = False, 

1167) -> "t.List[_GroupTuple]": 

1168 """Group a sequence of objects by an attribute using Python's 

1169 :func:`itertools.groupby`. The attribute can use dot notation for 

1170 nested access, like ``"address.city"``. Unlike Python's ``groupby``, 

1171 the values are sorted first so only one group is returned for each 

1172 unique value. 

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 

1245async def do_groupby( 

1246 environment: "Environment", 

1247 value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", 

1248 attribute: t.Union[str, int], 

1249 default: t.Optional[t.Any] = None, 

1250 case_sensitive: bool = False, 

1251) -> "t.List[_GroupTuple]": 

1252 expr = make_attrgetter( 

1253 environment, 

1254 attribute, 

1255 postprocess=ignore_case if not case_sensitive else None, 

1256 default=default, 

1257 ) 

1258 out = [ 

1259 _GroupTuple(key, await auto_to_list(values)) 

1260 for key, values in groupby(sorted(await auto_to_list(value), key=expr), expr) 

1261 ] 

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 

1272def sync_do_sum( 

1273 environment: "Environment", 

1274 iterable: "t.Iterable[V]", 

1275 attribute: t.Optional[t.Union[str, int]] = None, 

1276 start: V = 0, # type: ignore 

1277) -> V: 

1278 """Returns the sum of a sequence of numbers plus the value of parameter 

1279 'start' (which defaults to 0). When the sequence is empty it returns 

1280 start. 

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 

1299async def do_sum( 

1300 environment: "Environment", 

1301 iterable: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", 

1302 attribute: t.Optional[t.Union[str, int]] = None, 

1303 start: V = 0, # type: ignore 

1304) -> V: 

1305 rv = start 

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 

1320def sync_do_list(value: "t.Iterable[V]") -> "t.List[V]": 

1321 """Convert the value into a list. If it was a string the returned list 

1322 will be a list of characters. 

1323 """ 

1324 return list(value) 

1325 

1326 

1327@async_variant(sync_do_list) # type: ignore 

1328async def do_list(value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]") -> "t.List[V]": 

1329 return await auto_to_list(value) 

1330 

1331 

1332def do_mark_safe(value: str) -> Markup: 

1333 """Mark the value as safe which means that in an environment with automatic 

1334 escaping enabled this variable will not be escaped. 

1335 """ 

1336 return Markup(value) 

1337 

1338 

1339def do_mark_unsafe(value: str) -> str: 

1340 """Mark a value as unsafe. This is the reverse operation for :func:`safe`.""" 

1341 return str(value) 

1342 

1343 

1344@typing.overload 

1345def do_reverse(value: str) -> str: 

1346 ... 

1347 

1348 

1349@typing.overload 

1350def do_reverse(value: "t.Iterable[V]") -> "t.Iterable[V]": 

1351 ... 

1352 

1353 

1354def do_reverse(value: t.Union[str, t.Iterable[V]]) -> t.Union[str, t.Iterable[V]]: 

1355 """Reverse the object or return an iterator that iterates over it the other 

1356 way round. 

1357 """ 

1358 if isinstance(value, str): 

1359 return value[::-1] 

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 

1373def do_attr( 

1374 environment: "Environment", obj: t.Any, name: str 

1375) -> t.Union[Undefined, t.Any]: 

1376 """Get an attribute of an object. ``foo|attr("bar")`` works like 

1377 ``foo.bar`` just that always an attribute is returned and items are not 

1378 looked up. 

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 

1404def 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 

1411def sync_do_map( 

1412 context: "Context", 

1413 value: t.Iterable, 

1414 *, 

1415 attribute: str = ..., 

1416 default: t.Optional[t.Any] = None, 

1417) -> t.Iterable: 

1418 ... 

1419 

1420 

1421@pass_context 

1422def sync_do_map( 

1423 context: "Context", value: t.Iterable, *args: t.Any, **kwargs: t.Any 

1424) -> t.Iterable: 

1425 """Applies a filter on a sequence of objects or looks up an attribute. 

1426 This is useful when dealing with lists of objects but you are really 

1427 only interested in a certain value of it. 

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 

1472def do_map( 

1473 context: "Context", 

1474 value: t.Union[t.AsyncIterable, t.Iterable], 

1475 name: str, 

1476 *args: t.Any, 

1477 **kwargs: t.Any, 

1478) -> t.Iterable: 

1479 ... 

1480 

1481 

1482@typing.overload 

1483def do_map( 

1484 context: "Context", 

1485 value: t.Union[t.AsyncIterable, t.Iterable], 

1486 *, 

1487 attribute: str = ..., 

1488 default: t.Optional[t.Any] = None, 

1489) -> t.Iterable: 

1490 ... 

1491 

1492 

1493@async_variant(sync_do_map) # type: ignore 

1494async def do_map( 

1495 context: "Context", 

1496 value: t.Union[t.AsyncIterable, t.Iterable], 

1497 *args: t.Any, 

1498 **kwargs: t.Any, 

1499) -> t.AsyncIterable: 

1500 if value: 

1501 func = prepare_map(context, args, kwargs) 

1502 

1503 async for item in auto_aiter(value): 

1504 yield await auto_await(func(item)) 

1505 

1506 

1507@pass_context 

1508def sync_do_select( 

1509 context: "Context", value: "t.Iterable[V]", *args: t.Any, **kwargs: t.Any 

1510) -> "t.Iterator[V]": 

1511 """Filters a sequence of objects by applying a test to each object, 

1512 and only selecting the objects with the test succeeding. 

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 

1539async def do_select( 

1540 context: "Context", 

1541 value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", 

1542 *args: t.Any, 

1543 **kwargs: t.Any, 

1544) -> "t.AsyncIterator[V]": 

1545 return async_select_or_reject(context, value, args, kwargs, lambda x: x, False) 

1546 

1547 

1548@pass_context 

1549def sync_do_reject( 

1550 context: "Context", value: "t.Iterable[V]", *args: t.Any, **kwargs: t.Any 

1551) -> "t.Iterator[V]": 

1552 """Filters a sequence of objects by applying a test to each object, 

1553 and rejecting the objects with the test succeeding. 

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 

1575async def do_reject( 

1576 context: "Context", 

1577 value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", 

1578 *args: t.Any, 

1579 **kwargs: t.Any, 

1580) -> "t.AsyncIterator[V]": 

1581 return async_select_or_reject(context, value, args, kwargs, lambda x: not x, False) 

1582 

1583 

1584@pass_context 

1585def sync_do_selectattr( 

1586 context: "Context", value: "t.Iterable[V]", *args: t.Any, **kwargs: t.Any 

1587) -> "t.Iterator[V]": 

1588 """Filters a sequence of objects by applying a test to the specified 

1589 attribute of each object, and only selecting the objects with the 

1590 test succeeding. 

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 

1615async def do_selectattr( 

1616 context: "Context", 

1617 value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", 

1618 *args: t.Any, 

1619 **kwargs: t.Any, 

1620) -> "t.AsyncIterator[V]": 

1621 return async_select_or_reject(context, value, args, kwargs, lambda x: x, True) 

1622 

1623 

1624@pass_context 

1625def sync_do_rejectattr( 

1626 context: "Context", value: "t.Iterable[V]", *args: t.Any, **kwargs: t.Any 

1627) -> "t.Iterator[V]": 

1628 """Filters a sequence of objects by applying a test to the specified 

1629 attribute of each object, and rejecting the objects with the test 

1630 succeeding. 

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 

1653async def do_rejectattr( 

1654 context: "Context", 

1655 value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", 

1656 *args: t.Any, 

1657 **kwargs: t.Any, 

1658) -> "t.AsyncIterator[V]": 

1659 return async_select_or_reject(context, value, args, kwargs, lambda x: not x, True) 

1660 

1661 

1662@pass_eval_context 

1663def do_tojson( 

1664 eval_ctx: "EvalContext", value: t.Any, indent: t.Optional[int] = None 

1665) -> Markup: 

1666 """Serialize an object to a string of JSON, and mark it safe to 

1667 render in HTML. This filter is only for use in HTML documents. 

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 

1691def prepare_map( 

1692 context: "Context", args: t.Tuple, kwargs: t.Dict[str, t.Any] 

1693) -> t.Callable[[t.Any], t.Any]: 

1694 if not args and "attribute" in kwargs: 

1695 attribute = kwargs.pop("attribute") 

1696 default = kwargs.pop("default", None) 

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 

1719def prepare_select_or_reject( 

1720 context: "Context", 

1721 args: t.Tuple, 

1722 kwargs: t.Dict[str, t.Any], 

1723 modfunc: t.Callable[[t.Any], t.Any], 

1724 lookup_attr: bool, 

1725) -> t.Callable[[t.Any], t.Any]: 

1726 if lookup_attr: 

1727 try: 

1728 attr = args[0] 

1729 except LookupError: 

1730 raise FilterArgumentError("Missing parameter for attribute name") from None 

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 

1753def select_or_reject( 

1754 context: "Context", 

1755 value: "t.Iterable[V]", 

1756 args: t.Tuple, 

1757 kwargs: t.Dict[str, t.Any], 

1758 modfunc: t.Callable[[t.Any], t.Any], 

1759 lookup_attr: bool, 

1760) -> "t.Iterator[V]": 

1761 if value: 

1762 func = prepare_select_or_reject(context, args, kwargs, modfunc, lookup_attr) 

1763 

1764 for item in value: 

1765 if func(item): 

1766 yield item 

1767 

1768 

1769async def async_select_or_reject( 

1770 context: "Context", 

1771 value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", 

1772 args: t.Tuple, 

1773 kwargs: t.Dict[str, t.Any], 

1774 modfunc: t.Callable[[t.Any], t.Any], 

1775 lookup_attr: bool, 

1776) -> "t.AsyncIterator[V]": 

1777 if value: 

1778 func = prepare_select_or_reject(context, args, kwargs, modfunc, lookup_attr) 

1779 

1780 async for item in auto_aiter(value): 

1781 if func(item): 

1782 yield item 

1783 

1784 

1785FILTERS = { 

1786 "abs": abs, 

1787 "attr": do_attr, 

1788 "batch": do_batch, 

1789 "capitalize": do_capitalize, 

1790 "center": do_center, 

1791 "count": len, 

1792 "d": do_default, 

1793 "default": do_default, 

1794 "dictsort": do_dictsort, 

1795 "e": escape, 

1796 "escape": escape, 

1797 "filesizeformat": do_filesizeformat, 

1798 "first": do_first, 

1799 "float": do_float, 

1800 "forceescape": do_forceescape, 

1801 "format": do_format, 

1802 "groupby": do_groupby, 

1803 "indent": do_indent, 

1804 "int": do_int, 

1805 "join": do_join, 

1806 "last": do_last, 

1807 "length": len, 

1808 "list": do_list, 

1809 "lower": do_lower, 

1810 "items": do_items, 

1811 "map": do_map, 

1812 "min": do_min, 

1813 "max": do_max, 

1814 "pprint": do_pprint, 

1815 "random": do_random, 

1816 "reject": do_reject, 

1817 "rejectattr": do_rejectattr, 

1818 "replace": do_replace, 

1819 "reverse": do_reverse, 

1820 "round": do_round, 

1821 "safe": do_mark_safe, 

1822 "select": do_select, 

1823 "selectattr": do_selectattr, 

1824 "slice": do_slice, 

1825 "sort": do_sort, 

1826 "string": soft_str, 

1827 "striptags": do_striptags, 

1828 "sum": do_sum, 

1829 "title": do_title, 

1830 "trim": do_trim, 

1831 "truncate": do_truncate, 

1832 "unique": do_unique, 

1833 "upper": do_upper, 

1834 "urlencode": do_urlencode, 

1835 "urlize": do_urlize, 

1836 "wordcount": do_wordcount, 

1837 "wordwrap": do_wordwrap, 

1838 "xmlattr": do_xmlattr, 

1839 "tojson": do_tojson, 

1840}