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

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

584 statements  

1# mako/codegen.py 

2# Copyright 2006-2026 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 functionality for rendering a parsetree constructing into module 

8source code.""" 

9 

10import json 

11import re 

12import time 

13 

14from mako import ast 

15from mako import exceptions 

16from mako import filters 

17from mako import parsetree 

18from mako import util 

19from mako.pygen import PythonPrinter 

20 

21MAGIC_NUMBER = 10 

22 

23# names which are hardwired into the 

24# template and are not accessed via the 

25# context itself 

26TOPLEVEL_DECLARED = {"UNDEFINED", "STOP_RENDERING"} 

27RESERVED_NAMES = {"context", "loop"}.union(TOPLEVEL_DECLARED) 

28 

29 

30def compile( # noqa 

31 node, 

32 uri, 

33 filename=None, 

34 default_filters=None, 

35 buffer_filters=None, 

36 imports=None, 

37 future_imports=None, 

38 source_encoding=None, 

39 generate_magic_comment=True, 

40 strict_undefined=False, 

41 enable_loop=True, 

42 reserved_names=frozenset(), 

43): 

44 """Generate module source code given a parsetree node, 

45 uri, and optional source filename""" 

46 

47 buf = util.FastEncodingBuffer() 

48 

49 printer = PythonPrinter(buf) 

50 _GenerateRenderMethod( 

51 printer, 

52 _CompileContext( 

53 uri, 

54 filename, 

55 default_filters, 

56 buffer_filters, 

57 imports, 

58 future_imports, 

59 source_encoding, 

60 generate_magic_comment, 

61 strict_undefined, 

62 enable_loop, 

63 reserved_names, 

64 ), 

65 node, 

66 ) 

67 return buf.getvalue() 

68 

69 

70class _CompileContext: 

71 def __init__( 

72 self, 

73 uri, 

74 filename, 

75 default_filters, 

76 buffer_filters, 

77 imports, 

78 future_imports, 

79 source_encoding, 

80 generate_magic_comment, 

81 strict_undefined, 

82 enable_loop, 

83 reserved_names, 

84 ): 

85 self.uri = uri 

86 self.filename = filename 

87 self.default_filters = default_filters 

88 self.buffer_filters = buffer_filters 

89 self.imports = imports 

90 self.future_imports = future_imports 

91 self.source_encoding = source_encoding 

92 self.generate_magic_comment = generate_magic_comment 

93 self.strict_undefined = strict_undefined 

94 self.enable_loop = enable_loop 

95 self.reserved_names = reserved_names 

96 

97 

98class _GenerateRenderMethod: 

99 """A template visitor object which generates the 

100 full module source for a template. 

101 

102 """ 

103 

104 def __init__(self, printer, compiler, node): 

105 self.printer = printer 

106 self.compiler = compiler 

107 self.node = node 

108 self.identifier_stack = [None] 

109 self.in_def = isinstance(node, (parsetree.DefTag, parsetree.BlockTag)) 

110 

111 if self.in_def: 

112 name = "render_%s" % node.funcname 

113 args = node.get_argument_expressions() 

114 filtered = len(node.filter_args.args) > 0 

115 buffered = eval(node.attributes.get("buffered", "False")) 

116 cached = eval(node.attributes.get("cached", "False")) 

117 defs = None 

118 pagetag = None 

119 if node.is_block and not node.is_anonymous: 

120 args += ["**pageargs"] 

121 else: 

122 defs = self.write_toplevel() 

123 pagetag = self.compiler.pagetag 

124 name = "render_body" 

125 if pagetag is not None: 

126 args = pagetag.body_decl.get_argument_expressions() 

127 if not pagetag.body_decl.kwargs: 

128 args += ["**pageargs"] 

129 cached = eval(pagetag.attributes.get("cached", "False")) 

130 self.compiler.enable_loop = self.compiler.enable_loop or eval( 

131 pagetag.attributes.get("enable_loop", "False") 

132 ) 

133 else: 

134 args = ["**pageargs"] 

135 cached = False 

136 buffered = filtered = False 

137 if args is None: 

138 args = ["context"] 

139 else: 

140 args = [a for a in ["context"] + args] 

141 

142 self.write_render_callable( 

143 pagetag or node, name, args, buffered, filtered, cached 

144 ) 

145 

146 if defs is not None: 

147 for node in defs: 

148 _GenerateRenderMethod(printer, compiler, node) 

149 

150 if not self.in_def: 

151 self.write_metadata_struct() 

152 

153 def write_metadata_struct(self): 

154 self.printer.source_map[self.printer.lineno] = max( 

155 self.printer.source_map 

156 ) 

157 struct = { 

158 "filename": self.compiler.filename, 

159 "uri": self.compiler.uri, 

160 "source_encoding": self.compiler.source_encoding, 

161 "line_map": self.printer.source_map, 

162 } 

163 self.printer.writelines( 

164 '"""', 

165 "__M_BEGIN_METADATA", 

166 json.dumps(struct), 

167 "__M_END_METADATA\n" '"""', 

168 ) 

169 

170 @property 

171 def identifiers(self): 

172 return self.identifier_stack[-1] 

173 

174 def write_toplevel(self): 

175 """Traverse a template structure for module-level directives and 

176 generate the start of module-level code. 

177 

178 """ 

179 inherit = [] 

180 namespaces = {} 

181 module_code = [] 

182 

183 self.compiler.pagetag = None 

184 

185 class FindTopLevel: 

186 def visitInheritTag(s, node): 

187 inherit.append(node) 

188 

189 def visitNamespaceTag(s, node): 

190 namespaces[node.name] = node 

191 

192 def visitPageTag(s, node): 

193 self.compiler.pagetag = node 

194 

195 def visitCode(s, node): 

196 if node.ismodule: 

197 module_code.append(node) 

198 

199 f = FindTopLevel() 

200 for n in self.node.nodes: 

