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

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

493 statements  

1# mako/_ast_util.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""" 

8ast 

9~~~ 

10 

11This is a stripped down version of Armin Ronacher's ast module. 

12 

13:copyright: Copyright 2008 by Armin Ronacher. 

14:license: Python License. 

15""" 

16 

17from _ast import Add 

18from _ast import And 

19from _ast import AST 

20from _ast import BitAnd 

21from _ast import BitOr 

22from _ast import BitXor 

23from _ast import Div 

24from _ast import Eq 

25from _ast import FloorDiv 

26from _ast import Gt 

27from _ast import GtE 

28from _ast import If 

29from _ast import In 

30from _ast import Invert 

31from _ast import Is 

32from _ast import IsNot 

33from _ast import LShift 

34from _ast import Lt 

35from _ast import LtE 

36from _ast import Mod 

37from _ast import Mult 

38from _ast import Name 

39from _ast import Not 

40from _ast import NotEq 

41from _ast import NotIn 

42from _ast import Or 

43from _ast import PyCF_ONLY_AST 

44from _ast import RShift 

45from _ast import Sub 

46from _ast import UAdd 

47from _ast import USub 

48 

49BOOLOP_SYMBOLS = {And: "and", Or: "or"} 

50 

51BINOP_SYMBOLS = { 

52 Add: "+", 

53 Sub: "-", 

54 Mult: "*", 

55 Div: "/", 

56 FloorDiv: "//", 

57 Mod: "%", 

58 LShift: "<<", 

59 RShift: ">>", 

60 BitOr: "|", 

61 BitAnd: "&", 

62 BitXor: "^", 

63} 

64 

65CMPOP_SYMBOLS = { 

66 Eq: "==", 

67 Gt: ">", 

68 GtE: ">=", 

69 In: "in", 

70 Is: "is", 

71 IsNot: "is not", 

72 Lt: "<", 

73 LtE: "<=", 

74 NotEq: "!=", 

75 NotIn: "not in", 

76} 

77 

78UNARYOP_SYMBOLS = {Invert: "~", Not: "not", UAdd: "+", USub: "-"} 

79 

80ALL_SYMBOLS = {} 

81ALL_SYMBOLS.update(BOOLOP_SYMBOLS) 

82ALL_SYMBOLS.update(BINOP_SYMBOLS) 

83ALL_SYMBOLS.update(CMPOP_SYMBOLS) 

84ALL_SYMBOLS.update(UNARYOP_SYMBOLS) 

85 

86 

87def parse(expr, filename="<unknown>", mode="exec"): 

88 """Parse an expression into an AST node.""" 

89 return compile(expr, filename, mode, PyCF_ONLY_AST) 

90 

91 

92def iter_fields(node): 

93 """Iterate over all fields of a node, only yielding existing fields.""" 

94 

95 for field in node._fields: 

96 try: 

97 yield field, getattr(node, field) 

98 except AttributeError: 

99 pass 

100 

101 

102class NodeVisitor: 

103 """ 

104 Walks the abstract syntax tree and call visitor functions for every node 

105 found. The visitor functions may return values which will be forwarded 

106 by the `visit` method. 

107 

108 Per default the visitor functions for the nodes are ``'visit_'`` + 

109 class name of the node. So a `TryFinally` node visit function would 

110 be `visit_TryFinally`. This behavior can be changed by overriding 

111 the `get_visitor` function. If no visitor function exists for a node 

112 (return value `None`) the `generic_visit` visitor is used instead. 

113 

114 Don't use the `NodeVisitor` if you want to apply changes to nodes during 

115 traversing. For this a special visitor exists (`NodeTransformer`) that 

116 allows modifications. 

117 """ 

118 

119 def get_visitor(self, node): 

120 """ 

121 Return the visitor function for this node or `None` if no visitor 

122 exists for this node. In that case the generic visit function is 

123 used instead. 

