2011-10-10 11 views
5

Sto scrivendo un piccolo IDE python e voglio aggiungere un semplice debug. Non ho bisogno di tutte le funzionalità di winpdb. Come si avvia un programma python (per nome file) con un punto di interruzione impostato su un numero di riga in modo che venga eseguito fino a quel numero di riga e si fermi? Nota che non voglio farlo dalla riga di comando, e non voglio modificare l'origine (inserendo set_trace, ad esempio). E non voglio che si fermi alla prima riga, quindi devo eseguire il debugger da lì. Ho provato tutti i modi ovvi con pdb e bdb, ma mi manca qualcosa.Esecuzione di una sessione di debug di Python da un programma, non dalla console

+0

Quindi non si desidera utilizzare la riga di comando e non si desidera modificare la sorgente ... a quale altro modo si sta pensando? – Amber

risposta

7

Praticamente l'unico modo praticabile per farlo (per quanto ne so) è eseguire Python come sottoprocesso dall'interno dell'IDE. Questo evita "inquinamento" dall'attuale interprete Python, il che rende abbastanza probabile che il programma funzioni come se fosse stato avviato in modo indipendente. (Se avete problemi con questo, controllare l'ambiente sottoprocesso.) In questo modo, è possibile eseguire uno script in "modalità debug" usando

p = subprocess.Popen(args=[sys.executable, '-m', 'pdb', 'scriptname.py', 'arg1'], 
        stdin=subprocess.PIPE, 
        stdout=subprocess.PIPE, 
        stderr=subprocess.PIPE) 

Questo avvierà Python al prompt del debugger. Avrete bisogno di eseguire alcuni comandi del debugger per impostare i punti di interruzione, che si può fare in questo modo:

o,e = p.communicate('break scriptname.py:lineno') 

Se funziona, o dovrebbe essere l'uscita normale dell'interprete Python dopo che imposta un punto di interruzione, e dovrebbe e essere vuoto Ti suggerirei di giocare con questo e aggiungere alcuni controlli nel codice per assicurarti che i punti di interruzione siano stati impostati correttamente.

Dopo di che, si può avviare il programma in esecuzione con

p.communicate('continue') 

A questo punto si sarebbe probabilmente desidera agganciare l'input, output, e l'errore torrenti fino alla console che si sta incorporando nel vostro IDE. Si sarebbe probabilmente bisogno di fare questo con un ciclo di eventi, più o meno in questo modo:

while p.returncode is None: 
    o,e = p.communicate(console.read()) 
    console.write(o) 
    console.write(e) 

si dovrebbe considerare che frammento di essere efficace pseudocodice, dal momento che a seconda di come esattamente le tue opere di console, che probabilmente richiederà un certo armeggiare per fallo bene

Se questo vi sembra troppo disordinato, probabilmente si può semplificare il processo un po 'con le caratteristiche di pdb e bdb moduli di Python (sto cercando di indovinare "debugger Python" e debugger di base", rispettivamente). Il miglior riferimento su come fare questo è il codice sorgente del modulo pdb. Fondamentalmente, il modo in cui le responsabilità dei moduli sono suddivise è che bdb gestisce la funzionalità del debugger "sotto il cofano", come l'impostazione dei punti di interruzione o l'arresto e il riavvio dell'esecuzione, pdb è un wrapper attorno a questo che gestisce l'interazione dell'utente, vale a dire la lettura dei comandi e la visualizzazione dell'output

Per il debugger integrato in IDE, sarebbe opportuno regolare t il comportamento del modulo pdb in due modi che mi viene in mente:

  1. ho impostato automaticamente i punti di interruzione durante l'inizializzazione, senza dover inviare esplicitamente testuale comanda di farlo
  2. rendono prende input da e invia l'output alla tua console IDE

Queste due modifiche devono essere facilmente implementate dalla sottoclasse pdb.Pdb.È possibile creare una sottoclasse cui inizializzatore prende una lista di punti di interruzione come un ulteriore argomento:

class MyPDB(pdb.Pdb): 
    def __init__(self, breakpoints, completekey='tab', 
       stdin=None, stdout=None, skip=None): 
     pdb.Pdb.__init__(self, completekey, stdin, stdout, skip) 
     self._breakpoints = breakpoints 

il posto più logico per impostare in realtà i punti di interruzione è solo dopo il debugger legge il .pdbrc file, che si verifica nel metodo pdb.Pdb.setup. Per eseguire la configurazione attuale, utilizzare il metodo set_break ereditato da bdb.Bdb:

def setInitialBreakpoints(self): 
     _breakpoints = self._breakpoints 
     self._breakpoints = None # to avoid setting breaks twice 
     for bp in _breakpoints: 
      self.set_break(filename=bp.filename, line=bp.line, 
          temporary=bp.temporary, conditional=bp.conditional, 
          funcname=bp.funcname) 

    def setup(self, f, t): 
     pdb.Pdb.setup(self, f, t) 
     self.setInitialBreakpoints() 

Questo pezzo di codice funzionerebbe per ciascun punto di interruzione essendo passata come ad esempio una tupla chiamata Potresti anche sperimentare semplicemente la creazione di istanze bdb.Breakpoint direttamente, ma non sono sicuro che funzioni correttamente, dal momento che bdb.Bdb mantiene le proprie informazioni sui punti di interruzione.

Successivamente, sarà necessario creare un nuovo metodo main per il modulo che lo esegue nello stesso modo nell'esecuzione di pdb. In una certa misura, è possibile copiare il metodo main da pdb (e la dichiarazione if __name__ == '__main__' ovviamente), ma sarà necessario aumentarlo in qualche modo per passare le informazioni relative ai punti di interruzione aggiuntivi. Quello che suggerisco è scrivere i punti di interruzione in un file temporaneo presso il vostro IDE, e passando il nome di quel file come secondo argomento:

tmpfilename = ... 
# write breakpoint info 
p = subprocess.Popen(args=[sys.executable, '-m', 'mypdb', tmpfilename, ...], ...) 
# delete the temporary file 

Poi, nel mypdb.main(), si dovrebbe aggiungere qualcosa di simile:

def main(): 
    # code excerpted from pdb.main() 
    ... 
    del sys.argv[0] 

    # add this 
    bpfilename = sys.argv[0] 
    with open(bpfilename) as f: 
     # read breakpoint info 
     breakpoints = ... 
    del sys.argv[0] 
    # back to excerpt from pdb.main() 

    sys.path[0] = os.path.dirname(mainpyfile) 

    pdb = Pdb(breakpoints) # modified 

Ora è possibile utilizzare il nuovo modulo di debugger proprio come si farebbe con lo pdb, con la differenza che non è necessario inviare esplicitamente comandi break prima dell'avvio del processo. Questo ha il vantaggio di poter collegare direttamente lo standard input e output del subprocesso Python alla tua console, se ti permette di farlo.

+0

Grazie per l'input. Ma ottengo ValueError: operazione I/O su file chiuso non appena arriva a p.communicate ('continue'). Utilizzo close_fds = False nel Popen non aiuta ... –

+0

@antwin: ricorda che tutto questo è essenzialmente pseudocodice. Se lo esegui così com'è, ovviamente avrai tutti i tipi di errori. Devi adattarlo al tuo progetto, in modi in cui non potrei dirti nulla senza avere accesso al tuo codice sorgente. –

+0

Davvero! Così ho estratto il tuo suggerimento nel seguente file #!/Usr/bin/python import sys, subprocess fileName = '/tmp/test.py' lineno = 4 p = subprocess.Popen (args = [sys.executable , '-m', 'pdb', nome del file], stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.STDOUT) cmd = 'rottura% s:% d' % (filename, lineno stampa cmd o, e = p.communicate (cmd) p.communicate ('continue') Dovrebbe funzionare, e ho già usato simili in precedenza, ma restituisce comunque ValueError: operazione I/O su file chiuso dopo la prima comunicata ... –

Problemi correlati