(Aggiornamento del 28 maggio 2016) Utilizzo di RealGUD in Emacs
Per chiunque in Emacs, questo thread mostra come realizzare tutto ciò che è descritto nell'OP (e altro) usando
- un nuovo importante debugger in Emacs chiamato RealGUD che può operare con qualsiasi debugger (incluso
ipdb
).
- Il pacchetto Emacs
isend-mode
.
La combinazione di questi due pacchetti è estremamente potente e consente di ricreare esattamente il comportamento descritto nell'OP e fare ancora di più.
Maggiori informazioni sull'articolo wiki di RealGUD per ipdb.
Risposta originale:
Dopo aver provato molti metodi diversi per il debug di Python, incluso tutto quanto menzionato in questo thread, uno dei miei modi preferiti di debug di Python con IPython è con shell incorporate.
Definizione di una shell IPython integrata personalizzata:
Aggiungi quanto segue su uno script al tuo PYTHONPATH
, in modo che il metodo ipsh()
diventi disponibile.
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)
Quindi, ogni volta che voglio eseguire il debug di qualcosa nel mio codice, lo posiziono ipsh()
nel punto in cui devo eseguire l'ispezione degli oggetti, ecc. Ad esempio, dico che voglio eseguire il debugmy_function
seguito
Usandolo:
def my_function(b):
a = b
ipsh() # <- This will embed a full-fledged IPython interpreter
a = 4
e quindi invoco my_function(2)
in uno dei seguenti modi:
- O eseguendo un programma Python che richiama questa funzione da una shell Unix
- O invocandolo direttamente da IPython
Indipendentemente da come lo invoco, l'interprete si ferma sulla riga che dice ipsh()
. Una volta terminato, puoi farlo Ctrl-D
e Python riprenderà l'esecuzione (con eventuali aggiornamenti variabili che hai effettuato). Si noti che, se si esegue il codice da un normale IPython la shell IPython (caso 2 sopra), la nuova shell IPython verrà nidificata all'interno di quella da cui è stato invocato, il che va perfettamente bene, ma è bene esserne consapevoli. Ad ogni modo, una volta che l'interprete si ferma sulla posizione di ipsh
, posso controllare il valore di a
(che è 2
), vedere quali funzioni e oggetti sono definiti, ecc.
Il problema:
La soluzione sopra può essere usata per fare in modo che Python si fermi ovunque tu voglia nel tuo codice, e poi ti trascinerà in un interprete IPython a tutti gli effetti. Sfortunatamente non ti consente di aggiungere o rimuovere i punti di interruzione una volta invocato lo script, il che è molto frustrante. A mio avviso, questa è l' unica cosa che impedisce a IPython di diventare un ottimo strumento di debug per Python.
Il meglio che puoi fare per ora:
Una soluzione alternativa consiste nel posizionare ipsh()
a priori le diverse posizioni in cui si desidera che l'interprete Python avvii una shell IPython (ovvero a breakpoint
). È quindi possibile "saltare" tra diversi "punti di interruzione" predefiniti e codificati con Ctrl-D
, che uscirebbe dalla shell IPython incorporata corrente e si arresterebbe di nuovo ogni volta che l'interprete accede alla chiamata successiva ipsh()
.
Se segui questa strada, un modo per uscire dalla "modalità di debug" e ignorare tutti i punti di interruzione successivi, è usare ciò ipshell.dummy_mode = True
che farà in modo che Python ignori tutte le successive istanze ipshell
dell'oggetto che abbiamo creato sopra.
!
comando che esegue qualsiasi comando python al punto di interruzione