ast — Abstract Syntax Trees

Вихідний код: Lib/ast.py


Модуль ast допомагає програмам Python обробляти дерева граматики абстрактного синтаксису Python. Сам абстрактний синтаксис може змінюватися з кожним випуском Python; цей модуль допомагає програмно дізнатися, як виглядає поточна граматика.

Абстрактне синтаксичне дерево можна створити, передавши ast.PyCF_ONLY_AST як прапорець вбудованій функції compile() або використовуючи помічник parse(), наданий у цьому модулі. Результатом буде дерево об’єктів, усі класи яких успадковуються від ast.AST. Абстрактне синтаксичне дерево можна скомпілювати в об’єкт коду Python за допомогою вбудованої функції compile().

Класи вузлів

class ast.AST

This is the base of all AST node classes. The actual node classes are derived from the Parser/Python.asdl file, which is reproduced below. They are defined in the _ast C module and re-exported in ast.

Для кожного лівого символу в абстрактній граматиці визначено один клас (наприклад, ast.stmt або ast.expr). Крім того, є один клас, визначений для кожного конструктора в правій частині; ці класи успадковують класи для лівих дерев. Наприклад, ast.BinOp успадковується від ast.expr. Для виробничих правил з альтернативами (він же «суми») лівий клас є абстрактним: створюються лише екземпляри конкретних вузлів конструктора.

_fields

Each concrete class has an attribute _fields which gives the names of all child nodes.

Кожен екземпляр конкретного класу має один атрибут для кожного дочірнього вузла типу, визначеного в граматиці. Наприклад, екземпляри ast.BinOp мають атрибут left типу ast.expr.

Якщо ці атрибути позначені як необов’язкові в граматиці (використовуючи знак питання), значенням може бути None. Якщо атрибути можуть мати нуль або більше значень (позначених зірочкою), значення представлені у вигляді списків Python. Під час компіляції AST за допомогою compile() усі можливі атрибути повинні бути присутніми та мати дійсні значення.

lineno
col_offset
end_lineno
end_col_offset

Instances of ast.expr and ast.stmt subclasses have lineno, col_offset, lineno, and col_offset attributes. The lineno and end_lineno are the first and last line numbers of source text span (1-indexed so the first line is line 1) and the col_offset and end_col_offset are the corresponding UTF-8 byte offsets of the first and last tokens that generated the node. The UTF-8 offset is recorded because the parser uses UTF-8 internally.

Зауважте, що кінцеві позиції не потрібні компілятору і тому є необов’язковими. Кінцеве зміщення вказується після останнього символу, наприклад, можна отримати вихідний сегмент вузла однорядкового виразу за допомогою source_line[node.col_offset : node.end_col_offset].

Конструктор класу ast.T аналізує його аргументи наступним чином:

  • Якщо є позиційні аргументи, їх має бути стільки, скільки елементів у T._fields; вони будуть призначені як атрибути цих імен.

  • Якщо є ключові аргументи, вони встановлять атрибути з однаковими іменами на задані значення.

Наприклад, щоб створити та заповнити вузол ast.UnaryOp, ви можете використати

node = ast.UnaryOp()
node.op = ast.USub()
node.operand = ast.Constant()
node.operand.value = 5
node.operand.lineno = 0
node.operand.col_offset = 0
node.lineno = 0
node.col_offset = 0

or the more compact

node = ast.UnaryOp(ast.USub(), ast.Constant(5, lineno=0, col_offset=0),
                   lineno=0, col_offset=0)

Змінено в версії 3.8: Клас ast.Constant тепер використовується для всіх констант.

Застаріло починаючи з версії 3.8: Old classes ast.Num, ast.Str, ast.Bytes, ast.NameConstant and ast.Ellipsis are still available, but they will be removed in future Python releases. In the meanwhile, instantiating them will return an instance of a different class.

Абстрактна граматика

Наразі абстрактна граматика визначається наступним чином:

-- ASDL's 5 builtin types are:
-- identifier, int, string, object, constant

module Python
{
    mod = Module(stmt* body, type_ignore *type_ignores)
        | Interactive(stmt* body)
        | Expression(expr body)
        | FunctionType(expr* argtypes, expr returns)

        -- not really an actual node but useful in Jython's typesystem.
        | Suite(stmt* body)