124 """ 

125 method = "visit_" + node.__class__.__name__ 

126 return getattr(self, method, None) 

127 

128 def visit(self, node): 

129 """Visit a node.""" 

130 f = self.get_visitor(node) 

131 if f is not None: 

132 return f(node) 

133 return self.generic_visit(node) 

134 

135 def generic_visit(self, node): 

136 """Called if no explicit visitor function exists for a node.""" 

137 for field, value in iter_fields(node): 

138 if isinstance(value, list): 

139 for item in value: 

140 if isinstance(item, AST): 

141 self.visit(item) 

142 elif isinstance(value, AST): 

143 self.visit(value) 

144 

145 

146class NodeTransformer(NodeVisitor): 

147 """ 

148 Walks the abstract syntax tree and allows modifications of nodes. 

149 

150 The `NodeTransformer` will walk the AST and use the return value of the 

151 visitor functions to replace or remove the old node. If the return 

152 value of the visitor function is `None` the node will be removed 

153 from the previous location otherwise it's replaced with the return 

154 value. The return value may be the original node in which case no 

155 replacement takes place. 

156 

157 Here an example transformer that rewrites all `foo` to `data['foo']`:: 

158 

159 class RewriteName(NodeTransformer): 

160 

161 def visit_Name(self, node): 

162 return copy_location(Subscript( 

163 value=Name(id='data', ctx=Load()), 

164 slice=Index(value=Str(s=node.id)), 

165 ctx=node.ctx 

166 ), node) 

167 

168 Keep in mind that if the node you're operating on has child nodes 

169 you must either transform the child nodes yourself or call the generic 

170 visit function for the node first. 

171 

172 Nodes that were part of a collection of statements (that applies to 

173 all statement nodes) may also return a list of nodes rather than just 

174 a single node. 

175 

176 Usually you use the transformer like this:: 

177 

178 node = YourTransformer().visit(node) 

179 """ 

180 

181 def generic_visit(self, node): 

182 for field, old_value in iter_fields(node): 

183 old_value = getattr(node, field, None) 

184 if isinstance(old_value, list): 

185 new_values = [] 

186 for value in old_value: 

187 if isinstance(value, AST): 

188 value = self.visit(value) 

189 if value is None: 

190 continue 

191 elif not isinstance(value, AST): 

192 new_values.extend(value) 

193 continue 

194 new_values.append(value) 

195 old_value[:] = new_values 

196 elif isinstance(old_value, AST): 

197 new_node = self.visit(old_value) 

198 if new_node is None: 

199 delattr(node, field) 

200 else: 

201 setattr(node, field, new_node) 

202 return node 

203 

204 

205class SourceGenerator(NodeVisitor): 

206 """ 

207 This visitor is able to transform a well formed syntax tree into python 

208 sourcecode. For more details have a look at the docstring of the 

209 `node_to_source` function. 

210 """ 

211 

212 def __init__(self, indent_with): 

213 self.result = [] 

214 self.indent_with = indent_with 

215 self.indentation = 0 

216 self.new_lines = 0 

217 

218 def write(self, x): 

219 if self.new_lines: 

220 if self.result: 

221 self.result.append("\n" * self.new_lines) 

222 self.result.append(self.indent_with * self.indentation) 

223 self.new_lines = 0 

224 self.result.append(x) 

225 

226 def newline(self, n=1): 

227 self.new_lines = max(self.new_lines, n) 

228 

229 def body(self, statements): 

230 self.new_line = True 

231 self.indentation += 1 

232 for stmt in statements: 

233 self.visit(stmt) 

234 self.indentation -= 1 

235 

236 def body_or_else(self, node): 

237 self.body(node.body) 

238 if node.orelse: 

239 self.newline() 

240 self.write("else:") 

241 self.body(node.orelse) 

242 

243 def signature(self, node): 

244 want_comma = [] 

245 

246 def write_comma(): 

247 if want_comma: 

248 self.write(", ") 

249 else: 

250 want_comma.append(True) 

251 

252 padding = [None] * (len(node.args) - len(node.defaults)) 

253 for arg, default in zip(node.args, padding + node.defaults): 

254 write_comma() 

255 self.visit(arg) 

256 if default is not None: 

257 self.write("=") 

258 self.visit(default) 

259 if node.vararg is not None: 

260 write_comma() 

261 self.write("*" + node.vararg.arg) 

262 if node.kwarg is not None: 

263 write_comma() 

264 self.write("**" + node.kwarg.arg) 

265 

266 def decorators(self, node): 

267 for decorator in node.decorator_list: 

268 self.newline() 

269 self.write("@") 

270 self.visit(decorator) 

271 

272 # Statements 

273 

274 def visit_Assign(self, node): 

275 self.newline() 

276 for idx, target in enumerate(node.targets): 

277 if idx: 

278 self.write(", ") 

279 self.visit(target) 

280 self.write(" = ") 

281 self.visit(node.value) 

282 

283 def visit_AugAssign(self, node): 

284 self.newline() 

285 self.visit(node.target) 

286 self.write(BINOP_SYMBOLS[type(node.op)] + "=") 

287 self.visit(node.value) 

288 

289 def visit_ImportFrom(self, node): 

290 self.newline() 

291 self.write("from %s%s import " % ("." * node.level, node.module)) 

292 for idx, item in enumerate(node.names): 

293 if idx: 

294 self.write(", ") 

295 self.write(item) 

296 

297 def visit_Import(self, node): 

298 self.newline() 

299 for item in node.names: 

300 self.write("import ") 

301 self.visit(item) 

302 

303 def visit_Expr(self, node): 

304 self.newline() 

305 self.generic_visit(node) 

306 

307 def visit_FunctionDef(self, node): 

308 self.newline(n=2) 

309 self.decorators(node) 

310 self.newline() 

311 self.write("def %s(" % node.name) 

312 self.signature(node.args) 

313 self.write("):") 

314 self.body(node.body) 

315 

316 def visit_ClassDef(self, node): 

317 have_args = [] 

318 

319 def paren_or_comma(): 

320 if have_args: 

321 self.write(", ") 

322 else: 

323 have_args.append(True) 

324 self.write("(") 

325 

326 self.newline(n=3) 

327 self.decorators(node) 

328 self.newline() 

329 self.write("class %s" % node.name) 

330 for base in node.bases: 

331 paren_or_comma() 

332 self.visit(base) 

333 # XXX: the if here is used to keep this module compatible 

334 # with python 2.6. 

335 if hasattr(node, "keywords"): 

336 for keyword in node.keywords: 

337 paren_or_comma() 

338 self.write(keyword.arg + "=") 

339 self.visit(keyword.value) 

340 if getattr(node, "starargs", None): 

341 paren_or_comma() 

342 self.write("*") 

343 self.visit(node.starargs) 

344 if getattr(node, "kwargs", None): 

345 paren_or_comma() 

346 self.write("**") 

347 self.visit(node.kwargs) 

348 self.write(have_args and "):" or ":") 

349 self.body(node.body) 

350 

351 def visit_If(self, node): 

352 self.newline() 

353 self.write("if ") 

354 self.visit(node.test) 

355 self.write(":") 

356 self.body(node.body) 

357 while True: 

358 else_ = node.orelse 

359 if len(else_) == 1 and isinstance(else_[0], If): 

360 node = else_[0] 

361 self.newline() 

362 self.write("elif ") 

363 self.visit(node.test) 

364 self.write(":") 

365 self.body(node.body) 

366 else: 

367 self.newline() 

368 self.write("else:") 

369 self.body(else_) 

370 break 

371 

372 def visit_For(self, node): 

373 self.newline() 

374 self.write("for ") 

375 self.visit(node.target) 

376 self.write(" in ") 

377 self.visit(node.iter) 

378 self.write(":") 

379 self.body_or_else(node) 

380 

381 def visit_While(self, node): 

382 self.newline() 

383 self.write("while ") 

384 self.visit(node.test) 

385 self.write(":") 

386 self.body_or_else(node) 

387 

388 def visit_With(self, node): 

389 self.newline() 

390 self.write("with ") 

391 self.visit(node.context_expr) 

392 if node.optional_vars is not None: 

393 self.write(" as ") 

394 self.visit(node.optional_vars) 

395 self.write(":") 

396 self.body(node.body) 

397 

398 def visit_Pass(self, node): 

399 self.newline() 

400 self.write("pass") 

401 

402 def visit_Print(self, node): 

403 # XXX: python 2.6 only 

404 self.newline() 

405 self.write("print ") 

406 want_comma = False 

407 if node.dest is not None: 

408 self.write(" >> ") 

409 self.visit(node.dest) 

410 want_comma = True 

411 for value in node.values: 

412 if want_comma: 

413 self.write(", ") 

414 self.visit(value) 

415 want_comma = True 

416 if not node.nl: 

417 self.write(",") 

418 

419 def visit_Delete(self, node): 

420 self.newline() 

421 self.write("del ") 

422 for idx, target in enumerate(node): 

423 if idx: 

424 self.write(", ") 

425 self.visit(target) 

426 

427 def visit_TryExcept(self, node): 

428 self.newline() 

429 self.write("try:") 

430 self.body(node.body) 

431 for handler in node.handlers: 

432 self.visit(handler) 

433 

434 def visit_TryFinally(self, node): 

435 self.newline() 

436 self.write("try:") 

437 self.body(node.body) 

438 self.newline() 

439 self.write("finally:") 

440 self.body(node.finalbody) 

441 

442 def visit_Global(self, node): 

443 self.newline() 

444 self.write("global " + ", ".join(node.names)) 

445 

446 def visit_Nonlocal(self, node): 

447 self.newline() 

448 self.write("nonlocal " + ", ".join(node.names)) 

449 

450 def visit_Return(self, node): 

451 self.newline() 

452 self.write("return ") 

453 self.visit(node.value) 

454 

455 def visit_Break(self, node): 

456 self.newline() 

457 self.write("break") 

458 

459 def visit_Continue(self, node): 

460 self.newline() 

461 self.write("continue") 

462 

463 def visit_Raise(self, node): 

464 # XXX: Python 2.6 / 3.0 compatibility 

465 self.newline() 

466 self.write("raise") 

467 if hasattr(node, "exc") and node.exc is not None: 

468 self.write(" ") 

469 self.visit(node.exc) 

470 if node.cause is not None: 

471 self.write(" from ") 

472 self.visit(node.cause) 

473 elif hasattr(node, "type") and node.type is not None: 

474 self.visit(node.type) 

475 if node.inst is not None: 

476 self.write(", ") 

477 self.visit(node.inst) 

478 if node.tback is not None: 

479 self.write(", ") 

480 self.visit(node.tback) 

481 

482 # Expressions 

483 

484 def visit_Attribute(self, node): 

485 self.visit(node.value) 

486 self.write("." + node.attr) 

487 

488 def visit_Call(self, node): 

489 want_comma = [] 

490 

491 def write_comma(): 

492 if want_comma: 

493 self.write(", ") 

494 else: 

495 want_comma.append(True) 

496 

497 self.visit(node.func) 

498 self.write("(") 

499 for arg in node.args: 

500 write_comma() 

501 self.visit(arg) 

502 for keyword in node.keywords: 

503 write_comma() 

504 self.write(keyword.arg + "=") 

505 self.visit(keyword.value) 

506 if getattr(node, "starargs", None): 

507 write_comma() 

508 self.write("*") 

509 self.visit(node.starargs) 

510 if getattr(node, "kwargs", None): 

511 write_comma() 

512 self.write("**") 

513 self.visit(node.kwargs) 

514 self.write(")") 

515 

516 def visit_Name(self, node): 

517 self.write(node.id) 

518 

519 def visit_arg(self, node): 

520 self.write(node.arg) 

521 

522 def visit_Constant(self, node): 

523 self.write(repr(node.value)) 

524 

525 def visit_Tuple(self, node): 

526 self.write("(") 

527 idx = -1 

528 for idx, item in enumerate(node.elts): 

529 if idx: 

530 self.write(", ") 

531 self.visit(item) 

532 self.write(idx and ")" or ",)") 

533 

534 def sequence_visit(left, right): 

535 def visit(self, node): 

536 self.write(left) 

537 for idx, item in enumerate(node.elts): 

538 if idx: 

539 self.write(", ") 

540 self.visit(item) 

541 self.write(right) 

542 

543 return visit 

544 

545 visit_List = sequence_visit("[", "]") 

546 visit_Set = sequence_visit("{", "}") 

547 del sequence_visit 

548 

549 def visit_Dict(self, node): 

550 self.write("{") 

551 for idx, (key, value) in enumerate(zip(node.keys, node.values)): 

552 if idx: 

553 self.write(", ") 

554 self.visit(key) 

555 self.write(": ") 

556 self.visit(value) 

557 self.write("}") 

558 

559 def visit_BinOp(self, node): 

560 self.write("(") 

561 self.visit(node.left) 

562 self.write(" %s " % BINOP_SYMBOLS[type(node.op)]) 

563 self.visit(node.right) 

564 self.write(")") 

565 

566 def visit_BoolOp(self, node): 

567 self.write("(") 

568 for idx, value in enumerate(node.values): 

569 if idx: 

570 self.write(" %s " % BOOLOP_SYMBOLS[type(node.op)]) 

571 self.visit(value) 

572 self.write(")") 

573 

574 def visit_Compare(self, node): 

575 self.write("(") 

576 self.visit(node.left) 

577 for op, right in zip(node.ops, node.comparators): 

578 self.write(" %s " % CMPOP_SYMBOLS[type(op)]) 

579 self.visit(right) 

580 self.write(")") 

581 

582 def visit_UnaryOp(self, node): 

583 self.write("(") 

584 op = UNARYOP_SYMBOLS[type(node.op)] 

585 self.write(op) 

586 if op == "not": 

587 self.write(" ") 

588 self.visit(node.operand) 

589 self.write(")") 

590 

591 def visit_Subscript(self, node): 

592 self.visit(node.value) 

593 self.write("[") 

594 self.visit(node.slice) 

595 self.write("]") 

596 

597 def visit_Slice(self, node): 

598 if node.lower is not None: 

599 self.visit(node.lower) 

600 self.write(":") 

601 if node.upper is not None: 

602 self.visit(node.upper) 

603 if node.step is not None: 

604 self.write(":") 

605 if not (isinstance(node.step, Name) and node.step.id == "None"): 

606 self.visit(node.step) 

607 

608 def visit_ExtSlice(self, node): 

609 for idx, item in node.dims: 

610 if idx: 

611 self.write(", ") 

612 self.visit(item) 

613 

614 def visit_Yield(self, node): 

615 self.write("yield ") 

616 self.visit(node.value) 

617 

618 def visit_Lambda(self, node): 

619 self.write("lambda ") 

620 self.signature(node.args) 

621 self.write(": ") 

622 self.visit(node.body) 

623 

624 def generator_visit(left, right): 

625 def visit(self, node): 

626 self.write(left) 

627 self.visit(node.elt) 

628 for comprehension in node.generators: 

629 self.visit(comprehension) 

630 self.write(right) 

631 

632 return visit 

633 

634 visit_ListComp = generator_visit("[", "]") 

635 visit_GeneratorExp = generator_visit("(", ")") 

636 visit_SetComp = generator_visit("{", "}") 

637 del generator_visit 

638 

639 def visit_DictComp(self, node): 

640 self.write("{") 

641 self.visit(node.key) 

642 self.write(": ") 

643 self.visit(node.value) 

644 for comprehension in node.generators: 

645 self.visit(comprehension) 

646 self.write("}") 

647 

648 def visit_IfExp(self, node): 

649 self.visit(node.body) 

650 self.write(" if ") 

651 self.visit(node.test) 

652 self.write(" else ") 

653 self.visit(node.orelse) 

654 

655 def visit_Starred(self, node): 

656 self.write("*") 

657 self.visit(node.value) 

658 

659 def visit_Repr(self, node): 

660 # XXX: python 2.6 only 

661 self.write("`") 

662 self.visit(node.value) 

663 self.write("`") 

664 

665 # Helper Nodes 

666 

667 def visit_alias(self, node): 

668 self.write(node.name) 

669 if node.asname is not None: 

670 self.write(" as " + node.asname) 

671 

672 def visit_comprehension(self, node): 

673 self.write(" for ") 

674 self.visit(node.target) 

675 self.write(" in ") 

676 self.visit(node.iter) 

677 if node.ifs: 

678 for if_ in node.ifs: 

679 self.write(" if ") 

680 self.visit(if_) 

681 

682 def visit_excepthandler(self, node): 

683 self.newline() 

684 self.write("except") 

685 if node.type is not None: 

686 self.write(" ") 

687 self.visit(node.type) 

688 if node.name is not None: 

689 self.write(" as ") 

690 self.visit(node.name) 

691 self.write(":") 

692 self.body(node.body)