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

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

483 statements  

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

2 

3import functools 

4import sys 

5import typing as t 

6from collections import abc 

7from itertools import chain 

8 

9from markupsafe import escape # noqa: F401 

10from markupsafe import Markup 

11from markupsafe import soft_str 

12 

13from .async_utils import auto_aiter 

14from .async_utils import auto_await # noqa: F401 

15from .exceptions import TemplateNotFound # noqa: F401 

16from .exceptions import TemplateRuntimeError # noqa: F401 

17from .exceptions import UndefinedError 

18from .nodes import EvalContext 

19from .utils import _PassArg 

20from .utils import concat 

21from .utils import internalcode 

22from .utils import missing 

23from .utils import Namespace # noqa: F401 

24from .utils import object_type_repr 

25from .utils import pass_eval_context 

26 

27V = t.TypeVar("V") 

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

29 

30if t.TYPE_CHECKING: 

31 import logging 

32 

33 import typing_extensions as te 

34 

35 from .environment import Environment 

36 

37 class LoopRenderFunc(te.Protocol): 

38 def __call__( 

39 self, 

40 reciter: t.Iterable[V], 

41 loop_render_func: "LoopRenderFunc", 

42 depth: int = 0, 

43 ) -> str: ... 

44 

45 

46# these variables are exported to the template runtime 

47exported = [ 

48 "LoopContext", 

49 "TemplateReference", 

50 "Macro", 

51 "Markup", 

52 "TemplateRuntimeError", 

53 "missing", 

54 "escape", 

55 "markup_join", 

56 "str_join", 

57 "identity", 

58 "TemplateNotFound", 

59 "Namespace", 

60 "Undefined", 

61 "internalcode", 

62] 

63async_exported = [ 

64 "AsyncLoopContext", 

65 "auto_aiter", 

66 "auto_await", 

67] 

68 

69 

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

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

72 environment. 

73 """ 

74 return x 

75 

76 

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

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

79 buf = [] 

80 iterator = map(soft_str, seq) 

81 for arg in iterator: 

82 buf.append(arg) 

83 if hasattr(arg, "__html__"): 

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

85 return concat(buf) 

86 

87 

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

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

90 return concat(map(str, seq)) 

91 

92 

93def new_context( 

94 environment: "Environment", 

95 template_name: t.Optional[str], 

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

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

98 shared: bool = False, 

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

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

101) -> "Context": 

102 """Internal helper for context creation.""" 

103 if vars is None: 

104 vars = {} 

105 if shared: 

106 parent = vars 

107 else: 

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

109 if locals: 

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

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

112 if shared: 

113 parent = dict(parent) 

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

115 if value is not missing: 

116 parent[key] = value 

117 return environment.context_class( 

118 environment, parent, template_name, blocks, globals=globals 

119 ) 

120 

121 

122class TemplateReference: 

123 """The `self` in templates.""" 

124 

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

126 self.__context = context 

127 

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

129 blocks = self.__context.blocks[name] 

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

131 

132 def __repr__(self) -> str: 

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

134 

135 

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

137 @functools.wraps(dict_method) 

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

139 return dict_method(self.get_all()) 

140 

141 return t.cast(F, f_all) 

142 

143 

144@abc.Mapping.register 

145class Context: 

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

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

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

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

150 be created by hand. 

151 

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

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

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

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

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

157 

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

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

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

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

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

163 """ 

164 

165 def __init__( 

166 self, 

167 environment: "Environment", 

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

169 name: t.Optional[str], 

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

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

172 ): 

173 self.parent = parent 

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

175 self.environment: "Environment" = environment 

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

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

178 self.name = name 

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

180 

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

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

183 # from the template. 

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

185 