201 n.accept_visitor(f) 

202 

203 self.compiler.namespaces = namespaces 

204 

205 module_ident = set() 

206 for n in module_code: 

207 module_ident = module_ident.union(n.declared_identifiers()) 

208 

209 module_identifiers = _Identifiers(self.compiler) 

210 module_identifiers.declared = module_ident 

211 

212 # module-level names, python code 

213 if ( 

214 self.compiler.generate_magic_comment 

215 and self.compiler.source_encoding 

216 ): 

217 self.printer.writeline( 

218 "# -*- coding:%s -*-" % self.compiler.source_encoding 

219 ) 

220 

221 if self.compiler.future_imports: 

222 self.printer.writeline( 

223 "from __future__ import %s" 

224 % (", ".join(self.compiler.future_imports),) 

225 ) 

226 self.printer.writeline("from mako import runtime, filters, cache") 

227 self.printer.writeline("UNDEFINED = runtime.UNDEFINED") 

228 self.printer.writeline("STOP_RENDERING = runtime.STOP_RENDERING") 

229 self.printer.writeline("__M_dict_builtin = dict") 

230 self.printer.writeline("__M_locals_builtin = locals") 

231 self.printer.writeline("_magic_number = %r" % MAGIC_NUMBER) 

232 self.printer.writeline("_modified_time = %r" % time.time()) 

233 self.printer.writeline("_enable_loop = %r" % self.compiler.enable_loop) 

234 self.printer.writeline( 

235 "_template_filename = %r" % self.compiler.filename 

236 ) 

237 self.printer.writeline("_template_uri = %r" % self.compiler.uri) 

238 self.printer.writeline( 

239 "_source_encoding = %r" % self.compiler.source_encoding 

240 ) 

241 if self.compiler.imports: 

242 buf = "" 

243 for imp in self.compiler.imports: 

244 buf += imp + "\n" 

245 self.printer.writeline(imp) 

246 impcode = ast.PythonCode( 

247 buf, 

248 source="", 

249 lineno=0, 

250 pos=0, 

251 filename="template defined imports", 

252 ) 

253 else: 

254 impcode = None 

255 

256 main_identifiers = module_identifiers.branch(self.node) 

257 mit = module_identifiers.topleveldefs 

258 module_identifiers.topleveldefs = mit.union( 

259 main_identifiers.topleveldefs 

260 ) 

261 module_identifiers.declared.update(TOPLEVEL_DECLARED) 

262 if impcode: 

263 module_identifiers.declared.update(impcode.declared_identifiers) 

264 

265 self.compiler.identifiers = module_identifiers 

266 self.printer.writeline( 

267 "_exports = %r" 

268 % [n.name for n in main_identifiers.topleveldefs.values()] 

269 ) 

270 self.printer.write_blanks(2) 

271 

272 if len(module_code): 

273 self.write_module_code(module_code) 

274 

275 if len(inherit): 

276 self.write_namespaces(namespaces) 

277 self.write_inherit(inherit[-1]) 

278 elif len(namespaces): 

279 self.write_namespaces(namespaces) 

280 

281 return list(main_identifiers.topleveldefs.values()) 

282 

283 def write_render_callable( 

284 self, node, name, args, buffered, filtered, cached 

285 ): 

286 """write a top-level render callable. 

287 

288 this could be the main render() method or that of a top-level def.""" 

289 

290 if self.in_def: 

291 decorator = node.decorator 

292 if decorator: 

293 self.printer.writeline( 

294 "@runtime._decorate_toplevel(%s)" % decorator 

295 ) 

296 

297 self.printer.start_source(node.lineno) 

298 self.printer.writelines( 

299 "def %s(%s):" % (name, ",".join(args)), 

300 # push new frame, assign current frame to __M_caller 

301 "__M_caller = context.caller_stack._push_frame()", 

302 "try:", 

303 ) 

304 if buffered or filtered or cached: 

305 self.printer.writeline("context._push_buffer()") 

306 

307 self.identifier_stack.append( 

308 self.compiler.identifiers.branch(self.node) 

309 ) 

310 if (not self.in_def or self.node.is_block) and "**pageargs" in args: 

311 self.identifier_stack[-1].argument_declared.add("pageargs") 

312 

313 if not self.in_def and ( 

314 len(self.identifiers.locally_assigned) > 0 

315 or len(self.identifiers.argument_declared) > 0 

316 ): 

317 self.printer.writeline( 

318 "__M_locals = __M_dict_builtin(%s)" 

319 % ",".join( 

320 [ 

321 "%s=%s" % (x, x) 

322 for x in self.identifiers.argument_declared 

323 ] 

324 ) 

325 ) 

326 

327 self.write_variable_declares(self.identifiers, toplevel=True) 

328 

329 for n in self.node.nodes: 

330 n.accept_visitor(self) 

331 

332 self.write_def_finish(self.node, buffered, filtered, cached) 

333 self.printer.writeline(None) 

334 self.printer.write_blanks(2) 

335 if cached: 

336 self.write_cache_decorator( 

337 node, name, args, buffered, self.identifiers, toplevel=True 

338 ) 

339 

340 def write_module_code(self, module_code): 

341 """write module-level template code, i.e. that which 

342 is enclosed in <%! %> tags in the template.""" 

343 for n in module_code: 

344 self.printer.write_indented_block(n.text, starting_lineno=n.lineno) 

345 

346 def write_inherit(self, node): 

347 """write the module-level inheritance-determination callable.""" 

348 

349 self.printer.writelines( 

350 "def _mako_inherit(template, context):", 

351 "_mako_generate_namespaces(context)", 

352 "return runtime._inherit_from(context, %s, _template_uri)" 

353 % (node.parsed_attributes["file"]), 

354 None, 

355 ) 

356 

357 def write_namespaces(self, namespaces): 

358 """write the module-level namespace-generating callable.""" 

