Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/jinja2/runtime.py: 49%

484 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2023-09-25 06:15 +0000

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: 

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, # noqa: B902 

263 __obj: t.Callable[..., t.Any], 

264 *args: t.Any, 

265 **kwargs: t.Any, 

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

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

268 provided but inject the active context or environment as first 

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

270 :func:`pass_environment`. 

271 """ 

272 if __debug__: 

273 __traceback_hide__ = True # noqa 

274 

275 # Allow callable classes to take a context 

276 if ( 

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

278 and _PassArg.from_obj(__obj.__call__) is not None 

279 ): 

280 __obj = __obj.__call__ 

281 

282 pass_arg = _PassArg.from_obj(__obj) 

283 

284 if pass_arg is _PassArg.context: 

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

286 # loops and blocks without mutating the context itself 

287 if kwargs.get("_loop_vars"): 

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

289 if kwargs.get("_block_vars"): 

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

291 args = (__self,) + args 

292 elif pass_arg is _PassArg.eval_context: 

293 args = (__self.eval_ctx,) + args 

294 elif pass_arg is _PassArg.environment: 

295 args = (__self.environment,) + args 

296 

297 kwargs.pop("_block_vars", None) 

298 kwargs.pop("_loop_vars", None) 

299 

300 try: 

301 return __obj(*args, **kwargs) 

302 except StopIteration: 

303 return __self.environment.undefined( 

304 "value was undefined because a callable raised a" 

305 " StopIteration exception" 

306 ) 

307 

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

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

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

311 template that is independent. 

312 """ 

313 context = new_context( 

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

315 ) 

316 context.eval_ctx = self.eval_ctx 

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

318 return context 

319 

320 keys = _dict_method_all(dict.keys) 

321 values = _dict_method_all(dict.values) 

322 items = _dict_method_all(dict.items) 

323 

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

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

326 

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

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

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

330 """ 

331 item = self.resolve_or_missing(key) 

332 

333 if item is missing: 

334 raise KeyError(key) 

335 

336 return item 

337 

338 def __repr__(self) -> str: 

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

340 

341 

342class BlockReference: 

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

344 

345 def __init__( 

346 self, 

347 name: str, 

348 context: "Context", 

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

350 depth: int, 

351 ) -> None: 

352 self.name = name 

353 self._context = context 

354 self._stack = stack 

355 self._depth = depth 

356 

357 @property 

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

359 """Super the block.""" 

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

361 return self._context.environment.undefined( 

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

363 ) 

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

365 

366 @internalcode 

367 async def _async_call(self) -> str: 

368 rv = concat( 

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

370 ) 

371 

372 if self._context.eval_ctx.autoescape: 

373 return Markup(rv) 

374 

375 return rv 

376 

377 @internalcode 

378 def __call__(self) -> str: 

379 if self._context.environment.is_async: 

380 return self._async_call() # type: ignore 

381 

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

383 

384 if self._context.eval_ctx.autoescape: 

385 return Markup(rv) 

386 

387 return rv 

388 

389 

390class LoopContext: 

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

392 about the loop and iteration. 

393 """ 

394 

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

396 index0 = -1 

397 

398 _length: t.Optional[int] = None 

399 _after: t.Any = missing 

400 _current: t.Any = missing 

401 _before: t.Any = missing 

402 _last_changed_value: t.Any = missing 

403 

404 def __init__( 

405 self, 

406 iterable: t.Iterable[V], 

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

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

409 depth0: int = 0, 

410 ) -> None: 

411 """ 

412 :param iterable: Iterable to wrap. 

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

414 previous items. 

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

416 loop is marked recursive. 

417 :param depth0: Incremented when looping recursively. 

418 """ 

419 self._iterable = iterable 

420 self._iterator = self._to_iterator(iterable) 

421 self._undefined = undefined 

422 self._recurse = recurse 

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

424 self.depth0 = depth0 

425 

426 @staticmethod 

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

428 return iter(iterable) 

429 

430 @property 

431 def length(self) -> int: 

432 """Length of the iterable. 

433 

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

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

436 """ 

437 if self._length is not None: 

438 return self._length 

