Analysieren Sie eine .py-Datei, lesen Sie den AST, ändern Sie ihn und schreiben Sie dann den geänderten Quellcode zurück

Lesezeit: 10 Minuten

Analysieren Sie eine py Datei lesen Sie den AST andern Sie
Amandasaurus

Ich möchte den Python-Quellcode programmgesteuert bearbeiten. Grundsätzlich möchte ich a lesen .py Datei, erzeugen Sie die ASTund schreiben Sie dann den geänderten Python-Quellcode zurück (dh eine andere .py Datei).

Es gibt Möglichkeiten, Python-Quellcode mit Standard-Python-Modulen zu analysieren/kompilieren, wie z ast oder compiler. Ich glaube jedoch nicht, dass einer von ihnen Möglichkeiten unterstützt, den Quellcode zu ändern (z. B. diese Funktionsdeklaration zu löschen) und dann den ändernden Python-Quellcode zurückzuschreiben.

UPDATE: Der Grund, warum ich das tun möchte, ist, dass ich a schreiben möchte Bibliothek für Mutationstests für Python, hauptsächlich durch Löschen von Anweisungen / Ausdrücken, erneutes Ausführen von Tests und Sehen, was kaputt geht.

  • Veraltet seit Version 2.6: Das Compiler-Paket wurde in Python 3.0 entfernt.

    – dfa

    20. April 09 um 15:10 Uhr

  • Was können Sie die Quelle nicht bearbeiten? Warum kannst du keinen Dekorateur schreiben?

    – S. Lott

    20. April 09 um 15:53 ​​Uhr

  • Heiliger Bimbam! Ich wollte einen Mutationstester für Python mit der gleichen Technik erstellen (insbesondere ein Nose-Plugin erstellen). Planen Sie Open Source?

    – Ryan

    21. April 09 um 15:15 Uhr

  • @ Ryan Ja, ich werde alles, was ich erstelle, als Open Source öffnen. Wir sollten diesbezüglich in Kontakt bleiben

    – Amandasaurus

    21. April 2009 um 22:37 Uhr

  • Auf jeden Fall habe ich Ihnen eine E-Mail über Launchpad gesendet.

    – Ryan

    23. April 09 um 3:04 Uhr

Analysieren Sie eine py Datei lesen Sie den AST andern Sie
Ryan

Pythoskop macht dies mit den Testfällen, die es automatisch generiert, ebenso wie die 2zu3 Tool für Python 2.6 (es konvertiert Python 2.x-Quelle in Python 3.x-Quelle).

Beide Tools verwenden die lib2to3 Bibliothek, die eine Implementierung der Python-Parser/Compiler-Maschinerie ist, die Kommentare in der Quelle beibehalten kann, wenn sie von Quelle -> AST -> Quelle umgeleitet werden.

Der Seil Projekt kann Ihren Anforderungen entsprechen, wenn Sie mehr Refactoring wie Transformationen durchführen möchten.

Der Ast Modul ist Ihre andere Option, und Es gibt ein älteres Beispiel dafür, wie Syntaxbäume wieder in Code “entparst” werden (unter Verwendung des Parser-Moduls). Aber die ast -Modul ist nützlicher, wenn eine AST-Transformation für Code durchgeführt wird, der dann in ein Codeobjekt umgewandelt wird.

Der roter Baron Projekt könnte auch gut passen (ht Xavier Combelle)

  • Das Unparse-Beispiel wird weiterhin gepflegt, hier ist die aktualisierte py3k-Version: hg.python.org/cpython/log/tip/Tools/parser/unparse.py

    – Janus Troelsen

    10. Oktober 12 um 23:38 Uhr

  • Mit Empfehlungen an unparse.py Skript – es kann sehr umständlich sein, es von einem anderen Skript aus zu verwenden. Aber es gibt ein Paket namens astunparse (auf github, auf Pypi), die im Grunde eine ordnungsgemäß gepackte Version von ist unparse.py.

    – mbdevpl

    18. Mai ’16 um 6:35 Uhr

  • Könnten Sie Ihre Antwort vielleicht aktualisieren, indem Sie parso als bevorzugte Option hinzufügen? Es ist sehr gut und aktualisiert.

    – verpackt

    29. August 19 um 8:34 Uhr

  • @ Ryan. Können Sie mir bitte Tools geben, um AST und CFG für den Python-Quellcode zu erhalten?

    – Durchschn

    25. Juni 21 um 15:09 Uhr