359 self.printer.writelines( 

360 "def _mako_get_namespace(context, name):", 

361 "try:", 

362 "return context.namespaces[(__name__, name)]", 

363 "except KeyError:", 

364 "_mako_generate_namespaces(context)", 

365 "return context.namespaces[(__name__, name)]", 

366 None, 

367 None, 

368 ) 

369 self.printer.writeline("def _mako_generate_namespaces(context):") 

370 

371 for node in namespaces.values(): 

372 if "import" in node.attributes: 

373 self.compiler.has_ns_imports = True 

374 self.printer.start_source(node.lineno) 

375 if len(node.nodes): 

376 self.printer.writeline("def make_namespace():") 

377 export = [] 

378 identifiers = self.compiler.identifiers.branch(node) 

379 self.in_def = True 

380 

381 class NSDefVisitor: 

382 def visitDefTag(s, node): 

383 s.visitDefOrBase(node) 

384 

385 def visitBlockTag(s, node): 

386 s.visitDefOrBase(node) 

387 

388 def visitDefOrBase(s, node): 

389 if node.is_anonymous: 

390 raise exceptions.CompileException( 

391 "Can't put anonymous blocks inside " 

392 "<%namespace>", 

393 **node.exception_kwargs, 

394 ) 

395 self.write_inline_def(node, identifiers, nested=False) 

396 export.append(node.funcname) 

397 

398 vis = NSDefVisitor() 

399 for n in node.nodes: 

400 n.accept_visitor(vis) 

401 self.printer.writeline("return [%s]" % (",".join(export))) 

402 self.printer.writeline(None) 

403 self.in_def = False 

404 callable_name = "make_namespace()" 

405 else: 

406 callable_name = "None" 

407 

408 if "file" in node.parsed_attributes: 

409 self.printer.writeline( 

410 "ns = runtime.TemplateNamespace(%r," 

411 " context._clean_inheritance_tokens()," 

412 " templateuri=%s, callables=%s, " 

413 " calling_uri=_template_uri)" 

414 % ( 

415 node.name, 

416 node.parsed_attributes.get("file", "None"), 

417 callable_name, 

418 ) 

419 ) 

420 elif "module" in node.parsed_attributes: 

421 self.printer.writeline( 

422 "ns = runtime.ModuleNamespace(%r," 

423 " context._clean_inheritance_tokens()," 

424 " callables=%s, calling_uri=_template_uri," 

425 " module=%s)" 

426 % ( 

427 node.name, 

428 callable_name, 

429 node.parsed_attributes.get("module", "None"), 

430 ) 

431 ) 

432 else: 

433 self.printer.writeline( 

434 "ns = runtime.Namespace(%r," 

435 " context._clean_inheritance_tokens()," 

436 " callables=%s, calling_uri=_template_uri)" 

437 % (node.name, callable_name) 

438 ) 

439 if eval(node.attributes.get("inheritable", "False")): 

440 self.printer.writeline("context['self'].%s = ns" % (node.name)) 

441 

442 self.printer.writeline( 

443 "context.namespaces[(__name__, %s)] = ns" % repr(node.name) 

444 ) 

445 self.printer.write_blanks(1) 

446 if not len(namespaces): 

447 self.printer.writeline("pass") 

448 self.printer.writeline(None) 

449 

450 def write_variable_declares(self, identifiers, toplevel=False, limit=None): 

451 """write variable declarations at the top of a function. 

452 

453 the variable declarations are in the form of callable 

454 definitions for defs and/or name lookup within the 

455 function's context argument. the names declared are based 

456 on the names that are referenced in the function body, 

457 which don't otherwise have any explicit assignment 

458 operation. names that are assigned within the body are 

459 assumed to be locally-scoped variables and are not 

460 separately declared. 

461 

462 for def callable definitions, if the def is a top-level 

463 callable then a 'stub' callable is generated which wraps 

464 the current Context into a closure. if the def is not 

465 top-level, it is fully rendered as a local closure. 

466 

467 """ 

468 

469 # collection of all defs available to us in this scope 

470 comp_idents = {c.funcname: c for c in identifiers.defs} 

471 to_write = set() 

472 

473 # write "context.get()" for all variables we are going to 

474 # need that arent in the namespace yet 

475 to_write = to_write.union(identifiers.undeclared) 

476 

477 # write closure functions for closures that we define 

478 # right here 

479 to_write = to_write.union( 

480 [c.funcname for c in identifiers.closuredefs.values()] 

481 ) 

482 

483 # remove identifiers that are declared in the argument 

484 # signature of the callable 

485 to_write = to_write.difference(identifiers.argument_declared) 

486 

487 # remove identifiers that we are going to assign to. 

488 # in this way we mimic Python's behavior, 

489 # i.e. assignment to a variable within a block 

490 # means that variable is now a "locally declared" var, 

491 # which cannot be referenced beforehand. 

492 to_write = to_write.difference(identifiers.locally_declared) 

493 

494 if self.compiler.enable_loop: 

495 has_loop = "loop" in to_write 

496 to_write.discard("loop") 

497 else: 

498 has_loop = False 

499 

500 # if a limiting set was sent, constraint to those items in that list 

501 # (this is used for the caching decorator) 

502 if limit is not None: 

503 to_write = to_write.intersection(limit) 

504 

505 if toplevel and getattr(self.compiler, "has_ns_imports", False): 

506 self.printer.writeline("_import_ns = {}") 

507 self.compiler.has_imports = True 

508 for ident, ns in self.compiler.namespaces.items(): 

509 if "import" in ns.attributes: 

510 self.printer.writeline( 

511 "_mako_get_namespace(context, %r)." 

512 "_populate(_import_ns, %r)" 

513 % ( 

514 ident, 

515 re.split(r"\s*,\s*", ns.attributes["import"]), 

516 ) 

517 ) 

518 

519 if has_loop: 

520 self.printer.writeline("loop = __M_loop = runtime.LoopStack()") 

