-
-
Notifications
You must be signed in to change notification settings - Fork 53
Expand file tree
/
Copy pathast_printer.py
More file actions
133 lines (107 loc) · 3.18 KB
/
ast_printer.py
File metadata and controls
133 lines (107 loc) · 3.18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
"""
Print a representation of an AST
This prints a human readable representation of the nodes in the AST.
The goal is to make it easy to see what the AST looks like, and to
make it easy to compare two ASTs.
This is not intended to be a complete representation of the AST, some
fields or field names may be omitted for clarity. It should still be precise and unambiguous.
"""
import python_minifier.ast_compat as ast
from python_minifier.util import is_constant_node
INDENT = ' '
# The field name that can be omitted for each node
# Either it's the only field or would otherwise be obvious
default_fields = {
'Constant': 'value',
'Num': 'n',
'Str': 's',
'Bytes': 's',
'NameConstant': 'value',
'FormattedValue': 'value',
'JoinedStr': 'values',
'List': 'elts',
'Tuple': 'elts',
'Set': 'elts',
'Name': 'id',
'Expr': 'value',
'UnaryOp': 'op',
'BinOp': 'op',
'BoolOp': 'op',
'Call': 'func',
'Index': 'value',
'ExtSlice': 'dims',
'Assert': 'test',
'Delete': 'targets',
'Import': 'names',
'If': 'test',
'While': 'test',
'Try': 'handlers',
'TryExcept': 'handlers',
'With': 'items',
'withitem': 'context_expr',
'FunctionDef': 'name',
'arg': 'arg',
'Return': 'value',
'Yield': 'value',
'YieldFrom': 'value',
'Global': 'names',
'Nonlocal': 'names',
'ClassDef': 'name',
'AsyncFunctionDef': 'name',
'Await': 'value',
'AsyncWith': 'items',
'Raise': 'exc',
'Subscript': 'value',
'Attribute': 'value',
'AugAssign': 'op',
}
def is_literal(node, field):
if hasattr(ast, 'Constant') and isinstance(node, ast.Constant) and field == 'value':
return True
if is_constant_node(node, ast.Num) and field == 'n':
return True
if is_constant_node(node, ast.Str) and field == 's':
return True
if is_constant_node(node, ast.Bytes) and field == 's':
return True
if is_constant_node(node, ast.NameConstant) and field == 'value':
return True
return False
def print_ast(node):
if not isinstance(node, ast.AST):
return repr(node)
s = ''
node_name = node.__class__.__name__
s += node_name
s += '('
first = True
for field, value in ast.iter_fields(node):
if not value and not is_literal(node, field):
# Don't bother printing fields that are empty, except for literals
continue
if field == 'ctx':
# Don't print the ctx, it's always apparent from context
continue
if first:
first = False
else:
s += ', '
if default_fields.get(node_name) != field:
s += field + '='
if isinstance(value, ast.AST):
s += print_ast(value)
elif isinstance(value, list):
s += '['
first_list = True
for item in value:
if first_list:
first_list = False
else:
s += ','
for line in print_ast(item).splitlines():
s += '\n' + INDENT + line
s += '\n]'
else:
s += repr(value)
s += ')'
return s