Debug passo-passo con IPython


170

Da quello che ho letto, ci sono due modi per eseguire il debug del codice in Python:

  • Con un debugger tradizionale come pdbo ipdb. Questo supporta comandi come cfor continue, nfor step-over, sfor step-intoecc.), Ma non si ha accesso diretto a una shell IPython che può essere estremamente utile per l'ispezione degli oggetti.

  • Utilizzando IPython da incorporando un guscio IPython nel codice. Puoi farlo from ipython import embede quindi utilizzarlo embed()nel tuo codice. Quando il tuo programma / script colpisce embed()un'istruzione, verrai rilasciato in una shell IPython. Ciò consente l'ispezione completa degli oggetti e il test del codice Python utilizzando tutti i gadget IPython. Tuttavia, quando si utilizza embed()non è più possibile scorrere passo passo il codice con comode scorciatoie da tastiera.

C'è un modo per combinare il meglio di entrambi i mondi? ie

  1. Essere in grado di scorrere passo passo il codice con le pratiche scorciatoie da tastiera pdb / ipdb.
  2. In qualsiasi passaggio (ad esempio su una determinata istruzione), avere accesso a una shell IPython a tutti gli effetti .

Debug IPython come in MATLAB:

Un esempio di questo tipo di "debug avanzato" può essere trovato in MATLAB, dove l'utente ha sempre pieno accesso al motore / shell MATLAB e può comunque seguire passo passo il suo codice, definire punti di interruzione condizionati, ecc. ciò di cui ho discusso con altri utenti, questa è la funzione di debug che le persone mancano di più quando si spostano da MATLAB a IPython.

Debug di IPython in Emacs e altri editor:

Non voglio rendere la domanda troppo specifica, ma lavoro principalmente in Emacs, quindi mi chiedo se c'è un modo per incorporare questa funzionalità. Idealmente , Emacs (o l'editor) consentirebbe al programmatore di impostare punti di interruzione in qualsiasi punto del codice e di comunicare con l'interprete o il debugger per interromperlo nella posizione scelta e portare a un interprete IPython completo in quella posizione.


pdb ha un !comando che esegue qualsiasi comando python al punto di interruzione
Dmitry Galchinsky

5
Sto cercando anche un debugger Python simile a Matlab! Ad esempio, faccio molti prototipi nella shell di Python. Tutte le variabili vengono salvate con la shell. Ora incontro un problema. Spero di eseguire il debug di un piccolo pezzo di codice, con quelle variabili calcolate con la shell. Tuttavia, un nuovo debugger non può accedere alle vecchie variabili. Non è conveniente per la prototipazione.
user1914692

1
Per gli utenti di Emacs, RealGUD ha un'interfaccia incredibilmente buona.
Clément,

1
Grazie @ Clément Ho seguito il repository nell'ultimo mese e sono molto entusiasta del progetto :) Non l'ho ancora provato, ma una volta che lo faccio (o se lo fai) sentiti libero di scrivere una risposta qui che forse mostra come realizzare ciò che è richiesto. Per altri come riferimento, l'URL è github.com/rocky/emacs-dbgr
Amelio Vazquez-Reina

@ Clément Inoltre, se hai esperienza con RealGUD e ipdb, ho provato a usarlo come spiegato qui github.com/rocky/emacs-dbgr/issues/96 senza fortuna.
Amelio Vazquez-Reina,

Risposte:


61

Puoi usare la %pdbmagia di IPython . Chiama semplicemente %pdbIPython e quando si verifica un errore, verrai automaticamente spostato su ipdb. Mentre non hai immediatamente il passo, sei in ipdbseguito.

Ciò semplifica il debug delle singole funzioni, in quanto è possibile caricare un file %loade quindi eseguire una funzione. È possibile forzare un errore con un assertnella posizione corretta.

%pdbè una linea magica. Chiamatela come %pdb on, %pdb 1, %pdb offo %pdb 0. Se chiamato senza argomento, funziona come un interruttore.


6
^ Questa è la vera risposta
Cesar, il

C'è qualcosa di simile in cui pdb ha funzionalità simili a IPython senza essere inizialmente in IPython?
Lucas,

4
È inoltre possibile avviare il programma con ipython --pdb file.py -- argse vengono rilasciati su ipdb in caso di eccezione. Potrebbe valere la pena aggiungere alla risposta.
sebastian

110

Che dire di ipdb.set_trace ()? Nel tuo codice:

import ipdb; ipdb.set_trace()

aggiornamento : ora in Python 3.7, possiamo scrivere breakpoint(). Funziona allo stesso modo, ma obbedisce anche alla PYTHONBREAKPOINTvariabile d'ambiente. Questa funzione deriva da questo PEP .

Ciò consente un'ispezione completa del codice e si ha accesso a comandi come c(continua), n(esegui riga successiva), s(passo nel metodo al punto) e così via.

