Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/mako/runtime.py: 28%

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

449 statements  

1# mako/runtime.py 

2# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file> 

3# 

4# This module is part of Mako and is released under 

5# the MIT License: http://www.opensource.org/licenses/mit-license.php 

6 

7"""provides runtime services for templates, including Context, 

8Namespace, and various helper functions.""" 

9 

10import builtins 

11import functools 

12import sys 

13 

14from mako import compat 

15from mako import exceptions 

16from mako import util 

17 

18 

19class Context: 

20 

21 """Provides runtime namespace, output buffer, and various 

22 callstacks for templates. 

23 

24 See :ref:`runtime_toplevel` for detail on the usage of 

25 :class:`.Context`. 

26 

27 """ 

28 

29 def __init__(self, buffer, **data): 

30 self._buffer_stack = [buffer] 

31 

32 self._data = data 

33 

34 self._kwargs = data.copy() 

35 self._with_template = None 

36 self._outputting_as_unicode = None 

37 self.namespaces = {} 

38 

39 # "capture" function which proxies to the 

40 # generic "capture" function 

41 self._data["capture"] = functools.partial(capture, self) 

42 

43 # "caller" stack used by def calls with content 

44 self.caller_stack = self._data["caller"] = CallerStack() 

45 

46 def _set_with_template(self, t): 

47 self._with_template = t 

48 illegal_names = t.reserved_names.intersection(self._data) 

49 if illegal_names: 

50 raise exceptions.NameConflictError( 

51 "Reserved words passed to render(): %s" 

52 % ", ".join(illegal_names) 

53 ) 

54 

55 @property 

56 def lookup(self): 

57 """Return the :class:`.TemplateLookup` associated 

58 with this :class:`.Context`. 

59 

60 """ 

61 return self._with_template.lookup 

62 

63 @property 

64 def kwargs(self): 

65 """Return the dictionary of top level keyword arguments associated 

66 with this :class:`.Context`. 

67 

68 This dictionary only includes the top-level arguments passed to 

69 :meth:`.Template.render`. It does not include names produced within 

70 the template execution such as local variable names or special names 

71 such as ``self``, ``next``, etc. 

72 

73 The purpose of this dictionary is primarily for the case that 

74 a :class:`.Template` accepts arguments via its ``<%page>`` tag, 

75 which are normally expected to be passed via :meth:`.Template.render`, 

76 except the template is being called in an inheritance context, 

77 using the ``body()`` method. :attr:`.Context.kwargs` can then be 

78 used to propagate these arguments to the inheriting template:: 

79 

80 ${next.body(**context.kwargs)} 

81 

82 """ 

83 return self._kwargs.copy() 

84 

85 def push_caller(self, caller): 

86 """Push a ``caller`` callable onto the callstack for 

87 this :class:`.Context`.""" 

88 

89 self.caller_stack.append(caller) 

90 

91 def pop_caller(self): 

92 """Pop a ``caller`` callable onto the callstack for this 

93 :class:`.Context`.""" 

94 

95 del self.caller_stack[-1] 

96 

97 def keys(self): 

98 """Return a list of all names established in this :class:`.Context`.""" 

99 

100 return list(self._data.keys()) 

101 

102 def __getitem__(self, key): 

103 if key in self._data: 

104 return self._data[key] 

105 else: 

106 return builtins.__dict__[key] 

107 

108 def _push_writer(self): 

109 """push a capturing buffer onto this Context and return 

110 the new writer function.""" 

111 

112 buf = util.FastEncodingBuffer() 

113 self._buffer_stack.append(buf) 

114 return buf.write 

115 

116 def _pop_buffer_and_writer(self): 

117 """pop the most recent capturing buffer from this Context 

118 and return the current writer after the pop. 

119 

120 """ 

121 

122 buf = self._buffer_stack.pop() 

123 return buf, self._buffer_stack[-1].write 

124 

125 def _push_buffer(self): 

126 """push a capturing buffer onto this Context.""" 

127 

128 self._push_writer() 

129 

130 def _pop_buffer(self): 

131 """pop the most recent capturing buffer from this Context.""" 

132 

133 return self._buffer_stack.pop() 

134 

135 def get(self, key, default=None): 