521 

522 for ident in to_write: 

523 if ident in comp_idents: 

524 comp = comp_idents[ident] 

525 if comp.is_block: 

526 if not comp.is_anonymous: 

527 self.write_def_decl(comp, identifiers) 

528 else: 

529 self.write_inline_def(comp, identifiers, nested=True) 

530 else: 

531 if comp.is_root(): 

532 self.write_def_decl(comp, identifiers) 

533 else: 

534 self.write_inline_def(comp, identifiers, nested=True) 

535 

536 elif ident in self.compiler.namespaces: 

537 self.printer.writeline( 

538 "%s = _mako_get_namespace(context, %r)" % (ident, ident) 

539 ) 

540 else: 

541 if getattr(self.compiler, "has_ns_imports", False): 

542 if self.compiler.strict_undefined: 

543 self.printer.writelines( 

544 "%s = _import_ns.get(%r, UNDEFINED)" 

545 % (ident, ident), 

546 "if %s is UNDEFINED:" % ident, 

547 "try:", 

548 "%s = context[%r]" % (ident, ident), 

549 "except KeyError:", 

550 "raise NameError(\"'%s' is not defined\")" % ident, 

551 None, 

552 None, 

553 ) 

554 else: 

555 self.printer.writeline( 

556 "%s = _import_ns.get" 

557 "(%r, context.get(%r, UNDEFINED))" 

558 % (ident, ident, ident) 

559 ) 

560 else: 

561 if self.compiler.strict_undefined: 

562 self.printer.writelines( 

563 "try:", 

564 "%s = context[%r]" % (ident, ident), 

565 "except KeyError:", 

566 "raise NameError(\"'%s' is not defined\")" % ident, 

567 None, 

568 ) 

569 else: 

570 self.printer.writeline( 

571 "%s = context.get(%r, UNDEFINED)" % (ident, ident) 

572 ) 

573 

574 self.printer.writeline("__M_writer = context.writer()") 

575 

576 def write_def_decl(self, node, identifiers): 

577 """write a locally-available callable referencing a top-level def""" 

578 funcname = node.funcname 

579 namedecls = node.get_argument_expressions() 

580 nameargs = node.get_argument_expressions(as_call=True) 

581 

582 if not self.in_def and ( 

583 len(self.identifiers.locally_assigned) > 0 

584 or len(self.identifiers.argument_declared) > 0 

585 ): 

586 nameargs.insert(0, "context._locals(__M_locals)") 

587 else: 

588 nameargs.insert(0, "context") 

589 self.printer.writeline("def %s(%s):" % (funcname, ",".join(namedecls))) 

590 self.printer.writeline( 

591 "return render_%s(%s)" % (funcname, ",".join(nameargs)) 

592 ) 

593 self.printer.writeline(None) 

594 

595 def write_inline_def(self, node, identifiers, nested): 

596 """write a locally-available def callable inside an enclosing def.""" 

597 

598 namedecls = node.get_argument_expressions() 

599 

600 decorator = node.decorator 

601 if decorator: 

602 self.printer.writeline( 

603 "@runtime._decorate_inline(context, %s)" % decorator 

604 ) 

605 self.printer.writeline( 

606 "def %s(%s):" % (node.funcname, ",".join(namedecls)) 

607 ) 

608 filtered = len(node.filter_args.args) > 0 

609 buffered = eval(node.attributes.get("buffered", "False")) 

610 cached = eval(node.attributes.get("cached", "False")) 

611 self.printer.writelines( 

612 # push new frame, assign current frame to __M_caller 

613 "__M_caller = context.caller_stack._push_frame()", 

614 "try:", 

615 ) 

616 if buffered or filtered or cached: 

617 self.printer.writelines("context._push_buffer()") 

618 

619 identifiers = identifiers.branch(node, nested=nested) 

620 

621 self.write_variable_declares(identifiers) 

622 

623 self.identifier_stack.append(identifiers) 

624 for n in node.nodes: 

625 n.accept_visitor(self) 

626 self.identifier_stack.pop() 

627 

628 self.write_def_finish(node, buffered, filtered, cached) 

629 self.printer.writeline(None) 

630 if cached: 

631 self.write_cache_decorator( 

632 node, 

633 node.funcname, 

634 namedecls, 

635 False, 

636 identifiers, 

637 inline=True, 

638 toplevel=False, 

639 ) 

640 

641 def write_def_finish( 

642 self, node, buffered, filtered, cached, callstack=True 

643 ): 

644 """write the end section of a rendering function, either outermost or 

645 inline. 

646 

647 this takes into account if the rendering function was filtered, 

648 buffered, etc. and closes the corresponding try: block if any, and 

649 writes code to retrieve captured content, apply filters, send proper 

650 return value.""" 

651 

652 if not buffered and not cached and not filtered: 

653 self.printer.writeline("return ''") 

654 if callstack: 

655 self.printer.writelines( 

656 "finally:", "context.caller_stack._pop_frame()", None 

657 ) 

658 

659 if buffered or filtered or cached: 

660 if buffered or cached: 

661 # in a caching scenario, don't try to get a writer 

662 # from the context after popping; assume the caching 

663 # implemenation might be using a context with no 

664 # extra buffers 

665 self.printer.writelines( 

666 "finally:", "__M_buf = context._pop_buffer()" 

667 ) 

668 else: 

669 self.printer.writelines( 

670 "finally:", 

671 "__M_buf, __M_writer = context._pop_buffer_and_writer()", 

672 ) 

673 

674 if callstack: 

675 self.printer.writeline("context.caller_stack._pop_frame()") 

676 

677 s = "__M_buf.getvalue()" 

678 if filtered: 

679 s = self.create_filter_callable( 

680 node.filter_args.args, s, False 

681 ) 

682 self.printer.writeline(None) 

683 if buffered and not cached: 

