Chcę programowo edytować kod źródłowy Pythona. Zasadniczo chcę przeczytać .py
plik, wygenerować AST , a następnie zapisać z powrotem zmodyfikowany kod źródłowy Pythona (tj. Inny .py
plik).
Istnieją sposoby analizowania / kompilowania kodu źródłowego języka Python przy użyciu standardowych modułów języka Python, takich jak ast
lub compiler
. Jednak nie sądzę, aby którykolwiek z nich obsługiwał sposoby modyfikacji kodu źródłowego (np. Usunięcie tej deklaracji funkcji), a następnie zapisanie modyfikującego kodu źródłowego Pythona.
AKTUALIZACJA: Powodem, dla którego chcę to zrobić, jest to, że chciałbym napisać bibliotekę testującą mutacje dla Pythona, głównie poprzez usuwanie instrukcji / wyrażeń, ponowne uruchamianie testów i sprawdzanie, co się psuje.
Odpowiedzi:
Pythoscope robi to na przypadkach testowych, które generuje automatycznie, podobnie jak narzędzie 2to3 dla Pythona 2.6 (konwertuje źródło pythona 2.x na źródło python 3.x).
Oba te narzędzia wykorzystują bibliotekę lib2to3, która jest implementacją parsera / kompilatora języka Python, który może zachować komentarze w źródle, gdy jest w obie strony ze źródła -> AST -> źródło.
Projekt liny może zaspokoić Twoje potrzeby, jeśli chcesz wykonać więcej refaktoryzacji, np. Transformacje.
Ast moduł to inna opcja, a nie starszy przykład jak „unparse” drzew składniowych powrotem do kodu (za pomocą modułu analizatora). Ale
ast
moduł jest bardziej przydatny podczas wykonywania transformacji AST na kodzie, który jest następnie przekształcany w obiekt kodu.Projekt Redbaron również może być dobrym rozwiązaniem (ht Xavier Combelle)
źródło
unparse.py
skrypt - użycie go z innego skryptu może być naprawdę uciążliwe. Ale istnieje pakiet o nazwie astunparse ( na github , na pypi ), który jest właściwie spakowaną wersjąunparse.py
.Wydaje się, że wbudowany moduł AST nie ma metody konwersji z powrotem do źródła. Jednak moduł codegen zapewnia ładną drukarkę dla ast, która pozwoli ci to zrobić. na przykład.
Spowoduje to wydrukowanie:
Pamiętaj, że możesz utracić dokładne formatowanie i komentarze, ponieważ nie są one zachowywane.
Jednak może nie być konieczne. Jeśli wszystko, czego potrzebujesz, to wykonanie zastąpionego AST, możesz to zrobić po prostu wywołując compile () na ast i wykonując wynikowy obiekt kodu.
źródło
W innej odpowiedzi zasugerowałem użycie tego
astor
pakietu, ale od tego czasu znalazłem bardziej aktualny pakiet do un-parsowania AST o nazwieastunparse
:Przetestowałem to w Pythonie 3.5.
źródło
Może nie być konieczne ponowne generowanie kodu źródłowego. Oczywiście to trochę niebezpieczne, ponieważ nie wyjaśniłeś, dlaczego myślisz, że musisz wygenerować plik .py pełen kodu; ale:
Jeśli chcesz wygenerować plik .py, którego ludzie będą faktycznie używać, na przykład po to, aby mogli wypełnić formularz i uzyskać przydatny plik .py do wstawienia do swojego projektu, nie chcesz go zamieniać na AST i z powrotem, ponieważ stracisz
całe formatowanie (pomyśl o pustych wierszach, które sprawiają, że Python jest tak czytelny dzięki grupowaniu powiązanych zestawów wierszy)( węzły ast mająlineno
icol_offset
atrybuty ) komentarze. Zamiast tego prawdopodobnie będziesz chciał użyć silnika tworzenia szablonów ( na przykład język szablonów Django został zaprojektowany tak, aby ułatwić tworzenie szablonów nawet plików tekstowych), aby dostosować plik .py lub użyć rozszerzenia MetaPython Ricka Copelanda .Jeśli próbujesz dokonać zmiany podczas kompilacji modułu, pamiętaj, że nie musisz wracać do tekstu; możesz po prostu skompilować AST bezpośrednio, zamiast zamieniać go z powrotem w plik .py.
Ale w prawie każdym przypadku prawdopodobnie próbujesz zrobić coś dynamicznego, co język taki jak Python w rzeczywistości ułatwia, bez pisania nowych plików .py! Jeśli rozszerzysz swoje pytanie, aby poinformować nas, co tak naprawdę chcesz osiągnąć, nowe pliki .py prawdopodobnie nie będą w ogóle zaangażowane w odpowiedź; Widziałem setki projektów Pythona wykonujących setki rzeczy w prawdziwym świecie i żaden z nich nie był potrzebny do napisania pliku .py. Muszę więc przyznać, że jestem trochę sceptykiem, że znalazłeś pierwszy dobry przypadek użycia. :-)
Aktualizacja: teraz, gdy wyjaśniłeś, co próbujesz zrobić, i tak bym się kusił, aby po prostu operować na AST. Będziesz chciał mutować, usuwając nie wiersze pliku (co może skutkować połowicznymi instrukcjami, które po prostu umierają z błędem SyntaxError), ale całymi instrukcjami - a jakie jest lepsze miejsce do zrobienia tego niż w AST?
źródło
Przetwarzanie i modyfikowanie struktury kodu jest na pewno możliwe przy pomocy
ast
modułu i za chwilę pokażę to na przykładzie. Jednak zapis zmodyfikowanego kodu źródłowego nie jest możliwy w przypadkuast
samego modułu. Istnieją inne moduły dostępne do tego zadania, takie jak jeden tutaj .UWAGA: Poniższy przykład może być traktowany jako wprowadzenie do korzystania z
ast
modułu, ale bardziej obszerny przewodnik na temat korzystania zast
modułu jest dostępny tutaj w samouczku Green Tree snakes i oficjalnej dokumentacjiast
modułu .Wprowadzenie do
ast
:Możesz przeanalizować kod Pythona (przedstawiony w postaci ciągu znaków), po prostu wywołując interfejs API
ast.parse()
. To zwraca uchwyt do struktury abstrakcyjnego drzewa składni (AST). Co ciekawe, możesz skompilować tę strukturę i wykonać ją, jak pokazano powyżej.Innym bardzo przydatnym interfejsem API jest
ast.dump()
zrzucanie całego AST w postaci łańcucha. Może być używany do sprawdzania struktury drzewa i jest bardzo pomocny w debugowaniu. Na przykład,W Pythonie 2.7:
W Pythonie 3.5:
Zwróć uwagę na różnicę w składni instrukcji print w Pythonie 2.7 w porównaniu z Pythonem 3.5 i różnicę w typie węzła AST w odpowiednich drzewach.
Jak zmodyfikować kod za pomocą
ast
:Przyjrzyjmy się teraz przykładowi modyfikacji kodu w Pythonie według
ast
modułu. Głównym narzędziem do modyfikowania struktury AST jestast.NodeTransformer
klasa. Ilekroć trzeba zmodyfikować AST, musi utworzyć podklasę z niej i odpowiednio napisać transformację (e) węzła.W naszym przykładzie spróbujmy napisać proste narzędzie, które przekształca instrukcje Python 2, print na wywołania funkcji Python 3.
Wydrukuj instrukcję do narzędzia konwertera połączeń Fun: print2to3.py:
To narzędzie można wypróbować na małym pliku przykładowym, takim jak poniższy, i powinno działać dobrze.
Plik wejściowy testu: py2.py
Należy pamiętać, że powyższa transformacja jest tylko w celach
ast
samouczkowych iw prawdziwym przypadku trzeba będzie spojrzeć na wszystkie różne scenariusze, takie jakprint " x is %s" % ("Hello Python")
.źródło
Stworzyłem ostatnio całkiem stabilny (rdzeń jest naprawdę dobrze przetestowany) i rozszerzalny fragment kodu, który generuje kod z
ast
drzewa: https://github.com/paluh/code-formatter .Używam mojego projektu jako bazy dla małej wtyczki vim (której używam na co dzień), więc moim celem jest wygenerowanie naprawdę ładnego i czytelnego kodu w Pythonie.
PS Próbowałem rozszerzyć,
codegen
ale jego architektura jest oparta naast.NodeVisitor
interfejsie, więc elementy formatujące (visitor_
metody) to tylko funkcje. Uważam, że ta struktura jest dość ograniczona i trudna do optymalizacji (w przypadku długich i zagnieżdżonych wyrażeń łatwiej jest zachować drzewo obiektów i buforować niektóre częściowe wyniki - w inny sposób można osiągnąć wykładniczą złożoność, jeśli chcesz wyszukać najlepszy układ). ALEcodegen
jak każda praca mitsuhiko (którą przeczytałem) jest bardzo dobrze napisana i zwięzła.źródło
Jedna z pozostałych odpowiedzi zaleca
codegen
, która wydaje się być zastąpiona przezastor
. Wersjaastor
na PyPI (wersja 0.5 w chwili pisania tego tekstu) również wydaje się być nieco przestarzała, więc możesz zainstalować wersję rozwojową wastor
następujący sposób.Następnie możesz użyć
astor.to_source
do przekonwertowania Pythona AST na czytelny dla człowieka kod źródłowy Pythona:Przetestowałem to w Pythonie 3.5.
źródło
Jeśli patrzysz na to w 2019 roku, możesz użyć tego pakietu libcst . Ma składnię podobną do ast. Działa to jak urok i pozwala zachować strukturę kodu. Zasadniczo jest to pomocne w przypadku projektu, w którym musisz zachować komentarze, spacje, nową linię itp.
Jeśli nie musisz przejmować się zachowywaniem komentarzy, spacji i innych, dobrze działa kombinacja ast i astor .
źródło
Mieliśmy podobną potrzebę, której nie rozwiązały inne odpowiedzi tutaj. Dlatego stworzyliśmy do tego bibliotekę, ASTTokens , która pobiera drzewo AST utworzone za pomocą modułów ast lub astroid i oznacza je zakresami tekstu w oryginalnym kodzie źródłowym.
Nie modyfikuje kodu bezpośrednio, ale nie jest to trudne do dodania, ponieważ informuje o zakresie tekstu, który musisz zmodyfikować.
Na przykład otacza wywołanie funkcji
WRAP(...)
, zachowując komentarze i wszystko inne:Produkuje:
Mam nadzieję że to pomoże!
źródło
Transformacji ustrojowej Program jest narzędziem, które tekst źródłowy parsowań buduje ASTs, pozwala modyfikować je za źródło-źródło przemiany ( „jeśli widzisz ten wzór, wymień go do wzorca”). Takie narzędzia są idealne do wykonywania mutacji istniejących kodów źródłowych, które są po prostu „jeśli widzisz ten wzorzec, zastąp go wariantem wzorca”.
Oczywiście potrzebujesz mechanizmu transformacji programu, który może analizować język, który Cię interesuje, i nadal wykonywać transformacje sterowane wzorcem. Nasz DMS Software Reengineering Toolkit to system, który może to zrobić i obsługuje Python i wiele innych języków.
Zobacz tę odpowiedź SO, aby zobaczyć przykład przetworzonej przez DMS AST do przechwytywania komentarzy w Pythonie dokładnie . DMS może wprowadzać zmiany w AST i ponownie generować prawidłowy tekst, w tym komentarze. Możesz poprosić go o ładne wydrukowanie AST, używając jego własnych konwencji formatowania (możesz je zmienić) lub zrobić "drukowanie wierne", które wykorzystuje oryginalne informacje o wierszu i kolumnie, aby maksymalnie zachować oryginalny układ (pewna zmiana w układzie, gdy nowy kod jest włożona jest nieunikniona).
Aby zaimplementować regułę „mutacji” dla Pythona z DMS, możesz napisać:
Ta reguła zamienia „+” na „-” w poprawny składniowo sposób; działa na AST i dlatego nie dotyka napisów ani komentarzy, które wyglądają dobrze. Dodatkowym warunkiem na „mutate_this_place” jest umożliwienie kontroli, jak często to się dzieje; nie chcesz zmieniać każdego miejsca w programie.
Oczywiście chciałbyś mieć więcej reguł, takich jak ta, które wykrywają różne struktury kodu i zastępują je zmutowanymi wersjami. DMS z przyjemnością stosuje zestaw zasad. Zmutowane AST jest następnie ładnie wydrukowane.
źródło
Kiedyś używałem do tego barona, ale teraz przeszedłem na parso, ponieważ jest on aktualny z nowoczesnym Pythonem. Działa świetnie.
Potrzebowałem tego również do testera mutacji. Stworzenie takiego z parso jest naprawdę proste, sprawdź mój kod na https://github.com/boxed/mutmut
źródło