136 """Return a value from this :class:`.Context`.""" 

137 

138 return self._data.get(key, builtins.__dict__.get(key, default)) 

139 

140 def write(self, string): 

141 """Write a string to this :class:`.Context` object's 

142 underlying output buffer.""" 

143 

144 self._buffer_stack[-1].write(string) 

145 

146 def writer(self): 

147 """Return the current writer function.""" 

148 

149 return self._buffer_stack[-1].write 

150 

151 def _copy(self): 

152 c = Context.__new__(Context) 

153 c._buffer_stack = self._buffer_stack 

154 c._data = self._data.copy() 

155 c._kwargs = self._kwargs 

156 c._with_template = self._with_template 

157 c._outputting_as_unicode = self._outputting_as_unicode 

158 c.namespaces = self.namespaces 

159 c.caller_stack = self.caller_stack 

160 return c 

161 

162 def _locals(self, d): 

163 """Create a new :class:`.Context` with a copy of this 

164 :class:`.Context`'s current state, 

165 updated with the given dictionary. 

166 

167 The :attr:`.Context.kwargs` collection remains 

168 unaffected. 

169 

170 

171 """ 

172 

173 if not d: 

174 return self 

175 c = self._copy() 

176 c._data.update(d) 

177 return c 

178 

179 def _clean_inheritance_tokens(self): 

180 """create a new copy of this :class:`.Context`. with 

181 tokens related to inheritance state removed.""" 

182 

183 c = self._copy() 

184 x = c._data 

185 x.pop("self", None) 

186 x.pop("parent", None) 

187 x.pop("next", None) 

188 return c 

189 

190 

191class CallerStack(list): 

192 def __init__(self): 

193 self.nextcaller = None 

194 

195 def __nonzero__(self): 

196 return self.__bool__() 

197 

198 def __bool__(self): 

199 return len(self) and self._get_caller() and True or False 

200 

201 def _get_caller(self): 

202 # this method can be removed once 

203 # codegen MAGIC_NUMBER moves past 7 

204 return self[-1] 

205 

206 def __getattr__(self, key): 

207 return getattr(self._get_caller(), key) 

208 

209 def _push_frame(self): 

210 frame = self.nextcaller or None 

211 self.append(frame) 

212 self.nextcaller = None 

213 return frame 

214 

215 def _pop_frame(self): 

216 self.nextcaller = self.pop() 

217 

218 

219class Undefined: 

220 

221 """Represents an undefined value in a template. 

222 

223 All template modules have a constant value 

224 ``UNDEFINED`` present which is an instance of this 

225 object. 

226 

227 """ 

228 

229 def __str__(self): 

230 raise NameError("Undefined") 

231 

232 def __nonzero__(self): 

233 return self.__bool__() 

234 

235 def __bool__(self): 

236 return False 

237 

238 

239UNDEFINED = Undefined() 

240STOP_RENDERING = "" 

241 

242 

243class LoopStack: 

244 

245 """a stack for LoopContexts that implements the context manager protocol 

246 to automatically pop off the top of the stack on context exit 

247 """ 

248 

249 def __init__(self): 

250 self.stack = [] 

251 

252 def _enter(self, iterable): 

253 self._push(iterable) 

254 return self._top 

255 

256 def _exit(self): 

257 self._pop() 

258 return self._top 

259 

260 @property 

261 def _top(self): 

262 if self.stack: 

263 return self.stack[-1] 

264 else: 

265 return self 

266 

267 def _pop(self): 

268 return self.stack.pop() 

269 

270 def _push(self, iterable): 

271 new = LoopContext(iterable) 

272 if self.stack: 

273 new.parent = self.stack[-1] 

274 return self.stack.append(new) 

275 

276 def __getattr__(self, key): 

277 raise exceptions.RuntimeException("No loop context is established") 

278 

279 def __iter__(self): 

280 return iter(self._top) 

281 

282 

283class LoopContext: 

284 

