Debugowanie krok po kroku za pomocą IPython

170

Z tego, co przeczytałem, istnieją dwa sposoby debugowania kodu w Pythonie:

  • Z tradycyjnym debugerem, takim jak pdblub ipdb. Obsługuje polecenia takie jak cfor continue, nfor step-over, sfor step-intoitp.), Ale nie masz bezpośredniego dostępu do powłoki IPython, która może być niezwykle przydatna do inspekcji obiektów.

  • Korzystanie z IPython poprzez osadzanie powłoki IPython w kodzie. Możesz to zrobić from ipython import embed, a następnie użyć embed()w swoim kodzie. Kiedy twój program / skrypt trafia na embed()instrukcję, zostajesz przeniesiony do powłoki IPythona. Pozwala to na pełną inspekcję obiektów i testowanie kodu Pythona przy użyciu wszystkich zalet IPythona. Jednak podczas używania embed()nie można już krok po kroku przez kod za pomocą przydatnych skrótów klawiaturowych.

Czy istnieje sposób, aby połączyć to, co najlepsze z obu światów? To znaczy

  1. Być w stanie przejść przez kod krok po kroku za pomocą przydatnych skrótów klawiaturowych pdb / ipdb.
  2. Na każdym takim kroku (np. W danej instrukcji), uzyskaj dostęp do pełnej powłoki IPythona .

Debugowanie IPythona jak w MATLAB:

Przykład tego typu „ulepszonego debugowania” można znaleźć w MATLAB-ie, gdzie użytkownik zawsze ma pełny dostęp do silnika / powłoki MATLAB-a i nadal może krok po kroku przeglądać swój kod, definiować warunkowe punkty przerwania itp. o czym rozmawiałem z innymi użytkownikami, jest to funkcja debugowania, której ludzie najbardziej tęsknią podczas przechodzenia z MATLAB na IPython.

Debugowanie IPythona w Emacsie i innych edytorach:

Nie chcę, aby to pytanie było zbyt konkretne, ale pracuję głównie w Emacsie, więc zastanawiam się, czy jest jakiś sposób, aby wprowadzić do niego tę funkcjonalność. Idealnie byłoby, gdyby Emacs (lub edytor) pozwolił programiście ustawić punkty przerwania w dowolnym miejscu kodu i komunikować się z interpreterem lub debugerem, aby zatrzymał się w wybranej lokalizacji i doprowadził do pełnego interpretera IPythona w tej lokalizacji.

Amelio Vazquez-Reina
źródło
pdb ma !polecenie, które wykonuje dowolne polecenie Pythona w punkcie przerwania
Dmitry Galchinsky
5
Szukam też debuggera Pythona podobnego do Matlab! Na przykład wykonuję wiele prototypów w powłoce Pythona. Wszystkie zmienne są zapisywane w powłoce. Teraz mam problem. Mam nadzieję, że uda mi się zdebugować jeden mały fragment kodu z tymi obliczonymi zmiennymi w powłoce. Jednak nowy debugger nie może uzyskać dostępu do starych zmiennych. Nie jest to wygodne do prototypowania.
user1914692
1
Dla użytkowników Emacsa RealGUD ma niesamowicie dobry interfejs.
Clément
1
Dzięki @ Clément Śledzę repozytorium przez ostatni miesiąc i jestem bardzo podekscytowany projektem :) Jeszcze tego nie próbowałem, ale kiedy to zrobię (lub jeśli to zrobisz), napisz tutaj odpowiedź, która może pokazuje, jak osiągnąć to, o co prosisz. Dla innych w celach informacyjnych adres URL to github.com/rocky/emacs-dbgr
Amelio Vazquez-Reina
@ Clément Ponadto, jeśli masz jakieś doświadczenie z RealGUD i ipdb, próbowałem go użyć, jak wyjaśniono tutaj github.com/rocky/emacs-dbgr/issues/96 bez powodzenia.
Amelio Vazquez-Reina

Odpowiedzi:

61

Możesz użyć %pdbmagii IPythona . Po prostu zadzwoń %pdbw IPythonie, a gdy wystąpi błąd, automatycznie zostaniesz przeniesiony ipdb. Chociaż nie masz natychmiastowego wyjścia, jesteś ipdbpóźniej.

Ułatwia to debugowanie poszczególnych funkcji, ponieważ wystarczy załadować plik, %loada następnie uruchomić funkcję. Możesz wymusić błąd z assertwe właściwym miejscu.