    stmt = FunctionDef(identifier name, arguments args,
                       stmt* body, expr* decorator_list, expr? returns,
                       string? type_comment)
          | AsyncFunctionDef(identifier name, arguments args,
                             stmt* body, expr* decorator_list, expr? returns,
                             string? type_comment)

          | ClassDef(identifier name,
             expr* bases,
             keyword* keywords,
             stmt* body,
             expr* decorator_list)
          | Return(expr? value)

          | Delete(expr* targets)
          | Assign(expr* targets, expr value, string? type_comment)
          | AugAssign(expr target, operator op, expr value)
          -- 'simple' indicates that we annotate simple name without parens
          | AnnAssign(expr target, expr annotation, expr? value, int simple)

          -- use 'orelse' because else is a keyword in target languages
          | For(expr target, expr iter, stmt* body, stmt* orelse, string? type_comment)
          | AsyncFor(expr target, expr iter, stmt* body, stmt* orelse, string? type_comment)
          | While(expr test, stmt* body, stmt* orelse)
          | If(expr test, stmt* body, stmt* orelse)
          | With(withitem* items, stmt* body, string? type_comment)
          | AsyncWith(withitem* items, stmt* body, string? type_comment)

          | Raise(expr? exc, expr? cause)
          | Try(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)
          | Assert(expr test, expr? msg)

          | Import(alias* names)
          | ImportFrom(identifier? module, alias* names, int? level)

          | Global(identifier* names)
          | Nonlocal(identifier* names)
          | Expr(expr value)
          | Pass | Break | Continue

          -- XXX Jython will be different
          -- col_offset is the byte offset in the utf8 string the parser uses
          attributes (int lineno, int col_offset, int? end_lineno, int? end_col_offset)

          -- BoolOp() can use left & right?
    expr = BoolOp(boolop op, expr* values)
         | NamedExpr(expr target, expr value)
         | BinOp(expr left, operator op, expr right)
         | UnaryOp(unaryop op, expr operand)
         | Lambda(arguments args, expr body)
         | IfExp(expr test, expr body, expr orelse)
         | Dict(expr* keys, expr* values)
         | Set(expr* elts)
         | ListComp(expr elt, comprehension* generators)
         | SetComp(expr elt, comprehension* generators)
         | DictComp(expr key, expr value, comprehension* generators)
         | GeneratorExp(expr elt, comprehension* generators)
         -- the grammar constrains where yield expressions can occur
         | Await(expr value)
         | Yield(expr? value)
         | YieldFrom(expr value)
         -- need sequences for compare to distinguish between
         -- x < 4 < 3 and (x < 4) < 3
         | Compare(expr left, cmpop* ops, expr* comparators)
         | Call(expr func, expr* args, keyword* keywords)
         | FormattedValue(expr value, int? conversion, expr? format_spec)
         | JoinedStr(expr* values)
         | Constant(constant value, string? kind)

         -- the following expression can appear in assignment context
         | Attribute(expr value, identifier attr, expr_context ctx)
         | Subscript(expr value, slice slice, expr_context ctx)
         | Starred(expr value, expr_context ctx)
         | Name(identifier id, expr_context ctx)
         | List(expr* elts, expr_context ctx)
         | Tuple(expr* elts, expr_context ctx)

          -- col_offset is the byte offset in the utf8 string the parser uses
          attributes (int lineno, int col_offset, int? end_lineno, int? end_col_offset)

    expr_context = Load | Store | Del | AugLoad | AugStore | Param

    slice = Slice(expr? lower, expr? upper, expr? step)
          | ExtSlice(slice* dims)
          | Index(expr value)

    boolop = And | Or

    operator = Add | Sub | Mult | MatMult | Div | Mod | Pow | LShift
                 | RShift | BitOr | BitXor | BitAnd | FloorDiv

    unaryop = Invert | Not | UAdd | USub

    cmpop = Eq | NotEq | Lt | LtE | Gt | GtE | Is | IsNot | In | NotIn

    comprehension = (expr target, expr iter, expr* ifs, int is_async)

    excepthandler = ExceptHandler(expr? type, identifier? name, stmt* body)
                    attributes (int lineno, int col_offset, int? end_lineno, int? end_col_offset)

    arguments = (arg* posonlyargs, arg* args, arg? vararg, arg* kwonlyargs,
                 expr* kw_defaults, arg? kwarg, expr* defaults)