285 """A magic loop variable. 

286 Automatically accessible in any ``% for`` block. 

287 

288 See the section :ref:`loop_context` for usage 

289 notes. 

290 

291 :attr:`parent` -> :class:`.LoopContext` or ``None`` 

292 The parent loop, if one exists. 

293 :attr:`index` -> `int` 

294 The 0-based iteration count. 

295 :attr:`reverse_index` -> `int` 

296 The number of iterations remaining. 

297 :attr:`first` -> `bool` 

298 ``True`` on the first iteration, ``False`` otherwise. 

299 :attr:`last` -> `bool` 

300 ``True`` on the last iteration, ``False`` otherwise. 

301 :attr:`even` -> `bool` 

302 ``True`` when ``index`` is even. 

303 :attr:`odd` -> `bool` 

304 ``True`` when ``index`` is odd. 

305 """ 

306 

307 def __init__(self, iterable): 

308 self._iterable = iterable 

309 self.index = 0 

310 self.parent = None 

311 

312 def __iter__(self): 

313 for i in self._iterable: 

314 yield i 

315 self.index += 1 

316 

317 @util.memoized_instancemethod 

318 def __len__(self): 

319 return len(self._iterable) 

320 

321 @property 

322 def reverse_index(self): 

323 return len(self) - self.index - 1 

324 

325 @property 

326 def first(self): 

327 return self.index == 0 

328 

329 @property 

330 def last(self): 

331 return self.index == len(self) - 1 

332 

333 @property 

334 def even(self): 

335 return not self.odd 

336 

337 @property 

338 def odd(self): 

339 return bool(self.index % 2) 

340 

341 def cycle(self, *values): 

342 """Cycle through values as the loop progresses.""" 

343 if not values: 

344 raise ValueError("You must provide values to cycle through") 

345 return values[self.index % len(values)] 

346 

347 

348class _NSAttr: 

349 def __init__(self, parent): 

350 self.__parent = parent 

351 

352 def __getattr__(self, key): 

353 ns = self.__parent 

354 while ns: 

355 if hasattr(ns.module, key): 

356 return getattr(ns.module, key) 

357 else: 

358 ns = ns.inherits 

359 raise AttributeError(key) 

360 

361 

362class Namespace: 

363 

364 """Provides access to collections of rendering methods, which 

365 can be local, from other templates, or from imported modules. 

366 

367 To access a particular rendering method referenced by a 

368 :class:`.Namespace`, use plain attribute access: 

369 

370 .. sourcecode:: mako 

371 

372 ${some_namespace.foo(x, y, z)} 

373 

374 :class:`.Namespace` also contains several built-in attributes 

375 described here. 

376 

377 """ 

378 

379 def __init__( 

380 self, 

381 name, 

382 context, 

383 callables=None, 

384 inherits=None, 

385 populate_self=True, 

386 calling_uri=None, 

387 ): 

388 self.name = name 

389 self.context = context 

390 self.inherits = inherits 

391 if callables is not None: 

392 self.callables = {c.__name__: c for c in callables} 

393 

394 callables = () 

395 

396 module = None 

397 """The Python module referenced by this :class:`.Namespace`. 

398 

399 If the namespace references a :class:`.Template`, then 

400 this module is the equivalent of ``template.module``, 

401 i.e. the generated module for the template. 

402 

403 """ 

404 

405 template = None 

406 """The :class:`.Template` object referenced by this 

407 :class:`.Namespace`, if any. 

408 

409 """ 

410 

411 context = None 

412 """The :class:`.Context` object for this :class:`.Namespace`. 

413 

414 Namespaces are often created with copies of contexts that 

415 contain slightly different data, particularly in inheritance 

416 scenarios. Using the :class:`.Context` off of a :class:`.Namespace` one 

417 can traverse an entire chain of templates that inherit from 

418 one-another. 

419 

420 """ 

421 

422 filename = None 

423 """The path of the filesystem file used for this 

424 :class:`.Namespace`'s module or template. 

425 

426 If this is a pure module-based 

427 :class:`.Namespace`, this evaluates to ``module.__file__``. If a 

428 template-based namespace, it evaluates to the original 

429 template file location. 

430 

431 """ 

432 

433 uri = None 

434 """The URI for this :class:`.Namespace`'s template. 

435 

436 I.e. whatever was sent to :meth:`.TemplateLookup.get_template()`. 

437 

438 This is the equivalent of :attr:`.Template.uri`. 

439 

440 """ 

441 

442 _templateuri = None 

