32.2. ast — Árvores de Sintaxe Abstrata

Novo na versão 2.5: The low-level _ast module containing only the node classes.

Novo na versão 2.6: The high-level ast module containing all helpers.

Código Fonte: Lib/ast.py


O módulo ast ajuda os aplicativos 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 interna compile(), ou usando o auxiliar parse() fornecido neste módulo. O resultado será uma árvore de objetos cujas classes são herdadas de ast.AST. Uma árvore de sintaxe abstrata pode ser compilada em um objeto de código Python usando a função interna compile().

32.2.1. Classes de nó

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 dá 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

Instâncias das subclasses ast.expr e ast.stmt possuem atributos lineno e col_offset. O lineno é o número da linha do texto fonte (indexado em 1, de modo que a primeira linha é a linha 1) e o col_offset é o deslocamento de bytes UTF-8 do primeiro token que gerou o nó. O deslocamento UTF-8 é registrado porque o analisador usa o UTF-8 internamente.

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 de palavra-chave, 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.Num()
node.operand.n = 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.Num(5, lineno=0, col_offset=0),
                   lineno=0, col_offset=0)

Novo na versão 2.6: The constructor as explained above was added. In Python 2.5 nodes had to be created by calling the class constructor without arguments and setting the attributes afterwards.

32.2.2. Gramática Abstrata

The module defines a string constant __version__ which is the decimal Subversion revision number of the file shown below.

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

-- ASDL's five builtin types are identifier, int, string, object, bool

module Python version "$Revision$"
{
	mod = Module(stmt* body)
	    | Interactive(stmt* body)
	    | Expression(expr body)

	    -- 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)
	      | ClassDef(identifier name, expr* bases, stmt* body, expr* decorator_list)
	      | Return(expr? value)

	      | Delete(expr* targets)
	      | Assign(expr* targets, expr value)
	      | AugAssign(expr target, operator op, expr value)

	      -- not sure if bool is allowed, can always use int
 	      | Print(expr? dest, expr* values, bool nl)

	      -- use 'orelse' because else is a keyword in target languages
	      | For(expr target, expr iter, stmt* body, stmt* orelse)
	      | While(expr test, stmt* body, stmt* orelse)
	      | If(expr test, stmt* body, stmt* orelse)
	      | With(expr context_expr, expr? optional_vars, stmt* body)

	      -- 'type' is a bad name
	      | Raise(expr? type, expr? inst, expr? tback)
	      | TryExcept(stmt* body, excepthandler* handlers, stmt* orelse)
	      | TryFinally(stmt* body, stmt* finalbody)
	      | Assert(expr test, expr? msg)

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

	      -- Doesn't capture requirement that locals must be
	      -- defined if globals is
	      -- still supports use as a function!
	      | Exec(expr body, expr? globals, expr? locals)

	      | Global(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)

	      -- BoolOp() can use left & right?
	expr = BoolOp(boolop op, expr* values)
	     | 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
	     | Yield(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,
			 expr? starargs, expr? kwargs)
	     | Repr(expr value)
	     | Num(object n) -- a number as a PyObject.
	     | Str(string s) -- need to specify raw, unicode, etc?
	     -- other literals? bools?

	     -- the following expression can appear in assignment context
	     | Attribute(expr value, identifier attr, expr_context ctx)
	     | Subscript(expr value, slice slice, 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)

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

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

	boolop = And | Or 

	operator = Add | Sub | Mult | 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)

	-- not sure what to call the first argument for raise and except
	excepthandler = ExceptHandler(expr? type, expr? name, stmt* body)
	                attributes (int lineno, int col_offset)

	arguments = (expr* args, identifier? vararg, 
		     identifier? kwarg, expr* defaults)

        -- keyword arguments supplied to call
        keyword = (identifier arg, expr value)

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

32.2.3. Auxiliares de ast

Novo na versão 2.6.

Apart from the node classes, ast module defines these utility functions and classes for traversing abstract syntax trees:

ast.parse(source, filename='<unknown>', mode='exec')

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

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.

ast.literal_eval(node_or_string)

Safely evaluate an expression node or a Unicode or Latin-1 encoded string containing a Python literal or container display. The string or node provided may only consist of the following Python literal structures: strings, numbers, tuples, lists, dicts, booleans, and 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.

ast.get_docstring(node, clean=True)

Return the docstring of the given node (which must be a FunctionDef, ClassDef or Module node), or None if it has no docstring. If clean is true, clean up the docstring’s indentation with inspect.cleandoc().

ast.fix_missing_locations(node)

Quando você compila uma árvore de nós com compile(), o compilador espera atributos :attr:` 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 .

ast.increment_lineno(node, n=1)

Incrementa o número da linha de cada nó na árvore, começando em node por 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 e col_offset) de old_node para new_node se possível, e 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.

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 copy_location(Subscript(
            value=Name(id='data', ctx=Load()),
            slice=Index(value=Str(s=node.id)),
            ctx=node.ctx
        ), node)

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ó.

Normalmente você usa o transformador assim:

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

Retorna um despejo formatado da árvore em node. Isso é útil principalmente para fins de depuração. A string retornada mostrará os nomes e os valores dos campos. Isso torna o código impossível de ser avaliado, portanto, se a avaliação for desejada, annotate_fields deve ser definido como False. Atributos como números de linha e deslocamentos de coluna não são despejados por padrão. Se isso é desejado, include_attributes pode ser definido como True.