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

471 statements  

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

1"""The runtime functions and state used by compiled templates.""" 

2import functools 

3import sys 

4import typing as t 

5from collections import abc 

6from itertools import chain 

7 

8from markupsafe import escape # noqa: F401 

9from markupsafe import Markup 

10from markupsafe import soft_str 

11 

12from .async_utils import auto_aiter 

13from .async_utils import auto_await # noqa: F401 

14from .exceptions import TemplateNotFound # noqa: F401 

15from .exceptions import TemplateRuntimeError # noqa: F401 

16from .exceptions import UndefinedError 

17from .nodes import EvalContext 

18from .utils import _PassArg 

19from .utils import concat 

20from .utils import internalcode 

21from .utils import missing 

22from .utils import Namespace # noqa: F401 

23from .utils import object_type_repr 

24from .utils import pass_eval_context 

25 

26V = t.TypeVar("V") 

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

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 logging 

31 import typing_extensions as te 

32 from .environment import Environment 

33 

34 class LoopRenderFunc(te.Protocol): 

35 def __call__( 

36 self, 

37 reciter: t.Iterable[V], 

38 loop_render_func: "LoopRenderFunc", 

39 depth: int = 0, 

40 ) -> str: 

41 ... 

42 

43 

44# these variables are exported to the template runtime 

45exported = [ 

46 "LoopContext", 

47 "TemplateReference", 

48 "Macro", 

49 "Markup", 

50 "TemplateRuntimeError", 

51 "missing", 

52 "escape", 

53 "markup_join", 

54 "str_join", 

55 "identity", 

56 "TemplateNotFound", 

57 "Namespace", 

58 "Undefined", 

59 "internalcode", 

60] 

61async_exported = [ 

62 "AsyncLoopContext", 

63 "auto_aiter", 

64 "auto_await", 

65] 

66 

67 

68def identity(x: V) -> V: 

69 """Returns its argument. Useful for certain things in the 

70 environment. 

71 """ 

72 return x 

73 

74 

75def markup_join(seq: t.Iterable[t.Any]) -> str: 

76 """Concatenation that escapes if necessary and converts to string.""" 

77 buf = [] 

78 iterator = map(soft_str, seq) 

79 for arg in iterator: 

80 buf.append(arg) 

81 if hasattr(arg, "__html__"): 

82 return Markup("").join(chain(buf, iterator)) 

83 return concat(buf) 

84 

85 

86def str_join(seq: t.Iterable[t.Any]) -> str: 

87 """Simple args to string conversion and concatenation.""" 

88 return concat(map(str, seq)) 

89 

90 

91def new_context( 

92 environment: "Environment", 

93 template_name: t.Optional[str], 

94 blocks: t.Dict[str, t.Callable[["Context"], t.Iterator[str]]], 

95 vars: t.Optional[t.Dict[str, t.Any]] = None, 

96 shared: bool = False, 

97 globals: t.Optional[t.MutableMapping[str, t.Any]] = None, 

98 locals: t.Optional[t.Mapping[str, t.Any]] = None, 

99) -> "Context": 

100 """Internal helper for context creation.""" 

101 if vars is None: 

102 vars = {} 

103 if shared: 

104 parent = vars 

105 else: 

106 parent = dict(globals or (), **vars) 

107 if locals: 

108 # if the parent is shared a copy should be created because 

109 # we don't want to modify the dict passed 

110 if shared: 

111 parent = dict(parent) 

112 for key, value in locals.items(): 

113 if value is not missing: 

114 parent[key] = value 

115 return environment.context_class( 

116 environment, parent, template_name, blocks, globals=globals 

117 ) 

118 

119 

120class TemplateReference: 

121 """The `self` in templates.""" 

122 

123 def __init__(self, context: "Context") -> None: 

124 self.__context = context 

125 

126 def __getitem__(self, name: str) -> t.Any: 

127 blocks = self.__context.blocks[name] 

128 return BlockReference(name, self.__context, blocks, 0) 

129 

130 def __repr__(self) -> str: 

131 return f"<{type(self).__name__} {self.__context.name!r}>" 

132 

133 

134def _dict_method_all(dict_method: F) -> F: 

135 @functools.wraps(dict_method) 

136 def f_all(self: "Context") -> t.Any: 

137 return dict_method(self.get_all()) 

138 

139 return t.cast(F, f_all) 

140 

141 

142@abc.Mapping.register 

143class Context: 

