ast
— Árboles de sintaxis abstracta¶
Código fuente: Lib/ast.py
El módulo ast
ayuda a las aplicaciones de Python a procesar árboles de la gramática de sintaxis abstracta de Python. La sintaxis abstracta en sí misma puede cambiar con cada versión de Python; Este módulo ayuda a descubrir mediante programación cómo se ve la gramática actual.
Se puede generar un árbol de sintaxis abstracta pasando ast.PyCF_ONLY_AST
como un indicador de la función incorporada compile()
, o usando el ayudante parse()
provisto en este módulo. El resultado será un árbol de objetos cuyas clases todas heredan de ast.AST
. Se puede compilar un árbol de sintaxis abstracta en un objeto de código Python utilizando la función incorporada compile()
.
Clases Nodo¶
-
class
ast.
AST
¶ Esta es la base de todas las clases de nodo AST. Las clases de nodo reales se derivan del archivo
Parser/Python.asdl
, que se reproduce abajo. Se definen en el módulo_ast
C y se reexportan enast
.Hay una clase definida para cada símbolo del lado izquierdo en la gramática abstracta (por ejemplo,
ast.stmt
oast.expr
). Además, hay una clase definida para cada constructor en el lado derecho; estas clases heredan de las clases para los árboles del lado izquierdo. Por ejemplo,ast.BinOp
hereda deast.expr
. Para las reglas de producción con alternativas (también conocidas como «sumas»), la clase del lado izquierdo es abstracta: solo se crean instancias de nodos de constructor específicos.-
_fields
¶ Cada clase concreta tiene un atributo
_fields
que proporciona los nombres de todos los nodos secundarios.Cada instancia de una clase concreta tiene un atributo para cada nodo secundario, del tipo definido en la gramática. Por ejemplo, las instancias
ast.BinOp
tienen un atributoleft
de tipoast.expr
.Si estos atributos están marcados como opcionales en la gramática (usando un signo de interrogación), el valor podría ser
None
. Si los atributos pueden tener cero o más valores (marcados con un asterisco), los valores se representan como listas de Python. Todos los atributos posibles deben estar presentes y tener valores válidos al compilar un AST concompile()
.
-
lineno
¶ -
col_offset
¶ -
end_lineno
¶ -
end_col_offset
¶ Las instancias de las subclases
ast.expr
yast.stmt
tienen atributoslineno
,col_offset
,lineno
, ycol_offset
. Loslineno
yend_lineno
son los números de la primera y última línea del intervalo de texto de origen (1 indexado, por lo que la primera línea es la línea 1) y elcol_offset
yend_col_offset
son las correspondientes compensaciones de bytes UTF-8 del primer y último token que generó el nodo. El desplazamiento UTF-8 se registra porque el analizador utiliza UTF-8 internamente.Tenga en cuenta que el compilador no requiere las posiciones finales y, por lo tanto, son opcionales. El desplazamiento final es después del último símbolo, por ejemplo, uno puede obtener el segmento fuente de un nodo de expresión de una línea usando
source_line[node.col_offset: node.end_col_offset]
.
El constructor de una clase
ast.T
analiza sus argumentos de la siguiente manera:Si hay argumentos posicionales, debe haber tantos como elementos en
T._fields
; serán asignados como atributos de estos nombres.Si hay argumentos de palabras clave, establecerán los atributos de los mismos nombres a los valores dados.
Por ejemplo, para crear y completar un nodo
ast.UnaryOp
, puede usarnode = 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
o la más compacta
node = ast.UnaryOp(ast.USub(), ast.Constant(5, lineno=0, col_offset=0), lineno=0, col_offset=0)
-
Distinto en la versión 3.8: La clase ast.Constant
ahora se usa para todas las constantes.
Obsoleto desde la versión 3.8: Las clases antiguas ast.Num
, ast.Str
, ast.Bytes
, ast.NameConstant
y ast.Ellipsis
todavía están disponibles, pero se eliminarán en futuras versiones de Python. Mientras tanto, instanciarlos retornará una instancia de una clase diferente.
Gramática abstracta¶
La gramática abstracta se define actualmente de la siguiente manera:
-- 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)
}
Ayudantes de ast
¶
Además de las clases de nodo, el módulo ast
define estas funciones y clases de utilidad para atravesar árboles de sintaxis abstracta:
-
ast.
parse
(source, filename='<unknown>', mode='exec', *, type_comments=False, feature_version=None)¶ Analiza la fuente en un nodo AST. Equivalente a
compile(source, filename, mode, ast.PyCF_ONLY_AST)
.Si se proporciona
type_comments=True
, el analizador se modifica para verificar y retornar los comentarios de tipo según lo especificado por PEP 484 y PEP 526. Esto es equivalente a agregarast.PyCF_TYPE_COMMENTS
a los flags pasados acompile()
. Esto informará errores de sintaxis para comentarios de tipo fuera de lugar. Sin este flag, los comentarios de tipo se ignorarán y el campotype_comment
en los nodos AST seleccionados siempre seráNone
. Además, las ubicaciones de los comentarios# type: ignore
se retornarán como el atributotype_ignores
deModule
(de lo contrario, siempre es una lista vacía).Además, si
modo
es'func_type'
, la sintaxis de entrada se modifica para corresponder a PEP 484 «comentarios de tipo de firma», por ejemplo(str, int) -> List[str]
.Además, establece
feature_version
en una tupla(major, minor)
intentará analizar usando la gramática de esa versión de Python. Actualmentemajor
debe ser igual a3
. Por ejemplo, establecefeature_version=(3, 4)
permitirá el uso deasync
yawait
como nombres de variables. La versión más baja admitida es(3, 4)
; la más alto essys.version_info[0:2]
.Advertencia
Es posible bloquear el intérprete de Python con una cadena de caracteres suficientemente grande/compleja debido a las limitaciones de profundidad de pila en el compilador AST de Python.
Distinto en la versión 3.8: Se agregaron
type_comments
,mode='func_type'
yfeature_version
.
-
ast.
literal_eval
(node_or_string)¶ Evalúa de forma segura un nodo de expresión o una cadena de caracteres que contenga un literal de Python o un visualizador de contenedor. La cadena o nodo proporcionado solo puede consistir en las siguientes estructuras literales de Python: cadenas de caracteres, bytes, números, tuplas, listas, diccionarios, conjuntos, booleanos y
None
.Esto se puede usar para evaluar de forma segura las cadenas de caracteres que contienen valores de Python de fuentes no confiables sin la necesidad de analizar los valores uno mismo. No es capaz de evaluar expresiones complejas arbitrariamente, por ejemplo, que involucran operadores o indexación.
Advertencia
Es posible bloquear el intérprete de Python con una cadena de caracteres suficientemente grande/compleja debido a las limitaciones de profundidad de pila en el compilador AST de Python.
Distinto en la versión 3.2: Ahora permite bytes y establece literales.
-
ast.
get_docstring
(node, clean=True)¶ Retorna la cadena de caracteres de documentación del node dado (que debe ser un nodo
FunctionDef
,AsyncFunctionDef
,ClassDef
, oModule
), oNone
si no tiene docstring. Si clean es verdadero, limpia la sangría del docstring coninspect.cleandoc()
.Distinto en la versión 3.5:
AsyncFunctionDef
ahora está soportada.
-
ast.
get_source_segment
(source, node, *, padded=False)¶ Obtenga el segmento de código fuente del source que generó node. Si falta información de ubicación (
lineno
,end_lineno
,col_offset
, oend_col_offset
), retornaNone
.Si padded es
True
, la primera línea de una declaración de varias líneas se rellenará con espacios para que coincidan con su posición original.Nuevo en la versión 3.8.
-
ast.
fix_missing_locations
(node)¶ Cuando compila un árbol de nodos con
compile()
, el compilador espera los atributoslineno
ycol_offset
para cada nodo que los soporta. Es bastante tedioso completar los nodos generados, por lo que este ayudante agrega estos atributos de forma recursiva donde aún no están establecidos, configurándolos en los valores del nodo principal. Funciona de forma recursiva comenzando en node.
-
ast.
increment_lineno
(node, n=1)¶ Incremente el número de línea y el número de línea final de cada nodo en el árbol comenzando en node por n. Esto es útil para «mover código» a una ubicación diferente en un archivo.
-
ast.
copy_location
(new_node, old_node)¶ Copia la ubicación de origen (
lineno
,col_offset
,end_lineno
, yend_col_offset
) de old_node a new_node si es posible, y retorna new_node.
-
ast.
iter_fields
(node)¶ Produce (yield) una tupla de
(fieldname, value)
para cada campo ennode._fields
que está presente en node.
-
ast.
iter_child_nodes
(node)¶ Cede todos los nodos secundarios directos de node, es decir, todos los campos que son nodos y todos los elementos de campos que son listas de nodos.
-
ast.
walk
(node)¶ Recursivamente produce todos los nodos descendientes en el árbol comenzando en node (incluido node en sí mismo), en ningún orden especificado. Esto es útil si solo desea modificar los nodos en su lugar y no le importa el contexto.
-
class
ast.
NodeVisitor
¶ Una clase base de visitante de nodo que recorre el árbol de sintaxis abstracta y llama a una función de visitante para cada nodo encontrado. Esta función puede retornar un valor que se reenvía mediante el método
visit()
.Esta clase está destinada a ser subclase, con la subclase agregando métodos de visitante.
-
visit
(node)¶ Visita un nodo. La implementación predeterminada llama al método llamado
self.visit_classname
donde classname es el nombre de la clase de nodo, ogeneric_visit()
si ese método no existe.
-
generic_visit
(node)¶ Este visitante llama
visit()
en todos los hijos del nodo.Tenga en cuenta que los nodos secundarios de los nodos que tienen un método de visitante personalizado no se visitarán a menos que el visitante llame
generic_visit()
o los visite a sí mismo.
No use
NodeVisitor
si desea aplicar cambios a los nodos durante el recorrido. Para esto existe un visitante especial (NodeTransformer
) que permite modificaciones.Obsoleto desde la versión 3.8: Los métodos
visit_Num()
,visit_Str()
,visit_Bytes()
,visit_NameConstant()
yvisit_Ellipsis()
están en desuso ahora y no serán llamados en futuras versiones de Python. Agregue el métodovisit_Constant()
para manejar todos los nodos constantes.-
-
class
ast.
NodeTransformer
¶ Una subclase de
NodeVisitor
que recorre el árbol de sintaxis abstracta y permite la modificación de nodos.La clase
NodeTransformer
recorrerá el AST y usará el valor de retorno de los métodos del visitante para reemplazar o eliminar el nodo anterior. Si el valor de retorno del método de visitante esNone
, el nodo se eliminará de su ubicación; de lo contrario, se reemplazará con el valor de retorno. El valor de retorno puede ser el nodo original, en cuyo caso no se realiza ningún reemplazo.Aquí hay un transformador de ejemplo que reescribe todas las apariciones de búsquedas de nombres (
foo
) endata['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 )
Tenga en cuenta que si el nodo en el que está operando tiene nodos secundarios, debe transformar los nodos secundarios usted mismo o llamar primero al método
generic_visit()
para el nodo.Para los nodos que formaban parte de una colección de declaraciones (que se aplica a todos los nodos de declaración), el visitante también puede retornar una lista de nodos en lugar de solo un nodo.
Si
NodeTransformer
introduce nuevos nodos (que no eran parte del árbol original) sin darles información de ubicación (comolineno
),fix_missing_locations()
debería llamarse con el nuevo sub-árbol para recalcular la información de ubicacióntree = ast.parse('foo', mode='eval') new_tree = fix_missing_locations(RewriteName().visit(tree))
Usualmente usas el transformador así:
node = YourTransformer().visit(node)
-
ast.
dump
(node, annotate_fields=True, include_attributes=False)¶ Retorna un volcado formateado del árbol en node. Esto es principalmente útil para propósitos de depuración. Si annotate_fields es verdadero (por defecto), la cadena de caracteres retornada mostrará los nombres y los valores de los campos. Si annotate_fields es falso, la cadena de resultados será más compacta omitiendo nombres de campo no ambiguos. Los atributos como los números de línea y las compensaciones de columna no se vuelcan de forma predeterminada. Si esto se desea, include_attributes se puede establecer en verdadero.
Ver también
Green Tree Snakes, un recurso de documentación externo, tiene buenos detalles sobre cómo trabajar con Python AST.
ASTTokens annotates Python ASTs with the positions of tokens and text in the source code that generated them. This is helpful for tools that make source code transformations.
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 parses code as a Concrete Syntax Tree that looks like an ast tree and keeps all formatting details. It’s useful for building automated refactoring (codemod) applications and linters.
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.