439 

440 try: 

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

442 except TypeError: 

443 iterable = list(self._iterator) 

444 self._iterator = self._to_iterator(iterable) 

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

446 

447 return self._length 

448 

449 def __len__(self) -> int: 

450 return self.length 

451 

452 @property 

453 def depth(self) -> int: 

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

455 return self.depth0 + 1 

456 

457 @property 

458 def index(self) -> int: 

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

460 return self.index0 + 1 

461 

462 @property 

463 def revindex0(self) -> int: 

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

465 

466 Requires calculating :attr:`length`. 

467 """ 

468 return self.length - self.index 

469 

470 @property 

471 def revindex(self) -> int: 

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

473 

474 Requires calculating :attr:`length`. 

475 """ 

476 return self.length - self.index0 

477 

478 @property 

479 def first(self) -> bool: 

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

481 return self.index0 == 0 

482 

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

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

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

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

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

488 """ 

489 if self._after is not missing: 

490 return self._after 

491 

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

493 return self._after 

494 

495 @property 

496 def last(self) -> bool: 

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

498 

499 Causes the iterable to advance early. See 

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

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

502 """ 

503 return self._peek_next() is missing 

504 

505 @property 

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

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

508 first iteration. 

509 """ 

510 if self.first: 

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

512 

513 return self._before 

514 

515 @property 

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

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

518 iteration. 

519 

520 Causes the iterable to advance early. See 

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

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

523 """ 

524 rv = self._peek_next() 

525 

526 if rv is missing: 

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

528 

529 return rv 

530 

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

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

533 the current :attr:`index0`. 

534 

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

536 """ 

537 if not args: 

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

539 

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

541 

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

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

544 (including when called for the first time). 

545 

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

547 """ 

548 if self._last_changed_value != value: 

549 self._last_changed_value = value 

550 return True 

551 

552 return False 

553 

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

555 return self 

556 

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

558 if self._after is not missing: 

559 rv = self._after 

560 self._after = missing 

561 else: 

562 rv = next(self._iterator) 

563 

564 self.index0 += 1 

565 self._before = self._current 

566 self._current = rv 

567 return rv, self 

568 

569 @internalcode 

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

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

572 recursively with the given inner iterable data. 

573 

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