%pdbto magia linii. Nazwij to jak %pdb on, %pdb 1, %pdb offlub %pdb 0. Jeśli zostanie wywołany bez argumentów, działa jako przełącznik.

sebastian
źródło
6
^ To jest prawdziwa odpowiedź
Cesar
Czy jest coś podobnego, gdzie pdb ma funkcjonalność podobną do IPythona, bez początkowego przebywania w IPythonie?
Lucas
4
Możesz także uruchomić program za pomocą ipython --pdb file.py -- argsipdb i po wyjątku zostaniesz przeniesiony do ipdb. Może warto dodać do odpowiedzi.
sebastian
110

A co z ipdb.set_trace ()? W swoim kodzie:

import ipdb; ipdb.set_trace()

aktualizacja : teraz w Pythonie 3.7 możemy pisać breakpoint(). Działa tak samo, ale jest również posłuszna PYTHONBREAKPOINTzmiennej środowiskowej. Ta funkcja pochodzi z tego PEP .

Pozwala to na pełną kontrolę kodu i masz dostęp do poleceń, takich jak c(kontynuuj), n(wykonaj następną linię), s( przejdź do metody w punkcie) i tak dalej.

Zobacz repozytorium ipdb i listę poleceń . IPython nazywa się teraz (edit: part of) Jupyter .


ps: zauważ, że polecenie ipdb ma pierwszeństwo przed kodem Pythona. Aby więc pisać list(foo), potrzebujesz print list(foo).

Ponadto, jeśli podoba ci się zachęta ipython (jej tryby emacs i vim, historia, uzupełnienia,…) łatwo jest uzyskać to samo dla twojego projektu, ponieważ jest oparty na zestawie narzędzi podpowiedzi Pythona .

Ehvince
źródło
10
To jest sposób na zrobienie tego.
j08lue
Moja odpowiedź zawiera teraz, jak używać ipdbw Emacsie, używając RealGUDi isend-moderobić dokładnie to, o co prosi OP.
Amelio Vazquez-Reina
1
Jupyter nie zastępuje IPythona, ale IPython Notebook. Notatniki Jupyter używają jądra w tle. W przypadku notatnika Python jądro jest zwykle jądrem IPython. Projekt IPython jest kontynuowany.
FA
1
breakpoint()jest wspaniałe. W PyCharm przenosi Cię nawet do debuggera PyCharm. Jest to również szybki sposób na wejście do debuggera PyCharm z funkcji wklejonych do konsoli.
Richard Möhn,
40

(Aktualizacja 28 maja 2016 r.) Używanie RealGUD w Emacs

Dla każdego w Emacsie ten wątek pokazuje, jak osiągnąć wszystko, co opisano w OP (i nie tylko), używając

  1. nowy ważny debugger w Emacsie o nazwie RealGUD, który może działać z dowolnym debugerem (w tymipdb ).
  2. Pakiet Emacsa isend-mode.

Połączenie tych dwóch pakietów jest niezwykle potężne i pozwala odtworzyć dokładnie zachowanie opisane w OP i zrobić jeszcze więcej.

Więcej informacji w artykule wiki RealGUD dla ipdb.


Oryginalna odpowiedź:

Po wypróbowaniu wielu różnych metod debugowania Pythona, w tym wszystkiego, o czym wspomniano w tym wątku, jednym z moich preferowanych sposobów debugowania Pythona za pomocą IPythona są wbudowane powłoki.

Definiowanie niestandardowej osadzonej powłoki IPython:

Dodaj następujący skrypt do swojego skryptu PYTHONPATH, aby metoda ipsh()stała się dostępna.

import inspect

# First import the embed function
from IPython.terminal.embed import InteractiveShellEmbed
from IPython.config.loader import Config

# Configure the prompt so that I know I am in a nested (embedded) shell
cfg = Config()
prompt_config = cfg.PromptManager
prompt_config.in_template = 'N.In <\\#>: '
prompt_config.in2_template = '   .\\D.: '
prompt_config.out_template = 'N.Out<\\#>: '

# Messages displayed when I drop into and exit the shell.
banner_msg = ("\n**Nested Interpreter:\n"
"Hit Ctrl-D to exit interpreter and continue program.\n"
"Note that if you use %kill_embedded, you can fully deactivate\n"
"This embedded instance so it will never turn on again")   
exit_msg = '**Leaving Nested interpreter'

