#python #python-3.x #abstract-syntax-tree
#python #python-3.x #абстрактное синтаксическое дерево
Вопрос:
Я хотел бы отформатировать следующий синтаксический анализ ast:
>>> import ast
>>> print(ast.dump(ast.parse('-a b')))
Module(body=[Expr(value=BinOp(left=UnaryOp(op=USub(), operand=Name(id='a', ctx=Load())), op=Add(), right=Name(id='b', ctx=Load())))])
Похоже, что эта indent
опция была введена в python3.9, но я не вижу возможности «pretty-print» до этого. Какие существуют варианты для печати вывода с хорошим форматированием для синтаксического дерева?
Ответ №1:
Если вам нужно распечатать AST в более ранней версии python и вам нравится функция indent в Python3.9, почему бы просто не взять dump
функцию из 3.9 и реализовать ее в своем проекте? Исходный код находится здесь: https://github.com/python/cpython/blob/e56d54e447694c6ced2093d2273c3e3d60b36b6f/Lib/ast.py#L111-L175
И это не выглядит очень сложным и, похоже, не использует какие-либо функции, характерные для 3.9.
Ответ №2:
У меня был один вариант использования, в котором я не мог перейти на Python 3.9 (где был добавлен аргумент indent ), но мне нужен был способ улучшить результат ast.dump
.
Я написал следующий метод, который может принимать неформатированные ast.dump
выходные данные и печатать их таким образом, чтобы это было проще для глаз.
def prettify(ast_tree_str, indent=4):
ret = []
stack = []
in_string = False
curr_indent = 0
for i in range(len(ast_tree_str)):
char = ast_tree_str[i]
if in_string and char != ''' and char != '"':
ret.append(char)
elif char == '(' or char == '[':
ret.append(char)
if i < len(ast_tree_str) - 1:
next_char = ast_tree_str[i 1]
if next_char == ')' or next_char == ']':
curr_indent = indent
stack.append(char)
continue
print(''.join(ret))
ret.clear()
curr_indent = indent
ret.append(' ' * curr_indent)
stack.append(char)
elif char == ',':
ret.append(char)
print(''.join(ret))
ret.clear()
ret.append(' ' * curr_indent)
elif char == ')' or char == ']':
ret.append(char)
curr_indent -= indent
stack.pop()
elif char == ''' or char == '"':
if (len(ret) > 0 and ret[-1] == '\') or (in_string and stack[-1] != char):
ret.append(char)
continue
if len(stack) > 0 and stack[-1] == char:
ret.append(char)
in_string = False
stack.pop()
continue
in_string = True
ret.append(char)
stack.append(char)
elif char == ' ':
pass
else:
ret.append(char)
print(''.join(ret))
Использование:
if __name__ == '__main__':
content = """
@testdecorator
def my_method(a, b):
def ola():
print("Hello")
ola()
return (a b) * 5 "dasdas,da'sda\'asdas\'\'"
"""
ast_tree = ast.parse(source=content)
prettify(ast.dump(ast_tree))
PS.: Это не на 100% эквивалентно тому, что можно было бы извлечь из Python 3.9 ast.dump(...., indent=n)
, но на данный момент этого должно быть достаточно. Не стесняйтесь улучшать его