144 """The template context holds the variables of a template. It stores the 

145 values passed to the template and also the names the template exports. 

146 Creating instances is neither supported nor useful as it's created 

147 automatically at various stages of the template evaluation and should not 

148 be created by hand. 

149 

150 The context is immutable. Modifications on :attr:`parent` **must not** 

151 happen and modifications on :attr:`vars` are allowed from generated 

152 template code only. Template filters and global functions marked as 

153 :func:`pass_context` get the active context passed as first argument 

154 and are allowed to access the context read-only. 

155 

156 The template context supports read only dict operations (`get`, 

157 `keys`, `values`, `items`, `iterkeys`, `itervalues`, `iteritems`, 

158 `__getitem__`, `__contains__`). Additionally there is a :meth:`resolve` 

159 method that doesn't fail with a `KeyError` but returns an 

160 :class:`Undefined` object for missing variables. 

161 """ 

162 

163 def __init__( 

164 self, 

165 environment: "Environment", 

166 parent: t.Dict[str, t.Any], 

167 name: t.Optional[str], 

168 blocks: t.Dict[str, t.Callable[["Context"], t.Iterator[str]]], 

169 globals: t.Optional[t.MutableMapping[str, t.Any]] = None, 

170 ): 

171 self.parent = parent 

172 self.vars: t.Dict[str, t.Any] = {} 

173 self.environment: "Environment" = environment 

174 self.eval_ctx = EvalContext(self.environment, name) 

175 self.exported_vars: t.Set[str] = set() 

176 self.name = name 

177 self.globals_keys = set() if globals is None else set(globals) 

178 

179 # create the initial mapping of blocks. Whenever template inheritance 

180 # takes place the runtime will update this mapping with the new blocks 

181 # from the template. 

182 self.blocks = {k: [v] for k, v in blocks.items()} 

183 

184 def super( 

185 self, name: str, current: t.Callable[["Context"], t.Iterator[str]] 

186 ) -> t.Union["BlockReference", "Undefined"]: 

187 """Render a parent block.""" 

188 try: 

189 blocks = self.blocks[name] 

190 index = blocks.index(current) + 1 

191 blocks[index] 

192 except LookupError: 

193 return self.environment.undefined( 

194 f"there is no parent block called {name!r}.", name="super" 

195 ) 

196 return BlockReference(name, self, blocks, index) 

197 

198 def get(self, key: str, default: t.Any = None) -> t.Any: 

199 """Look up a variable by name, or return a default if the key is 

200 not found. 

201 

202 :param key: The variable name to look up. 

203 :param default: The value to return if the key is not found. 

204 """ 

205 try: 

206 return self[key] 

207 except KeyError: 

208 return default 

209 

210 def resolve(self, key: str) -> t.Union[t.Any, "Undefined"]: 

211 """Look up a variable by name, or return an :class:`Undefined` 

212 object if the key is not found. 

213 

214 If you need to add custom behavior, override 

215 :meth:`resolve_or_missing`, not this method. The various lookup 

216 functions use that method, not this one. 

217 

218 :param key: The variable name to look up. 

219 """ 

220 rv = self.resolve_or_missing(key) 

221 

222 if rv is missing: 

223 return self.environment.undefined(name=key) 

224 

225 return rv 

226 

227 def resolve_or_missing(self, key: str) -> t.Any: 

228 """Look up a variable by name, or return a ``missing`` sentinel 

229 if the key is not found. 

230 

231 Override this method to add custom lookup behavior. 

232 :meth:`resolve`, :meth:`get`, and :meth:`__getitem__` use this 

233 method. Don't call this method directly. 

234 

235 :param key: The variable name to look up. 

236 """ 

237 if key in self.vars: 

238 return self.vars[key] 

239 

240 if key in self.parent: 

241 return self.parent[key] 

242 

243 return missing 

244 

245 def get_exported(self) -> t.Dict[str, t.Any]: 

246 """Get a new dict with the exported variables.""" 

247 return {k: self.vars[k] for k in self.exported_vars} 

248 

249 def get_all(self) -> t.Dict[str, t.Any]: 

250 """Return the complete context as dict including the exported 

251 variables. For optimizations reasons this might not return an 

252 actual copy so be careful with using it. 

253 """ 

254 if not self.vars: 

255 return self.parent 

256 if not self.parent: 

257 return self.vars 

258 return dict(self.parent, **self.vars) 

259 

260 @internalcode 

