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-2025 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 

21 

22MAGIC_NUMBER = 10 

23 

24# names which are hardwired into the 

25# template and are not accessed via the 

26# context itself 

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

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

29 

30 

31def compile( # noqa 

32 node, 

33 uri, 

34 filename=None, 

35 default_filters=None, 

36 buffer_filters=None, 

37 imports=None, 

38 future_imports=None, 

39 source_encoding=None, 

40 generate_magic_comment=True, 

41 strict_undefined=False, 

42 enable_loop=True, 

43 reserved_names=frozenset(), 

44): 

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

46 uri, and optional source filename""" 

47 

48 buf = util.FastEncodingBuffer() 

49 

50 printer = PythonPrinter(buf) 

51 _GenerateRenderMethod( 

52 printer, 

53 _CompileContext( 

54 uri, 

55 filename, 

56 default_filters, 

57 buffer_filters, 

58 imports, 

59 future_imports, 

60 source_encoding, 

61 generate_magic_comment, 

62 strict_undefined, 

63 enable_loop, 

64 reserved_names, 

65 ), 

66 node, 

67 ) 

68 return buf.getvalue() 

69 

70 

71class _CompileContext: 

72 def __init__( 

73 self, 

74 uri, 

75 filename, 

76 default_filters, 

77 buffer_filters, 

78 imports, 

79 future_imports, 

80 source_encoding, 

81 generate_magic_comment, 

82 strict_undefined, 

83 enable_loop, 

84 reserved_names, 

85 ): 

86 self.uri = uri 

87 self.filename = filename 

88 self.default_filters = default_filters 

89 self.buffer_filters = buffer_filters 

90 self.imports = imports 

91 self.future_imports = future_imports 

92 self.source_encoding = source_encoding 

93 self.generate_magic_comment = generate_magic_comment 

94 self.strict_undefined = strict_undefined 

95 self.enable_loop = enable_loop 

96 self.reserved_names = reserved_names 

97 

98 

99class _GenerateRenderMethod: 

100 

101 """A template visitor object which generates the 

102 full module source for a template. 

103 

104 """ 

105 

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

107 self.printer = printer 

108 self.compiler = compiler 

109 self.node = node 

110 self.identifier_stack = [None] 

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

112 

113 if self.in_def: 

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

115 args = node.get_argument_expressions() 

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

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

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

119 defs = None 

120 pagetag = None 

121 if node.is_block and not node.is_anonymous: 

122 args += ["**pageargs"] 

123 else: 

124 defs = self.write_toplevel() 

125 pagetag = self.compiler.pagetag 

126 name = "render_body" 

127 if pagetag is not None: 

128 args = pagetag.body_decl.get_argument_expressions() 

129 if not pagetag.body_decl.kwargs: 

130 args += ["**pageargs"] 

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

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

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

134 ) 

135 else: 

136 args = ["**pageargs"] 

137 cached = False 

138 buffered = filtered = False 

139 if args is None: 

140 args = ["context"] 

141 else: 

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

143 

144 self.write_render_callable( 

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

146 ) 

147 

148 if defs is not None: 

149 for node in defs: 

150 _GenerateRenderMethod(printer, compiler, node) 

151 

152 if not self.in_def: 

153 self.write_metadata_struct() 

154 

155 def write_metadata_struct(self): 

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

157 self.printer.source_map 

158 ) 

159 struct = { 

160 "filename": self.compiler.filename, 

161 "uri": self.compiler.uri, 

162 "source_encoding": self.compiler.source_encoding, 

163 "line_map": self.printer.source_map, 

164 } 

165 self.printer.writelines( 

166 '"""', 

167 "__M_BEGIN_METADATA", 

168 json.dumps(struct), 

169 "__M_END_METADATA\n" '"""', 

170 ) 

171 

172 @property 

173 def identifiers(self): 

174 return self.identifier_stack[-1] 

175 

176 def write_toplevel(self): 

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

178 generate the start of module-level code. 

179 