684 s = self.create_filter_callable( 

685 self.compiler.buffer_filters, s, False 

686 ) 

687 if buffered or cached: 

688 self.printer.writeline("return %s" % s) 

689 else: 

690 self.printer.writelines("__M_writer(%s)" % s, "return ''") 

691 

692 def write_cache_decorator( 

693 self, 

694 node_or_pagetag, 

695 name, 

696 args, 

697 buffered, 

698 identifiers, 

699 inline=False, 

700 toplevel=False, 

701 ): 

702 """write a post-function decorator to replace a rendering 

703 callable with a cached version of itself.""" 

704 

705 self.printer.writeline("__M_%s = %s" % (name, name)) 

706 cachekey = node_or_pagetag.parsed_attributes.get( 

707 "cache_key", repr(name) 

708 ) 

709 

710 cache_args = {} 

711 if self.compiler.pagetag is not None: 

712 cache_args.update( 

713 (pa[6:], self.compiler.pagetag.parsed_attributes[pa]) 

714 for pa in self.compiler.pagetag.parsed_attributes 

715 if pa.startswith("cache_") and pa != "cache_key" 

716 ) 

717 cache_args.update( 

718 (pa[6:], node_or_pagetag.parsed_attributes[pa]) 

719 for pa in node_or_pagetag.parsed_attributes 

720 if pa.startswith("cache_") and pa != "cache_key" 

721 ) 

722 if "timeout" in cache_args: 

723 cache_args["timeout"] = int(eval(cache_args["timeout"])) 

724 

725 self.printer.writeline("def %s(%s):" % (name, ",".join(args))) 

726 

727 # form "arg1, arg2, arg3=arg3, arg4=arg4", etc. 

728 pass_args = [ 

729 "%s=%s" % ((a.split("=")[0],) * 2) if "=" in a else a for a in args 

730 ] 

731 

732 self.write_variable_declares( 

733 identifiers, 

734 toplevel=toplevel, 

735 limit=node_or_pagetag.undeclared_identifiers(), 

736 ) 

737 if buffered: 

738 s = ( 

739 "context.get('local')." 

740 "cache._ctx_get_or_create(" 

741 "%s, lambda:__M_%s(%s), context, %s__M_defname=%r)" 

742 % ( 

743 cachekey, 

744 name, 

745 ",".join(pass_args), 

746 "".join( 

747 ["%s=%s, " % (k, v) for k, v in cache_args.items()] 

748 ), 

749 name, 

750 ) 

751 ) 

752 # apply buffer_filters 

753 s = self.create_filter_callable( 

754 self.compiler.buffer_filters, s, False 

755 ) 

756 self.printer.writelines("return " + s, None) 

757 else: 

758 self.printer.writelines( 

759 "__M_writer(context.get('local')." 

760 "cache._ctx_get_or_create(" 

761 "%s, lambda:__M_%s(%s), context, %s__M_defname=%r))" 

762 % ( 

763 cachekey, 

764 name, 

765 ",".join(pass_args), 

766 "".join( 

767 ["%s=%s, " % (k, v) for k, v in cache_args.items()] 

768 ), 

769 name, 

770 ), 

771 "return ''", 

772 None, 

773 ) 

774 

775 def create_filter_callable(self, args, target, is_expression): 

776 """write a filter-applying expression based on the filters 

777 present in the given filter names, adjusting for the global 

778 'default' filter aliases as needed.""" 

779 

780 def locate_encode(name): 

781 if re.match(r"decode\..+", name): 

782 return "filters." + name 

783 else: 

784 return filters.DEFAULT_ESCAPES.get(name, name) 

785 

786 if "n" not in args: 

787 if is_expression: 

788 if self.compiler.pagetag: 

789 args = self.compiler.pagetag.filter_args.args + args 

790 if self.compiler.default_filters and "n" not in args: 

791 args = self.compiler.default_filters + args 

792 for e in args: 

793 # if filter given as a function, get just the identifier portion 

794 if e == "n": 

795 continue 

796 m = re.match(r"(.+?)(\(.*\))", e) 

797 if m: 

798 ident, fargs = m.group(1, 2) 

799 f = locate_encode(ident) 

800 e = f + fargs 

801 else: 

802 e = locate_encode(e) 

803 assert e is not None 

804 target = "%s(%s)" % (e, target) 

805 return target 

806 

807 def visitExpression(self, node): 

808 self.printer.start_source(node.lineno) 

809 if ( 

810 len(node.escapes) 

811 or ( 

812 self.compiler.pagetag is not None 

813 and len(self.compiler.pagetag.filter_args.args) 

814 ) 

815 or len(self.compiler.default_filters) 

816 ): 

817 s = self.create_filter_callable( 

818 node.escapes_code.args, "%s" % node.text, True 

819 ) 

820 self.printer.writeline("__M_writer(%s)" % s) 

821 else: 

822 self.printer.writeline("__M_writer(%s)" % node.text) 

823 

824 def visitControlLine(self, node): 

825 if node.isend: 

826 self.printer.writeline(None) 

827 if node.has_loop_context: 

828 self.printer.writeline("finally:") 

829 self.printer.writeline("loop = __M_loop._exit()") 

830 self.printer.writeline(None) 

831 else: 

832 self.printer.start_source(node.lineno) 

833 if self.compiler.enable_loop and node.keyword == "for": 

834 text = mangle_mako_loop(node, self.printer) 

835 else: 

836 text = node.text 

837 self.printer.writeline(text) 

838 children = node.get_children() 

839 

840 # this covers the four situations where we want to insert a pass: 

841 # 1) a ternary control line with no children, 

842 # 2) a primary control line with nothing but its own ternary 

843 # and end control lines, and 

844 # 3) any control line with no content other than comments 

845 # 4) the first control block with no content other than comments 

846 def _search_for_control_line(): 

847 for c in children: 

848 if isinstance(c, parsetree.Comment): 

849 continue 