443 

444 @util.memoized_property 

445 def attr(self): 

446 """Access module level attributes by name. 

447 

448 This accessor allows templates to supply "scalar" 

449 attributes which are particularly handy in inheritance 

450 relationships. 

451 

452 .. seealso:: 

453 

454 :ref:`inheritance_attr` 

455 

456 :ref:`namespace_attr_for_includes` 

457 

458 """ 

459 return _NSAttr(self) 

460 

461 def get_namespace(self, uri): 

462 """Return a :class:`.Namespace` corresponding to the given ``uri``. 

463 

464 If the given ``uri`` is a relative URI (i.e. it does not 

465 contain a leading slash ``/``), the ``uri`` is adjusted to 

466 be relative to the ``uri`` of the namespace itself. This 

467 method is therefore mostly useful off of the built-in 

468 ``local`` namespace, described in :ref:`namespace_local`. 

469 

470 In 

471 most cases, a template wouldn't need this function, and 

472 should instead use the ``<%namespace>`` tag to load 

473 namespaces. However, since all ``<%namespace>`` tags are 

474 evaluated before the body of a template ever runs, 

475 this method can be used to locate namespaces using 

476 expressions that were generated within the body code of 

477 the template, or to conditionally use a particular 

478 namespace. 

479 

480 """ 

481 key = (self, uri) 

482 if key in self.context.namespaces: 

483 return self.context.namespaces[key] 

484 ns = TemplateNamespace( 

485 uri, 

486 self.context._copy(), 

487 templateuri=uri, 

488 calling_uri=self._templateuri, 

489 ) 

490 self.context.namespaces[key] = ns 

491 return ns 

492 

493 def get_template(self, uri): 

494 """Return a :class:`.Template` from the given ``uri``. 

495 

496 The ``uri`` resolution is relative to the ``uri`` of this 

497 :class:`.Namespace` object's :class:`.Template`. 

498 

499 """ 

500 return _lookup_template(self.context, uri, self._templateuri) 

501 

502 def get_cached(self, key, **kwargs): 

503 """Return a value from the :class:`.Cache` referenced by this 

504 :class:`.Namespace` object's :class:`.Template`. 

505 

506 The advantage to this method versus direct access to the 

507 :class:`.Cache` is that the configuration parameters 

508 declared in ``<%page>`` take effect here, thereby calling 

509 up the same configured backend as that configured 

510 by ``<%page>``. 

511 

512 """ 

513 

514 return self.cache.get(key, **kwargs) 

515 

516 @property 

517 def cache(self): 

518 """Return the :class:`.Cache` object referenced 

519 by this :class:`.Namespace` object's 

520 :class:`.Template`. 

521 

522 """ 

523 return self.template.cache 

524 

525 def include_file(self, uri, **kwargs): 

526 """Include a file at the given ``uri``.""" 

527 

528 _include_file(self.context, uri, self._templateuri, **kwargs) 

529 

530 def _populate(self, d, l): 

531 for ident in l: 

532 if ident == "*": 

533 for k, v in self._get_star(): 

534 d[k] = v 

535 else: 

536 d[ident] = getattr(self, ident) 

537 

538 def _get_star(self): 

539 if self.callables: 

540 for key in self.callables: 

541 yield (key, self.callables[key]) 

542 

543 def __getattr__(self, key): 

544 if key in self.callables: 

545 val = self.callables[key] 

546 elif self.inherits: 

547 val = getattr(self.inherits, key) 

548 else: 

549 raise AttributeError( 

550 "Namespace '%s' has no member '%s'" % (self.name, key) 

551 ) 

552 setattr(self, key, val) 

553 return val 

554 

555 

556class TemplateNamespace(Namespace): 

557 

558 """A :class:`.Namespace` specific to a :class:`.Template` instance.""" 

559 

560 def __init__( 

561 self, 

562 name, 

563 context, 

564 template=None, 

565 templateuri=None, 

566 callables=None, 

567 inherits=None, 

568 populate_self=True, 

569 calling_uri=None, 

570 ): 

571 self.name = name 

572 self.context = context 

573 self.inherits = inherits 

574 if callables is not None: 

575 self.callables = {c.__name__: c for c in callables} 

576 

577 if templateuri is not None: 

578 self.template = _lookup_template(context, templateuri, calling_uri) 

579 self._templateuri = self.template.module._template_uri 

580 elif template is not None: 

581 self.template = template 

582 self._templateuri = template.module._template_uri 

583 else: 

584 raise TypeError("'template' argument is required.") 

585 

586 if populate_self: 

587 lclcallable, lclcontext = _populate_self_namespace( 

588 context, self.template, self_ns=self 

589 ) 

590 

591 @property 

592 def module(self): 

593 """The Python module referenced by this :class:`.Namespace`. 

594 

595 If the namespace references a :class:`.Template`, then 

596 this module is the equivalent of ``template.module``, 

597 i.e. the generated module for the template. 

598 

599 """ 

600 return self.template.module 

601 

602 @property 

603 def filename(self): 

604 """The path of the filesystem file used for this 

605 :class:`.Namespace`'s module or template. 

606 """ 

607 return self.template.filename 

608 

609 @property 

610 def uri(self): 

611 """The URI for this :class:`.Namespace`'s template. 

612 

613 I.e. whatever was sent to :meth:`.TemplateLookup.get_template()`. 

614 

615 This is the equivalent of :attr:`.Template.uri`. 

616 

617 """ 

618 return self.template.uri 

619 

620 def _get_star(self): 

621 if self.callables: 

622 for key in self.callables: 

623 yield (key, self.callables[key]) 

624 

625 def get(key): 

626 callable_ = self.template._get_def_callable(key) 

627 return functools.partial(callable_, self.context) 

628 

629 for k in self.template.module._exports: 

630 yield (k, get(k)) 

631 

632 def __getattr__(self, key): 

633 if key in self.callables: 

634 val = self.callables[key] 

635 elif self.template.has_def(key): 

636 callable_ = self.template._get_def_callable(key) 

637 val = functools.partial(callable_, self.context) 

638 elif self.inherits: 

639 val = getattr(self.inherits, key) 

640 

641 else: 

642 raise AttributeError( 

643 "Namespace '%s' has no member '%s'" % (self.name, key) 

644 ) 

645 setattr(self, key, val) 

646 return val 

647 

648 

649class ModuleNamespace(Namespace): 

650 

651 """A :class:`.Namespace` specific to a Python module instance.""" 

652 

653 def __init__( 

654 self, 

655 name, 

656 context, 

657 module, 

658 callables=None, 

659 inherits=None, 

660 populate_self=True, 

661 calling_uri=None, 

662 ): 

663 self.name = name 

664 self.context = context 

665 self.inherits = inherits 

666 if callables is not None: 

667 self.callables = {c.__name__: c for c in callables} 

668 

669 mod = __import__(module) 

670 for token in module.split(".")[1:]: 

671 mod = getattr(mod, token) 

672 self.module = mod 

673 

674 @property 

675 def filename(self): 

676 """The path of the filesystem file used for this 

677 :class:`.Namespace`'s module or template. 

678 """ 

679 return self.module.__file__ 

680 

681 def _get_star(self): 

682 if self.callables: 

683 for key in self.callables: 

684 yield (key, self.callables[key]) 

685 for key in dir(self.module): 

686 if key[0] != "_": 

687 callable_ = getattr(self.module, key) 

688 if callable(callable_): 

689 yield key, functools.partial(callable_, self.context) 

690 

691 def __getattr__(self, key): 

692 if key in self.callables: 

693 val = self.callables[key] 

694 elif hasattr(self.module, key): 

695 callable_ = getattr(self.module, key) 

696 val = functools.partial(callable_, self.context) 

697 elif self.inherits: 

698 val = getattr(self.inherits, key) 

699 else: 

700 raise AttributeError( 

701 "Namespace '%s' has no member '%s'" % (self.name, key) 

702 ) 

703 setattr(self, key, val) 

704 return val 

705 

706 

707def supports_caller(func): 

708 """Apply a caller_stack compatibility decorator to a plain 

709 Python function. 

710 

711 See the example in :ref:`namespaces_python_modules`. 

712 

713 """ 

714 

715 def wrap_stackframe(context, *args, **kwargs): 

716 context.caller_stack._push_frame() 