# Wrap it in a function that gives me more context:
def ipsh():
    ipshell = InteractiveShellEmbed(config=cfg, banner1=banner_msg, exit_msg=exit_msg)

    frame = inspect.currentframe().f_back
    msg   = 'Stopped at {0.f_code.co_filename} at line {0.f_lineno}'.format(frame)

    # Go back one level! 
    # This is needed because the call to ipshell is inside the function ipsh()
    ipshell(msg,stack_depth=2)

Następnie za każdym razem, gdy chcę debugować coś w moim kodzie, umieszczam ipsh()bezpośrednio w miejscu, w którym muszę przeprowadzić inspekcję obiektu itp. Na przykład, powiedzmy, że chcę debugowaćmy_function poniżej

Użyj tego:

def my_function(b):
  a = b
  ipsh() # <- This will embed a full-fledged IPython interpreter
  a = 4

a następnie wywołuję my_function(2)w jeden z następujących sposobów:

  1. Albo przez uruchomienie programu w Pythonie, który wywołuje tę funkcję z powłoki systemu Unix
  2. Lub wywołując go bezpośrednio z IPythona

Niezależnie od tego, jak ją wywołam, tłumacz zatrzymuje się na linii, która mówi ipsh(). Gdy skończysz, możesz to zrobić, Ctrl-Da Python wznowi wykonywanie (z wszelkimi aktualizacjami zmiennych, które wprowadziłeś). Zauważ, że jeśli uruchomisz kod ze zwykłego IPythona w powłoce IPython (przykład 2 powyżej), nowa powłoka IPython zostanie zagnieżdżona w tej, z której ją wywołałeś, co jest w porządku, ale dobrze jest być tego świadomym. Tak czy inaczej, gdy tłumacz zatrzyma się na lokalizacji ipsh, mogę sprawdzić wartość a(która jest 2), zobaczyć, jakie funkcje i obiekty są zdefiniowane itp.

Problem:

Powyższe rozwiązanie może służyć do zatrzymania języka Python w dowolnym miejscu w kodzie, a następnie przeniesienia do pełnoprawnego interpretera IPythona. Niestety nie pozwala na dodawanie ani usuwanie punktów przerwania po wywołaniu skryptu, co jest bardzo frustrujące. Moim zdaniem to jedyna rzecz, która uniemożliwia IPythonowi stanie się świetnym narzędziem do debugowania w Pythonie.

Najlepsze, co możesz teraz zrobić:

Obejściem jest umieszczenie ipsh()a priori w różnych lokalizacjach, w których interpreter języka Python ma uruchamiać powłokę IPython (tj. A breakpoint). Następnie można „przeskakiwać” między różnymi predefiniowanymi, zakodowanymi na stałe „punktami przerwania” Ctrl-D, które powodują wyjście z bieżącej wbudowanej powłoki IPython i zatrzymanie się ponownie, gdy interpreter dotrze do następnego wywołania ipsh().

Jeśli pójdziesz tą drogą, jednym ze sposobów wyjścia z trybu debugowania i zignorowania wszystkich kolejnych punktów przerwania jest użycie, ipshell.dummy_mode = Truektóre spowoduje, że Python zignoruje wszelkie kolejne instancje ipshellobiektu, który utworzyliśmy powyżej.

Amelio Vazquez-Reina
źródło
2
Zaktualizuj to, gdy znajdziesz jeszcze lepsze rozwiązanie. Ale to wygląda świetnie. Wykorzystam to.
Phani
1
Gdzie / jak definiujesz / importujesz cfg, banner_msg, exit_msg i sprawdzasz?
Gordon Bean
1
Wydaje mi się, że to działa dobrze: github.com/kdodia/snippets/blob/master/ipsh.py
karan.dodia
1
Musiałem dodać, import inspectzanim zadziałało. Jednak wydaje mi się, że dostosowywanie wiersza poleceń nie działa.
Pascal
7
Dlaczego po prostu nie używać import ipdb; ipdb.set_trace()? Może czegoś mi brakuje, ale nie widzę korzyści z twojej znacznie bardziej skomplikowanej metody.
luator
19

Możesz rozpocząć sesję IPython z pudb i wrócić do sesji debugowania, jak chcesz.

Przy okazji, ipdb używa IPython za kulisami i możesz faktycznie korzystać z funkcji IPythona, takich jak uzupełnianie TAB i magiczne polecenia (ta zaczyna się od %). Jeśli nie masz nic przeciwko ipdb, możesz uruchomić go z IPythona za pomocą poleceń takich jak %runi %debug. Sesja ipdb jest w rzeczywistości lepsza niż zwykła sesja IPythona w tym sensie, że można przesuwać się w górę iw dół w śledzeniu stosu itp. Czego brakuje w ipdb do „inspekcji obiektów”?

Ponadto python.el dołączony do Emacsa> = 24.3 ma dobrą obsługę ipdb.

tkf
źródło
1
Dzięki tkf. Jestem wielkim fanem twojego pakietu Emacs-Jedi. Kiedy powiedziałeś, że Emacs 24.3 ma dobre wsparcie dla ipdb, czy mógłbyś to rozwinąć? Zwykle uruchamiam IPython w oddzielnym M-x ansi-termbuforze, a następnie używam trybu isend, aby powiązać moje bufory źródłowe z buforem IPython, dzięki czemu mogę wysłać kod do interpretera IPython za pomocą skrótu klawiaturowego, który automatycznie wysyła %pastemagię do bufora IPython. To pozwala mi na szybkie testowanie regionów w IPythonie. Zawsze uruchamiam swoje programy z tej powłoki IPythona runi używam embed()do zatrzymania.
Amelio Vazquez-Reina
3
Na przykład podczas przechodzenia przez kod kod źródłowy jest otwierany w drugim buforze ze strzałką wskazującą bieżący punkt wykonania. Masz również polecenie wysyłania regionu, tak jak robisz to z isend-mode.
tkf
Dzięki @tkf Jak mogę rozpocząć ipdbsesję debugowania przy użyciu python.eli mieć takie rzeczy jak send-regionitp do odpowiedniej powłoki? Ogólnie, gdzie mogę znaleźć więcej informacji na ten temat?
Amelio Vazquez-Reina
Używam %runlub %debug. Myślę, że import ipdb; ipdb.set_trace()też działa. Nie sądzę, abyś mógł wysłać wiele linii do ipdb. To jest ograniczenie ipdb. Ale prawdopodobnie %pastesztuczka działa. Możesz wysłać prośbę o funkcję do IPython dev.
tkf,
13

Wygląda na to, że podejście w odpowiedzi @ gaborous jest przestarzałe .

Wydaje się, że nowe podejście to:

from IPython.core import debugger
debug = debugger.Pdb().set_trace

def buggy_method():
    debug()
Elliot Larson
źródło
Czy pozwala to na używanie ipdbinstrukcji w powłoce ipython podczas debugowania?
alpha_989
6

Przedrostek „!” symbol do poleceń wpisywanych w pdb wydaje się mieć taki sam efekt, jak robienie czegoś w powłoce IPythona. Działa to przy dostępie do pomocy dla określonej funkcji lub nawet nazw zmiennych. Może to do pewnego stopnia ci pomoże. Na przykład,

ipdb> help(numpy.transpose)
*** No help on (numpy.transpose)

Ale! Help (numpy.transpose) wyświetli oczekiwaną stronę pomocy na numpy.transpose. Podobnie w przypadku nazw zmiennych, powiedzmy, że masz zmienną l, wpisanie "l" w pdb wyświetla kod, ale! L wypisuje wartość l.

user1953384
źródło
2
To jest paskudny pdb i warto o nim wiedzieć.
Wilfred Hughes
4

Czy wypróbowałeś tę wskazówkę ?

Albo jeszcze lepiej, użyj ipython i zadzwoń:

from IPython.Debugger import Tracer; debug_here = Tracer()

wtedy możesz po prostu użyć

debug_here()

kiedy chcesz ustawić punkt przerwania

wytworny
źródło
4

Możesz zacząć IPython od wewnątrz ipdb !

Wywołaj ipdbdebugger 1 :

import idpb; ipdb.set_trace()

Wprowadź ipython od wewnątrz w ipdb>konsoli 2 :

from IPython import embed; embed()

Wróć do ipdb>konsoli od wewnątrz IPython:

exit

Jeśli masz szczęście, że używasz Emacsa, wszystko może być jeszcze wygodniejsze!

Wymaga to użycia M-x shell. Używając yasnippeti bm , zdefiniuj następujący fragment. Spowoduje to zastąpienie tekstu ipdbw edytorze set-tracelinią. Po wstawieniu skrawka linia zostanie podświetlona, ​​aby była łatwo zauważalna i łatwa do nawigacji. Użyj M-x bm-nextdo nawigacji.

# -*- mode: snippet -*-
# name: ipdb
# key: ipdb
# expand-env: ((yas-after-exit-snippet-hook #'bm-toggle))
# --
import ipdb; ipdb.set_trace()

1 Wszystko w jednej linii dla łatwego usuwania. Ponieważ importszdarza się to tylko raz, ten formularz zapewnia, ipdbże zostanie zaimportowany, gdy go potrzebujesz, bez dodatkowych kosztów.

2 Możesz zaoszczędzić sobie pisania, importując IPython w swoim .pdbrcpliku :

try:
    from IPython import embed
except:
    pass

Pozwala to po prostu dzwonić embed()z wewnątrz ipdb(oczywiście tylko wtedy, gdy jest zainstalowany IPython).

Lorem Ipsum
źródło
3

Jedną z opcji jest użycie IDE, takiego jak Spyder, które powinno umożliwić interakcję z kodem podczas debugowania (w rzeczywistości przy użyciu konsoli IPython). W rzeczywistości Spyder jest bardzo podobny do MATLABA, co, jak przypuszczam, było zamierzone. Obejmuje to inspektorów zmiennych, edycję zmiennych, wbudowany dostęp do dokumentacji itp.

Benjamin B.
źródło
3

właściwą, łatwą, fajną i dokładną odpowiedzią na to pytanie jest użycie makra% run z flagą -d.

In [4]: run -d myscript.py
NOTE: Enter 'c' at the ipdb>  prompt to continue execution.        
> /cygdrive/c/Users/mycodefolder/myscript.py(4)<module>()
      2                                                            
      3                        
----> 4 a=1                                            
      5 b=2
Świst
źródło
To powinno być wyżej. Jeśli muszę wstawić wiersz do mojego kodu (w module), muszę ponownie uruchomić IPython, aby ponownie załadować moduł. Dzięki temu mogę po prostu %save /tmp/foo.py x-ykod, który chcę uruchomić i debugować, a następnie %run -d /tmp/foo.pyustawić punkt przerwania w dowolnym miejscu. Świetna odpowiedź!
Romeo Valentin
2

Jeśli wpiszesz exit () w konsoli embed (), kod będzie kontynuowany i przejdź do następnego wiersza embed ().

TurynTurambar
źródło
2

Pyzo IDE ma podobne możliwości jak OP prosiłem. Nie musisz zaczynać w trybie debugowania. Podobnie jak w MATLAB-ie, polecenia są wykonywane w powłoce. Po skonfigurowaniu punktu przerwania w jakiejś linii kodu źródłowego IDE zatrzymuje wykonywanie w tym miejscu i można również debugować i wydawać zwykłe polecenia IPythona.

Wydaje się jednak, że wejście w nie działa (jeszcze?) Dobrze (tj. Zatrzymanie się w jednej linii, a następnie przejście do innej funkcji), chyba że ustawisz inny punkt przerwania.

Mimo to, pochodzący z MATLAB, wydaje się być najlepszym rozwiązaniem, jakie znalazłem.

user6715080
źródło
2

Od Pythona 3.2 masz interactpolecenie, które daje dostęp do pełnej przestrzeni poleceń Python / ipython.

alpha_989
źródło
1

Uruchamianie z wnętrza powłoki IPythona Emacsa i punktu przerwania ustawionego przez pdb.set_trace () powinno działać.

Sprawdzone w python-mode.el, Mx ipython RET itp.

Andreas Röhler
źródło
1

Opracowywanie nowego kodu

Debugowanie w IPythonie

  1. Użyj wykonywania komórek Jupyter / IPython, aby przyspieszyć iteracje eksperymentów
  2. Użyj debugowania %% do przejścia

Przykład komórki:

%%debug
...: for n in range(4):
...:    n>2

Debugowanie istniejącego kodu

IPython w debugowaniu

  1. Debugowanie uszkodzonego testu jednostkowego: pytest ... --pdbcls=IPython.terminal.debugger:TerminalPdb --pdb
  2. Debugowania poza przypadku testowego: breakpoint(), python -m ipdb, itd.
  3. IPython.embed () zapewnia pełną funkcjonalność IPython, gdy jest to potrzebne w debugerze

Myśli o Pythonie

Zgadzam się z OP, że wiele rzeczy, które MATLAB robi dobrze, Python wciąż nie ma, a naprawdę powinien, ponieważ prawie wszystko w języku sprzyja szybkości rozwoju nad szybkością produkcji. Może kiedyś wniesie coś więcej niż trywialne poprawki błędów do CPythona.

https://github.com/ipython/ipython/commit/f042f3fea7560afcb518a1940daa46a72fbcfa68

Zobacz także Czy można uruchamiać polecenia w IPythonie z debugowaniem?

SwimBikeRun
źródło