575 """ 

576 if self._recurse is None: 

577 raise TypeError( 

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

579 ) 

580 

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

582 

583 def __repr__(self) -> str: 

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

585 

586 

587class AsyncLoopContext(LoopContext): 

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

589 

590 @staticmethod 

591 def _to_iterator( # type: ignore 

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

593 ) -> t.AsyncIterator[V]: 

594 return auto_aiter(iterable) 

595 

596 @property 

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

598 if self._length is not None: 

599 return self._length 

600 

601 try: 

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

603 except TypeError: 

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

605 self._iterator = self._to_iterator(iterable) 

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

607 

608 return self._length 

609 

610 @property 

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

612 return await self.length - self.index 

613 

614 @property 

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

616 return await self.length - self.index0 

617 

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

619 if self._after is not missing: 

620 return self._after 

621 

622 try: 

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

624 except StopAsyncIteration: 

625 self._after = missing 

626 

627 return self._after 

628 

629 @property 

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

631 return await self._peek_next() is missing 

632 

633 @property 

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

635 rv = await self._peek_next() 

636 

637 if rv is missing: 

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

639 

640 return rv 

641 

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

643 return self 

644 

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

646 if self._after is not missing: 

647 rv = self._after 

648 self._after = missing 

649 else: 

650 rv = await self._iterator.__anext__() 

651 

652 self.index0 += 1 

653 self._before = self._current 

654 self._current = rv 

655 return rv, self 

656 

657 

658class Macro: 

659 """Wraps a macro function.""" 

660 

661 def __init__( 

662 self, 

663 environment: "Environment", 

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

665 name: str, 

666 arguments: t.List[str], 

667 catch_kwargs: bool, 

668 catch_varargs: bool, 

669 caller: bool, 

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

671 ): 

672 self._environment = environment 

673 self._func = func 

674 self._argument_count = len(arguments) 

675 self.name = name 

676 self.arguments = arguments 

677 self.catch_kwargs = catch_kwargs 

678 self.catch_varargs = catch_varargs 

679 self.caller = caller 

680 self.explicit_caller = "caller" in arguments 

681 

682 if default_autoescape is None: 

683 if callable(environment.autoescape): 

684 default_autoescape = environment.autoescape(None) 

685 else: 

686 default_autoescape = environment.autoescape 

687 

688 self._default_autoescape = default_autoescape 

689 

690 @internalcode 

691 @pass_eval_context 

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

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

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

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

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

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

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

699 # 

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

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

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

703 # check here. 

704 # 

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

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

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

708 # time autoescape flag. 

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

710 autoescape = args[0].autoescape 

711 args = args[1:] 

712 else: 

713 autoescape = self._default_autoescape 

714 

715 # try to consume the positional arguments 

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

717 off = len(arguments) 

718 

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

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

721 found_caller = False 

722 

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

724 # arguments expected we start filling in keyword arguments 

725 # and defaults. 

726 if off != self._argument_count: 

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

728 try: 

729 value = kwargs.pop(name) 

730 except KeyError: 

731 value = missing 

732 if name == "caller": 

733 found_caller = True 

734 arguments.append(value) 

735 else: 

736 found_caller = self.explicit_caller 

737 

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

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

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

741 if self.caller and not found_caller: 

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

743 if caller is None: 

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

745 arguments.append(caller) 

746 

747 if self.catch_kwargs: 

748 arguments.append(kwargs) 

749 elif kwargs: 

750 if "caller" in kwargs: 

751 raise TypeError( 

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

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

754 ) 

755 raise TypeError( 

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

757 ) 

758 if self.catch_varargs: 

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

760 elif len(args) > self._argument_count: 

761 raise TypeError( 

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

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

764 ) 

765 

766 return self._invoke(arguments, autoescape) 

767 

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

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

770 

771 if autoescape: 

772 return Markup(rv) 

773 

774 return rv # type: ignore 

775 

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

777 if self._environment.is_async: 

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

779 

780 rv = self._func(*arguments) 

781 

782 if autoescape: 

783 rv = Markup(rv) 

784 

785 return rv 

786 

787 def __repr__(self) -> str: 

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

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

790 

791 

792class Undefined: 

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

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

795 

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

797 >>> str(foo) 

798 '' 

799 >>> not foo 

800 True 

801 >>> foo + 42 

802 Traceback (most recent call last): 

803 ... 

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

805 """ 

806 

807 __slots__ = ( 

808 "_undefined_hint", 

809 "_undefined_obj", 

810 "_undefined_name", 

811 "_undefined_exception", 

812 ) 

813 

814 def __init__( 

815 self, 

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

817 obj: t.Any = missing, 

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

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

820 ) -> None: 

821 self._undefined_hint = hint 

822 self._undefined_obj = obj 

823 self._undefined_name = name 

824 self._undefined_exception = exc 

825 

826 @property 

827 def _undefined_message(self) -> str: 

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

829 accessed. 

830 """ 

831 if self._undefined_hint: 

832 return self._undefined_hint 

833 

834 if self._undefined_obj is missing: 

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

836 

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

838 return ( 

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

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

841 ) 

842 

843 return ( 

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

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

846 ) 

847 

848 @internalcode 

849 def _fail_with_undefined_error( 

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

851 ) -> "te.NoReturn": 

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

853 on the undefined value. 

854 """ 

855 raise self._undefined_exception(self._undefined_message) 

856 

857 @internalcode 

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

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

860 raise AttributeError(name) 

861 

862 return self._fail_with_undefined_error() 

863 

864 __add__ = __radd__ = __sub__ = __rsub__ = _fail_with_undefined_error 

865 __mul__ = __rmul__ = __div__ = __rdiv__ = _fail_with_undefined_error 

866 __truediv__ = __rtruediv__ = _fail_with_undefined_error 

867 __floordiv__ = __rfloordiv__ = _fail_with_undefined_error 

868 __mod__ = __rmod__ = _fail_with_undefined_error 