850 elif isinstance(c, parsetree.ControlLine): 

851 return True 

852 return False 

853 

854 if ( 

855 not children 

856 or all( 

857 isinstance(c, (parsetree.Comment, parsetree.ControlLine)) 

858 for c in children 

859 ) 

860 and all( 

861 (node.is_ternary(c.keyword) or c.isend) 

862 for c in children 

863 if isinstance(c, parsetree.ControlLine) 

864 ) 

865 or _search_for_control_line() 

866 ): 

867 self.printer.writeline("pass") 

868 

869 def visitText(self, node): 

870 self.printer.start_source(node.lineno) 

871 self.printer.writeline("__M_writer(%s)" % repr(node.content)) 

872 

873 def visitTextTag(self, node): 

874 filtered = len(node.filter_args.args) > 0 

875 if filtered: 

876 self.printer.writelines( 

877 "__M_writer = context._push_writer()", "try:" 

878 ) 

879 for n in node.nodes: 

880 n.accept_visitor(self) 

881 if filtered: 

882 self.printer.writelines( 

883 "finally:", 

884 "__M_buf, __M_writer = context._pop_buffer_and_writer()", 

885 "__M_writer(%s)" 

886 % self.create_filter_callable( 

887 node.filter_args.args, "__M_buf.getvalue()", False 

888 ), 

889 None, 

890 ) 

891 

892 def visitCode(self, node): 

893 if not node.ismodule: 

894 self.printer.write_indented_block( 

895 node.text, starting_lineno=node.lineno 

896 ) 

897 

898 if not self.in_def and len(self.identifiers.locally_assigned) > 0: 

899 # if we are the "template" def, fudge locally 

900 # declared/modified variables into the "__M_locals" dictionary, 

901 # which is used for def calls within the same template, 

902 # to simulate "enclosing scope" 

903 self.printer.writeline( 

904 "__M_locals_builtin_stored = __M_locals_builtin()" 

905 ) 

906 self.printer.writeline( 

907 "__M_locals.update(__M_dict_builtin([(__M_key," 

908 " __M_locals_builtin_stored[__M_key]) for __M_key in" 

909 " [%s] if __M_key in __M_locals_builtin_stored]))" 

910 % ",".join([repr(x) for x in node.declared_identifiers()]) 

911 ) 

912 

913 def visitIncludeTag(self, node): 

914 self.printer.start_source(node.lineno) 

915 args = node.attributes.get("args") 

916 if args: 

917 self.printer.writeline( 

918 "runtime._include_file(context, %s, _template_uri, %s)" 

919 % (node.parsed_attributes["file"], args) 

920 ) 

921 else: 

922 self.printer.writeline( 

923 "runtime._include_file(context, %s, _template_uri)" 

924 % (node.parsed_attributes["file"]) 

925 ) 

926 

927 def visitNamespaceTag(self, node): 

928 pass 

929 

930 def visitDefTag(self, node): 

931 pass 

932 

933 def visitBlockTag(self, node): 

934 if node.is_anonymous: 

935 self.printer.writeline("%s()" % node.funcname) 

936 else: 

937 nameargs = node.get_argument_expressions(as_call=True) 

938 nameargs += ["**pageargs"] 

939 self.printer.writeline( 

940 "if 'parent' not in context._data or " 

941 "not hasattr(context._data['parent'], '%s'):" % node.funcname 

942 ) 

943 self.printer.writeline( 

944 "context['self'].%s(%s)" % (node.funcname, ",".join(nameargs)) 

945 ) 

946 self.printer.writeline("\n") 

947 

948 def visitCallNamespaceTag(self, node): 

949 # TODO: we can put namespace-specific checks here, such 

950 # as ensure the given namespace will be imported, 

951 # pre-import the namespace, etc. 

952 self.visitCallTag(node) 

953 

954 def visitCallTag(self, node): 

955 self.printer.writeline("def ccall(caller):") 

956 export = ["body"] 

957 callable_identifiers = self.identifiers.branch(node, nested=True) 

958 body_identifiers = callable_identifiers.branch(node, nested=False) 

959 # we want the 'caller' passed to ccall to be used 

960 # for the body() function, but for other non-body() 

961 # <%def>s within <%call> we want the current caller 

962 # off the call stack (if any) 

963 body_identifiers.add_declared("caller") 

964 

965 self.identifier_stack.append(body_identifiers) 

966 

967 class DefVisitor: 

968 def visitDefTag(s, node): 

969 s.visitDefOrBase(node) 

970 

971 def visitBlockTag(s, node): 

972 s.visitDefOrBase(node) 

973 

974 def visitDefOrBase(s, node): 

975 self.write_inline_def(node, callable_identifiers, nested=False) 

976 if not node.is_anonymous: 

977 export.append(node.funcname) 

978 # remove defs that are within the <%call> from the 

979 # "closuredefs" defined in the body, so they dont render twice 

980 if node.funcname in body_identifiers.closuredefs: 

981 del body_identifiers.closuredefs[node.funcname] 

982 

983 vis = DefVisitor() 

984 for n in node.nodes: 

985 n.accept_visitor(vis) 

986 self.identifier_stack.pop() 

987 

988 bodyargs = node.body_decl.get_argument_expressions() 

989 self.printer.writeline("def body(%s):" % ",".join(bodyargs)) 

990 

991 # TODO: figure out best way to specify 

992 # buffering/nonbuffering (at call time would be better) 

993 buffered = False 

994 if buffered: 

995 self.printer.writelines("context._push_buffer()", "try:") 

996 self.write_variable_declares(body_identifiers) 

997 self.identifier_stack.append(body_identifiers) 

998 

999 for n in node.nodes: 

1000 n.accept_visitor(self) 

1001 self.identifier_stack.pop() 

1002 

1003 self.write_def_finish(node, buffered, False, False, callstack=False) 

1004 self.printer.writelines(None, "return [%s]" % (",".join(export)), None) 