1643982969 435 Analysieren Sie eine py Datei lesen Sie den AST andern Sie
Brian

Das eingebaute Ast-Modul scheint keine Methode zum Zurückwandeln in den Quellcode zu haben. Allerdings ist die Codegen Modul bietet hier einen hübschen Drucker für den Ast, der Ihnen dies ermöglichen würde. z.B.

import ast
import codegen

expr="""
def foo():
   print("hello world")
"""
p=ast.parse(expr)

p.body[0].body = [ ast.parse("return 42").body[0] ] # Replace function body with "return 42"

print(codegen.to_source(p))

Dies wird gedruckt:

def foo():
    return 42

Beachten Sie, dass Sie möglicherweise die genaue Formatierung und Kommentare verlieren, da diese nicht erhalten bleiben.

Möglicherweise müssen Sie dies jedoch nicht. Wenn Sie nur den ersetzten AST ausführen müssen, können Sie dies einfach tun, indem Sie compile() für den Ast aufrufen und das resultierende Codeobjekt ausführen.

  • Nur für alle, die dies in Zukunft verwenden, Codegen ist weitgehend veraltet und weist einige Fehler auf. Ich habe ein paar davon repariert; Ich habe dies als Gist auf GitHub: gist.github.com/791312

    – Mattbasta

    22. Januar 11 um 18:54 Uhr

  • Beachten Sie, dass das neueste Codegen 2012 aktualisiert wurde, was nach dem obigen Kommentar erfolgt, also denke ich, dass Codegen aktualisiert wurde. @mattbasta

    – zjffdu

    9. August 16 um 23:56 Uhr

  • Astor scheint ein gewarteter Nachfolger von Codegen zu sein

    – Medmunds

    5. Juni 18 um 19:44 Uhr


In einer anderen Antwort schlug ich vor, die zu verwenden astor Paket, aber ich habe seitdem ein aktuelleres AST-Unparsing-Paket namens gefunden astunparse:

>>> import ast
>>> import astunparse
>>> print(astunparse.unparse(ast.parse('def foo(x): return 2 * x')))


def foo(x):
    return (2 * x)

Ich habe dies auf Python 3.5 getestet.

1643982970 100 Analysieren Sie eine py Datei lesen Sie den AST andern Sie
Brandon Rhodes

Möglicherweise müssen Sie den Quellcode nicht neu generieren. Das ist natürlich ein bisschen gefährlich für mich, da Sie nicht wirklich erklärt haben, warum Sie denken, dass Sie eine .py-Datei voller Code generieren müssen; aber:

  • Wenn Sie eine .py-Datei generieren möchten, die die Leute tatsächlich verwenden, vielleicht damit sie ein Formular ausfüllen und eine nützliche .py-Datei zum Einfügen in ihr Projekt erhalten, dann möchten Sie sie nicht in eine AST-Datei ändern und zurück, weil du verlieren wirst alle Formatierungen (denken Sie an die leeren Zeilen, die Python so lesbar machen, indem Sie verwandte Zeilengruppen zusammenfassen) (Ast Knoten haben lineno und col_offset Attribute) Bemerkungen. Stattdessen möchten Sie wahrscheinlich eine Templating-Engine (die Django-Vorlagensprachewurde beispielsweise entwickelt, um das Erstellen von Vorlagen auch für Textdateien zu vereinfachen), um die .py-Datei anzupassen, oder verwenden Sie Rick Copelands MetaPython Verlängerung.

  • Wenn Sie versuchen, während der Kompilierung eines Moduls eine Änderung vorzunehmen, beachten Sie, dass Sie nicht bis zum Text zurückgehen müssen; Sie können die AST einfach direkt kompilieren, anstatt sie wieder in eine .py-Datei umzuwandeln.

  • Aber in fast jedem Fall versuchen Sie wahrscheinlich, etwas Dynamisches zu tun, das eine Sprache wie Python eigentlich sehr einfach macht, ohne neue .py-Dateien zu schreiben! Wenn Sie Ihre Frage erweitern, um uns mitzuteilen, was Sie tatsächlich erreichen möchten, werden neue .py-Dateien wahrscheinlich überhaupt nicht in die Antwort einbezogen. Ich habe Hunderte von Python-Projekten gesehen, die Hunderte von realen Dingen ausgeführt haben, und kein einziges davon musste jemals eine .py-Datei schreiben. Also, ich muss zugeben, ich bin etwas skeptisch, dass Sie den ersten guten Anwendungsfall gefunden haben. 🙂

