Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/mako/_ast_util.py: 50%
501 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:02 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:02 +0000
1# mako/_ast_util.py
2# Copyright 2006-2023 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"""
8 ast
9 ~~~
11 This is a stripped down version of Armin Ronacher's ast module.
13 :copyright: Copyright 2008 by Armin Ronacher.
14 :license: Python License.
15"""
18from _ast import Add
19from _ast import And
20from _ast import AST
21from _ast import BitAnd
22from _ast import BitOr
23from _ast import BitXor
24from _ast import Div
25from _ast import Eq
26from _ast import FloorDiv
27from _ast import Gt
28from _ast import GtE
29from _ast import If
30from _ast import In
31from _ast import Invert
32from _ast import Is
33from _ast import IsNot
34from _ast import LShift
35from _ast import Lt
36from _ast import LtE
37from _ast import Mod
38from _ast import Mult
39from _ast import Name
40from _ast import Not
41from _ast import NotEq
42from _ast import NotIn
43from _ast import Or
44from _ast import PyCF_ONLY_AST
45from _ast import RShift
46from _ast import Sub
47from _ast import UAdd
48from _ast import USub
51BOOLOP_SYMBOLS = {And: "and", Or: "or"}
53BINOP_SYMBOLS = {
54 Add: "+",
55 Sub: "-",
56 Mult: "*",
57 Div: "/",
58 FloorDiv: "//",
59 Mod: "%",
60 LShift: "<<",
61 RShift: ">>",
62 BitOr: "|",
63 BitAnd: "&",
64 BitXor: "^",
65}
67CMPOP_SYMBOLS = {
68 Eq: "==",
69 Gt: ">",
70 GtE: ">=",
71 In: "in",
72 Is: "is",
73 IsNot: "is not",
74 Lt: "<",
75 LtE: "<=",
76 NotEq: "!=",
77 NotIn: "not in",
78}
80UNARYOP_SYMBOLS = {Invert: "~", Not: "not", UAdd: "+", USub: "-"}
82ALL_SYMBOLS = {}
83ALL_SYMBOLS.update(BOOLOP_SYMBOLS)
84ALL_SYMBOLS.update(BINOP_SYMBOLS)
85ALL_SYMBOLS.update(CMPOP_SYMBOLS)
86ALL_SYMBOLS.update(UNARYOP_SYMBOLS)
89def parse(expr, filename="<unknown>", mode="exec"):
90 """Parse an expression into an AST node."""
91 return compile(expr, filename, mode, PyCF_ONLY_AST)
94def iter_fields(node):
95 """Iterate over all fields of a node, only yielding existing fields."""
97 for field in node._fields:
98 try:
99 yield field, getattr(node, field)
100 except AttributeError:
101 pass
104class NodeVisitor:
106 """
107 Walks the abstract syntax tree and call visitor functions for every node
108 found. The visitor functions may return values which will be forwarded
109 by the `visit` method.
111 Per default the visitor functions for the nodes are ``'visit_'`` +
112 class name of the node. So a `TryFinally` node visit function would
113 be `visit_TryFinally`. This behavior can be changed by overriding
114 the `get_visitor` function. If no visitor function exists for a node
115 (return value `None`) the `generic_visit` visitor is used instead.
117 Don't use the `NodeVisitor` if you want to apply changes to nodes during
118 traversing. For this a special visitor exists (`NodeTransformer`) that
119 allows modifications.
120 """
122 def get_visitor(self, node):
123 """
124 Return the visitor function for this node or `None` if no visitor
125 exists for this node. In that case the generic visit function is
126 used instead.
127 """
128 method = "visit_" + node.__class__.__name__
129 return getattr(self, method, None)
131 def visit(self, node):
132 """Visit a node."""
133 f = self.get_visitor(node)
134 if f is not None:
135 return f(node)
136 return self.generic_visit(node)
138 def generic_visit(self, node):
139 """Called if no explicit visitor function exists for a node."""
140 for field, value in iter_fields(node):
141 if isinstance(value, list):
142 for item in value:
143 if isinstance(item, AST):
144 self.visit(item)
145 elif isinstance(value, AST):
146 self.visit(value)
149class NodeTransformer(NodeVisitor):
151 """
152 Walks the abstract syntax tree and allows modifications of nodes.
154 The `NodeTransformer` will walk the AST and use the return value of the
155 visitor functions to replace or remove the old node. If the return
156 value of the visitor function is `None` the node will be removed
157 from the previous location otherwise it's replaced with the return
158 value. The return value may be the original node in which case no
159 replacement takes place.
161 Here an example transformer that rewrites all `foo` to `data['foo']`::
163 class RewriteName(NodeTransformer):
165 def visit_Name(self, node):
166 return copy_location(Subscript(
167 value=Name(id='data', ctx=Load()),
168 slice=Index(value=Str(s=node.id)),
169 ctx=node.ctx
170 ), node)
172 Keep in mind that if the node you're operating on has child nodes
173 you must either transform the child nodes yourself or call the generic
174 visit function for the node first.
176 Nodes that were part of a collection of statements (that applies to
177 all statement nodes) may also return a list of nodes rather than just
178 a single node.
180 Usually you use the transformer like this::
182 node = YourTransformer().visit(node)
183 """
185 def generic_visit(self, node):
186 for field, old_value in iter_fields(node):
187 old_value = getattr(node, field, None)
188 if isinstance(old_value, list):
189 new_values = []
190 for value in old_value:
191 if isinstance(value, AST):
192 value = self.visit(value)
193 if value is None:
194 continue
195 elif not isinstance(value, AST):
196 new_values.extend(value)
197 continue
198 new_values.append(value)
199 old_value[:] = new_values
200 elif isinstance(old_value, AST):
201 new_node = self.visit(old_value)
202 if new_node is None:
203 delattr(node, field)
204 else:
205 setattr(node, field, new_node)
206 return node
209class SourceGenerator(NodeVisitor):
211 """
212 This visitor is able to transform a well formed syntax tree into python
213 sourcecode. For more details have a look at the docstring of the
214 `node_to_source` function.
215 """
217 def __init__(self, indent_with):
218 self.result = []
219 self.indent_with = indent_with
220 self.indentation = 0
221 self.new_lines = 0
223 def write(self, x):
224 if self.new_lines:
225 if self.result:
226 self.result.append("\n" * self.new_lines)
227 self.result.append(self.indent_with * self.indentation)
228 self.new_lines = 0
229 self.result.append(x)
231 def newline(self, n=1):
232 self.new_lines = max(self.new_lines, n)
234 def body(self, statements):
235 self.new_line = True
236 self.indentation += 1
237 for stmt in statements:
238 self.visit(stmt)
239 self.indentation -= 1
241 def body_or_else(self, node):
242 self.body(node.body)
243 if node.orelse:
244 self.newline()
245 self.write("else:")
246 self.body(node.orelse)
248 def signature(self, node):
249 want_comma = []
251 def write_comma():
252 if want_comma:
253 self.write(", ")
254 else:
255 want_comma.append(True)
257 padding = [None] * (len(node.args) - len(node.defaults))
258 for arg, default in zip(node.args, padding + node.defaults):
259 write_comma()
260 self.visit(arg)
261 if default is not None:
262 self.write("=")
263 self.visit(default)
264 if node.vararg is not None:
265 write_comma()
266 self.write("*" + node.vararg.arg)
267 if node.kwarg is not None:
268 write_comma()
269 self.write("**" + node.kwarg.arg)
271 def decorators(self, node):
272 for decorator in node.decorator_list:
273 self.newline()
274 self.write("@")
275 self.visit(decorator)
277 # Statements
279 def visit_Assign(self, node):
280 self.newline()
281 for idx, target in enumerate(node.targets):
282 if idx:
283 self.write(", ")
284 self.visit(target)
285 self.write(" = ")
286 self.visit(node.value)
288 def visit_AugAssign(self, node):
289 self.newline()
290 self.visit(node.target)
291 self.write(BINOP_SYMBOLS[type(node.op)] + "=")
292 self.visit(node.value)
294 def visit_ImportFrom(self, node):
295 self.newline()
296 self.write("from %s%s import " % ("." * node.level, node.module))
297 for idx, item in enumerate(node.names):
298 if idx:
299 self.write(", ")
300 self.write(item)
302 def visit_Import(self, node):
303 self.newline()
304 for item in node.names:
305 self.write("import ")
306 self.visit(item)
308 def visit_Expr(self, node):
309 self.newline()
310 self.generic_visit(node)
312 def visit_FunctionDef(self, node):
313 self.newline(n=2)
314 self.decorators(node)
315 self.newline()
316 self.write("def %s(" % node.name)
317 self.signature(node.args)
318 self.write("):")
319 self.body(node.body)
321 def visit_ClassDef(self, node):
322 have_args = []
324 def paren_or_comma():
325 if have_args:
326 self.write(", ")
327 else:
328 have_args.append(True)
329 self.write("(")
331 self.newline(n=3)
332 self.decorators(node)
333 self.newline()
334 self.write("class %s" % node.name)
335 for base in node.bases:
336 paren_or_comma()
337 self.visit(base)
338 # XXX: the if here is used to keep this module compatible
339 # with python 2.6.
340 if hasattr(node, "keywords"):
341 for keyword in node.keywords:
342 paren_or_comma()
343 self.write(keyword.arg + "=")
344 self.visit(keyword.value)
345 if getattr(node, "starargs", None):
346 paren_or_comma()
347 self.write("*")
348 self.visit(node.starargs)
349 if getattr(node, "kwargs", None):
350 paren_or_comma()
351 self.write("**")
352 self.visit(node.kwargs)
353 self.write(have_args and "):" or ":")
354 self.body(node.body)
356 def visit_If(self, node):
357 self.newline()
358 self.write("if ")
359 self.visit(node.test)
360 self.write(":")
361 self.body(node.body)
362 while True:
363 else_ = node.orelse
364 if len(else_) == 1 and isinstance(else_[0], If):
365 node = else_[0]
366 self.newline()
367 self.write("elif ")
368 self.visit(node.test)
369 self.write(":")
370 self.body(node.body)
371 else:
372 self.newline()
373 self.write("else:")
374 self.body(else_)
375 break
377 def visit_For(self, node):
378 self.newline()
379 self.write("for ")
380 self.visit(node.target)
381 self.write(" in ")
382 self.visit(node.iter)
383 self.write(":")
384 self.body_or_else(node)
386 def visit_While(self, node):
387 self.newline()
388 self.write("while ")
389 self.visit(node.test)
390 self.write(":")
391 self.body_or_else(node)
393 def visit_With(self, node):
394 self.newline()
395 self.write("with ")
396 self.visit(node.context_expr)
397 if node.optional_vars is not None:
398 self.write(" as ")
399 self.visit(node.optional_vars)
400 self.write(":")
401 self.body(node.body)
403 def visit_Pass(self, node):
404 self.newline()
405 self.write("pass")
407 def visit_Print(self, node):
408 # XXX: python 2.6 only
409 self.newline()
410 self.write("print ")
411 want_comma = False
412 if node.dest is not None:
413 self.write(" >> ")
414 self.visit(node.dest)
415 want_comma = True
416 for value in node.values:
417 if want_comma:
418 self.write(", ")
419 self.visit(value)
420 want_comma = True
421 if not node.nl:
422 self.write(",")
424 def visit_Delete(self, node):
425 self.newline()
426 self.write("del ")
427 for idx, target in enumerate(node):
428 if idx:
429 self.write(", ")
430 self.visit(target)
432 def visit_TryExcept(self, node):
433 self.newline()
434 self.write("try:")
435 self.body(node.body)
436 for handler in node.handlers:
437 self.visit(handler)
439 def visit_TryFinally(self, node):
440 self.newline()
441 self.write("try:")
442 self.body(node.body)
443 self.newline()
444 self.write("finally:")
445 self.body(node.finalbody)
447 def visit_Global(self, node):
448 self.newline()
449 self.write("global " + ", ".join(node.names))
451 def visit_Nonlocal(self, node):
452 self.newline()
453 self.write("nonlocal " + ", ".join(node.names))
455 def visit_Return(self, node):
456 self.newline()
457 self.write("return ")
458 self.visit(node.value)
460 def visit_Break(self, node):
461 self.newline()
462 self.write("break")
464 def visit_Continue(self, node):
465 self.newline()
466 self.write("continue")
468 def visit_Raise(self, node):
469 # XXX: Python 2.6 / 3.0 compatibility
470 self.newline()
471 self.write("raise")
472 if hasattr(node, "exc") and node.exc is not None:
473 self.write(" ")
474 self.visit(node.exc)
475 if node.cause is not None:
476 self.write(" from ")
477 self.visit(node.cause)
478 elif hasattr(node, "type") and node.type is not None:
479 self.visit(node.type)
480 if node.inst is not None:
481 self.write(", ")
482 self.visit(node.inst)
483 if node.tback is not None:
484 self.write(", ")
485 self.visit(node.tback)
487 # Expressions
489 def visit_Attribute(self, node):
490 self.visit(node.value)
491 self.write("." + node.attr)
493 def visit_Call(self, node):
494 want_comma = []
496 def write_comma():
497 if want_comma:
498 self.write(", ")
499 else:
500 want_comma.append(True)
502 self.visit(node.func)
503 self.write("(")
504 for arg in node.args:
505 write_comma()
506 self.visit(arg)
507 for keyword in node.keywords:
508 write_comma()
509 self.write(keyword.arg + "=")
510 self.visit(keyword.value)
511 if getattr(node, "starargs", None):
512 write_comma()
513 self.write("*")
514 self.visit(node.starargs)
515 if getattr(node, "kwargs", None):
516 write_comma()
517 self.write("**")
518 self.visit(node.kwargs)
519 self.write(")")
521 def visit_Name(self, node):
522 self.write(node.id)
524 def visit_NameConstant(self, node):
525 self.write(str(node.value))
527 def visit_arg(self, node):
528 self.write(node.arg)
530 def visit_Str(self, node):
531 self.write(repr(node.s))
533 def visit_Bytes(self, node):
534 self.write(repr(node.s))
536 def visit_Num(self, node):
537 self.write(repr(node.n))
539 # newly needed in Python 3.8
540 def visit_Constant(self, node):
541 self.write(repr(node.value))
543 def visit_Tuple(self, node):
544 self.write("(")
545 idx = -1
546 for idx, item in enumerate(node.elts):
547 if idx:
548 self.write(", ")
549 self.visit(item)
550 self.write(idx and ")" or ",)")
552 def sequence_visit(left, right):
553 def visit(self, node):
554 self.write(left)
555 for idx, item in enumerate(node.elts):
556 if idx:
557 self.write(", ")
558 self.visit(item)
559 self.write(right)
561 return visit
563 visit_List = sequence_visit("[", "]")
564 visit_Set = sequence_visit("{", "}")
565 del sequence_visit
567 def visit_Dict(self, node):
568 self.write("{")
569 for idx, (key, value) in enumerate(zip(node.keys, node.values)):
570 if idx:
571 self.write(", ")
572 self.visit(key)
573 self.write(": ")
574 self.visit(value)
575 self.write("}")
577 def visit_BinOp(self, node):
578 self.write("(")
579 self.visit(node.left)
580 self.write(" %s " % BINOP_SYMBOLS[type(node.op)])
581 self.visit(node.right)
582 self.write(")")
584 def visit_BoolOp(self, node):
585 self.write("(")
586 for idx, value in enumerate(node.values):
587 if idx:
588 self.write(" %s " % BOOLOP_SYMBOLS[type(node.op)])
589 self.visit(value)
590 self.write(")")
592 def visit_Compare(self, node):
593 self.write("(")
594 self.visit(node.left)
595 for op, right in zip(node.ops, node.comparators):
596 self.write(" %s " % CMPOP_SYMBOLS[type(op)])
597 self.visit(right)
598 self.write(")")
600 def visit_UnaryOp(self, node):
601 self.write("(")
602 op = UNARYOP_SYMBOLS[type(node.op)]
603 self.write(op)
604 if op == "not":
605 self.write(" ")
606 self.visit(node.operand)
607 self.write(")")
609 def visit_Subscript(self, node):
610 self.visit(node.value)
611 self.write("[")
612 self.visit(node.slice)
613 self.write("]")
615 def visit_Slice(self, node):
616 if node.lower is not None:
617 self.visit(node.lower)
618 self.write(":")
619 if node.upper is not None:
620 self.visit(node.upper)
621 if node.step is not None:
622 self.write(":")
623 if not (isinstance(node.step, Name) and node.step.id == "None"):
624 self.visit(node.step)
626 def visit_ExtSlice(self, node):
627 for idx, item in node.dims:
628 if idx:
629 self.write(", ")
630 self.visit(item)
632 def visit_Yield(self, node):
633 self.write("yield ")
634 self.visit(node.value)
636 def visit_Lambda(self, node):
637 self.write("lambda ")
638 self.signature(node.args)
639 self.write(": ")
640 self.visit(node.body)
642 def visit_Ellipsis(self, node):
643 self.write("Ellipsis")
645 def generator_visit(left, right):
646 def visit(self, node):
647 self.write(left)
648 self.visit(node.elt)
649 for comprehension in node.generators:
650 self.visit(comprehension)
651 self.write(right)
653 return visit
655 visit_ListComp = generator_visit("[", "]")
656 visit_GeneratorExp = generator_visit("(", ")")
657 visit_SetComp = generator_visit("{", "}")
658 del generator_visit
660 def visit_DictComp(self, node):
661 self.write("{")
662 self.visit(node.key)
663 self.write(": ")
664 self.visit(node.value)
665 for comprehension in node.generators:
666 self.visit(comprehension)
667 self.write("}")
669 def visit_IfExp(self, node):
670 self.visit(node.body)
671 self.write(" if ")
672 self.visit(node.test)
673 self.write(" else ")
674 self.visit(node.orelse)
676 def visit_Starred(self, node):
677 self.write("*")
678 self.visit(node.value)
680 def visit_Repr(self, node):
681 # XXX: python 2.6 only
682 self.write("`")
683 self.visit(node.value)
684 self.write("`")
686 # Helper Nodes
688 def visit_alias(self, node):
689 self.write(node.name)
690 if node.asname is not None:
691 self.write(" as " + node.asname)
693 def visit_comprehension(self, node):
694 self.write(" for ")
695 self.visit(node.target)
696 self.write(" in ")
697 self.visit(node.iter)
698 if node.ifs:
699 for if_ in node.ifs:
700 self.write(" if ")
701 self.visit(if_)
703 def visit_excepthandler(self, node):
704 self.newline()
705 self.write("except")
706 if node.type is not None:
707 self.write(" ")
708 self.visit(node.type)
709 if node.name is not None:
710 self.write(" as ")
711 self.visit(node.name)
712 self.write(":")
713 self.body(node.body)