2011-11-29 6 views
41

Nel debug di uno script Python, mi piacerebbe davvero conoscere l'intero stack di chiamate per l'intero mio programma. Una situazione ideale sarebbe se ci fosse un flag da riga di comando per Python che indurrebbe Python a stampare tutti i nomi di funzioni come vengono chiamati (ho controllato man Python2.7, ma non ho trovato nulla di simile).Come si stampano le funzioni così come sono chiamate

A causa del numero di funzioni in questo script, preferirei non aggiungere un'istruzione di stampa all'inizio di ogni funzione e/o classe, se possibile.

Una soluzione intermedia sarebbe utilizzare il debugger di PyDev, posizionare un paio di punti di interruzione e controllare lo stack di chiamate per i punti dati nel mio programma, quindi userò questo approccio per il momento.

Preferisco ancora vedere un elenco completo di tutte le funzioni chiamate durante la durata del programma, se esiste un tale metodo.

+1

profiler ti dicono tutte le funzioni chiamate per esempio http://docs.python.org/library/profile.html ma non esattamente quello che hai chiesto - è sufficiente? – Mark

risposta

70

È possibile farlo con una funzione di trace (puntelli a Spacedman per migliorare la versione originale di questo per tracciare i rendimenti e utilizzare qualche bel rientro):

def tracefunc(frame, event, arg, indent=[0]): 
     if event == "call": 
      indent[0] += 2 
      print "-" * indent[0] + "> call function", frame.f_code.co_name 
     elif event == "return": 
      print "<" + "-" * indent[0], "exit function", frame.f_code.co_name 
      indent[0] -= 2 
     return tracefunc 

import sys 
sys.settrace(tracefunc) 

main() # or whatever kicks off your script 

noti che oggetto il codice di una funzione di solito ha la stessa nome come funzione associata, ma non sempre, poiché le funzioni possono essere create dinamicamente. Sfortunatamente, Python non traccia gli oggetti funzione nello stack (a volte ho fantasticato sull'invio di una patch per questo). Tuttavia, questo è sicuramente "abbastanza buono" nella maggior parte dei casi.

Se questo diventa un problema, è possibile estrarre il nome della funzione "reale" dal codice sorgente - Python traccia il nome del file e il numero di riga - oppure chiedere al garbage collector di scoprire quale oggetto funzione si riferisce all'oggetto codice. Potrebbe esserci più di una funzione che condivide l'oggetto codice, ma ognuno dei loro nomi potrebbe essere abbastanza buono.

Tornando a rivisitare questo quattro anni più tardi, mi convien dire che in Python 2.6 e successive, è possibile ottenere prestazioni migliori utilizzando sys.setprofile() piuttosto che sys.settrace(). È possibile utilizzare la stessa funzione di traccia; è solo che la funzione profilo viene chiamata solo quando una funzione viene inserita o chiusa, quindi ciò che è dentro la funzione viene eseguita a tutta velocità.

+0

+1 per il codice attuale. –

+1

ho modificato per mostrare i ritorni di funzione. spero che sia ok! – Spacedman

+0

sicuro, più siamo e meglio è :-) – kindall

2
import traceback 
def foo(): 
    traceback.print_stack() 
def bar(): 
    foo() 
def car(): 
    bar(): 

car() 
File "<string>", line 1, in <module> 
File "C:\Python27\lib\idlelib\run.py", line 97, in main 
    ret = method(*args, **kwargs) 
File "C:\Python27\lib\idlelib\run.py", line 298, in runcode 
    exec code in self.locals 
File "<pyshell#494>", line 1, in <module> 
File "<pyshell#493>", line 2, in car 
File "<pyshell#490>", line 2, in bar 
File "<pyshell#486>", line 2, in foo 

traceback

+1

Questo è solo un modo meno conveniente e meno flessibile di fare ciò che l'OP ha già fatto usando un debugger e breakpoint. –

7

Ci sono alcune opzioni. Se un debugger non è sufficiente, è possibile impostare una funzione di traccia utilizzando sys.settrace(). Questa funzione sarà essenzialmente chiamata su ogni riga di codice Python eseguita, ma è facile identificare le chiamate di funzione - consultare la documentazione collegata.

Potresti anche essere interessato al modulo trace, anche se non fa esattamente quello che hai chiesto. Assicurati di esaminare l'opzione --trackcalls.

+0

Sì, 'sys.settrace()', in combinazione con la funzione di traccia suggerita di @ kindall sopra, ha funzionato come un incantesimo. :) Anche il modulo 'trace' sembra davvero utile ... Lo terrò a mente per i futuri progetti di debug. – James

2

È possibile utilizzare settrace, come descritto qui: Tracing python code. Utilizza la versione vicino alla fine della pagina. Inserisco il codice di quella pagina nel mio codice per vedere esattamente quali linee vengono eseguite quando il mio codice è in esecuzione. Puoi anche filtrare in modo da vedere solo i nomi delle funzioni chiamate.

5

Un altro valido strumento di essere a conoscenza è il modulo trace:

$ cat foo.py 
def foo(): 
    bar() 

def bar(): 
    print "in bar!" 

foo() 

$ python -m trace --listfuncs foo.py 
in bar! 

functions called: 
filename: /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/trace.py, modulename: trace, funcname: _unsettrace 
filename: foo.py, modulename: foo, funcname: 
filename: foo.py, modulename: foo, funcname: bar 
filename: foo.py, modulename: foo, funcname: foo 

$python -m trace --trace foo.py 
--- modulename: foo, funcname: 
foo.py(1): def foo(): 
foo.py(4): def bar(): 
foo.py(7): foo() 
--- modulename: foo, funcname: foo 
foo.py(2): bar() 
--- modulename: foo, funcname: bar 
foo.py(5): print "in bar!" 
in bar! 
--- modulename: trace, funcname: _unsettrace 
trace.py(80):   sys.settrace(None) 

+1

'trace' è utile, ma non sono riuscito a trovare come produrre l'output richiesto da OP: '-l' mostra solo una volta ciascuna funzione,' -t' mostra ogni riga. –