Aktualisieren: Jetzt, wo Sie erklärt haben, was Sie versuchen zu tun, wäre ich sowieso versucht, einfach am AST zu operieren. Sie werden mutieren wollen, indem Sie nicht Zeilen einer Datei entfernen (was dazu führen könnte, dass halbe Anweisungen einfach mit einem SyntaxError sterben), sondern ganze Anweisungen – und wo könnte man das besser tun als im AST?

Hat eine Weile gedauert, aber Python 3.9 hat Folgendes:
https://docs.python.org/3.9/whatsnew/3.9.html#ast
https://docs.python.org/3.9/library/ast.html#ast.unparse

ast.unparse(ast_obj)

Entparsen eines ast.AST-Objekts und Generieren einer Zeichenfolge mit Code, der ein äquivalentes ast.AST-Objekt erzeugen würde, wenn es mit ast.parse() zurückgeparst würde.

Das Analysieren und Ändern der Codestruktur ist mit Hilfe von sicherlich möglich ast Modul und ich werde es gleich in einem Beispiel zeigen. Ein Zurückschreiben des geänderten Quellcodes ist jedoch mit nicht möglich ast Modul allein. Für diesen Job stehen andere Module zur Verfügung, z Hier.

HINWEIS: Das folgende Beispiel kann als einführendes Tutorial zur Verwendung von behandelt werden ast Modul, sondern eine umfassendere Anleitung zur Verwendung ast Modul gibt es hier unter Green Tree Schlangen-Tutorial und offizielle Dokumentation auf ast Modul.

Einführung zu ast:

>>> import ast
>>> tree = ast.parse("print 'Hello Python!!'")
>>> exec(compile(tree, filename="<ast>", mode="exec"))
Hello Python!!

Sie können den Python-Code (dargestellt als Zeichenfolge) parsen, indem Sie einfach die API aufrufen ast.parse(). Dies gibt das Handle an die Abstract Syntax Tree (AST)-Struktur zurück. Interessanterweise können Sie diese Struktur zurückkompilieren und wie oben gezeigt ausführen.

Eine weitere sehr nützliche API ist ast.dump() die den gesamten AST in einer Zeichenfolgenform ausgibt. Es kann verwendet werden, um die Baumstruktur zu inspizieren und ist sehr hilfreich beim Debuggen. Beispielsweise,

Auf Python 2.7:

>>> import ast
>>> tree = ast.parse("print 'Hello Python!!'")
>>> ast.dump(tree)
"Module(body=[Print(dest=None, values=[Str(s="Hello Python!!")], nl=True)])"

Auf Python 3.5:

>>> import ast
>>> tree = ast.parse("print ('Hello Python!!')")
>>> ast.dump(tree)
"Module(body=[Expr(value=Call(func=Name(id='print', ctx=Load()), args=[Str(s="Hello Python!!")], keywords=[]))])"