180 """ 

181 inherit = [] 

182 namespaces = {} 

183 module_code = [] 

184 

185 self.compiler.pagetag = None 

186 

187 class FindTopLevel: 

188 def visitInheritTag(s, node): 

189 inherit.append(node) 

190 

191 def visitNamespaceTag(s, node): 

192 namespaces[node.name] = node 

193 

194 def visitPageTag(s, node): 

195 self.compiler.pagetag = node 

196 

197 def visitCode(s, node): 

198 if node.ismodule: 

199 module_code.append(node) 

200 

201 f = FindTopLevel() 

202 for n in self.node.nodes: 

203 n.accept_visitor(f) 

204 

205 self.compiler.namespaces = namespaces 

206 

207 module_ident = set() 

208 for n in module_code: 

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

210 

211 module_identifiers = _Identifiers(self.compiler) 

212 module_identifiers.declared = module_ident 

213 

214 # module-level names, python code 

215 if ( 

216 self.compiler.generate_magic_comment 

217 and self.compiler.source_encoding 

218 ): 

219 self.printer.writeline( 

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

221 ) 

222 

223 if self.compiler.future_imports: 

224 self.printer.writeline( 

225 "from __future__ import %s" 

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

227 ) 

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

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

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

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

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

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

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

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

236 self.printer.writeline( 

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

238 ) 

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

240 self.printer.writeline( 

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

242 ) 

243 if self.compiler.imports: 

244 buf = "" 

245 for imp in self.compiler.imports: 

246 buf += imp + "\n" 

247 self.printer.writeline(imp) 

248 impcode = ast.PythonCode( 

249 buf, 

250 source="", 

251 lineno=0, 

252 pos=0, 

253 filename="template defined imports", 

254 ) 

255 else: 

256 impcode = None 

257 

258 main_identifiers = module_identifiers.branch(self.node) 

259 mit = module_identifiers.topleveldefs 

260 module_identifiers.topleveldefs = mit.union( 

261 main_identifiers.topleveldefs 

262 ) 

263 module_identifiers.declared.update(TOPLEVEL_DECLARED) 

264 if impcode: 

265 module_identifiers.declared.update(impcode.declared_identifiers) 

266 

267 self.compiler.identifiers = module_identifiers 

268 self.printer.writeline( 

269 "_exports = %r" 

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

271 ) 

272 self.printer.write_blanks(2) 

273 

274 if len(module_code): 

275 self.write_module_code(module_code) 

276 

277 if len(inherit): 

278 self.write_namespaces(namespaces) 

279 self.write_inherit(inherit[-1]) 

280 elif len(namespaces): 

281 self.write_namespaces(namespaces) 

282 

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

284 

285 def write_render_callable( 

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

287 ): 

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

289 

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

291 

292 if self.in_def: 

293 decorator = node.decorator 

294 if decorator: 

295 self.printer.writeline( 

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

297 ) 

298 

299 self.printer.start_source(node.lineno) 

300 self.printer.writelines( 

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

302 # push new frame, assign current frame to __M_caller 

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

304 "try:", 

305 ) 

306 if buffered or filtered or cached: 

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

308 

309 self.identifier_stack.append( 

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

311 ) 

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

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

314 

315 if not self.in_def and ( 

316 len(self.identifiers.locally_assigned) > 0 

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

318 ): 

319 self.printer.writeline( 

320 "__M_locals = __M_dict_builtin(%s)" 

321 % ",".join( 

322 [ 

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

324 for x in self.identifiers.argument_declared 

325 ] 

326 ) 

327 ) 

328 

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

330 

331 for n in self.node.nodes: 

332 n.accept_visitor(self) 

333 

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

335 self.printer.writeline(None) 

336 self.printer.write_blanks(2) 

337 if cached: 

338 self.write_cache_decorator( 

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

340 ) 

341 

342 def write_module_code(self, module_code): 

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

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

345 for n in module_code: 

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

347 

348 def write_inherit(self, node): 

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

350 

351 self.printer.writelines( 

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

353 "_mako_generate_namespaces(context)", 

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

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

356 None, 

357 ) 

358 

359 def write_namespaces(self, namespaces): 

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

361 self.printer.writelines( 

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

363 "try:", 

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

365 "except KeyError:", 

366 "_mako_generate_namespaces(context)", 

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

368 None, 

369 None, 

370 ) 

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

372 

373 for node in namespaces.values(): 

374 if "import" in node.attributes: 

375 self.compiler.has_ns_imports = True 

376 self.printer.start_source(node.lineno) 

377 if len(node.nodes): 

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

379 export = [] 

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

381 self.in_def = True 

382 

383 class NSDefVisitor: 

384 def visitDefTag(s, node): 

385 s.visitDefOrBase(node) 

386 

387 def visitBlockTag(s, node): 

388 s.visitDefOrBase(node) 

389 

390 def visitDefOrBase(s, node): 

391 if node.is_anonymous: 

392 raise exceptions.CompileException( 

393 "Can't put anonymous blocks inside " 

394 "<%namespace>", 

395 **node.exception_kwargs, 

396 ) 

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

398 export.append(node.funcname) 

399 

400 vis = NSDefVisitor() 

401 for n in node.nodes: 

402 n.accept_visitor(vis) 

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

404 self.printer.writeline(None) 

405 self.in_def = False 

406 callable_name = "make_namespace()" 

407 else: 

408 callable_name = "None" 

409 

410 if "file" in node.parsed_attributes: 

411 self.printer.writeline( 

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

413 " context._clean_inheritance_tokens()," 

414 " templateuri=%s, callables=%s, " 

415 " calling_uri=_template_uri)" 

416 % ( 

417 node.name, 

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

419 callable_name, 

420 ) 

421 ) 

422 elif "module" in node.parsed_attributes: 

423 self.printer.writeline( 

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

425 " context._clean_inheritance_tokens()," 

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

427 " module=%s)" 

428 % ( 

429 node.name, 

430 callable_name, 

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

432 ) 

433 ) 

434 else: 

435 self.printer.writeline( 

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

437 " context._clean_inheritance_tokens()," 

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

439 % (node.name, callable_name) 

440 ) 

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

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

443 

444 self.printer.writeline( 

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

446 ) 

447 self.printer.write_blanks(1) 

448 if not len(namespaces): 

449 self.printer.writeline("pass") 

450 self.printer.writeline(None) 

451 

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

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

454 

455 the variable declarations are in the form of callable 

456 definitions for defs and/or name lookup within the 

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

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

459 which don't otherwise have any explicit assignment 

460 operation. names that are assigned within the body are 

461 assumed to be locally-scoped variables and are not 

462 separately declared. 

463 

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

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

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

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

468 

469 """ 

470 

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

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

473 to_write = set() 

474 

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

476 # need that arent in the namespace yet 

477 to_write = to_write.union(identifiers.undeclared) 

478 

479 # write closure functions for closures that we define 

480 # right here 

481 to_write = to_write.union( 

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

483 ) 

484 

485 # remove identifiers that are declared in the argument 

486 # signature of the callable 

487 to_write = to_write.difference(identifiers.argument_declared) 

488 

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

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

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

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

493 # which cannot be referenced beforehand. 

494 to_write = to_write.difference(identifiers.locally_declared) 

495 

496 if self.compiler.enable_loop: 

497 has_loop = "loop" in to_write 

498 to_write.discard("loop") 

499 else: 

500 has_loop = False 

501 

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

503 # (this is used for the caching decorator) 

504 if limit is not None: 

505 to_write = to_write.intersection(limit) 

506 

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

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

509 self.compiler.has_imports = True 

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

511 if "import" in ns.attributes: 

512 self.printer.writeline( 

513 "_mako_get_namespace(context, %r)." 

514 "_populate(_import_ns, %r)" 

515 % ( 

516 ident, 

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

518 ) 

519 ) 

520 

521 if has_loop: 

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

523 

524 for ident in to_write: 

525 if ident in comp_idents: 

526 comp = comp_idents[ident] 

527 if comp.is_block: 

528 if not comp.is_anonymous: 

529 self.write_def_decl(comp, identifiers) 

530 else: 

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

532 else: 

533 if comp.is_root(): 

534 self.write_def_decl(comp, identifiers) 

535 else: 

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

537 

538 elif ident in self.compiler.namespaces: 

539 self.printer.writeline( 

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

541 ) 

542 else: 

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

544 if self.compiler.strict_undefined: 

545 self.printer.writelines( 

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

547 % (ident, ident), 

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

549 "try:", 

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

551 "except KeyError:", 

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

553 None, 

554 None, 

555 ) 

556 else: 

557 self.printer.writeline( 

558 "%s = _import_ns.get" 

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

560 % (ident, ident, ident) 

561 ) 

562 else: 

563 if self.compiler.strict_undefined: 

564 self.printer.writelines( 

565 "try:", 

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

567 "except KeyError:", 

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

569 None, 

570 ) 

571 else: 

572 self.printer.writeline( 

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

574 ) 

575 

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

577 

578 def write_def_decl(self, node, identifiers): 

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

580 funcname = node.funcname 

581 namedecls = node.get_argument_expressions() 

