Quiero editar programáticamente el código fuente de Python. Básicamente, quiero leer un .py
archivo, generar el AST y luego escribir el código fuente modificado de Python (es decir, otro .py
archivo).
Hay formas de analizar / compilar el código fuente de Python utilizando módulos Python estándar, como ast
o compiler
. Sin embargo, no creo que ninguno de ellos admita formas de modificar el código fuente (por ejemplo, eliminar esta declaración de función) y luego volver a escribir el código fuente de modificación de Python.
ACTUALIZACIÓN: La razón por la que quiero hacer esto es que me gustaría escribir una biblioteca de prueba de Mutación para python, principalmente eliminando declaraciones / expresiones, volviendo a ejecutar pruebas y viendo qué se rompe.
Respuestas:
Pythoscope hace esto a los casos de prueba que genera automáticamente al igual que la herramienta 2to3 para python 2.6 (convierte la fuente python 2.x en la fuente python 3.x).
Ambas herramientas utilizan la biblioteca lib2to3 , que es una implementación de la maquinaria del compilador / analizador de Python que puede preservar los comentarios en la fuente cuando se activa por completo desde la fuente -> AST -> fuente.
El proyecto de la cuerda puede satisfacer sus necesidades si desea hacer más refactorización como transformaciones.
El módulo ast es su otra opción, y hay un ejemplo más antiguo de cómo "analizar" los árboles de sintaxis de nuevo en el código (usando el módulo analizador). Pero el
ast
módulo es más útil cuando se realiza una transformación AST en el código que luego se transforma en un objeto de código.El proyecto redbaron también puede encajar bien (ht Xavier Combelle)
fuente
unparse.py
script, puede ser realmente engorroso usarlo desde otro script. Pero, hay un paquete llamado astunparse ( en github , en pypi ) que es básicamente una versión correctamente empaquetada deunparse.py
.El módulo ast incorporado no parece tener un método para volver a convertir a la fuente. Sin embargo, el módulo codegen aquí proporciona una impresora bonita para el ast que le permitiría hacerlo. p.ej.
Esto imprimirá:
Tenga en cuenta que puede perder el formato exacto y los comentarios, ya que no se conservan.
Sin embargo, es posible que no sea necesario. Si todo lo que necesita es ejecutar el AST reemplazado, puede hacerlo simplemente llamando a compile () en el ast y ejecutando el objeto de código resultante.
fuente
En una respuesta diferente, sugerí usar el
astor
paquete, pero desde entonces he encontrado un paquete de análisis AST más actualizado llamadoastunparse
:He probado esto en Python 3.5.
fuente
Es posible que no necesite volver a generar el código fuente. Es un poco peligroso para mí decirlo, por supuesto, ya que en realidad no ha explicado por qué cree que necesita generar un archivo .py lleno de código; pero:
Si desea generar un archivo .py que la gente realmente usará, tal vez para que puedan completar un formulario y obtener un archivo .py útil para insertar en su proyecto, entonces no desea cambiarlo a un AST y de vuelta porque si no se pierden
todo el formato (pensar en las líneas en blanco que hacen Python de manera legible mediante la agrupación de conjuntos relacionados de líneas juntas)( nodos ASTlineno
ycol_offset
atributos ) comentarios. En su lugar, es probable que desee utilizar un motor de plantillas (el lenguaje de plantillas de Django , por ejemplo, está diseñado para facilitar la creación de plantillas incluso de archivos de texto) para personalizar el archivo .py o utilizar la extensión MetaPython de Rick Copeland .Si está intentando realizar un cambio durante la compilación de un módulo, tenga en cuenta que no tiene que volver al texto; simplemente puede compilar el AST directamente en lugar de convertirlo nuevamente en un archivo .py.
Pero en casi todos los casos, probablemente esté intentando hacer algo dinámico que un lenguaje como Python realmente hace muy fácil, ¡sin escribir nuevos archivos .py! Si expande su pregunta para hacernos saber lo que realmente quiere lograr, los nuevos archivos .py probablemente no estarán involucrados en la respuesta; He visto cientos de proyectos de Python haciendo cientos de cosas del mundo real, y ninguno de ellos necesitaba escribir un archivo .py. Entonces, debo admitir, soy un poco escéptico de que hayas encontrado el primer buen caso de uso. :-)
Actualización: ahora que ha explicado lo que está tratando de hacer, me sentiría tentado a operar el AST de todos modos. Querrá mutar eliminando, no líneas de un archivo (lo que podría dar como resultado medias declaraciones que simplemente mueren con un SyntaxError), sino declaraciones completas, ¿y qué mejor lugar para hacerlo que en el AST?
fuente
Sin duda, es posible analizar y modificar la estructura del código con la ayuda del
ast
módulo y lo mostraré en un ejemplo en un momento. Sin embargo, no es posible volver a escribir el código fuente modificadoast
solo con el módulo. Hay otros módulos disponibles para este trabajo, como uno aquí .NOTA: El ejemplo a continuación puede tratarse como un tutorial introductorio sobre el uso del
ast
módulo, pero una guía más completa sobre el uso delast
módulo está disponible aquí en el tutorial sobre serpientes de Green Tree y la documentación oficial sobre elast
módulo .Introducción a
ast
:Puede analizar el código de Python (representado en una cadena) simplemente llamando a la API
ast.parse()
. Esto devuelve el identificador a la estructura del Árbol de sintaxis abstracta (AST). Curiosamente, puede volver a compilar esta estructura y ejecutarla como se muestra arriba.Otra API muy útil es la
ast.dump()
que descarga todo el AST en forma de cadena. Se puede usar para inspeccionar la estructura de árbol y es muy útil en la depuración. Por ejemplo,En Python 2.7:
En Python 3.5:
Observe la diferencia en la sintaxis para la declaración de impresión en Python 2.7 frente a Python 3.5 y la diferencia en el tipo de nodo AST en los árboles respectivos.
Cómo modificar el código usando
ast
:Ahora, echemos un vistazo a un ejemplo de modificación del código de Python por
ast
módulo. La herramienta principal para modificar la estructura AST es laast.NodeTransformer
clase. Cada vez que uno necesita modificar el AST, él / ella necesita subclase de él y escribir Transformaciones de Nodo en consecuencia.Para nuestro ejemplo, intentemos escribir una utilidad simple que transforme las declaraciones de Python 2, print en llamadas a funciones de Python 3.
Declaración de impresión en la utilidad de conversión de llamadas Fun: print2to3.py:
Esta utilidad se puede probar en un pequeño archivo de ejemplo, como el siguiente, y debería funcionar bien.
Archivo de entrada de prueba: py2.py
Tenga en cuenta que la transformación anterior es solo para
ast
fines de tutoría y, en el caso real, el escenario tendrá que observar todos los escenarios diferentes, tales comoprint " x is %s" % ("Hello Python")
.fuente
Recientemente he creado una pieza de código bastante estable (el núcleo está muy bien probado) y extensible que genera código del
ast
árbol: https://github.com/paluh/code-formatter .Estoy usando mi proyecto como base para un pequeño complemento vim (que uso todos los días), por lo que mi objetivo es generar un código Python realmente agradable y legible.
PD: Intenté extenderlo,
codegen
pero su arquitectura se basa en laast.NodeVisitor
interfaz, por lo que los formateadores (visitor_
métodos) son solo funciones. He encontrado esta estructura bastante limitante y difícil de optimizar (en caso de expresiones largas y anidadas, es más fácil mantener el árbol de objetos y almacenar en caché algunos resultados parciales; de otra manera, puede alcanzar la complejidad exponencial si desea buscar el mejor diseño). PEROcodegen
como cada pieza del trabajo de mitsuhiko (que he leído) está muy bien escrita y concisa.fuente
Una de las otras respuestas recomienda
codegen
, que parece haber sido reemplazado porastor
. La versión deastor
en PyPI (versión 0.5 a partir de este escrito) también parece estar un poco desactualizada, por lo que puede instalar la versión de desarrollo de laastor
siguiente manera.Luego puede usar
astor.to_source
para convertir un Python AST a un código fuente Python legible para humanos:He probado esto en Python 3.5.
fuente
Si está viendo esto en 2019, puede usar este libcst paquete . Tiene una sintaxis similar a ast. Esto funciona de maravilla y preserva la estructura del código. Básicamente es útil para el proyecto donde debe conservar comentarios, espacios en blanco, nueva línea, etc.
Si no necesita preocuparse por la conservación de comentarios, espacios en blanco y otros, entonces la combinación de ast y astor funciona bien.
fuente
Tuvimos una necesidad similar, que no fue resuelta por otras respuestas aquí. Entonces creamos una biblioteca para esto, ASTTokens , que toma un árbol AST producido con el ast o astroid módulos , y lo marca con los rangos de texto en el código fuente original.
No realiza modificaciones de código directamente, pero eso no es difícil de agregar en la parte superior, ya que le indica el rango de texto que debe modificar.
Por ejemplo, esto envuelve una llamada de función
WRAP(...)
, conservando comentarios y todo lo demás:Produce:
¡Espero que esto ayude!
fuente
Un sistema de transformación del programa es una herramienta que analiza el texto fuente, crea AST, le permite modificarlos usando transformaciones de fuente a fuente ("si ve este patrón, reemplácelo por ese patrón"). Dichas herramientas son ideales para la mutación de los códigos fuente existentes, que son solo "si ve este patrón, reemplácelo por una variante de patrón".
Por supuesto, necesita un motor de transformación de programas que pueda analizar el lenguaje que le interese y aún así realizar las transformaciones dirigidas por patrones. Nuestro DMS Software Reengineering Toolkit es un sistema que puede hacer eso y maneja Python y una variedad de otros idiomas.
Vea esta respuesta SO para ver un ejemplo de AST analizado por DMS para Python capturando comentarios con precisión. DMS puede realizar cambios en el AST y regenerar texto válido, incluidos los comentarios. Puede pedirle que imprima el AST, utilizando sus propias convenciones de formato (puede cambiarlas), o hacer "impresión de fidelidad", que utiliza la información original de la línea y la columna para preservar al máximo el diseño original (algunos cambios en el diseño donde el nuevo código se inserta es inevitable).
Para implementar una regla de "mutación" para Python con DMS, puede escribir lo siguiente:
Esta regla reemplaza "+" con "-" de una manera sintácticamente correcta; funciona en el AST y, por lo tanto, no tocará cadenas o comentarios que se vean bien. La condición adicional en "mutate_this_place" es permitirle controlar con qué frecuencia ocurre esto; no quieres mutar todos los lugares del programa.
Obviamente, querría un montón más de reglas como esta que detecten varias estructuras de código y las reemplacen por las versiones mutadas. DMS se complace en aplicar un conjunto de reglas. El AST mutado es entonces bastante impreso.
fuente
Solía usar baron para esto, pero ahora he cambiado a parso porque está actualizado con Python moderno. Funciona muy bien
También necesitaba esto para un probador de mutaciones. Realmente es bastante simple hacer uno con parso, mira mi código en https://github.com/boxed/mutmut
fuente