Vedi il repository ipdb e un elenco di comandi . IPython ora è chiamato (modifica: parte di) Jupyter .


ps: nota che un comando ipdb ha la precedenza sul codice python. Quindi per scrivere list(foo)avresti bisogno print list(foo).

Inoltre, se ti piace il prompt di ipython (le sue modalità emacs e vim, cronologia, completamenti, ...) è facile ottenere lo stesso per il tuo progetto poiché è basato sul toolkit del prompt di python .


10
Questo è il modo di farlo.
j08lue,

La mia risposta ora include come usare ipdbEmacs usando RealGUDe isend-modefare esattamente ciò che l'OP chiede.
Amelio Vazquez-Reina,

1
Jupyter non è un sostituto di IPython, ma di Notebook IPython. I notebook Jupyter usano il kernel in background. Per il notebook Python, il kernel è generalmente un kernel IPython. Il progetto IPython continua ulteriormente.
FA

1
breakpoint()è meraviglioso. In PyCharm ti fa persino cadere nel debugger di PyCharm. È anche un modo rapido per accedere al debugger PyCharm dalle funzioni che sono state incollate nella console.
Richard Möhn,

40

(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

  1. un nuovo importante debugger in Emacs chiamato RealGUD che può operare con qualsiasi debugger (inclusoipdb ).
  2. 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:

  1. O eseguendo un programma Python che richiama questa funzione da una shell Unix
  2. 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-De 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 = Trueche farà in modo che Python ignori tutte le successive istanze ipshelldell'oggetto che abbiamo creato sopra.


2
Ti preghiamo di aggiornarlo ogni volta che trovi una soluzione ancora migliore. Ma questo sembra fantastico. Lo userò.
Phani,

1
Dove / come definire / importare cfg, banner_msg, exit_msg e ispezionare?
Gordon Bean,

1
Questo sembra funzionare bene per me: github.com/kdodia/snippets/blob/master/ipsh.py
karan.dodia,

1
Ho dovuto aggiungere import inspectprima che funzionasse. La personalizzazione della riga di comando sembra essere rotta per me.
Pascal,

7
Perché non usare solo import ipdb; ipdb.set_trace()? Forse mi manca qualcosa, ma non vedo il vantaggio del tuo metodo molto più complicato.
Luator,

19

Puoi avviare la sessione IPython da pudb e tornare alla sessione di debug come preferisci.

A proposito, ipdb sta usando IPython dietro le quinte e puoi effettivamente usare la funzionalità IPython come il completamento della TAB e i comandi magici (quello inizia con %). Se stai bene con ipdb puoi avviarlo da IPython usando comandi come %rune %debug. La sessione ipdb è in realtà migliore di una semplice IPython, nel senso che puoi andare su e giù nella traccia dello stack, ecc. Cosa manca in ipdb per "ispezione degli oggetti"?

Inoltre, python.el in bundle con Emacs> = 24.3 ha un buon supporto ipdb.


1
Grazie tkf. Sono un grande fan del tuo pacchetto Emacs-Jedi. Quando hai detto che Emacs 24.3 ha un buon supporto per ipdb, ti dispiacerebbe elaborare? Di solito avvio IPython in un M-x ansi-termbuffer separato , quindi utilizzo isend-mode per associare i miei buffer di origine al buffer IPython in modo da poter inviare il codice all'interprete IPython con una scorciatoia da tastiera che invia automaticamente la %pastemagia al buffer IPython. Questo mi permette di testare rapidamente le regioni in IPython. Eseguo sempre i miei programmi da questa shell IPython con rune uso embed()per interrompere.
Amelio Vazquez-Reina,

3
Ad esempio, quando si passa attraverso il codice, il codice sorgente viene aperto nell'altro buffer con una freccia che indica il punto di esecuzione corrente. Hai anche il comando send-region, come fai con isend-mode.
tkf

Grazie @tkf Come posso iniziare una ipdbsessione di debug usando python.ele avere cose come send-regionla shell corrispondente? In generale, dove posso trovare maggiori informazioni su questo?
Amelio Vazquez-Reina,

Io uso %runo %debug. Immagino che funzioni import ipdb; ipdb.set_trace()anche. Non credo che tu possa inviare più linee a ipdb. Questa è la limitazione di ipdb. Ma probabilmente il %pastetrucco funziona. È possibile che si desideri inviare una richiesta di funzionalità allo sviluppatore IPython.
TKF

13

Sembra che l'approccio nella risposta di @ gaborous sia deprecato .

Il nuovo approccio sembra essere:

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

def buggy_method():
    debug()

Questo ti consente di usare le ipdbistruzioni all'interno della shell ipython, durante il debug?
alpha_989,

6

Prefisso "!" il simbolo per i comandi digitati in pdb sembra avere lo stesso effetto di fare qualcosa in una shell IPython. Funziona per l'accesso alla guida per una determinata funzione o anche per i nomi delle variabili. Forse questo ti aiuterà in una certa misura. Per esempio,

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

Ma! Help (numpy.transpose) ti darà la pagina di aiuto prevista su numpy.transpose. Analogamente per i nomi delle variabili, supponiamo che tu abbia una variabile l, digitando "l" in pdb elenca il codice, ma! L stampa il valore di l.


2
Questo è un brutto gotcha pdb, e vale la pena conoscerlo.
Wilfred Hughes,

4

Hai provato questo consiglio ?

O meglio ancora, usa ipython e chiama:

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

allora puoi semplicemente usare

debug_here()

ogni volta che vuoi impostare un breakpoint


4

Puoi iniziare IPython dall'interno ipdb !

Indurre il ipdbdebugger 1 :

import idpb; ipdb.set_trace()

Immettere IPython dall'interno nella ipdb>console 2 :

from IPython import embed; embed()

Ritorna alla ipdb>console dall'interno IPython:

exit

Se sei abbastanza fortunato da usare Emacs, le cose possono essere rese ancora più convenienti!

Questo richiede l'utilizzo M-x shell. Usando yasnippete bm , definisci il seguente frammento. Ciò sostituirà il testo ipdbnell'editor con la set-traceriga. Dopo aver inserito lo snippet, la linea verrà evidenziata in modo che sia facilmente visibile e navigabile. Utilizzare M-x bm-nextper navigare.

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

1 Tutto su una riga per una facile eliminazione. Poiché importssi verifica solo una volta, questo modulo garantisce ipdbche verrà importato quando ne avrai bisogno senza costi aggiuntivi.

2 Puoi salvare te stesso digitando importando IPython nel tuo .pdbrcfile :

try:
    from IPython import embed
except:
    pass

Ciò consente di chiamare semplicemente embed()dall'interno ipdb(ovviamente, solo quando è installato IPython).


3

Un'opzione è usare un IDE come Spyder che dovrebbe permetterti di interagire con il tuo codice durante il debug (usando una console IPython, in effetti). In effetti, Spyder è molto simile a MATLAB, che presumo fosse intenzionale. Ciò include ispettori variabili, modifica variabile, accesso integrato alla documentazione, ecc.


3

la risposta giusta, facile, bella, esatta alla domanda è usare% run macro con -d flag.

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

Questo dovrebbe essere più in alto. Se devo inserire una riga nel mio codice (all'interno di un modulo), devo riavviare IPython per ricaricare il modulo. Con questo, posso solo %save /tmp/foo.py x-yIl codice che voglio eseguire e il debug e quindi %run -d /tmp/foo.pyimpostare il punto di interruzione dove mi piace. Bella risposta!
Romeo Valentin

2

Se si digita exit () nella console di embed () il codice continua e si passa alla riga embed () successiva.


2

Il Pyzo IDE ha funzionalità simili a quelle richieste dall'OP. Non è necessario iniziare in modalità debug. Analogamente a MATLAB, i comandi vengono eseguiti nella shell. Quando si imposta un punto di interruzione in alcune righe del codice sorgente, l'IDE interrompe l'esecuzione lì ed è possibile eseguire il debug e inviare anche i normali comandi IPython.

Sembra tuttavia che il passaggio in (non ancora?) Non funzioni bene (cioè fermarsi in una riga e poi passare a un'altra funzione) a meno che non si imposti un altro punto di interruzione.

Tuttavia, proveniente da MATLAB, questa sembra la migliore soluzione che ho trovato.


2

Da python 3.2, hai il interactcomando, che ti dà accesso all'intero spazio dei comandi python / ipython.


1

L'esecuzione dall'interno della shell IPython di Emacs e il set di breakpoint tramite pdb.set_trace () dovrebbero funzionare.

Controllato con python-mode.el, Mx ipython RET ecc.


1

Sviluppo di un nuovo codice

Debug all'interno di IPython

  1. Utilizzare l'esecuzione della cella Jupyter / IPython per accelerare le iterazioni dell'esperimento
  2. Utilizzare il debug %% per il passaggio

Esempio di cella:

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

Debug del codice esistente

IPython all'interno del debug

  1. Debug di un unit test non funzionante: pytest ... --pdbcls=IPython.terminal.debugger:TerminalPdb --pdb
  2. Debug al di fuori del caso di test: breakpoint(),python -m ipdb , etc.
  3. IPython.embed () per la funzionalità IPython completa dove necessario mentre si è nel debugger

Pensieri su Python

Concordo con l'OP sul fatto che molte cose che MATLAB fa bene Python non ha ancora e dovrebbe davvero, dato che quasi tutto nel linguaggio favorisce la velocità di sviluppo rispetto alla velocità di produzione. Forse un giorno contribuirò più che banali correzioni di bug a CPython.

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

Vedi anche È possibile eseguire comandi in IPython con debug?

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.