Python + debugger = PDB! Poszukajmy Tych Insektów
- Jarosław Piszczała
- 02 Apr, 2020
- 02 Apr, 2020
Na problemy - printuj wszystko. To sposób rozwiązywania problemów znany nam wszystkim. Mnie też mimo wszystko zdarza się to robić (Gynvael Coldwind także przyznaje, że jest to dobra metoda). Jednak czasem to za mało. Ilość danych, których potrzebujemy do weryfikacji, zaczyna być coraz dłuższa. Na takie problemy mamy Python Debugger!
Python Debugger
PDB to interaktywny debugger kodu źródłowego języka Python. Pozwala nam na ustawianie w kodzie tzw. breakpoint
czyli miejsc, w których chcemy sprawdzić, co się dzieje.
Użycie
Przypuśćmy, że mamy taki kod jak poniżej. Po uruchomieniu tego skryptu, w terminalu otrzymamy wyjątek AssertionError
. Pojawia się, gdyż logika użyta w instrukcji assert
jest niepoprawna, i w jej wyniku otrzymaliśmy False
. Wstępnie wygląda na to, że nasza funkcja zwraca niepoprawną wartość.
W pierwszej kolejności skorzystajmy z debuggera! Aby go wywołać, musimy w naszym kodzie zostawić tzw. pułapkę. W przypadku Pythona jest to funkcja breakpoint()
lub import pdb; pdb.set_trace()
dla wersji pythona < 3.7. Następnie uruchamiamy skrypt ponownie tak jak wcześniej.
Terminal się zatrzymał i nie wywołał wyjątku. Jednak to, co widać w terminalu, jest czymś innym niż to, do czego się przyzwyczailiśmy.
W pierwszej linii dostajemy informacje o pliku, w którym jesteśmy, linii, która zostanie wywołana jako następna oraz funkcja, w której ciele się znajdujemy. Następna linia to kod, który zostanie wykonany jako kolejny. Na koniec (Pdb)
, które jest znakiem zachęty. System w ten sposób oczekuje na wprowadzenie danych przez użytkownika. A co takiego użytkownik może?
Komendy
Po pierwsze komendy. Po wpisaniu help
otrzymamy całą tabelę komend. Dla użytkowników gdb
komendy te prawdopodobnie nie będą zaskoczeniem. Aby przeczytać o komendach, możemy użyć komendy help <komenda>
.
Dla przykładu sprawdziliśmy, czym jest komenda pp
, która pomaga prezentować skomplikowane dane w czytelniejszy sposób.
Python Expression
Jedna z ważniejszych rzeczy, która ma zastąpić nasze print
. Dzięki temu możemy sprawdzać zmienne, ale także wykonywać jednolinijkowy kod pythonowy. Możemy podejrzeć zmienną words
. Następnie sprawdzamy, co ukrywa się pod zmienną title_words
. Już widzimy, gdzie jest problem, jednak szukajmy dalej. Zmienna new_string
to wartość, którą zwrócimy z funkcji, i która jest niepoprawna.
Ponieważ pdb
pozwala nam na jednolinijkowy kod Pythonowy, to możemy zacząć projektować poprawkę. Po pierwsze spróbujmy wyciąć część listy, która jest poprawna. Następnie sklejmy je z pierwszy słowem, które nie powinno być przekształcone. Wygląda na to, że znaleźliśmy miejsce problemu oraz napisaliśmy rozwiązanie.
Interpreter
Jeżeli jednak sprawdzanie danych to dla nas mało, możemy wejść do interpretera pythona, w którym możemy operować już na zmiennych czy importować biblioteki. Do tego użyjemy komendy interact
.
Aby opuścić ten widok, należy wywołać funkcje exit()
lub użyć skrótu Ctrl+D
. Pamiętajmy, że zmiany w zmiennych wykonane tam, nie wpłyną na nasz skrypt.
Wyjście
Mamy dwie metody wyjścia z debuggera w zależności od tego, jak chcemy wynik osiągnąć. Użycie exit()
spowoduje wygenerowanie wyjątku BdbQuit
.
Aby wyjść z debuggera i kontynuować działanie naszego kodu należy użyć komendy continue
. Spowoduje ona, że nasz kod będzie kontynuować działanie - w naszym przypadku wygeneruje błąd, ponieważ kod nie został poprawiony.
Nawigacja
Największą siłą według mnie są komendy do nawigacji. Aby je pokazać, zmienimy miejsce, w którym uruchomimy nasz debugger.
Komendy te pozwalają poruszać się krokowo po naszym kodzie. Dzięki temu, mamy możliwość weryfikacji tego co dzieje się w środowisku po wywołaniu kolejnych linii kodu.
Next
Po pierwsze komenda n(ext)
, która wykonuje krok w przód. W naszym przypadku breakpoint
ustawiony jest przed wywołaniem funkcji. Po wykonaniu komendy next
zostanie wywołana linia z przypisaniem wartości funkcji lower_camel_case
do zmiennej result
. Kolejny krok n
przenosi nas do linii z instrukcją assert
, a ta wywołuje wyjątek.
Step
W naszym przypadku przyda się inny krok. Jest to step
który robi krok w przód lub do środka jeżeli kolejna komenda to funkcja. W poniższym przykładzie, gdy użyjemy tej komendy, wejdziemy do kodu funkcji lower_camel_case
. Zostanie to zasygnalizowane poprzez linię --Call--
, oraz zatrzymanie się na definicji funkcji. Robiąc kolejne kroki, możemy zauważyć, że debugger wszedł głębiej do list comprehension.
Gdy step
wychodzi z funkcji, zostanie to nam zasygnalizowane poprzez linię --Return--
oraz wyświetlenia co zostało zwrócone z tej funkcji. W ten sposób widzimy, że wynikiem funkcji listcomp
jest ['Simple', 'Function', 'Name']
, a wynikiem naszej funkcji lower_camel_case
jest 'SimpleFunctionName'
.
Zaplecze
Nawigując po kodzie, można się pogubić. Aby nie skakać między debuggerem a naszym edytorem, możemy kod wyświetlić bezpośrednio w debuggerze. Służy do tego komenda list
. Wyświetla ona okolice linii, która jest w kolejce do wywołania.
Mimo że debugger pozwala nam na odczyt danych, są pewne komendy, które usprawniają ich odczyt. Zdążyliśmy sobie powiedzieć już o komendzie pp
przydatnej gdy mamy do czynienia z dużym słownikiem. Gdy nasza funkcja przyjmuje dużą ilość argumentów, dużo wygodniej jest użyć komendy args
. Wyświetli ona wszystkie argumenty funkcji, w której ciele się znajdujemy.
To na tyle, poznaliśmy debugger i podstawowe komendy, z których najczęściej korzystam w pracy zawodowej. Myślę, że taki zestaw będzie wystarczający na wszystkie przypadki, gdy print
to za mało. Podziel się w komentarzach komendami, z których Ty najczęściej korzystasz!
FAQ
Jak mogę uniknąć wielokrotnego wpisywania next
, aby przejść do kolejnych linii ? To męczące.
Właśnie dlatego, w kodzie możemy użyć wielu pułapek, i zostawiać je w miejscach, które nas interesują. Gdy użyjemy komendy c
lub continue
to kod będzie się wykonywał bez naszej kontroli do czasu, aż trafi na kolejną pułapkę.
Są dwa sposoby na ich stawianie. Jednym z nich jest użycie komendy break <numer linii>
. Dzięki temu postawimy pułapki, będąc już w debuggerze. Użycie komendy break
bez podania numeru, pozwoli wyświetlić wszystkie dotąd postawione pułapki (pułapki postawione w kodzie poprzez breakpoint()
nie zostaną tutaj doliczone). Jeżeli chcielibyśmy usunąć nasze pułapki, należy użyć komendy clear
.
Drugi sposób, to wstawienie w naszym kodzie pułapek w kilku miejscach, tam gdzie chcemy. Należy pamiętać, aby je później usunąć, ale zdecydowanie pomaga to gdy robimy poprawki i uruchamiamy kilka razy nasz debugger. W przypadku komendy break
musielibyśmy za każdym razem wpisywać na nowo nasze pułapki!
Jak wyjść z debugera gdy mam breakpoint w pętli, a exit()
nie chce działać?
W sytuacji kryzysowej możemy skorzystać z os._exit
, które spowoduje nagłe zakończenie działania skryptu: