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
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
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
7"""
8ast
9~~~
11This is a stripped down version of Armin Ronacher's ast module.
13:copyright: Copyright 2008 by Armin Ronacher.
14:license: Python License.
15"""
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
49BOOLOP_SYMBOLS = {And: "and", Or: "or"}
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}
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}
78UNARYOP_SYMBOLS = {Invert: "~", Not: "not", UAdd: "+", USub: "-"}
80ALL_SYMBOLS = {}
81ALL_SYMBOLS.update(BOOLOP_SYMBOLS)
82ALL_SYMBOLS.update(BINOP_SYMBOLS)
83ALL_SYMBOLS.update(CMPOP_SYMBOLS)
84ALL_SYMBOLS.update(UNARYOP_SYMBOLS)
87def parse(expr, filename="<unknown>", mode="exec"):
88 """Parse an expression into an AST node."""
89 return compile(expr, filename, mode, PyCF_ONLY_AST)
92def iter_fields(node):
93 """Iterate over all fields of a node, only yielding existing fields."""
95 for field in node._fields:
96 try:
97 yield field, getattr(node, field)
98 except AttributeError:
99 pass
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.
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.
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 """
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)
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)
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)
146class NodeTransformer(NodeVisitor):
147 """
148 Walks the abstract syntax tree and allows modifications of nodes.
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.
157 Here an example transformer that rewrites all `foo` to `data['foo']`::
159 class RewriteName(NodeTransformer):
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)
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.
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.
176 Usually you use the transformer like this::
178 node = YourTransformer().visit(node)
179 """
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
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 """
212 def __init__(self, indent_with):
213 self.result = []
214 self.indent_with = indent_with
215 self.indentation = 0
216 self.new_lines = 0
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)
226 def newline(self, n=1):
227 self.new_lines = max(self.new_lines, n)
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
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)
243 def signature(self, node):
244 want_comma = []
246 def write_comma():
247 if want_comma:
248 self.write(", ")
249 else:
250 want_comma.append(True)
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)
266 def decorators(self, node):
267 for decorator in node.decorator_list:
268 self.newline()
269 self.write("@")
270 self.visit(decorator)
272 # Statements
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)
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)
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)
297 def visit_Import(self, node):
298 self.newline()
299 for item in node.names:
300 self.write("import ")
301 self.visit(item)
303 def visit_Expr(self, node):
304 self.newline()
305 self.generic_visit(node)
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)
316 def visit_ClassDef(self, node):
317 have_args = []
319 def paren_or_comma():
320 if have_args:
321 self.write(", ")
322 else:
323 have_args.append(True)
324 self.write("(")
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)
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
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)
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)
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)
398 def visit_Pass(self, node):
399 self.newline()
400 self.write("pass")
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(",")
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)
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)
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)
442 def visit_Global(self, node):
443 self.newline()
444 self.write("global " + ", ".join(node.names))
446 def visit_Nonlocal(self, node):
447 self.newline()
448 self.write("nonlocal " + ", ".join(node.names))
450 def visit_Return(self, node):
451 self.newline()
452 self.write("return ")
453 self.visit(node.value)
455 def visit_Break(self, node):
456 self.newline()
457 self.write("break")
459 def visit_Continue(self, node):
460 self.newline()
461 self.write("continue")
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)
482 # Expressions
484 def visit_Attribute(self, node):
485 self.visit(node.value)
486 self.write("." + node.attr)
488 def visit_Call(self, node):
489 want_comma = []
491 def write_comma():
492 if want_comma:
493 self.write(", ")
494 else:
495 want_comma.append(True)
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(")")
516 def visit_Name(self, node):
517 self.write(node.id)
519 def visit_arg(self, node):
520 self.write(node.arg)
522 def visit_Constant(self, node):
523 self.write(repr(node.value))
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 ",)")
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)
543 return visit
545 visit_List = sequence_visit("[", "]")
546 visit_Set = sequence_visit("{", "}")
547 del sequence_visit
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("}")
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(")")
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(")")
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(")")
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(")")
591 def visit_Subscript(self, node):
592 self.visit(node.value)
593 self.write("[")
594 self.visit(node.slice)
595 self.write("]")
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)
608 def visit_ExtSlice(self, node):
609 for idx, item in node.dims:
610 if idx:
611 self.write(", ")
612 self.visit(item)
614 def visit_Yield(self, node):
615 self.write("yield ")
616 self.visit(node.value)
618 def visit_Lambda(self, node):
619 self.write("lambda ")
620 self.signature(node.args)
621 self.write(": ")
622 self.visit(node.body)
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)
632 return visit
634 visit_ListComp = generator_visit("[", "]")
635 visit_GeneratorExp = generator_visit("(", ")")
636 visit_SetComp = generator_visit("{", "}")
637 del generator_visit
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("}")
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)
655 def visit_Starred(self, node):
656 self.write("*")
657 self.visit(node.value)
659 def visit_Repr(self, node):
660 # XXX: python 2.6 only
661 self.write("`")
662 self.visit(node.value)
663 self.write("`")
665 # Helper Nodes
667 def visit_alias(self, node):
668 self.write(node.name)
669 if node.asname is not None:
670 self.write(" as " + node.asname)
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_)
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)