186 def super( 

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

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

189 """Render a parent block.""" 

190 try: 

191 blocks = self.blocks[name] 

192 index = blocks.index(current) + 1 

193 blocks[index] 

194 except LookupError: 

195 return self.environment.undefined( 

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

197 ) 

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

199 

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

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

202 not found. 

203 

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

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

206 """ 

207 try: 

208 return self[key] 

209 except KeyError: 

210 return default 

211 

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

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

214 object if the key is not found. 

215 

216 If you need to add custom behavior, override 

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

218 functions use that method, not this one. 

219 

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

221 """ 

222 rv = self.resolve_or_missing(key) 

223 

224 if rv is missing: 

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

226 

227 return rv 

228 

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

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

231 if the key is not found. 

232 

233 Override this method to add custom lookup behavior. 

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

235 method. Don't call this method directly. 

236 

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

238 """ 

239 if key in self.vars: 

240 return self.vars[key] 

241 

242 if key in self.parent: 

243 return self.parent[key] 

244 

245 return missing 

246 

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

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

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

250 

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

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

253 variables. For optimizations reasons this might not return an 

254 actual copy so be careful with using it. 

255 """ 

256 if not self.vars: 

257 return self.parent 

258 if not self.parent: 

259 return self.vars 

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

261 

262 @internalcode 

263 def call( 

264 __self, 

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

266 *args: t.Any, 

267 **kwargs: t.Any, # noqa: B902 

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

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

270 provided but inject the active context or environment as first 

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

272 :func:`pass_environment`. 

273 """ 

274 if __debug__: 

275 __traceback_hide__ = True # noqa 

276 

277 # Allow callable classes to take a context 

278 if ( 

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

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

281 ): 

282 __obj = __obj.__call__ 

283 

284 pass_arg = _PassArg.from_obj(__obj) 

285 

286 if pass_arg is _PassArg.context: 

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

288 # loops and blocks without mutating the context itself 

289 if kwargs.get("_loop_vars"): 

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

291 if kwargs.get("_block_vars"): 

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

293 args = (__self,) + args 

294 elif pass_arg is _PassArg.eval_context: 

295 args = (__self.eval_ctx,) + args 

296 elif pass_arg is _PassArg.environment: 

297 args = (__self.environment,) + args 

298 

299 kwargs.pop("_block_vars", None) 

300 kwargs.pop("_loop_vars", None) 

301 

302 try: 

303 return __obj(*args, **kwargs) 

304 except StopIteration: 

305 return __self.environment.undefined( 

306 "value was undefined because a callable raised a" 

307 " StopIteration exception" 

308 ) 

309 

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

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

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

313 template that is independent. 

314 """ 

315 context = new_context( 

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

317 ) 

318 context.eval_ctx = self.eval_ctx 

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

320 return context 

321 

322 keys = _dict_method_all(dict.keys) 

323 values = _dict_method_all(dict.values) 

324 items = _dict_method_all(dict.items) 

325 

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

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

328 

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

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

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

332 """ 

333 item = self.resolve_or_missing(key) 

334 

335 if item is missing: 

336 raise KeyError(key) 

337 

338 return item 

339 

340 def __repr__(self) -> str: 

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

342 

343 

344class BlockReference: 

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

346 

347 def __init__( 

348 self, 

349 name: str, 

350 context: "Context", 

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

352 depth: int, 

353 ) -> None: 

354 self.name = name 

355 self._context = context 

356 self._stack = stack 

357 self._depth = depth 

358 

359 @property 

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

361 """Super the block.""" 

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

363 return self._context.environment.undefined( 

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

365 ) 

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

367 

368 @internalcode 

369 async def _async_call(self) -> str: 

370 rv = concat( 

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

372 ) 

373 

374 if self._context.eval_ctx.autoescape: 

375 return Markup(rv) 

376 

377 return rv 

378 

379 @internalcode 

380 def __call__(self) -> str: 

381 if self._context.environment.is_async: 

382 return self._async_call() # type: ignore 

383 

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

385 

386 if self._context.eval_ctx.autoescape: 

387 return Markup(rv) 

388 

389 return rv 

390 

391 

392class LoopContext: 

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

394 about the loop and iteration. 

395 """ 

396 

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

398 index0 = -1 

399 

400 _length: t.Optional[int] = None 

401 _after: t.Any = missing 

402 _current: t.Any = missing 

403 _before: t.Any = missing 

404 _last_changed_value: t.Any = missing 

405 

406 def __init__( 

407 self, 

408 iterable: t.Iterable[V], 

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

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

411 depth0: int = 0, 

412 ) -> None: 

413 """ 

414 :param iterable: Iterable to wrap. 

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

416 previous items. 

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

418 loop is marked recursive. 

419 :param depth0: Incremented when looping recursively. 

420 """ 

421 self._iterable = iterable 

422 self._iterator = self._to_iterator(iterable) 

423 self._undefined = undefined 

424 self._recurse = recurse 

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

426 self.depth0 = depth0 

427 

428 @staticmethod 

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

430 return iter(iterable) 

431 

432 @property 

433 def length(self) -> int: 

434 """Length of the iterable. 

435 

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

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

438 """ 

439 if self._length is not None: 

440 return self._length 

441 

442 try: 

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

444 except TypeError: 

445 iterable = list(self._iterator) 

446 self._iterator = self._to_iterator(iterable) 

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

448 

449 return self._length 

450 

451 def __len__(self) -> int: 

452 return self.length 

453 

454 @property 

455 def depth(self) -> int: 

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

457 return self.depth0 + 1 

458 

459 @property 

460 def index(self) -> int: 

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

462 return self.index0 + 1 

463 

464 @property 

465 def revindex0(self) -> int: 

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

467 

468 Requires calculating :attr:`length`. 

469 """ 

470 return self.length - self.index 

471 

472 @property 

473 def revindex(self) -> int: 

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

475 

476 Requires calculating :attr:`length`. 

477 """ 

478 return self.length - self.index0 

479 

480 @property 

481 def first(self) -> bool: 

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

483 return self.index0 == 0 

484 

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

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

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

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

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

490 """ 

491 if self._after is not missing: 

492 return self._after 

493 

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

495 return self._after 

496 

497 @property 

498 def last(self) -> bool: 

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

500 

501 Causes the iterable to advance early. See 

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

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

504 """ 

505 return self._peek_next() is missing 

506 

507 @property 

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

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

510 first iteration. 

511 """ 

512 if self.first: 

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

514 

515 return self._before 

516 

517 @property 

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

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

520 iteration. 

521 

522 Causes the iterable to advance early. See 

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

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

525 """ 

526 rv = self._peek_next() 

527 

528 if rv is missing: 

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

530 

531 return rv 

532 

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

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

535 the current :attr:`index0`. 

536 

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

538 """ 

539 if not args: 

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

541 

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

543 

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

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

546 (including when called for the first time). 

547 

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

549 """ 

550 if self._last_changed_value != value: 

551 self._last_changed_value = value 

552 return True 

553 

554 return False 

555 

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

557 return self 

558 

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

560 if self._after is not missing: 

561 rv = self._after 

562 self._after = missing 

563 else: 

564 rv = next(self._iterator) 

565 

566 self.index0 += 1 

567 self._before = self._current 

568 self._current = rv 

569 return rv, self 

570 

571 @internalcode 

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

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

574 recursively with the given inner iterable data. 

575 

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

577 """ 

578 if self._recurse is None: 

579 raise TypeError( 

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

581 ) 

582 

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

584 

585 def __repr__(self) -> str: 

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

587 

588 

589class AsyncLoopContext(LoopContext): 

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

591 

592 @staticmethod 

593 def _to_iterator( # type: ignore 

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

595 ) -> t.AsyncIterator[V]: 

596 return auto_aiter(iterable) 

597 

598 @property 

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

600 if self._length is not None: 

601 return self._length 

602 

603 try: 

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

605 except TypeError: 

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

607 self._iterator = self._to_iterator(iterable) 

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

609 

610 return self._length 

611 

612 @property 

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

614 return await self.length - self.index 

615 

616 @property 

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

618 return await self.length - self.index0 

619 

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

621 if self._after is not missing: 

622 return self._after 

623 

624 try: 

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

626 except StopAsyncIteration: 

627 self._after = missing 

628 

629 return self._after 

630 

631 @property 

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

633 return await self._peek_next() is missing 

634 

635 @property 

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

637 rv = await self._peek_next() 

638 

639 if rv is missing: 

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

641 

642 return rv 

643 

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

645 return self 

646 

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

648 if self._after is not missing: 

649 rv = self._after 

650 self._after = missing 

651 else: 

652 rv = await self._iterator.__anext__() 

653 

654 self.index0 += 1 

655 self._before = self._current 

656 self._current = rv 

657 return rv, self 

658 

659 

660class Macro: 

661 """Wraps a macro function.""" 

662 

663 def __init__( 

664 self, 

665 environment: "Environment", 

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

667 name: str, 

668 arguments: t.List[str], 

669 catch_kwargs: bool, 

670 catch_varargs: bool, 

671 caller: bool, 

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

673 ): 

674 self._environment = environment 

675 self._func = func 

676 self._argument_count = len(arguments) 

677 self.name = name 

678 self.arguments = arguments 

679 self.catch_kwargs = catch_kwargs 

680 self.catch_varargs = catch_varargs 

681 self.caller = caller 

682 self.explicit_caller = "caller" in arguments 

683 

684 if default_autoescape is None: 

685 if callable(environment.autoescape): 

686 default_autoescape = environment.autoescape(None) 

687 else: 

688 default_autoescape = environment.autoescape 

689 

690 self._default_autoescape = default_autoescape 

691 

692 @internalcode 

693 @pass_eval_context 

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

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

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

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

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

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

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

701 # 

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

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

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

705 # check here. 

706 # 

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

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

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

710 # time autoescape flag. 

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

712 autoescape = args[0].autoescape 

713 args = args[1:] 

714 else: 

715 autoescape = self._default_autoescape 

716 

717 # try to consume the positional arguments 

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

719 off = len(arguments) 

720 

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

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

723 found_caller = False 

724 

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

726 # arguments expected we start filling in keyword arguments 

727 # and defaults. 

728 if off != self._argument_count: 

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

730 try: 

731 value = kwargs.pop(name) 

732 except KeyError: 

733 value = missing 

734 if name == "caller": 

735 found_caller = True 

736 arguments.append(value) 

737 else: 

738 found_caller = self.explicit_caller 

739 

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

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

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

743 if self.caller and not found_caller: 

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

745 if caller is None: 

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

747 arguments.append(caller) 

748 

749 if self.catch_kwargs: 

750 arguments.append(kwargs) 

751 elif kwargs: 

752 if "caller" in kwargs: 

753 raise TypeError( 

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

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

756 ) 

757 raise TypeError( 

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

759 ) 

760 if self.catch_varargs: 

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

762 elif len(args) > self._argument_count: 

763 raise TypeError( 

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

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

766 ) 

767 

768 return self._invoke(arguments, autoescape) 

769 

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

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

772 

773 if autoescape: 

774 return Markup(rv) 

775 

776 return rv # type: ignore 

777 

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

779 if self._environment.is_async: 

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

781 

782 rv = self._func(*arguments) 

783 

784 if autoescape: 

785 rv = Markup(rv) 

786 

787 return rv 

788 

789 def __repr__(self) -> str: 

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

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

792 

793 

794class Undefined: 

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

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

797 

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

799 >>> str(foo) 

800 '' 

801 >>> not foo 

802 True 

803 >>> foo + 42 

804 Traceback (most recent call last): 

805 ... 

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

807 """ 

808 

809 __slots__ = ( 

810 "_undefined_hint", 

811 "_undefined_obj", 

812 "_undefined_name", 

813 "_undefined_exception", 

814 ) 

815 

816 def __init__( 

817 self, 

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

819 obj: t.Any = missing, 

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

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

822 ) -> None: 

823 self._undefined_hint = hint 

824 self._undefined_obj = obj 

825 self._undefined_name = name 

826 self._undefined_exception = exc 

827 

828 @property 

829 def _undefined_message(self) -> str: 

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

831 accessed. 

832 """ 

833 if self._undefined_hint: 

834 return self._undefined_hint 

835 

836 if self._undefined_obj is missing: 

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

838 

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

840 return ( 

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

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

843 ) 

844 

845 return ( 

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

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

848 ) 

849 

850 @internalcode 

851 def _fail_with_undefined_error( 

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

853 ) -> "te.NoReturn": 

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

855 on the undefined value. 

856 """ 

857 raise self._undefined_exception(self._undefined_message) 

858 

859 @internalcode 

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

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

862 raise AttributeError(name) 

863 

864 return self._fail_with_undefined_error() 

865 

866 __add__ = __radd__ = __sub__ = __rsub__ = _fail_with_undefined_error 

867 __mul__ = __rmul__ = __div__ = __rdiv__ = _fail_with_undefined_error 

868 __truediv__ = __rtruediv__ = _fail_with_undefined_error 

869 __floordiv__ = __rfloordiv__ = _fail_with_undefined_error 

870 __mod__ = __rmod__ = _fail_with_undefined_error 

871 __pos__ = __neg__ = _fail_with_undefined_error 

872 __call__ = __getitem__ = _fail_with_undefined_error 

873 __lt__ = __le__ = __gt__ = __ge__ = _fail_with_undefined_error 

874 __int__ = __float__ = __complex__ = _fail_with_undefined_error 

875 __pow__ = __rpow__ = _fail_with_undefined_error 

876 

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

878 return type(self) is type(other) 

879 

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

881 return not self.__eq__(other) 

882 

883 def __hash__(self) -> int: 

884 return id(type(self)) 

885 

886 def __str__(self) -> str: 

887 return "" 

888 

889 def __len__(self) -> int: 

890 return 0 

891 

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

893 yield from () 

894 

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

896 for _ in (): 

897 yield 

898 

899 def __bool__(self) -> bool: 

900 return False 

901 

902 def __repr__(self) -> str: 

903 return "Undefined" 

904 

905 

906def make_logging_undefined( 

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

908) -> t.Type[Undefined]: 

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

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

911 logger is given a default logger is created. 

912 

913 Example:: 

914 

915 logger = logging.getLogger(__name__) 

916 LoggingUndefined = make_logging_undefined( 

917 logger=logger, 

918 base=Undefined 

919 ) 

920 

921 .. versionadded:: 2.8 

922 

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

924 is created. 

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

926 defaults to :class:`Undefined`. 

927 """ 

928 if logger is None: 

929 import logging 

930 

931 logger = logging.getLogger(__name__) 

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

933 

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

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

936 

937 class LoggingUndefined(base): # type: ignore 

938 __slots__ = () 

939 

940 def _fail_with_undefined_error( # type: ignore 

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

942 ) -> "te.NoReturn": 

943 try: 

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

945 except self._undefined_exception as e: 

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

947 raise e 

948 

949 def __str__(self) -> str: 

950 _log_message(self) 

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

952 

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

954 _log_message(self) 

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

956 

957 def __bool__(self) -> bool: 

958 _log_message(self) 

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

960 

961 return LoggingUndefined 

962 

963 

964class ChainableUndefined(Undefined): 

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

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

967 :exc:`UndefinedError`. 

968 

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

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

971 '' 

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

973 Traceback (most recent call last): 

974 ... 

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

976 

977 .. versionadded:: 2.11.0 

978 """ 

979 

980 __slots__ = () 

981 

982 def __html__(self) -> str: 

983 return str(self) 

984 

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

986 return self 

987 

988 __getitem__ = __getattr__ # type: ignore 

989 

990 

991class DebugUndefined(Undefined): 

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

993 

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

995 >>> str(foo) 

996 '{{ foo }}' 

997 >>> not foo 

998 True 

999 >>> foo + 42 

1000 Traceback (most recent call last): 

1001 ... 

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

1003 """ 

1004 

1005 __slots__ = () 

1006 

1007 def __str__(self) -> str: 

1008 if self._undefined_hint: 

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

1010 

1011 elif self._undefined_obj is missing: 

1012 message = self._undefined_name # type: ignore 

1013 

1014 else: 

1015 message = ( 

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

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

1018 ) 

1019 

1020 return f"{{{{ {message} }}}}" 

1021 

1022 

1023class StrictUndefined(Undefined): 

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

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

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

1027 

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

1029 >>> str(foo) 

1030 Traceback (most recent call last): 

1031 ... 

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

1033 >>> not foo 

1034 Traceback (most recent call last): 

1035 ... 

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

1037 >>> foo + 42 

1038 Traceback (most recent call last): 

1039 ... 

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

1041 """ 

1042 

1043 __slots__ = () 

1044 __iter__ = __str__ = __len__ = Undefined._fail_with_undefined_error 

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

1046 __contains__ = Undefined._fail_with_undefined_error 

1047 

1048 

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

1050# unneeded and contain wrong data for subclasses. 

1051del ( 

1052 Undefined.__slots__, 

1053 ChainableUndefined.__slots__, 

1054 DebugUndefined.__slots__, 

1055 StrictUndefined.__slots__, 

1056)