    arg = (identifier arg, expr? annotation, string? type_comment)
           attributes (int lineno, int col_offset, int? end_lineno, int? end_col_offset)

    -- keyword arguments supplied to call (NULL identifier for **kwargs)
    keyword = (identifier? arg, expr value)

    -- import name with optional 'as' alias.
    alias = (identifier name, identifier? asname)

    withitem = (expr context_expr, expr? optional_vars)

    type_ignore = TypeIgnore(int lineno, string tag)
}

ast Помічники

Окрім класів вузлів, модуль ast визначає ці службові функції та класи для обходу абстрактних синтаксичних дерев:

ast.parse(source, filename='<unknown>', mode='exec', *, type_comments=False, feature_version=None)

Parse the source into an AST node. Equivalent to compile(source, filename, mode, ast.PyCF_ONLY_AST).

If type_comments=True is given, the parser is modified to check and return type comments as specified by PEP 484 and PEP 526. This is equivalent to adding ast.PyCF_TYPE_COMMENTS to the flags passed to compile(). This will report syntax errors for misplaced type comments. Without this flag, type comments will be ignored, and the type_comment field on selected AST nodes will always be None. In addition, the locations of # type: ignore comments will be returned as the type_ignores attribute of Module (otherwise it is always an empty list).

Крім того, якщо mode є 'func_type', синтаксис введення змінюється відповідно до PEP 484 «коментарів типу підпису», напр. (str, int) -> Список[str].

Also, setting feature_version to a tuple (major, minor) will attempt to parse using that Python version’s grammar. Currently major must equal to 3. For example, setting feature_version=(3, 4) will allow the use of async and await as variable names. The lowest supported version is (3, 4); the highest is sys.version_info[0:2].

Попередження

Можливий збій інтерпретатора Python із досить великим/складним рядком через обмеження глибини стеку в компіляторі AST Python.

Змінено в версії 3.8: Додано type_comments, mode='func_type' і feature_version.

ast.literal_eval(node_or_string)

Safely evaluate an expression node or a string containing a Python literal or container display. The string or node provided may only consist of the following Python literal structures: strings, bytes, numbers, tuples, lists, dicts, sets, booleans, and None.

This can be used for safely evaluating strings containing Python values from untrusted sources without the need to parse the values oneself. It is not capable of evaluating arbitrarily complex expressions, for example involving operators or indexing.

Попередження

Можливий збій інтерпретатора Python із досить великим/складним рядком через обмеження глибини стеку в компіляторі AST Python.

Змінено в версії 3.2: Тепер дозволяє байти та встановлені літерали.

ast.get_docstring(node, clean=True)

Повертає рядок документації даного вузла (який має бути FunctionDef, AsyncFunctionDef, ClassDef або Module вузол), або None, якщо він не має рядка документації. Якщо clean має значення true, очистіть відступ у рядку документа за допомогою inspect.cleandoc().

Змінено в версії 3.5: AsyncFunctionDef тепер підтримується.

ast.get_source_segment(source, node, *, padded=False)

Get source code segment of the source that generated node. If some location information (lineno, end_lineno, col_offset, or end_col_offset) is missing, return None.

Якщо paded має значення True, перший рядок багаторядкового оператора буде доповнено пробілами відповідно до його вихідної позиції.

Нове в версії 3.8.

ast.fix_missing_locations(node)

When you compile a node tree with compile(), the compiler expects lineno and col_offset attributes for every node that supports them. This is rather tedious to fill in for generated nodes, so this helper adds these attributes recursively where not already set, by setting them to the values of the parent node. It works recursively starting at node.

ast.increment_lineno(node, n=1)

Збільште номер рядка та номер кінцевого рядка кожного вузла в дереві, починаючи з вузла, на n. Це корисно для «переміщення коду» в інше місце у файлі.

ast.copy_location(new_node, old_node)

Copy source location (lineno, col_offset, end_lineno, and end_col_offset) from old_node to new_node if possible, and return new_node.

ast.iter_fields(node)

Отримайте кортеж (fieldname, value) для кожного поля node._fields, який присутній на node.

ast.iter_child_nodes(node)

Видає всі прямі дочірні вузли node, тобто всі поля, які є вузлами, і всі елементи полів, які є списками вузлів.