869 __pos__ = __neg__ = _fail_with_undefined_error 

870 __call__ = __getitem__ = _fail_with_undefined_error 

871 __lt__ = __le__ = __gt__ = __ge__ = _fail_with_undefined_error 

872 __int__ = __float__ = __complex__ = _fail_with_undefined_error 

873 __pow__ = __rpow__ = _fail_with_undefined_error 

874 

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

876 return type(self) is type(other) 

877 

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

879 return not self.__eq__(other) 

880 

881 def __hash__(self) -> int: 

882 return id(type(self)) 

883 

884 def __str__(self) -> str: 

885 return "" 

886 

887 def __len__(self) -> int: 

888 return 0 

889 

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

891 yield from () 

892 

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

894 for _ in (): 

895 yield 

896 

897 def __bool__(self) -> bool: 

898 return False 

899 

900 def __repr__(self) -> str: 

901 return "Undefined" 

902 

903 

904def make_logging_undefined( 

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

906) -> t.Type[Undefined]: 

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

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

909 logger is given a default logger is created. 

910 

911 Example:: 

912 

913 logger = logging.getLogger(__name__) 

914 LoggingUndefined = make_logging_undefined( 

915 logger=logger, 

916 base=Undefined 

917 ) 

918 

919 .. versionadded:: 2.8 

920 

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

922 is created. 

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

924 defaults to :class:`Undefined`. 

925 """ 

926 if logger is None: 

927 import logging 

928 

929 logger = logging.getLogger(__name__) 

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

931 

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

933 logger.warning("Template variable warning: %s", undef._undefined_message) 

934 

935 class LoggingUndefined(base): # type: ignore 

936 __slots__ = () 

937 

938 def _fail_with_undefined_error( # type: ignore 

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

940 ) -> "te.NoReturn": 

941 try: 

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

943 except self._undefined_exception as e: 

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

945 raise e 

946 

947 def __str__(self) -> str: 

948 _log_message(self) 

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

950 

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

952 _log_message(self) 

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

954 

955 def __bool__(self) -> bool: 

956 _log_message(self) 

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

958 

959 return LoggingUndefined 

960 

961 

962class ChainableUndefined(Undefined): 

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

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

965 :exc:`UndefinedError`. 

966 

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

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

969 '' 

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

971 Traceback (most recent call last): 

972 ... 

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

974 

975 .. versionadded:: 2.11.0 

976 """ 

977 

978 __slots__ = () 

979 

980 def __html__(self) -> str: 

981 return str(self) 

982 

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

984 return self 

985 

986 __getitem__ = __getattr__ # type: ignore 

987 

988 

989class DebugUndefined(Undefined): 

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

991 

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

993 >>> str(foo) 

994 '{{ foo }}' 

995 >>> not foo 

996 True 

997 >>> foo + 42 

998 Traceback (most recent call last): 

999 ... 

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

1001 """ 

1002 

1003 __slots__ = () 

1004 

1005 def __str__(self) -> str: 

1006 if self._undefined_hint: 

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

1008 

1009 elif self._undefined_obj is missing: 

1010 message = self._undefined_name # type: ignore 

1011 

1012 else: 

1013 message = ( 

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

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

1016 ) 

1017 

1018 return f"{{{{ {message} }}}}" 

1019 

1020 

1021class StrictUndefined(Undefined): 

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

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

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

1025 

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

1027 >>> str(foo) 

1028 Traceback (most recent call last): 

1029 ... 

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

1031 >>> not foo 

1032 Traceback (most recent call last): 

1033 ... 

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

1035 >>> foo + 42 

1036 Traceback (most recent call last): 

1037 ... 

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

1039 """ 

1040 

1041 __slots__ = () 

1042 __iter__ = __str__ = __len__ = Undefined._fail_with_undefined_error 

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

1044 __contains__ = Undefined._fail_with_undefined_error 

1045 

1046 

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

1048# unneeded and contain wrong data for subclasses. 

1049del ( 

1050 Undefined.__slots__, 

1051 ChainableUndefined.__slots__, 

1052 DebugUndefined.__slots__, 

1053 StrictUndefined.__slots__, 

1054)