261 def call( 

262 __self, __obj: t.Callable, *args: t.Any, **kwargs: t.Any # noqa: B902 

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

264 """Call the callable with the arguments and keyword arguments 

265 provided but inject the active context or environment as first 

266 argument if the callable has :func:`pass_context` or 

267 :func:`pass_environment`. 

268 """ 

269 if __debug__: 

270 __traceback_hide__ = True # noqa 

271 

272 # Allow callable classes to take a context 

273 if ( 

274 hasattr(__obj, "__call__") # noqa: B004 

275 and _PassArg.from_obj(__obj.__call__) is not None # type: ignore 

276 ): 

277 __obj = __obj.__call__ # type: ignore 

278 

279 pass_arg = _PassArg.from_obj(__obj) 

280 

281 if pass_arg is _PassArg.context: 

282 # the active context should have access to variables set in 

283 # loops and blocks without mutating the context itself 

284 if kwargs.get("_loop_vars"): 

285 __self = __self.derived(kwargs["_loop_vars"]) 

286 if kwargs.get("_block_vars"): 

287 __self = __self.derived(kwargs["_block_vars"]) 

288 args = (__self,) + args 

289 elif pass_arg is _PassArg.eval_context: 

290 args = (__self.eval_ctx,) + args 

291 elif pass_arg is _PassArg.environment: 

292 args = (__self.environment,) + args 

293 

294 kwargs.pop("_block_vars", None) 

295 kwargs.pop("_loop_vars", None) 

296 

297 try: 

298 return __obj(*args, **kwargs) 

299 except StopIteration: 

300 return __self.environment.undefined( 

301 "value was undefined because a callable raised a" 

302 " StopIteration exception" 

303 ) 

304 

305 def derived(self, locals: t.Optional[t.Dict[str, t.Any]] = None) -> "Context": 

306 """Internal helper function to create a derived context. This is 

307 used in situations where the system needs a new context in the same 

308 template that is independent. 

309 """ 

310 context = new_context( 

311 self.environment, self.name, {}, self.get_all(), True, None, locals 

312 ) 

313 context.eval_ctx = self.eval_ctx 

314 context.blocks.update((k, list(v)) for k, v in self.blocks.items()) 

315 return context 

316 

317 keys = _dict_method_all(dict.keys) 

318 values = _dict_method_all(dict.values) 

319 items = _dict_method_all(dict.items) 

320 

321 def __contains__(self, name: str) -> bool: 

322 return name in self.vars or name in self.parent 

323 

324 def __getitem__(self, key: str) -> t.Any: 

325 """Look up a variable by name with ``[]`` syntax, or raise a 

326 ``KeyError`` if the key is not found. 

327 """ 

328 item = self.resolve_or_missing(key) 

329 

330 if item is missing: 

331 raise KeyError(key) 

332 

333 return item 

334 

335 def __repr__(self) -> str: 

336 return f"<{type(self).__name__} {self.get_all()!r} of {self.name!r}>" 

337 

338 

339class BlockReference: 

340 """One block on a template reference.""" 

341 

342 def __init__( 

343 self, 

344 name: str, 

345 context: "Context", 

346 stack: t.List[t.Callable[["Context"], t.Iterator[str]]], 

347 depth: int, 

348 ) -> None: 

349 self.name = name 

350 self._context = context 

351 self._stack = stack 

352 self._depth = depth 

353 

354 @property 

355 def super(self) -> t.Union["BlockReference", "Undefined"]: 

356 """Super the block.""" 

357 if self._depth + 1 >= len(self._stack): 

358 return self._context.environment.undefined( 

359 f"there is no parent block called {self.name!r}.", name="super" 

360 ) 

361 return BlockReference(self.name, self._context, self._stack, self._depth + 1) 

362 

363 @internalcode 

364 async def _async_call(self) -> str: 

365 rv = concat( 

366 [x async for x in self._stack[self._depth](self._context)] # type: ignore 

367 ) 

368 

369 if self._context.eval_ctx.autoescape: 

370 return Markup(rv) 

371 

372 return rv 

373 

374 @internalcode 

375 def __call__(self) -> str: 

376 if self._context.environment.is_async: 

377 return self._async_call() # type: ignore 

378 

379 rv = concat(self._stack[self._depth](self._context)) 

380 

381 if self._context.eval_ctx.autoescape: 

382 return Markup(rv) 

383 

384 return rv 

385 

386 

387class LoopContext: 

388 """A wrapper iterable for dynamic ``for`` loops, with information 

389 about the loop and iteration. 

390 """ 

391 

392 #: Current iteration of the loop, starting at 0. 

393 index0 = -1 

394 

395 _length: t.Optional[int] = None 

396 _after: t.Any = missing 

397 _current: t.Any = missing 

398 _before: t.Any = missing 

399 _last_changed_value: t.Any = missing 

400 

401 def __init__( 

402 self, 

403 iterable: t.Iterable[V], 

404 undefined: t.Type["Undefined"], 

405 recurse: t.Optional["LoopRenderFunc"] = None, 

406 depth0: int = 0, 

407 ) -> None: 

408 """ 

409 :param iterable: Iterable to wrap. 

410 :param undefined: :class:`Undefined` class to use for next and 

411 previous items. 

412 :param recurse: The function to render the loop body when the 

413 loop is marked recursive. 

414 :param depth0: Incremented when looping recursively. 

415 """ 

416 self._iterable = iterable 

417 self._iterator = self._to_iterator(iterable) 

418 self._undefined = undefined 

419 self._recurse = recurse 

420 #: How many levels deep a recursive loop currently is, starting at 0. 

421 self.depth0 = depth0 

422 

423 @staticmethod 

424 def _to_iterator(iterable: t.Iterable[V]) -> t.Iterator[V]: 

425 return iter(iterable) 

426 

427 @property 

428 def length(self) -> int: 

429 """Length of the iterable. 

430 

431 If the iterable is a generator or otherwise does not have a 

432 size, it is eagerly evaluated to get a size. 

433 """ 

434 if self._length is not None: 

435 return self._length 

436 

437 try: 

438 self._length = len(self._iterable) # type: ignore 

439 except TypeError: 

440 iterable = list(self._iterator) 

441 self._iterator = self._to_iterator(iterable) 

442 self._length = len(iterable) + self.index + (self._after is not missing) 

443 

444 return self._length 

445 

446 def __len__(self) -> int: 

447 return self.length 

448 

449 @property 

450 def depth(self) -> int: 

451 """How many levels deep a recursive loop currently is, starting at 1.""" 

452 return self.depth0 + 1 

453 

454 @property 

455 def index(self) -> int: 

456 """Current iteration of the loop, starting at 1.""" 

457 return self.index0 + 1 

458 

459 @property 

460 def revindex0(self) -> int: 

461 """Number of iterations from the end of the loop, ending at 0. 

462 

463 Requires calculating :attr:`length`. 

464 """ 

465 return self.length - self.index 

466 

467 @property 

468 def revindex(self) -> int: 

469 """Number of iterations from the end of the loop, ending at 1. 

470 

471 Requires calculating :attr:`length`. 

472 """ 

473 return self.length - self.index0 

474 

475 @property 

476 def first(self) -> bool: 

477 """Whether this is the first iteration of the loop.""" 

478 return self.index0 == 0 

479 

480 def _peek_next(self) -> t.Any: 

481 """Return the next element in the iterable, or :data:`missing` 

482 if the iterable is exhausted. Only peeks one item ahead, caching 

483 the result in :attr:`_last` for use in subsequent checks. The 

484 cache is reset when :meth:`__next__` is called. 

485 """ 

486 if self._after is not missing: 

487 return self._after 

488 

489 self._after = next(self._iterator, missing) 

490 return self._after 

491 

492 @property 

493 def last(self) -> bool: 

494 """Whether this is the last iteration of the loop. 

495 

496 Causes the iterable to advance early. See 

497 :func:`itertools.groupby` for issues this can cause. 

498 The :func:`groupby` filter avoids that issue. 

499 """ 

500 return self._peek_next() is missing 

501 

502 @property 

503 def previtem(self) -> t.Union[t.Any, "Undefined"]: 

504 """The item in the previous iteration. Undefined during the 

505 first iteration. 

506 """ 

507 if self.first: 

508 return self._undefined("there is no previous item") 

509 

510 return self._before 

511 

512 @property 

513 def nextitem(self) -> t.Union[t.Any, "Undefined"]: 

514 """The item in the next iteration. Undefined during the last 

515 iteration. 

516 

517 Causes the iterable to advance early. See 

518 :func:`itertools.groupby` for issues this can cause. 

519 The :func:`jinja-filters.groupby` filter avoids that issue. 

520 """ 

521 rv = self._peek_next() 

522 

523 if rv is missing: 

524 return self._undefined("there is no next item") 

525 

526 return rv 

527 

528 def cycle(self, *args: V) -> V: 

529 """Return a value from the given args, cycling through based on 

530 the current :attr:`index0`. 

531 

532 :param args: One or more values to cycle through. 

533 """ 

534 if not args: 

535 raise TypeError("no items for cycling given") 

536 

537 return args[self.index0 % len(args)] 

538 

539 def changed(self, *value: t.Any) -> bool: 

540 """Return ``True`` if previously called with a different value 

541 (including when called for the first time). 

542 

543 :param value: One or more values to compare to the last call. 

544 """ 

545 if self._last_changed_value != value: 

546 self._last_changed_value = value 

547 return True 

548 

549 return False 

550 

551 def __iter__(self) -> "LoopContext": 

552 return self 

553 

554 def __next__(self) -> t.Tuple[t.Any, "LoopContext"]: 

555 if self._after is not missing: 

556 rv = self._after 

557 self._after = missing 

558 else: 

559 rv = next(self._iterator) 

560 

561 self.index0 += 1 

562 self._before = self._current 

563 self._current = rv 

564 return rv, self 

565 

566 @internalcode 

567 def __call__(self, iterable: t.Iterable[V]) -> str: 

568 """When iterating over nested data, render the body of the loop 

569 recursively with the given inner iterable data. 

570 

571 The loop must have the ``recursive`` marker for this to work. 

572 """ 

573 if self._recurse is None: 

574 raise TypeError( 

575 "The loop must have the 'recursive' marker to be called recursively." 

576 ) 

577 

578 return self._recurse(iterable, self._recurse, depth=self.depth) 

579 

580 def __repr__(self) -> str: 

581 return f"<{type(self).__name__} {self.index}/{self.length}>" 

582 

583 

584class AsyncLoopContext(LoopContext): 

585 _iterator: t.AsyncIterator[t.Any] # type: ignore 

586 

587 @staticmethod 

588 def _to_iterator( # type: ignore 

589 iterable: t.Union[t.Iterable[V], t.AsyncIterable[V]] 

590 ) -> t.AsyncIterator[V]: 

591 return auto_aiter(iterable) 

592 

593 @property 

594 async def length(self) -> int: # type: ignore 

595 if self._length is not None: 

596 return self._length 

597 

598 try: 

599 self._length = len(self._iterable) # type: ignore 

600 except TypeError: 

601 iterable = [x async for x in self._iterator] 

602 self._iterator = self._to_iterator(iterable) 

603 self._length = len(iterable) + self.index + (self._after is not missing) 

604 

605 return self._length 

606 

607 @property 

608 async def revindex0(self) -> int: # type: ignore 

609 return await self.length - self.index 

610 

611 @property 

612 async def revindex(self) -> int: # type: ignore 

613 return await self.length - self.index0 

614 

615 async def _peek_next(self) -> t.Any: 

616 if self._after is not missing: 

617 return self._after 

618 

619 try: 

620 self._after = await self._iterator.__anext__() 

621 except StopAsyncIteration: 

622 self._after = missing 

623 

624 return self._after 

625 

626 @property 

627 async def last(self) -> bool: # type: ignore 

628 return await self._peek_next() is missing 

629 

630 @property 

631 async def nextitem(self) -> t.Union[t.Any, "Undefined"]: 

632 rv = await self._peek_next() 

633 

634 if rv is missing: 

635 return self._undefined("there is no next item") 

636 

637 return rv 

638 

639 def __aiter__(self) -> "AsyncLoopContext": 

640 return self 

641 

642 async def __anext__(self) -> t.Tuple[t.Any, "AsyncLoopContext"]: 

643 if self._after is not missing: 

644 rv = self._after 

645 self._after = missing 

646 else: 

647 rv = await self._iterator.__anext__() 

648 

649 self.index0 += 1 

650 self._before = self._current 

651 self._current = rv 

652 return rv, self 

653 

654 

655class Macro: 

656 """Wraps a macro function.""" 

657 

658 def __init__( 

659 self, 

660 environment: "Environment", 

661 func: t.Callable[..., str], 

662 name: str, 

663 arguments: t.List[str], 

664 catch_kwargs: bool, 

665 catch_varargs: bool, 

666 caller: bool, 

667 default_autoescape: t.Optional[bool] = None, 

668 ): 

669 self._environment = environment 

670 self._func = func 

671 self._argument_count = len(arguments) 

672 self.name = name 

673 self.arguments = arguments 

674 self.catch_kwargs = catch_kwargs 

675 self.catch_varargs = catch_varargs 

676 self.caller = caller 

677 self.explicit_caller = "caller" in arguments 

678 

679 if default_autoescape is None: 

680 if callable(environment.autoescape): 

681 default_autoescape = environment.autoescape(None) 

682 else: 

683 default_autoescape = environment.autoescape 

684 

685 self._default_autoescape = default_autoescape 

686 

687 @internalcode 

688 @pass_eval_context 

689 def __call__(self, *args: t.Any, **kwargs: t.Any) -> str: 

690 # This requires a bit of explanation, In the past we used to 

691 # decide largely based on compile-time information if a macro is 

692 # safe or unsafe. While there was a volatile mode it was largely 

693 # unused for deciding on escaping. This turns out to be 

694 # problematic for macros because whether a macro is safe depends not 

695 # on the escape mode when it was defined, but rather when it was used. 

696 # 

697 # Because however we export macros from the module system and 

698 # there are historic callers that do not pass an eval context (and 

699 # will continue to not pass one), we need to perform an instance 

700 # check here. 

701 # 

702 # This is considered safe because an eval context is not a valid 

703 # argument to callables otherwise anyway. Worst case here is 

704 # that if no eval context is passed we fall back to the compile 

705 # time autoescape flag. 

706 if args and isinstance(args[0], EvalContext): 

707 autoescape = args[0].autoescape 

708 args = args[1:] 

709 else: 

710 autoescape = self._default_autoescape 

711 

712 # try to consume the positional arguments 

713 arguments = list(args[: self._argument_count]) 

714 off = len(arguments) 

715 

716 # For information why this is necessary refer to the handling 

717 # of caller in the `macro_body` handler in the compiler. 

718 found_caller = False 

719 

720 # if the number of arguments consumed is not the number of 

721 # arguments expected we start filling in keyword arguments 

722 # and defaults. 

723 if off != self._argument_count: 

724 for name in self.arguments[len(arguments) :]: 

725 try: 

726 value = kwargs.pop(name) 

727 except KeyError: 

728 value = missing 

729 if name == "caller": 

730 found_caller = True 

731 arguments.append(value) 

732 else: 

733 found_caller = self.explicit_caller 

734 

735 # it's important that the order of these arguments does not change 

736 # if not also changed in the compiler's `function_scoping` method. 

737 # the order is caller, keyword arguments, positional arguments! 

738 if self.caller and not found_caller: 

739 caller = kwargs.pop("caller", None) 

740 if caller is None: 

741 caller = self._environment.undefined("No caller defined", name="caller") 

742 arguments.append(caller) 

743 

744 if self.catch_kwargs: 

745 arguments.append(kwargs) 

746 elif kwargs: 

747 if "caller" in kwargs: 

748 raise TypeError( 

749 f"macro {self.name!r} was invoked with two values for the special" 

750 " caller argument. This is most likely a bug." 

751 ) 

752 raise TypeError( 

753 f"macro {self.name!r} takes no keyword argument {next(iter(kwargs))!r}" 

754 ) 

755 if self.catch_varargs: 

756 arguments.append(args[self._argument_count :]) 

757 elif len(args) > self._argument_count: 

758 raise TypeError( 

759 f"macro {self.name!r} takes not more than" 

760 f" {len(self.arguments)} argument(s)" 

761 ) 

762 

763 return self._invoke(arguments, autoescape) 

764 

765 async def _async_invoke(self, arguments: t.List[t.Any], autoescape: bool) -> str: 

766 rv = await self._func(*arguments) # type: ignore 

767 

768 if autoescape: 

769 return Markup(rv) 

770 

771 return rv # type: ignore 

772 

773 def _invoke(self, arguments: t.List[t.Any], autoescape: bool) -> str: 

774 if self._environment.is_async: 

775 return self._async_invoke(arguments, autoescape) # type: ignore 

776 

777 rv = self._func(*arguments) 

778 

779 if autoescape: 

780 rv = Markup(rv) 

781 

782 return rv 

783 

784 def __repr__(self) -> str: 

785 name = "anonymous" if self.name is None else repr(self.name) 

786 return f"<{type(self).__name__} {name}>" 

787 

788 

789class Undefined: 

790 """The default undefined type. This undefined type can be printed and 

791 iterated over, but every other access will raise an :exc:`UndefinedError`: 

792 

793 >>> foo = Undefined(name='foo') 

794 >>> str(foo) 

795 '' 

796 >>> not foo 

797 True 

798 >>> foo + 42 

799 Traceback (most recent call last): 

800 ... 

801 jinja2.exceptions.UndefinedError: 'foo' is undefined 

802 """ 

803 

804 __slots__ = ( 

805 "_undefined_hint", 

806 "_undefined_obj", 

807 "_undefined_name", 

808 "_undefined_exception", 

809 ) 

810 

811 def __init__( 

812 self, 

813 hint: t.Optional[str] = None, 

814 obj: t.Any = missing, 

815 name: t.Optional[str] = None, 

816 exc: t.Type[TemplateRuntimeError] = UndefinedError, 

817 ) -> None: 

818 self._undefined_hint = hint 

819 self._undefined_obj = obj 

820 self._undefined_name = name 

821 self._undefined_exception = exc 

822 

823 @property 

824 def _undefined_message(self) -> str: 

825 """Build a message about the undefined value based on how it was 

826 accessed. 

827 """ 

828 if self._undefined_hint: 

829 return self._undefined_hint 

830 

831 if self._undefined_obj is missing: 

832 return f"{self._undefined_name!r} is undefined" 

833 

834 if not isinstance(self._undefined_name, str): 

835 return ( 

836 f"{object_type_repr(self._undefined_obj)} has no" 

837 f" element {self._undefined_name!r}" 

838 ) 

839 

840 return ( 

841 f"{object_type_repr(self._undefined_obj)!r} has no" 

842 f" attribute {self._undefined_name!r}" 

843 ) 

844 

845 @internalcode 

846 def _fail_with_undefined_error( 

847 self, *args: t.Any, **kwargs: t.Any 

848 ) -> "te.NoReturn": 

849 """Raise an :exc:`UndefinedError` when operations are performed 

850 on the undefined value. 

851 """ 

852 raise self._undefined_exception(self._undefined_message) 

853 

854 @internalcode 

855 def __getattr__(self, name: str) -> t.Any: 

856 if name[:2] == "__": 

857 raise AttributeError(name) 

858 

859 return self._fail_with_undefined_error() 

860 

861 __add__ = __radd__ = __sub__ = __rsub__ = _fail_with_undefined_error 

862 __mul__ = __rmul__ = __div__ = __rdiv__ = _fail_with_undefined_error 

863 __truediv__ = __rtruediv__ = _fail_with_undefined_error 

864 __floordiv__ = __rfloordiv__ = _fail_with_undefined_error 

865 __mod__ = __rmod__ = _fail_with_undefined_error 

866 __pos__ = __neg__ = _fail_with_undefined_error 

867 __call__ = __getitem__ = _fail_with_undefined_error 

868 __lt__ = __le__ = __gt__ = __ge__ = _fail_with_undefined_error 

869 __int__ = __float__ = __complex__ = _fail_with_undefined_error 

870 __pow__ = __rpow__ = _fail_with_undefined_error 

871 

872 def __eq__(self, other: t.Any) -> bool: 

873 return type(self) is type(other) 

874 

875 def __ne__(self, other: t.Any) -> bool: 

876 return not self.__eq__(other) 

877 

878 def __hash__(self) -> int: 

879 return id(type(self)) 

880 

881 def __str__(self) -> str: 

882 return "" 

883 

884 def __len__(self) -> int: 

885 return 0 

886 

887 def __iter__(self) -> t.Iterator[t.Any]: 

888 yield from () 

889 

890 async def __aiter__(self) -> t.AsyncIterator[t.Any]: 

891 for _ in (): 

892 yield 

893 

894 def __bool__(self) -> bool: 

895 return False 

896 

897 def __repr__(self) -> str: 

898 return "Undefined" 

899 

900 

901def make_logging_undefined( 

902 logger: t.Optional["logging.Logger"] = None, base: t.Type[Undefined] = Undefined 

903) -> t.Type[Undefined]: 

904 """Given a logger object this returns a new undefined class that will 

905 log certain failures. It will log iterations and printing. If no 

906 logger is given a default logger is created. 

907 

908 Example:: 

909 

910 logger = logging.getLogger(__name__) 

911 LoggingUndefined = make_logging_undefined( 

912 logger=logger, 

913 base=Undefined 

914 ) 

915 

916 .. versionadded:: 2.8 

917 

918 :param logger: the logger to use. If not provided, a default logger 

919 is created. 

920 :param base: the base class to add logging functionality to. This 

921 defaults to :class:`Undefined`. 

922 """ 

923 if logger is None: 

924 import logging 

925 

926 logger = logging.getLogger(__name__) 

927 logger.addHandler(logging.StreamHandler(sys.stderr)) 

928 

929 def _log_message(undef: Undefined) -> None: 

930 logger.warning( # type: ignore 

931 "Template variable warning: %s", undef._undefined_message 

932 ) 

933 

934 class LoggingUndefined(base): # type: ignore 

935 __slots__ = () 

936 

937 def _fail_with_undefined_error( # type: ignore 

938 self, *args: t.Any, **kwargs: t.Any 

939 ) -> "te.NoReturn": 

940 try: 

941 super()._fail_with_undefined_error(*args, **kwargs) 

942 except self._undefined_exception as e: 

943 logger.error("Template variable error: %s", e) # type: ignore 

944 raise e 

945 

946 def __str__(self) -> str: 

947 _log_message(self) 

948 return super().__str__() # type: ignore 

949 

950 def __iter__(self) -> t.Iterator[t.Any]: 

951 _log_message(self) 

952 return super().__iter__() # type: ignore 

953 

954 def __bool__(self) -> bool: 

955 _log_message(self) 

956 return super().__bool__() # type: ignore 

957 

958 return LoggingUndefined 

959 

960 

961class ChainableUndefined(Undefined): 

962 """An undefined that is chainable, where both ``__getattr__`` and 

963 ``__getitem__`` return itself rather than raising an 

964 :exc:`UndefinedError`. 

965 

966 >>> foo = ChainableUndefined(name='foo') 

967 >>> str(foo.bar['baz']) 

968 '' 

969 >>> foo.bar['baz'] + 42 

970 Traceback (most recent call last): 

971 ... 

972 jinja2.exceptions.UndefinedError: 'foo' is undefined 

973 

974 .. versionadded:: 2.11.0 

975 """ 

976 

977 __slots__ = () 

978 

979 def __html__(self) -> str: 

980 return str(self) 

981 

982 def __getattr__(self, _: str) -> "ChainableUndefined": 

983 return self 

984 

985 __getitem__ = __getattr__ # type: ignore 

986 

987 

988class DebugUndefined(Undefined): 

989 """An undefined that returns the debug info when printed. 

990 

991 >>> foo = DebugUndefined(name='foo') 

992 >>> str(foo) 

993 '{{ foo }}' 

994 >>> not foo 

995 True 

996 >>> foo + 42 

997 Traceback (most recent call last): 

998 ... 

999 jinja2.exceptions.UndefinedError: 'foo' is undefined 

1000 """ 

1001 

1002 __slots__ = () 

1003 

1004 def __str__(self) -> str: 

1005 if self._undefined_hint: 

1006 message = f"undefined value printed: {self._undefined_hint}" 

1007 

1008 elif self._undefined_obj is missing: 

1009 message = self._undefined_name # type: ignore 

1010 

1011 else: 

1012 message = ( 

1013 f"no such element: {object_type_repr(self._undefined_obj)}" 

1014 f"[{self._undefined_name!r}]" 

1015 ) 

1016 

1017 return f"{{{{ {message} }}}}" 

1018 

1019 

1020class StrictUndefined(Undefined): 

1021 """An undefined that barks on print and iteration as well as boolean 

1022 tests and all kinds of comparisons. In other words: you can do nothing 

1023 with it except checking if it's defined using the `defined` test. 

1024 

1025 >>> foo = StrictUndefined(name='foo') 

1026 >>> str(foo) 

1027 Traceback (most recent call last): 

1028 ... 

1029 jinja2.exceptions.UndefinedError: 'foo' is undefined 

1030 >>> not foo 

1031 Traceback (most recent call last): 

1032 ... 

1033 jinja2.exceptions.UndefinedError: 'foo' is undefined 

1034 >>> foo + 42 

1035 Traceback (most recent call last): 

1036 ... 

1037 jinja2.exceptions.UndefinedError: 'foo' is undefined 

1038 """ 

1039 

1040 __slots__ = () 

1041 __iter__ = __str__ = __len__ = Undefined._fail_with_undefined_error 

1042 __eq__ = __ne__ = __bool__ = __hash__ = Undefined._fail_with_undefined_error 

1043 __contains__ = Undefined._fail_with_undefined_error 

1044 

1045 

1046# Remove slots attributes, after the metaclass is applied they are 

1047# unneeded and contain wrong data for subclasses. 

1048del ( 

1049 Undefined.__slots__, 

1050 ChainableUndefined.__slots__, 

1051 DebugUndefined.__slots__, 

1052 StrictUndefined.__slots__, 

1053)