582 nameargs = node.get_argument_expressions(as_call=True) 

583 

584 if not self.in_def and ( 

585 len(self.identifiers.locally_assigned) > 0 

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

587 ): 

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

589 else: 

590 nameargs.insert(0, "context") 

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

592 self.printer.writeline( 

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

594 ) 

595 self.printer.writeline(None) 

596 

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

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

599 

600 namedecls = node.get_argument_expressions() 

601 

602 decorator = node.decorator 

603 if decorator: 

604 self.printer.writeline( 

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

606 ) 

607 self.printer.writeline( 

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

609 ) 

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

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

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

613 self.printer.writelines( 

614 # push new frame, assign current frame to __M_caller 

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

616 "try:", 

617 ) 

618 if buffered or filtered or cached: 

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

620 

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

622 

623 self.write_variable_declares(identifiers) 

624 

625 self.identifier_stack.append(identifiers) 

626 for n in node.nodes: 

627 n.accept_visitor(self) 

628 self.identifier_stack.pop() 

629 

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

631 self.printer.writeline(None) 

632 if cached: 

633 self.write_cache_decorator( 

634 node, 

635 node.funcname, 

636 namedecls, 

637 False, 

638 identifiers, 

639 inline=True, 

640 toplevel=False, 

641 ) 

642 

643 def write_def_finish( 

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

645 ): 

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

647 inline. 

648 

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

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

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