1005 

1006 self.printer.writelines( 

1007 # push on caller for nested call 

1008 "context.caller_stack.nextcaller = " 

1009 "runtime.Namespace('caller', context, " 

1010 "callables=ccall(__M_caller))", 

1011 "try:", 

1012 ) 

1013 self.printer.start_source(node.lineno) 

1014 self.printer.writelines( 

1015 "__M_writer(%s)" 

1016 % self.create_filter_callable([], node.expression, True), 

1017 "finally:", 

1018 "context.caller_stack.nextcaller = None", 

1019 None, 

1020 ) 

1021 

1022 

1023class _Identifiers: 

1024 """tracks the status of identifier names as template code is rendered.""" 

1025 

1026 def __init__(self, compiler, node=None, parent=None, nested=False): 

1027 if parent is not None: 

1028 # if we are the branch created in write_namespaces(), 

1029 # we don't share any context from the main body(). 

1030 if isinstance(node, parsetree.NamespaceTag): 

1031 self.declared = set() 

1032 self.topleveldefs = util.SetLikeDict() 

1033 else: 

1034 # things that have already been declared 

1035 # in an enclosing namespace (i.e. names we can just use) 

1036 self.declared = ( 

1037 set(parent.declared) 

1038 .union([c.name for c in parent.closuredefs.values()]) 

1039 .union(parent.locally_declared) 

1040 .union(parent.argument_declared) 

1041 ) 

1042 

1043 # if these identifiers correspond to a "nested" 

1044 # scope, it means whatever the parent identifiers 

1045 # had as undeclared will have been declared by that parent, 

1046 # and therefore we have them in our scope. 

1047 if nested: 

1048 self.declared = self.declared.union(parent.undeclared) 

1049 

1050 # top level defs that are available 

1051 self.topleveldefs = util.SetLikeDict(**parent.topleveldefs) 

1052 else: 

1053 self.declared = set() 

1054 self.topleveldefs = util.SetLikeDict() 

1055 

1056 self.compiler = compiler 

1057 

1058 # things within this level that are referenced before they 

1059 # are declared (e.g. assigned to) 

1060 self.undeclared = set() 

1061 

1062 # things that are declared locally. some of these things 

1063 # could be in the "undeclared" list as well if they are 

1064 # referenced before declared 

1065 self.locally_declared = set() 

1066 

1067 # assignments made in explicit python blocks. 

1068 # these will be propagated to 

1069 # the context of local def calls. 

1070 self.locally_assigned = set() 

1071 

1072 # things that are declared in the argument 

1073 # signature of the def callable 

1074 self.argument_declared = set() 

1075 

1076 # closure defs that are defined in this level 

1077 self.closuredefs = util.SetLikeDict() 

1078 

1079 self.node = node 

1080 

1081 if node is not None: 

1082 node.accept_visitor(self) 

1083 

1084 illegal_names = self.compiler.reserved_names.intersection( 

1085 self.locally_declared 

1086 ) 

1087 if illegal_names: 

1088 raise exceptions.NameConflictError( 

1089 "Reserved words declared in template: %s" 

1090 % ", ".join(illegal_names) 

1091 ) 

1092 

1093 def branch(self, node, **kwargs): 

1094 """create a new Identifiers for a new Node, with 

1095 this Identifiers as the parent.""" 

1096 

1097 return _Identifiers(self.compiler, node, self, **kwargs) 

1098 

1099 @property 

1100 def defs(self): 

1101 return set(self.topleveldefs.union(self.closuredefs).values()) 

1102 

1103 def __repr__(self): 

1104 return ( 

1105 "Identifiers(declared=%r, locally_declared=%r, " 

1106 "undeclared=%r, topleveldefs=%r, closuredefs=%r, " 

1107 "argumentdeclared=%r)" 

1108 % ( 

1109 list(self.declared), 

1110 list(self.locally_declared), 

1111 list(self.undeclared), 

1112 [c.name for c in self.topleveldefs.values()], 

1113 [c.name for c in self.closuredefs.values()], 

1114 self.argument_declared, 

1115 ) 

1116 ) 

1117 

1118 def check_declared(self, node): 

1119 """update the state of this Identifiers with the undeclared 

1120 and declared identifiers of the given node.""" 

1121 

1122 for ident in node.undeclared_identifiers(): 

1123 if ident != "context" and ident not in self.declared.union( 

1124 self.locally_declared 

1125 ): 

1126 self.undeclared.add(ident) 

1127 for ident in node.declared_identifiers(): 

1128 self.locally_declared.add(ident) 

1129 

1130 def add_declared(self, ident): 

1131 self.declared.add(ident) 

1132 if ident in self.undeclared: 

1133 self.undeclared.remove(ident) 

1134 

1135 def visitExpression(self, node): 

1136 self.check_declared(node) 

1137 

1138 def visitControlLine(self, node): 

1139 self.check_declared(node) 

1140 

1141 def visitCode(self, node): 

1142 if not node.ismodule: 

1143 self.check_declared(node) 

1144 self.locally_assigned = self.locally_assigned.union( 

1145 node.declared_identifiers() 

1146 ) 

1147 

1148 def visitNamespaceTag(self, node): 

1149 # only traverse into the sub-elements of a 

1150 # <%namespace> tag if we are the branch created in 

1151 # write_namespaces() 

1152 if self.node is node: 

1153 for n in node.nodes: 

1154 n.accept_visitor(self) 

1155 

1156 def _check_name_exists(self, collection, node): 

1157 existing = collection.get(node.funcname) 

1158 collection[node.funcname] = node 

1159 if ( 

1160 existing is not None 

1161 and existing is not node 

1162 and (node.is_block or existing.is_block) 

1163 ): 

1164 raise exceptions.CompileException( 

1165 "%%def or %%block named '%s' already " 

1166 "exists in this template." % node.funcname, 

1167 **node.exception_kwargs, 

1168 ) 

