ast — Árvores de Sintaxe Abstrata

Código-fonte: Lib/ast.py


O módulo ast ajuda as aplicações Python a processar árvores da gramática de sintaxe abstrata do Python. A sintaxe abstrata em si pode mudar em cada lançamento do Python; este módulo ajuda a descobrir programaticamente como é a gramática atual.

Uma árvore de sintaxe abstrata pode ser gerada passando ast.PyCF_ONLY_AST como um sinalizador para a função embutida compile(), ou usando o auxiliar parse() fornecido neste módulo. O resultado será uma árvore de objetos cujas classes herdam de ast.AST. Uma árvore de sintaxe abstrata pode ser compilada em um objeto código Python usando a função embutida compile().

Classes de nós

class ast.AST

Esta é a base de todas as classes de nós AST. As classes de nós reais são derivadas do arquivo Parser/Python.asdl, o qual é reproduzido abaixo. Elas são definidas no módulo C _ast e reexportadas em ast.

Há uma classe definida para cada símbolo do lado esquerdo na gramática abstrata (por exemplo, ast.stmt ou ast.expr). Além disso, existe uma classe definida para cada construtor no lado direito; essas classes herdam das classes para as árvores do lado esquerdo. Por exemplo, ast.BinOp herda de ast.expr. Para regras de produção com alternativas (“somas”), a classe do lado esquerdo é abstrata: apenas instâncias de nós construtores específicos são criadas.

_fields

Cada classe concreta possui um atributo _fields que fornece os nomes de todos os nós filhos.

Cada instância de uma classe concreta tem um atributo para cada nó filho, do tipo definido na gramática. Por exemplo, as instâncias ast.BinOp possuem um atributo left do tipo ast.expr.

Se estes atributos estiverem marcados como opcionais na gramática (usando um ponto de interrogação), o valor pode ser None. Se os atributos puderem ter valor zero ou mais (marcados com um asterisco), os valores serão representados como listas do Python. Todos os atributos possíveis devem estar presentes e ter valores válidos ao compilar uma AST com compile().

lineno
col_offset
end_lineno
end_col_offset

As instâncias das subclasses ast.expr e ast.stmt possuem lineno, col_offset, lineno e col_offset . O lineno e end_lineno são o primeiro e o último número de linha do intervalo do texto de origem (indexado em 1, para que a primeira linha seja a linha 1) e o col_offset e end_col_offset são os deslocamentos de byte UTF-8 correspondentes do primeiro e do último tokens que geraram o nó. O deslocamento UTF-8 é registrado porque o analisador usa UTF-8 internamente.

Observe que as posições finais não são exigidas pelo compilador e, portanto, são opcionais. O deslocamento final está após o último símbolo, por exemplo, é possível obter o segmento de origem de um nó de expressão de uma linha usando source_line[node.col_offset : node.end_col_offset].

O construtor de uma classe ast.T analisa seus argumentos da seguinte forma:

  • Se houver argumentos posicionais, deve haver tantos quanto houver itens em T._fields; eles serão atribuídos como atributos desses nomes.

  • Se houver argumentos nomeados, eles definirão os atributos dos mesmos nomes para os valores fornecidos.

Por exemplo, para criar e popular um nó ast.UnaryOp, você poderia usar

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

ou a forma mais compacta

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

Alterado na versão 3.8: A classe ast.Constant é agora usada para todas as constantes.

Obsoleto desde a versão 3.8: Classes antigas ast.Num, ast.Str, ast.Bytes, ast.NameConstant e ast.Ellipsis ainda estão disponíveis, mas eles serão removidos em versões futuras do Python. Enquanto isso, instanciá-los retornará uma instância de uma classe diferente.

Gramática Abstrata

A gramática abstrata está atualmente definida da seguinte forma:

-- 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)
}

Auxiliares de ast

Além das classes de nós, o módulo ast define essas funções e classes utilitárias para percorrer árvores de sintaxe abstrata:

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

Analisa a fonte em um nó AST. Equivalente a compile(source, filename, mode, ast.PyCF_ONLY_AST).

Se type_comments=True é fornecido, o analisador é modificado para verificar e retornar comentários do tipo, conforme especificado por PEP 484 e PEP 526. Isso é equivalente a adicionar ast.PyCF_TYPE_COMMENTS aos sinalizadores passados para compile(). Isso relatará erros de sintaxe para comentários do tipo extraviado. Sem esse sinalizador, os comentários do tipo serão ignorados e o campo type_comment nos nós AST selecionados sempre será None. Além disso, os locais dos comentários # type: ignore serão retornados como o atributo type_ignores de Module (caso contrário, é sempre uma lista vazia).

Além disso, se mode for 'func_type', a sintaxe de entrada é modificada para corresponder a “comentários de tipo de assinatura” de PEP 484, por exemplo, (str, int) -> List[str].

Além disso, definir feature_version como uma tupla (maior, menor) tentará analisar usando a gramática dessa versão do Python. Atualmente, maior deve ser igual a 3. Por exemplo, definir feature_version=(3, 4) permitirá o uso de async e waitit como nomes de variáveis. A versão mais baixa suportada é (3, 4); a mais alta é sys.version_info[0:2].

Aviso

É possível travar o interpretador Python com uma string suficientemente grande/complexa devido às limitações de profundidade da pilha no compilador de AST do Python.

Alterado na versão 3.8: Adicionado type_comments, mode='func_type' e feature_version.

ast.literal_eval(node_or_string)

Avalia com segurança um nó de expressão ou uma string contendo um literal Python ou exibição de contêiner. A string ou o nó fornecido pode consistir apenas nas seguintes estruturas literais de Python: strings, bytes, números, tuplas, listas, dicts, conjuntos, booleanos e None.