652 return value.""" 

653 

654 if not buffered and not cached and not filtered: 

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

656 if callstack: 

657 self.printer.writelines( 

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

659 ) 

660 

661 if buffered or filtered or cached: 

662 if buffered or cached: 

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

664 # from the context after popping; assume the caching 

665 # implemenation might be using a context with no 

666 # extra buffers 

667 self.printer.writelines( 

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

669 ) 

670 else: 

671 self.printer.writelines( 

672 "finally:", 

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

674 ) 

675 

676 if callstack: 

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

678 

679 s = "__M_buf.getvalue()" 

680 if filtered: 

681 s = self.create_filter_callable( 

682 node.filter_args.args, s, False 

683 ) 

684 self.printer.writeline(None) 

685 if buffered and not cached: 

686 s = self.create_filter_callable( 

687 self.compiler.buffer_filters, s, False 

688 ) 

689 if buffered or cached: 

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

691 else: 

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

693 

694 def write_cache_decorator( 

695 self, 

696 node_or_pagetag, 

697 name, 

698 args, 

699 buffered, 

700 identifiers, 

701 inline=False, 

702 toplevel=False, 

703 ): 

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

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

706 

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

708 cachekey = node_or_pagetag.parsed_attributes.get( 

709 "cache_key", repr(name) 

710 ) 

711 

712 cache_args = {} 

713 if self.compiler.pagetag is not None: 

714 cache_args.update( 

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

716 for pa in self.compiler.pagetag.parsed_attributes 

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

718 ) 

719 cache_args.update( 

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

721 for pa in node_or_pagetag.parsed_attributes 

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

723 ) 

724 if "timeout" in cache_args: 

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

726 

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

728 

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

730 pass_args = [ 

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

732 ] 

733 

734 self.write_variable_declares( 

735 identifiers, 

736 toplevel=toplevel, 

737 limit=node_or_pagetag.undeclared_identifiers(), 

738 ) 

739 if buffered: 

740 s = ( 

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

742 "cache._ctx_get_or_create(" 

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

744 % ( 

745 cachekey, 

746 name, 

747 ",".join(pass_args), 

748 "".join( 

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

750 ), 

751 name, 

752 ) 

753 ) 

754 # apply buffer_filters 

755 s = self.create_filter_callable( 

756 self.compiler.buffer_filters, s, False 

757 ) 

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

759 else: 

760 self.printer.writelines( 

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

762 "cache._ctx_get_or_create(" 

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

764 % ( 

765 cachekey, 

766 name, 

767 ",".join(pass_args), 

768 "".join( 

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

770 ), 

771 name, 

772 ), 

773 "return ''", 

774 None, 

775 ) 

776 

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

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

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

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

781 

782 def locate_encode(name): 

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

784 return "filters." + name 

785 else: 

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

787 

788 if "n" not in args: 

789 if is_expression: 

790 if self.compiler.pagetag: 

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

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

793 args = self.compiler.default_filters + args 

794 for e in args: 

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

796 if e == "n": 

797 continue 

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

799 if m: 

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

801 f = locate_encode(ident) 

802 e = f + fargs 

803 else: 

804 e = locate_encode(e) 

805 assert e is not None 

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

807 return target 

808 

809 def visitExpression(self, node): 

810 self.printer.start_source(node.lineno) 

811 if ( 

812 len(node.escapes) 

813 or ( 

814 self.compiler.pagetag is not None 

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

816 ) 

817 or len(self.compiler.default_filters) 

818 ): 

819 s = self.create_filter_callable( 

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

821 ) 

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

823 else: 

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

825 

826 def visitControlLine(self, node): 

827 if node.isend: 

828 self.printer.writeline(None) 

829 if node.has_loop_context: 

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

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

832 self.printer.writeline(None) 

833 else: 

834 self.printer.start_source(node.lineno) 

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

836 text = mangle_mako_loop(node, self.printer) 

837 else: 

838 text = node.text 

839 self.printer.writeline(text) 

840 children = node.get_children() 

841 

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

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

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

845 # and end control lines, and 

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

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

848 def _search_for_control_line(): 

849 for c in children: 

850 if isinstance(c, parsetree.Comment): 

851 continue 

852 elif isinstance(c, parsetree.ControlLine): 

853 return True 

854 return False 

855 

856 if ( 

857 not children 

858 or all( 

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

860 for c in children 

861 ) 

862 and all( 

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

864 for c in children 

865 if isinstance(c, parsetree.ControlLine) 

866 ) 

867 or _search_for_control_line() 

868 ): 

869 self.printer.writeline("pass") 

870 

871 def visitText(self, node): 

872 self.printer.start_source(node.lineno) 

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

874 

875 def visitTextTag(self, node): 

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

877 if filtered: 

878 self.printer.writelines( 

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

880 ) 

881 for n in node.nodes: 

882 n.accept_visitor(self) 

883 if filtered: 

884 self.printer.writelines( 

885 "finally:", 

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

887 "__M_writer(%s)" 

888 % self.create_filter_callable( 

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

890 ), 

891 None, 

892 ) 

893 

894 def visitCode(self, node): 

895 if not node.ismodule: 

896 self.printer.write_indented_block( 

897 node.text, starting_lineno=node.lineno 

898 ) 

899 

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

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

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

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

904 # to simulate "enclosing scope" 

905 self.printer.writeline( 

906 "__M_locals_builtin_stored = __M_locals_builtin()" 

907 ) 

908 self.printer.writeline( 

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

910 " __M_locals_builtin_stored[__M_key]) for __M_key in" 

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

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

913 ) 

914 

915 def visitIncludeTag(self, node): 

916 self.printer.start_source(node.lineno) 

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

918 if args: 

919 self.printer.writeline( 

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

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

922 ) 

923 else: 

924 self.printer.writeline( 

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

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

927 ) 

928 

929 def visitNamespaceTag(self, node): 

930 pass 

931 

932 def visitDefTag(self, node): 

933 pass 

934 

935 def visitBlockTag(self, node): 

936 if node.is_anonymous: 

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

938 else: 

939 nameargs = node.get_argument_expressions(as_call=True) 

940 nameargs += ["**pageargs"] 

941 self.printer.writeline( 

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

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

944 ) 

945 self.printer.writeline( 

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

947 ) 

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

949 

950 def visitCallNamespaceTag(self, node): 

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

952 # as ensure the given namespace will be imported, 

953 # pre-import the namespace, etc. 

954 self.visitCallTag(node) 

955 

956 def visitCallTag(self, node): 

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

958 export = ["body"] 

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

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

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

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

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

964 # off the call stack (if any) 

965 body_identifiers.add_declared("caller") 

966 

967 self.identifier_stack.append(body_identifiers) 

968 

969 class DefVisitor: 

970 def visitDefTag(s, node): 

971 s.visitDefOrBase(node) 

972 

973 def visitBlockTag(s, node): 

974 s.visitDefOrBase(node) 

975 

976 def visitDefOrBase(s, node): 

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

978 if not node.is_anonymous: 

979 export.append(node.funcname) 

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

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

982 if node.funcname in body_identifiers.closuredefs: 

983 del body_identifiers.closuredefs[node.funcname] 

984 

985 vis = DefVisitor() 

986 for n in node.nodes: 

987 n.accept_visitor(vis) 

988 self.identifier_stack.pop() 

989 

990 bodyargs = node.body_decl.get_argument_expressions() 

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

992 

993 # TODO: figure out best way to specify 

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

995 buffered = False 

996 if buffered: 

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

998 self.write_variable_declares(body_identifiers) 

999 self.identifier_stack.append(body_identifiers) 

1000 

1001 for n in node.nodes: 

1002 n.accept_visitor(self) 

1003 self.identifier_stack.pop() 

1004 

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

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

1007 

1008 self.printer.writelines( 

1009 # push on caller for nested call 

1010 "context.caller_stack.nextcaller = " 

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

1012 "callables=ccall(__M_caller))", 

1013 "try:", 

1014 ) 

1015 self.printer.start_source(node.lineno) 

1016 self.printer.writelines( 

1017 "__M_writer(%s)" 

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

1019 "finally:", 

1020 "context.caller_stack.nextcaller = None", 

1021 None, 

1022 ) 

1023 

1024 

1025class _Identifiers: 

1026 

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

1028 

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

1030 if parent is not None: 

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

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

1033 if isinstance(node, parsetree.NamespaceTag): 

1034 self.declared = set() 

1035 self.topleveldefs = util.SetLikeDict() 

1036 else: 

1037 # things that have already been declared 

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

1039 self.declared = ( 

1040 set(parent.declared) 

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

1042 .union(parent.locally_declared) 

1043 .union(parent.argument_declared) 

1044 ) 

1045 

1046 # if these identifiers correspond to a "nested" 

1047 # scope, it means whatever the parent identifiers 

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

1049 # and therefore we have them in our scope. 

1050 if nested: 

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

1052 

1053 # top level defs that are available 

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

1055 else: 

1056 self.declared = set() 

1057 self.topleveldefs = util.SetLikeDict() 

1058 

1059 self.compiler = compiler 

1060 

1061 # things within this level that are referenced before they 

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

1063 self.undeclared = set() 

1064 

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

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

1067 # referenced before declared 

1068 self.locally_declared = set() 

1069 

1070 # assignments made in explicit python blocks. 

1071 # these will be propagated to 

1072 # the context of local def calls. 

1073 self.locally_assigned = set() 

1074 

1075 # things that are declared in the argument 

1076 # signature of the def callable 

1077 self.argument_declared = set() 

1078 

1079 # closure defs that are defined in this level 

1080 self.closuredefs = util.SetLikeDict() 

1081 

1082 self.node = node 

1083 

1084 if node is not None: 

1085 node.accept_visitor(self) 

1086 

1087 illegal_names = self.compiler.reserved_names.intersection( 

1088 self.locally_declared 

1089 ) 

1090 if illegal_names: 

1091 raise exceptions.NameConflictError( 

1092 "Reserved words declared in template: %s" 

1093 % ", ".join(illegal_names) 

1094 ) 

1095 

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

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

1098 this Identifiers as the parent.""" 

