(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
- nowy ważny debugger w Emacsie o nazwie RealGUD, który może działać z dowolnym debugerem (w tym
ipdb
).
- 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:
- Albo przez uruchomienie programu w Pythonie, który wywołuje tę funkcję z powłoki systemu Unix
- 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-D
a 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 = True
które spowoduje, że Python zignoruje wszelkie kolejne instancje ipshell
obiektu, który utworzyliśmy powyżej.
!
polecenie, które wykonuje dowolne polecenie Pythona w punkcie przerwania