717 try: 

718 return func(context, *args, **kwargs) 

719 finally: 

720 context.caller_stack._pop_frame() 

721 

722 return wrap_stackframe 

723 

724 

725def capture(context, callable_, *args, **kwargs): 

726 """Execute the given template def, capturing the output into 

727 a buffer. 

728 

729 See the example in :ref:`namespaces_python_modules`. 

730 

731 """ 

732 

733 if not callable(callable_): 

734 raise exceptions.RuntimeException( 

735 "capture() function expects a callable as " 

736 "its argument (i.e. capture(func, *args, **kwargs))" 

737 ) 

738 context._push_buffer() 

739 try: 

740 callable_(*args, **kwargs) 

741 finally: 

742 buf = context._pop_buffer() 

743 return buf.getvalue() 

744 

745 

746def _decorate_toplevel(fn): 

747 def decorate_render(render_fn): 

748 def go(context, *args, **kw): 

749 def y(*args, **kw): 

750 return render_fn(context, *args, **kw) 

751 

752 try: 

753 y.__name__ = render_fn.__name__[7:] 

754 except TypeError: 

755 # < Python 2.4 

756 pass 

757 return fn(y)(context, *args, **kw) 

758 

759 return go 

760 

761 return decorate_render 

762 

763 

764def _decorate_inline(context, fn): 

765 def decorate_render(render_fn): 

766 dec = fn(render_fn) 

767 

768 def go(*args, **kw): 

769 return dec(context, *args, **kw) 

770 

771 return go 

772 

773 return decorate_render 

774 

775 

776def _include_file(context, uri, calling_uri, **kwargs): 

777 """locate the template from the given uri and include it in 

778 the current output.""" 

779 

780 template = _lookup_template(context, uri, calling_uri) 

781 (callable_, ctx) = _populate_self_namespace( 

782 context._clean_inheritance_tokens(), template 

783 ) 

784 kwargs = _kwargs_for_include(callable_, context._data, **kwargs) 

785 if template.include_error_handler: 

786 try: 

787 callable_(ctx, **kwargs) 

788 except Exception: 

789 result = template.include_error_handler(ctx, compat.exception_as()) 

790 if not result: 

791 raise 

792 else: 

793 callable_(ctx, **kwargs) 

794 

795 

796def _inherit_from(context, uri, calling_uri): 

797 """called by the _inherit method in template modules to set 

798 up the inheritance chain at the start of a template's 

799 execution.""" 

800 

801 if uri is None: 

802 return None 

803 template = _lookup_template(context, uri, calling_uri) 

804 self_ns = context["self"] 

805 ih = self_ns 

806 while ih.inherits is not None: 

807 ih = ih.inherits 

808 lclcontext = context._locals({"next": ih}) 

809 ih.inherits = TemplateNamespace( 

810 "self:%s" % template.uri, 

811 lclcontext, 

812 template=template, 

813 populate_self=False, 

814 ) 

815 context._data["parent"] = lclcontext._data["local"] = ih.inherits 

816 callable_ = getattr(template.module, "_mako_inherit", None) 

817 if callable_ is not None: 

818 ret = callable_(template, lclcontext) 

819 if ret: 

820 return ret 

821 

822 gen_ns = getattr(template.module, "_mako_generate_namespaces", None) 

823 if gen_ns is not None: 

824 gen_ns(context) 

825 return (template.callable_, lclcontext) 

826 

827 

828def _lookup_template(context, uri, relativeto): 

829 lookup = context._with_template.lookup 

830 if lookup is None: 

831 raise exceptions.TemplateLookupException( 

832 "Template '%s' has no TemplateLookup associated" 

833 % context._with_template.uri 

834 ) 

835 uri = lookup.adjust_uri(uri, relativeto) 

836 try: 

837 return lookup.get_template(uri) 

838 except exceptions.TopLevelLookupException as e: 

839 raise exceptions.TemplateLookupException( 

840 str(compat.exception_as()) 

841 ) from e 

842 

843 

844def _populate_self_namespace(context, template, self_ns=None): 

845 if self_ns is None: 

846 self_ns = TemplateNamespace( 

847 "self:%s" % template.uri, 

848 context, 

849 template=template, 

850 populate_self=False, 

851 ) 