ast.walk(node)

Рекурсивно створювати всі вузли-нащадки в дереві, починаючи з node (включаючи сам node), у невизначеному порядку. Це корисно, якщо ви хочете лише змінити вузли на місці й не дбаєте про контекст.

class ast.NodeVisitor

Базовий клас відвідувача вузла, який проходить абстрактне синтаксичне дерево та викликає функцію відвідувача для кожного знайденого вузла. Ця функція може повертати значення, яке пересилається методом visit().

Цей клас призначений для створення підкласу, який додає методи відвідувачів.

visit(node)

Відвідайте вузол. Стандартна реалізація викликає метод під назвою self.visit_classname, де classname є назвою класу вузла, або generic_visit(), якщо цей метод не існує.

generic_visit(node)

Цей відвідувач викликає visit() для всіх дітей вузла.

Зауважте, що дочірні вузли вузлів, які мають спеціальний метод відвідувача, не будуть відвідані, якщо відвідувач не викличе generic_visit() або відвідає їх сам.

Не використовуйте NodeVisitor, якщо ви хочете застосувати зміни до вузлів під час обходу. Для цього існує спеціальний відвідувач (NodeTransformer), який дозволяє вносити зміни.

Застаріло починаючи з версії 3.8: Methods visit_Num(), visit_Str(), visit_Bytes(), visit_NameConstant() and visit_Ellipsis() are deprecated now and will not be called in future Python versions. Add the visit_Constant() method to handle all constant nodes.

class ast.NodeTransformer

Підклас NodeVisitor, який проходить абстрактне синтаксичне дерево та дозволяє модифікувати вузли.

NodeTransformer пройде AST і використає значення, що повертається методами відвідувача, щоб замінити або видалити старий вузол. Якщо значенням, що повертається методом відвідувача, є None, вузол буде видалено зі свого розташування, інакше він замінюється значенням, що повертається. Поверненим значенням може бути вихідний вузол, і в цьому випадку заміна не відбувається.

Ось приклад трансформатора, який переписує всі випадки пошуку імен (foo) на data['foo']:

class RewriteName(NodeTransformer):

    def visit_Name(self, node):
        return Subscript(
            value=Name(id='data', ctx=Load()),
            slice=Index(value=Constant(value=node.id)),
            ctx=node.ctx
        )

Keep in mind that if the node you’re operating on has child nodes you must either transform the child nodes yourself or call the generic_visit() method for the node first.

Для вузлів, які були частиною набору операторів (що стосується всіх вузлів операторів), відвідувач також може повернути список вузлів, а не лише один вузол.

If NodeTransformer introduces new nodes (that weren’t part of original tree) without giving them location information (such as lineno), fix_missing_locations() should be called with the new sub-tree to recalculate the location information:

tree = ast.parse('foo', mode='eval')
new_tree = fix_missing_locations(RewriteName().visit(tree))

Зазвичай ви використовуєте трансформатор таким чином:

node = YourTransformer().visit(node)
ast.dump(node, annotate_fields=True, include_attributes=False)

Повернути відформатований дамп дерева у node. Це в основному корисно для цілей налагодження. Якщо annotate_fields має значення true (за замовчуванням), у поверненому рядку відображатимуться імена та значення для полів. Якщо annotate_fields має значення false, рядок результату буде більш компактним за рахунок пропуску однозначних імен полів. Такі атрибути, як номери рядків і зміщення стовпців, не скидаються за замовчуванням. Якщо це потрібно, include_attributes можна встановити на true.

Дивись також

Green Tree Snakes, зовнішній ресурс документації, містить хороші відомості про роботу з Python AST.

ASTTokens анотує AST Python за допомогою позицій токенів і тексту у вихідному коді, який їх створив. Це корисно для інструментів, які здійснюють перетворення вихідного коду.

leoAst.py unifies the token-based and parse-tree-based views of python programs by inserting two-way links between tokens and ast nodes.

LibCST аналізує код як конкретне синтаксичне дерево, яке виглядає як дерево ast і зберігає всі деталі форматування. Це корисно для створення додатків і лінтерів для автоматизованого рефакторинга (codemod).

Parso is a Python parser that supports error recovery and round-trip parsing for different Python versions (in multiple Python versions). Parso is also able to list multiple syntax errors in your python file.