Isso pode ser usado para avaliar com segurança strings contendo valores Python de fontes não confiáveis sem a necessidade de analisar os valores por si próprio. Não é capaz de avaliar expressões arbitrariamente complexas, por exemplo, envolvendo operadores ou indexação.

Aviso

É possível travar o interpretador Python com uma string suficientemente grande/complexa devido às limitações de profundidade da pilha no compilador de AST do Python.

Alterado na versão 3.2: Agora permite bytes e literais de conjuntos.

ast.get_docstring(node, clean=True)

Retorna a docstring do node dado (que deve ser um nó FunctionDef, AsyncFunctionDef, ClassDef ou Module) ou None se não tiver uma docstring. Se clean for verdadeiro, limpa o recuo da docstring com inspect.cleandoc().

Alterado na versão 3.5: Não há suporte a AsyncFunctionDef.

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

Obtém o segmento de código-fonte de source que gerou node. Se algumas informações de local (lineno, end_lineno, col_offset ou end_col_offset) estiverem faltando, retorna None.

Se padded for True, a primeira linha de uma instrução multilinha será preenchida com espaços para corresponder à sua posição original.

Novo na versão 3.8.

ast.fix_missing_locations(node)

Quando você compila uma árvore de nós com compile(), o compilador espera atributos lineno e col_offset para cada nó que os suporta. Isso é tedioso para preencher nós gerados, portanto, esse auxiliar adiciona esses atributos recursivamente, onde ainda não estão definidos, definindo-os para os valores do nó pai. Ele funciona recursivamente a partir do node.

ast.increment_lineno(node, n=1)

Incrementa o número da linhas e o número da linha final de cada nó na árvore começando em node em n. Isso é útil para “mover código” para um local diferente em um arquivo.

ast.copy_location(new_node, old_node)

Copia o local de origem (lineno, col_offset, end_lineno e end_col_offset) de old_node para new_node se possível e, então, retorna new_node.

ast.iter_fields(node)

Produz uma tupla de (fieldname, value) para cada campo em node._fields que esteja presente em node.

ast.iter_child_nodes(node)

Produz todos os nós filhos diretos de node, ou seja, todos os campos que são nós e todos os itens de campos que são listas de nós.

ast.walk(node)

Produz recursivamente todos os nós descendentes na árvore começando em node (incluindo o próprio node), em nenhuma ordem especificada. Isso é útil se você quiser apenas modificar nós no lugar e não se importar com o contexto.

class ast.NodeVisitor

Uma classe base de visitante de nó que percorre a árvore de sintaxe abstrata e chama uma função de visitante para cada nó encontrado. Esta função pode retornar um valor que é encaminhado pelo método visit().

Esta classe deve ser uma subclasse, com a subclasse adicionando métodos visitantes.

visit(node)

Visita um nó. A implementação padrão chama o método chamado self.visit_nomedaclasse sendo nomedaclasse o nome da classe do nó, ou generic_visit() se aquele método não existir.

generic_visit(node)

Este visitante chama visit() em todos os filhos do nó.

Observe que nós filhos de nós que possuem um método de visitante personalizado não serão visitados, a menos que o visitante chame generic_visit() ou os visite por conta própria.

Não use o NodeVisitor se você quiser aplicar mudanças nos nós durante a travessia. Para isso existe um visitante especial (NodeTransformer) que permite modificações.

Obsoleto desde a versão 3.8: Os métodos visit_Num(), visit_Str(), visit_Bytes(), visit_NameConstant() e visit_Ellipsis() estão agora descontinuados e não serão chamados em futuras versões do Python. Adicione um método visit_Constant() para lidar com nós de constantes.

class ast.NodeTransformer

A subclasse NodeVisitor que percorre a árvore de sintaxe abstrata e permite a modificação de nós.

O NodeTransformer percorrerá a AST e usará o valor de retorno dos métodos do visitante para substituir ou remover o nó antigo. Se o valor de retorno do método visitante for None, o nó será removido de seu local, caso contrário, ele será substituído pelo valor de retorno. O valor de retorno pode ser o nó original, caso em que não há substituição.

Aqui está um exemplo de transformador que rescreve todas as ocorrências de procuras por nome (foo) para 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
        )

Tenha em mente que, se o nó em que você está operando tiver nós filhos, você deve transformar os nós filhos por conta própria ou chamar o método generic_visit() para o nó primeiro.

Para nós que faziam parte de uma coleção de instruções (que se aplica a todos os nós de instrução), o visitante também pode retornar uma lista de nós em vez de apenas um único nó.

Se NodeTransformer introduz novos nós (que não faziam parte da árvore original) sem fornecer informações de localização (como lineno), fix_missing_locations() deve ser chamado com o novo subárvore para recalcular as informações de localização:

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

Normalmente você usa o transformador assim:

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

Retorne um despejo formatado da árvore em node. Isso é útil principalmente para fins de depuração. Se annotate_fields for verdadeiro (por padrão), a sequência retornada mostrará os nomes e os valores para os campos. Se annotate_fields for falso, a sequência de resultados será mais compacta ao omitir nomes de campos não ambíguos. Atributos como números de linha e deslocamentos de coluna não são despejados por padrão. Se isso for desejado, include_attributes pode ser definido como verdadeiro.

Ver também

Green Tree Snakes, um recurso de documentação externo, possui bons detalhes sobre trabalhar com ASTs do Python.

ASTTokens anota ASTs do Python com as posições de tokens e texto no código-fonte que as gerou. Isso é útil para ferramentas que fazem transformações de código-fonte.

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 analisa o código como uma árvore de sintaxe concreta que se parece com uma árvore ast e mantém todos os detalhes de formatação. É útil para construir linters e aplicações de refatoração automatizada (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.