1169 

1170 def visitDefTag(self, node): 

1171 if node.is_root() and not node.is_anonymous: 

1172 self._check_name_exists(self.topleveldefs, node) 

1173 elif node is not self.node: 

1174 self._check_name_exists(self.closuredefs, node) 

1175 

1176 for ident in node.undeclared_identifiers(): 

1177 if ident != "context" and ident not in self.declared.union( 

1178 self.locally_declared 

1179 ): 

1180 self.undeclared.add(ident) 

1181 

1182 # visit defs only one level deep 

1183 if node is self.node: 

1184 for ident in node.declared_identifiers(): 

1185 self.argument_declared.add(ident) 

1186 

1187 for n in node.nodes: 

1188 n.accept_visitor(self) 

1189 

1190 def visitBlockTag(self, node): 

1191 if node is not self.node and not node.is_anonymous: 

1192 if isinstance(self.node, parsetree.DefTag): 

1193 raise exceptions.CompileException( 

1194 "Named block '%s' not allowed inside of def '%s'" 

1195 % (node.name, self.node.name), 

1196 **node.exception_kwargs, 

1197 ) 

1198 elif isinstance( 

1199 self.node, (parsetree.CallTag, parsetree.CallNamespaceTag) 

1200 ): 

1201 raise exceptions.CompileException( 

1202 "Named block '%s' not allowed inside of <%%call> tag" 

1203 % (node.name,), 

1204 **node.exception_kwargs, 

1205 ) 

1206 

1207 for ident in node.undeclared_identifiers(): 

1208 if ident != "context" and ident not in self.declared.union( 

1209 self.locally_declared 

1210 ): 

1211 self.undeclared.add(ident) 

1212 

1213 if not node.is_anonymous: 

1214 self._check_name_exists(self.topleveldefs, node) 

1215 self.undeclared.add(node.funcname) 

1216 elif node is not self.node: 

1217 self._check_name_exists(self.closuredefs, node) 

1218 for ident in node.declared_identifiers(): 

1219 self.argument_declared.add(ident) 

1220 for n in node.nodes: 

1221 n.accept_visitor(self) 

1222 

1223 def visitTextTag(self, node): 

1224 for ident in node.undeclared_identifiers(): 

1225 if ident != "context" and ident not in self.declared.union( 

1226 self.locally_declared 

1227 ): 

1228 self.undeclared.add(ident) 

1229 

1230 def visitIncludeTag(self, node): 

1231 self.check_declared(node) 

1232 

1233 def visitPageTag(self, node): 

1234 for ident in node.declared_identifiers(): 

1235 self.argument_declared.add(ident) 

1236 self.check_declared(node) 

1237 

1238 def visitCallNamespaceTag(self, node): 

1239 self.visitCallTag(node) 

1240 

1241 def visitCallTag(self, node): 

1242 if node is self.node: 

1243 for ident in node.undeclared_identifiers(): 

1244 if ident != "context" and ident not in self.declared.union( 

1245 self.locally_declared 

1246 ): 

1247 self.undeclared.add(ident) 

1248 for ident in node.declared_identifiers(): 

1249 self.argument_declared.add(ident) 

1250 for n in node.nodes: 

1251 n.accept_visitor(self) 

1252 else: 

1253 for ident in node.undeclared_identifiers(): 

1254 if ident != "context" and ident not in self.declared.union( 

1255 self.locally_declared 

1256 ): 

1257 self.undeclared.add(ident) 

1258 

1259 

1260_FOR_LOOP = re.compile( 

1261 r"^for\s+((?:\(?)\s*" 

1262 r"(?:\(?)\s*[A-Za-z_][A-Za-z_0-9]*" 

1263 r"(?:\s*,\s*(?:[A-Za-z_][A-Za-z_0-9]*),??)*\s*(?:\)?)" 

1264 r"(?:\s*,\s*(?:" 

1265 r"(?:\(?)\s*[A-Za-z_][A-Za-z_0-9]*" 

1266 r"(?:\s*,\s*(?:[A-Za-z_][A-Za-z_0-9]*),??)*\s*(?:\)?)" 

1267 r"),??)*\s*(?:\)?))\s+in\s+(.*):" 

1268) 

1269 

1270 

1271def mangle_mako_loop(node, printer): 

1272 """converts a for loop into a context manager wrapped around a for loop 

1273 when access to the `loop` variable has been detected in the for loop body 

1274 """ 

1275 loop_variable = LoopVariable() 

1276 node.accept_visitor(loop_variable) 

1277 if loop_variable.detected: 

1278 node.nodes[-1].has_loop_context = True 

1279 match = _FOR_LOOP.match(node.text) 

1280 if match: 

1281 printer.writelines( 

1282 "loop = __M_loop._enter(%s)" % match.group(2), 

1283 "try:", 

1284 # 'with __M_loop(%s) as loop:' % match.group(2) 

1285 ) 

1286 text = "for %s in loop:" % match.group(1) 

1287 else: 

1288 raise SyntaxError("Couldn't apply loop context: %s" % node.text) 

1289 else: 

1290 text = node.text 

1291 return text 

1292 

1293 

1294class LoopVariable: 

1295 """A node visitor which looks for the name 'loop' within undeclared 

1296 identifiers.""" 

1297 

1298 def __init__(self): 

1299 self.detected = False 

1300 

1301 def _loop_reference_detected(self, node): 

1302 if "loop" in node.undeclared_identifiers(): 

1303 self.detected = True 

1304 else: 

1305 for n in node.get_children(): 

1306 n.accept_visitor(self) 

1307 

1308 def visitControlLine(self, node): 

1309 self._loop_reference_detected(node) 

1310 

1311 def visitCode(self, node): 

1312 self._loop_reference_detected(node) 

1313 

1314 def visitExpression(self, node): 

1315 self._loop_reference_detected(node)