Beachten Sie den Unterschied in der Syntax für die print-Anweisung in Python 2.7 im Vergleich zu Python 3.5 und den Unterschied im Typ des AST-Knotens in den jeweiligen Bäumen.


So ändern Sie Code mit ast:

Schauen wir uns nun ein Beispiel für die Änderung von Python-Code an ast Modul. Das Hauptwerkzeug zum Ändern der AST-Struktur ist ast.NodeTransformer Klasse. Wann immer jemand den AST modifizieren muss, muss er/sie davon ableiten und Knotentransformation(en) entsprechend schreiben.

Lassen Sie uns für unser Beispiel versuchen, ein einfaches Dienstprogramm zu schreiben, das die Python 2 -, print -Anweisungen in Python 3-Funktionsaufrufe umwandelt.

Drucken Sie die Anweisung an das Fun Call Converter-Dienstprogramm: print2to3.py:

#!/usr/bin/env python
'''
This utility converts the python (2.7) statements to Python 3 alike function calls before running the code.

USAGE:
     python print2to3.py <filename>
'''
import ast
import sys

class P2to3(ast.NodeTransformer):
    def visit_Print(self, node):
        new_node = ast.Expr(value=ast.Call(func=ast.Name(id='print', ctx=ast.Load()),
            args=node.values,
            keywords=[], starargs=None, kwargs=None))
        ast.copy_location(new_node, node)
        return new_node

def main(filename=None):
    if not filename:
        return

    with open(filename, 'r') as fp:
        data = fp.readlines()
    data="".join(data)
    tree = ast.parse(data)

    print "Converting python 2 print statements to Python 3 function calls"
    print "-" * 35
    P2to3().visit(tree)
    ast.fix_missing_locations(tree)
    # print ast.dump(tree)

    exec(compile(tree, filename="p23", mode="exec"))

if __name__ == '__main__':
    if len(sys.argv) <=1:
        print ("nUSAGE:nt print2to3.py <filename>")
        sys.exit(1)
    else:
        main(sys.argv[1])

Dieses Dienstprogramm kann an einer kleinen Beispieldatei wie der folgenden ausprobiert werden, und es sollte gut funktionieren.

Testeingabedatei: py2.py

class A(object):
    def __init__(self):
        pass

def good():
    print "I am good"

main = good

if __name__ == '__main__':
    print "I am in main"
    main()

Bitte beachten Sie, dass die obige Transformation nur für ist ast Tutorialzweck und im Realfall-Szenario muss man sich alle verschiedenen Szenarien ansehen, wie z print " x is %s" % ("Hello Python").

Ich habe kürzlich ein ziemlich stabiles (Kern ist wirklich gut getestetes) und erweiterbares Stück Code erstellt, aus dem Code generiert wird ast Baum: https://github.com/paluh/code-formatter .

Ich verwende mein Projekt als Basis für ein kleines Vim-Plugin (das ich jeden Tag verwende), also ist es mein Ziel, wirklich schönen und lesbaren Python-Code zu generieren.

PS Ich habe versucht zu verlängern codegen aber seine Architektur basiert auf ast.NodeVisitor Schnittstelle, also Formatierer (visitor_ Methoden) sind nur Funktionen. Ich fand diese Struktur ziemlich einschränkend und schwer zu optimieren (bei langen und verschachtelten Ausdrücken ist es einfacher, den Objektbaum beizubehalten und einige Teilergebnisse zwischenzuspeichern – auf andere Weise können Sie eine exponentielle Komplexität erreichen, wenn Sie nach dem besten Layout suchen möchten). ABER codegen da jedes stück von mitsuhikos arbeit (das ich gelesen habe) sehr gut geschrieben und prägnant ist.

.

769970cookie-checkAnalysieren Sie eine .py-Datei, lesen Sie den AST, ändern Sie ihn und schreiben Sie dann den geänderten Quellcode zurück

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy