Je souhaite modifier le code source Python par programmation. Fondamentalement, je veux lire un .py
fichier, générer l' AST , puis réécrire le code source python modifié (c'est-à-dire un autre .py
fichier).
Il existe des moyens d'analyser / compiler le code source python à l'aide de modules python standard, tels que ast
ou compiler
. Cependant, je ne pense pas qu'aucun d'entre eux prenne en charge les moyens de modifier le code source (par exemple, supprimez cette déclaration de fonction), puis de réécrire le code source python de modification.
MISE À JOUR: La raison pour laquelle je veux faire cela est que j'aimerais écrire une bibliothèque de tests de mutation pour python, principalement en supprimant des instructions / expressions, en réexécutant des tests et en voyant ce qui se brise.
Réponses:
Pythoscope le fait pour les cas de test qu'il génère automatiquement, tout comme l' outil 2to3 pour python 2.6 (il convertit la source python 2.x en source python 3.x).
Ces deux outils utilisent la bibliothèque lib2to3 qui est une implémentation de la machinerie de l'analyseur / compilateur python qui peut conserver les commentaires dans la source lorsqu'il est déclenché à partir de la source -> AST -> source.
Le projet de corde peut répondre à vos besoins si vous souhaitez faire plus de refactoring comme des transformations.
Le module ast est votre autre option, et il existe un exemple plus ancien de la façon de "désanalyser" les arbres de syntaxe dans le code (en utilisant le module parser). Mais le
ast
module est plus utile lors d'une transformation AST sur du code qui est ensuite transformé en objet code.Le projet redbaron peut également convenir (ht Xavier Combelle)
la source
unparse.py
script - il peut être très fastidieux de l'utiliser à partir d'un autre script. Mais, il existe un package appelé astunparse ( sur github , sur pypi ) qui est essentiellement une version correctement empaquetée deunparse.py
.Le module ast intégré ne semble pas avoir de méthode pour revenir à la source. Cependant, le module codegen fournit ici une jolie imprimante pour l'ast qui vous permettrait de le faire. par exemple.
Cela imprimera:
Notez que vous risquez de perdre la mise en forme exacte et les commentaires, car ils ne sont pas conservés.
Cependant, vous n'en aurez peut-être pas besoin. Si tout ce dont vous avez besoin est d'exécuter l'AST remplacé, vous pouvez le faire simplement en appelant compile () sur l'ast et en exécutant l'objet de code résultant.
la source
Dans une réponse différente, j'ai suggéré d'utiliser le
astor
package, mais j'ai depuis trouvé un package de décompression AST plus à jour appeléastunparse
:J'ai testé cela sur Python 3.5.
la source
Vous n'aurez peut-être pas besoin de régénérer le code source. C'est un peu dangereux pour moi de dire, bien sûr, puisque vous n'avez pas réellement expliqué pourquoi vous pensez avoir besoin de générer un fichier .py plein de code; mais:
Si vous voulez générer un fichier .py que les gens utiliseront réellement, peut-être pour qu'ils puissent remplir un formulaire et obtenir un fichier .py utile à insérer dans leur projet, alors vous ne voulez pas le changer en AST et retour parce que vous perdrez
tout le formatage (pensez aux lignes vides qui rendent Python si lisible en regroupant des ensembles de lignes connexes ensemble)(les nœuds astlineno
et lescol_offset
attributs ) des commentaires. Au lieu de cela, vous voudrez probablement utiliser un moteur de création de modèles (le langage de modèle Django , par exemple, est conçu pour faciliter la création de modèles même de fichiers texte) pour personnaliser le fichier .py, ou bien utiliser l' extension MetaPython de Rick Copeland .Si vous essayez d'apporter une modification lors de la compilation d'un module, notez que vous n'êtes pas obligé de revenir au texte; vous pouvez simplement compiler l'AST directement au lieu de le transformer en un fichier .py.
Mais dans presque tous les cas, vous essayez probablement de faire quelque chose de dynamique qu'un langage comme Python rend très facile, sans écrire de nouveaux fichiers .py! Si vous développez votre question pour nous faire savoir ce que vous voulez réellement accomplir, les nouveaux fichiers .py ne seront probablement pas du tout impliqués dans la réponse; J'ai vu des centaines de projets Python faire des centaines de choses du monde réel, et pas un seul d'entre eux n'avait besoin d'écrire un fichier .py. Donc, je dois admettre que je suis un peu sceptique que vous ayez trouvé le premier bon cas d'utilisation. :-)
Mise à jour: maintenant que vous avez expliqué ce que vous essayez de faire, je serais tenté d'opérer de toute façon sur l'AST. Vous voudrez muter en supprimant, non des lignes d'un fichier (ce qui pourrait entraîner des demi-instructions qui meurent simplement avec une SyntaxError), mais des instructions entières - et quel meilleur endroit pour le faire que dans l'AST?
la source
L'analyse et la modification de la structure du code est certainement possible à l'aide du
ast
module et je vais le montrer dans un exemple dans un instant. Cependant, la réécriture du code source modifié n'est pas possible avec least
module seul. Il existe d'autres modules disponibles pour ce travail, comme un ici .REMARQUE: l'exemple ci-dessous peut être traité comme un didacticiel d'introduction sur l'utilisation du
ast
module, mais un guide plus complet sur l'utilisation duast
module est disponible ici sur le didacticiel Green Tree serpents et la documentation officielle sur least
module .Introduction à
ast
:Vous pouvez analyser le code python (représenté sous forme de chaîne) en appelant simplement l'API
ast.parse()
. Cela renvoie le descripteur à la structure AST (Abstract Syntax Tree). Il est intéressant de noter que vous pouvez recompiler cette structure et l'exécuter comme indiqué ci-dessus.Une autre API très utile est de
ast.dump()
vider tout l'AST sous forme de chaîne. Il peut être utilisé pour inspecter l'arborescence et est très utile pour le débogage. Par exemple,Sur Python 2.7:
Sur Python 3.5:
Notez la différence de syntaxe pour l'instruction d'impression dans Python 2.7 par rapport à Python 3.5 et la différence de type de nœud AST dans les arbres respectifs.
Comment modifier le code en utilisant
ast
:Voyons maintenant un exemple de modification de code python par
ast
module. Le principal outil de modification de la structure AST est laast.NodeTransformer
classe. Chaque fois que l'on a besoin de modifier l'AST, il / elle doit en faire une sous-classe et écrire la (les) transformation (s) de nœud en conséquence.Pour notre exemple, essayons d'écrire un utilitaire simple qui transforme les instructions Python 2, print en appels de fonction Python 3.
Imprimer l'instruction dans l'utilitaire de conversion d'appel Fun: print2to3.py:
Cet utilitaire peut être essayé sur un petit fichier d'exemple, tel que celui ci-dessous, et il devrait fonctionner correctement.
Fichier d'entrée de test: py2.py
Veuillez noter que la transformation ci-dessus est uniquement à
ast
des fins de didacticiel et que dans un scénario réel, il faudra examiner tous les différents scénarios tels queprint " x is %s" % ("Hello Python")
.la source
J'ai créé récemment un morceau de code assez stable (le noyau est vraiment bien testé) et extensible qui génère du code à partir de l'
ast
arbre: https://github.com/paluh/code-formatter .J'utilise mon projet comme base pour un petit plugin vim (que j'utilise tous les jours), donc mon objectif est de générer un code python vraiment sympa et lisible.
PS J'ai essayé d'étendre
codegen
mais son architecture est basée sur l'ast.NodeVisitor
interface, donc les formateurs (visitor_
méthodes) ne sont que des fonctions. J'ai trouvé cette structure assez restrictive et difficile à optimiser (dans le cas d'expressions longues et imbriquées, il est plus facile de conserver l'arborescence des objets et de mettre en cache certains résultats partiels - d'une autre manière, vous pouvez atteindre une complexité exponentielle si vous souhaitez rechercher la meilleure mise en page). MAIScodegen
comme chaque morceau du travail de mitsuhiko (que j'ai lu) est très bien écrit et concis.la source
L'une des autres réponses recommande
codegen
, qui semble avoir été remplacée parastor
. La version deastor
sur PyPI (version 0.5 à ce jour) semble également un peu obsolète, vous pouvez donc installer la version de développement deastor
comme suit.Ensuite, vous pouvez utiliser
astor.to_source
pour convertir un Python AST en code source Python lisible par l'homme:J'ai testé cela sur Python 3.5.
la source
Si vous regardez cela en 2019, vous pouvez utiliser cette libcst package . Il a une syntaxe similaire à ast. Cela fonctionne à merveille et préserve la structure du code. C'est fondamentalement utile pour le projet où vous devez conserver les commentaires, les espaces, les sauts de ligne, etc.
Si vous n'avez pas besoin de vous soucier des commentaires, des espaces blancs et autres, alors la combinaison d'ast et astor fonctionne bien.
la source
Nous avions un besoin similaire, qui n'a pas été résolu par d'autres réponses ici. Nous avons donc créé une bibliothèque pour cela, ASTTokens , qui prend un arbre AST produit avec les modules ast ou astroid , et le marque avec les plages de texte dans le code source d'origine.
Il ne modifie pas directement le code, mais ce n'est pas difficile à ajouter, car il vous indique la plage de texte que vous devez modifier.
Par exemple, cela encapsule un appel de fonction
WRAP(...)
, en préservant les commentaires et tout le reste:Produit:
J'espère que cela t'aides!
la source
Un système de transformation de programme est un outil qui analyse le texte source, construit des AST, vous permet de les modifier en utilisant des transformations source-à-source («si vous voyez ce modèle, remplacez-le par ce modèle»). De tels outils sont idéaux pour faire la mutation des codes sources existants, qui sont simplement "si vous voyez ce modèle, remplacez-le par une variante de modèle".
Bien sûr, vous avez besoin d'un moteur de transformation de programme capable d'analyser le langage qui vous intéresse, tout en effectuant les transformations orientées modèle. Notre boîte à outils de réingénierie logicielle DMS est un système qui peut le faire et gère Python et une variété d'autres langages.
Voir cette réponse SO pour un exemple d'un AST analysé par DMS pour Python capturant les commentaires avec précision. Le DMS peut apporter des modifications à l'AST et régénérer du texte valide, y compris les commentaires. Vous pouvez lui demander d'imprimer l'AST, en utilisant ses propres conventions de mise en forme (vous pouvez les modifier), ou de faire une "impression de fidélité", qui utilise les informations de ligne et de colonne d'origine pour préserver au maximum la mise en page d'origine (certains changements de mise en page où le nouveau code est inséré est inévitable).
Pour implémenter une règle de «mutation» pour Python avec DMS, vous pouvez écrire ce qui suit:
Cette règle remplace "+" par "-" d'une manière syntaxiquement correcte; il fonctionne sur l'AST et ne touchera donc pas les chaînes ou les commentaires qui semblent corrects. La condition supplémentaire sur "mutate_this_place" est de vous permettre de contrôler la fréquence à laquelle cela se produit; vous ne voulez pas muter à chaque endroit du programme.
Vous voudriez évidemment un tas d'autres règles comme celle-ci qui détectent diverses structures de code et les remplacent par les versions mutées. DMS est heureux d'appliquer un ensemble de règles. L'AST muté est alors joliment imprimé.
la source
J'utilisais baron pour cela, mais je suis maintenant passé au parso car il est à jour avec le python moderne. Cela fonctionne très bien.
J'en avais également besoin pour un testeur de mutation. C'est vraiment assez simple d'en faire un avec parso, consultez mon code sur https://github.com/boxed/mutmut
la source