852 context._data["self"] = context._data["local"] = self_ns 

853 if hasattr(template.module, "_mako_inherit"): 

854 ret = template.module._mako_inherit(template, context) 

855 if ret: 

856 return ret 

857 return (template.callable_, context) 

858 

859 

860def _render(template, callable_, args, data, as_unicode=False): 

861 """create a Context and return the string 

862 output of the given template and template callable.""" 

863 

864 if as_unicode: 

865 buf = util.FastEncodingBuffer() 

866 else: 

867 buf = util.FastEncodingBuffer( 

868 encoding=template.output_encoding, errors=template.encoding_errors 

869 ) 

870 context = Context(buf, **data) 

871 context._outputting_as_unicode = as_unicode 

872 context._set_with_template(template) 

873 

874 _render_context( 

875 template, 

876 callable_, 

877 context, 

878 *args, 

879 **_kwargs_for_callable(callable_, data), 

880 ) 

881 return context._pop_buffer().getvalue() 

882 

883 

884def _kwargs_for_callable(callable_, data): 

885 argspec = compat.inspect_getargspec(callable_) 

886 # for normal pages, **pageargs is usually present 

887 if argspec[2]: 

888 return data 

889 

890 # for rendering defs from the top level, figure out the args 

891 namedargs = argspec[0] + [v for v in argspec[1:3] if v is not None] 

892 kwargs = {} 

893 for arg in namedargs: 

894 if arg != "context" and arg in data and arg not in kwargs: 

895 kwargs[arg] = data[arg] 

896 return kwargs 

897 

898 

899def _kwargs_for_include(callable_, data, **kwargs): 

900 argspec = compat.inspect_getargspec(callable_) 

901 namedargs = argspec[0] + [v for v in argspec[1:3] if v is not None] 

902 for arg in namedargs: 

903 if arg != "context" and arg in data and arg not in kwargs: 

904 kwargs[arg] = data[arg] 

905 return kwargs 

906 

907 

908def _render_context(tmpl, callable_, context, *args, **kwargs): 

909 import mako.template as template 

910 

911 # create polymorphic 'self' namespace for this 

912 # template with possibly updated context 

913 if not isinstance(tmpl, template.DefTemplate): 

914 # if main render method, call from the base of the inheritance stack 

915 (inherit, lclcontext) = _populate_self_namespace(context, tmpl) 

916 _exec_template(inherit, lclcontext, args=args, kwargs=kwargs) 

917 else: 

918 # otherwise, call the actual rendering method specified 

919 (inherit, lclcontext) = _populate_self_namespace(context, tmpl.parent) 

920 _exec_template(callable_, context, args=args, kwargs=kwargs) 

921 

922 

923def _exec_template(callable_, context, args=None, kwargs=None): 

924 """execute a rendering callable given the callable, a 

925 Context, and optional explicit arguments 

926 

927 the contextual Template will be located if it exists, and 

928 the error handling options specified on that Template will 

929 be interpreted here. 

930 """ 

931 template = context._with_template 

932 if template is not None and ( 

933 template.format_exceptions or template.error_handler 

934 ): 

935 try: 

936 callable_(context, *args, **kwargs) 

937 except Exception: 

938 _render_error(template, context, compat.exception_as()) 

939 except: 

940 e = sys.exc_info()[0] 

941 _render_error(template, context, e) 

942 else: 

943 callable_(context, *args, **kwargs) 

944 

945 

946def _render_error(template, context, error): 

947 if template.error_handler: 

948 result = template.error_handler(context, error) 

949 if not result: 

950 tp, value, tb = sys.exc_info() 

951 if value and tb: 

952 raise value.with_traceback(tb) 

953 else: 

954 raise error 

955 else: 

956 error_template = exceptions.html_error_template() 

957 if context._outputting_as_unicode: 

958 context._buffer_stack[:] = [util.FastEncodingBuffer()] 

959 else: 

960 context._buffer_stack[:] = [ 

961 util.FastEncodingBuffer( 

962 error_template.output_encoding, 

963 error_template.encoding_errors, 

964 ) 

965 ] 

966 

967 context._set_with_template(error_template) 

968 error_template.render_context(context, error=error)