1099 

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

1101 

1102 @property 

1103 def defs(self): 

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

1105 

1106 def __repr__(self): 

1107 return ( 

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

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

1110 "argumentdeclared=%r)" 

1111 % ( 

1112 list(self.declared), 

1113 list(self.locally_declared), 

1114 list(self.undeclared), 

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

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

1117 self.argument_declared, 

1118 ) 

1119 ) 

1120 

1121 def check_declared(self, node): 

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

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

1124 

1125 for ident in node.undeclared_identifiers(): 

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

1127 self.locally_declared 

1128 ): 

1129 self.undeclared.add(ident) 

1130 for ident in node.declared_identifiers(): 

1131 self.locally_declared.add(ident) 

1132 

1133 def add_declared(self, ident): 

1134 self.declared.add(ident) 

1135 if ident in self.undeclared: 

1136 self.undeclared.remove(ident) 

1137 

1138 def visitExpression(self, node): 

1139 self.check_declared(node) 

1140 

1141 def visitControlLine(self, node): 

1142 self.check_declared(node) 

1143 

1144 def visitCode(self, node): 

1145 if not node.ismodule: 

1146 self.check_declared(node) 

1147 self.locally_assigned = self.locally_assigned.union( 

1148 node.declared_identifiers() 

1149 ) 

1150 

1151 def visitNamespaceTag(self, node): 

1152 # only traverse into the sub-elements of a 

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

1154 # write_namespaces() 

1155 if self.node is node: 

1156 for n in node.nodes: 

1157 n.accept_visitor(self) 

1158 

1159 def _check_name_exists(self, collection, node): 

1160 existing = collection.get(node.funcname) 

1161 collection[node.funcname] = node 

1162 if ( 

1163 existing is not None 

1164 and existing is not node 

1165 and (node.is_block or existing.is_block) 

1166 ): 

1167 raise exceptions.CompileException( 

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

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

1170 **node.exception_kwargs, 

1171 ) 

1172 

1173 def visitDefTag(self, node): 

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

1175 self._check_name_exists(self.topleveldefs, node) 

1176 elif node is not self.node: 

1177 self._check_name_exists(self.closuredefs, node) 

1178 

1179 for ident in node.undeclared_identifiers(): 

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

1181 self.locally_declared 

1182 ): 

1183 self.undeclared.add(ident) 

1184 

1185 # visit defs only one level deep 

1186 if node is self.node: 

1187 for ident in node.declared_identifiers(): 

1188 self.argument_declared.add(ident) 

1189 

1190 for n in node.nodes: 

1191 n.accept_visitor(self) 

1192 

1193 def visitBlockTag(self, node): 

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

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

1196 raise exceptions.CompileException( 

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

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

1199 **node.exception_kwargs, 

1200 ) 

1201 elif isinstance( 

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

1203 ): 

1204 raise exceptions.CompileException( 

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

1206 % (node.name,), 

1207 **node.exception_kwargs, 

1208 ) 

1209 

1210 for ident in node.undeclared_identifiers(): 

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

1212 self.locally_declared 

1213 ): 

1214 self.undeclared.add(ident) 

1215 

1216 if not node.is_anonymous: 

1217 self._check_name_exists(self.topleveldefs, node) 

1218 self.undeclared.add(node.funcname) 

1219 elif node is not self.node: 

1220 self._check_name_exists(self.closuredefs, node) 

1221 for ident in node.declared_identifiers(): 

1222 self.argument_declared.add(ident) 

1223 for n in node.nodes: 

1224 n.accept_visitor(self) 

1225 

1226 def visitTextTag(self, node): 

1227 for ident in node.undeclared_identifiers(): 

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

1229 self.locally_declared 

1230 ): 

1231 self.undeclared.add(ident) 

1232 

1233 def visitIncludeTag(self, node): 

1234 self.check_declared(node) 

1235 

1236 def visitPageTag(self, node): 

1237 for ident in node.declared_identifiers(): 

1238 self.argument_declared.add(ident) 

1239 self.check_declared(node) 

1240 

1241 def visitCallNamespaceTag(self, node): 

1242 self.visitCallTag(node) 

1243 

1244 def visitCallTag(self, node): 

1245 if node is self.node: 

1246 for ident in node.undeclared_identifiers(): 

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

1248 self.locally_declared 

1249 ): 

1250 self.undeclared.add(ident) 

1251 for ident in node.declared_identifiers(): 

1252 self.argument_declared.add(ident) 

1253 for n in node.nodes: 

1254 n.accept_visitor(self) 

1255 else: 

1256 for ident in node.undeclared_identifiers(): 

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

1258 self.locally_declared 

1259 ): 

1260 self.undeclared.add(ident) 

1261 

1262 

1263_FOR_LOOP = re.compile( 

1264 r"^for\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*(?:" 

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

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

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

1271) 

1272 

1273 

1274def mangle_mako_loop(node, printer): 

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

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

1277 """ 

1278 loop_variable = LoopVariable() 

1279 node.accept_visitor(loop_variable) 

1280 if loop_variable.detected: 

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

1282 match = _FOR_LOOP.match(node.text) 

1283 if match: 

1284 printer.writelines( 

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

1286 "try:" 

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

1288 ) 

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

1290 else: 

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

1292 else: 

1293 text = node.text 

1294 return text 

1295 

1296 

1297class LoopVariable: 

1298 

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

1300 identifiers.""" 

1301 

1302 def __init__(self): 

1303 self.detected = False 

1304 

1305 def _loop_reference_detected(self, node): 

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

1307 self.detected = True 

1308 else: 

1309 for n in node.get_children(): 

1310 n.accept_visitor(self) 

1311 

1312 def visitControlLine(self, node): 

1313 self._loop_reference_detected(node) 

1314 

1315 def visitCode(self, node): 

1316 self._loop_reference_detected(node) 

1317 

1318 def visitExpression(self, node): 

1319 self._loop_reference_detected(node)