2011-11-29 10 views
41

Al depurar una secuencia de comandos de Python, me gustaría conocer la pila de llamadas completa para todo mi programa. Una situación ideal sería si hubiera un indicador de línea de comando para python que causaría que Python imprima todos los nombres de funciones como se llaman (marqué man Python2.7, pero no encontré nada de este tipo).Cómo imprimo funciones como se llaman

Debido a la cantidad de funciones en este script, preferiría no agregar una declaración de impresión al comienzo de cada función y/o clase, si es posible.

Una solución intermedia sería usar el depurador de PyDev, colocar un par de puntos de interrupción y verificar la pila de llamadas para determinados puntos en mi programa, así que usaré este enfoque por el momento.

Todavía prefiero ver una lista completa de todas las funciones llamadas a lo largo de la vida del programa, si tal método existe.

+1

perfiladores te dicen todas las funciones llamadas por ejemplo, http://docs.python.org/library/profile.html pero no es exactamente lo que solicitó: ¿es esto suficiente? – Mark

Respuesta

70

Usted puede hacer esto con una función de rastreo (apoyos a Spacedman para mejorar la versión original de esta rastrear devoluciones y utilizar un poco de sangrado agradable):

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 

Recuerde que el objeto de código de una función por lo general tiene la misma nombre como la función asociada, pero no siempre, ya que las funciones se pueden crear dinámicamente. Desafortunadamente, Python no rastrea los objetos de función en la pila (a veces he fantaseado con enviar un parche para esto). Aún así, esto es ciertamente "lo suficientemente bueno" en la mayoría de los casos.

Si esto llega a ser un problema, puede extraer el nombre de la función "real" del código fuente-Python rastrea el nombre de archivo y el número de línea -o pregunte al recolector de basura qué objeto de función hace referencia al objeto de código. Podría haber más de una función compartiendo el objeto de código, pero cualquiera de sus nombres podría ser lo suficientemente bueno.

Volviendo a visitar este cuatro años más tarde, Me es menester mencionar que en Python 2.6 y versiones posteriores, se puede obtener un mejor rendimiento mediante el uso de sys.setprofile() en lugar de sys.settrace(). Se puede usar la misma función de rastreo; simplemente se llama a la función de perfil solo cuando se ingresa o sale una función, por lo que lo que está dentro de la función se ejecuta a toda velocidad.

+0

+1 para el código real. –

+1

he modificado para mostrar los resultados de la función. Espero que esté bien! – Spacedman

+0

seguro, cuanto más, mejor :-) – 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

Esta es una manera menos conveniente y menos flexible de hacer lo que el OP ya hizo usando un depurador y puntos de interrupción. –

7

Hay algunas opciones. Si un depurador no es suficiente, puede establecer una función de seguimiento usando sys.settrace(). Esta función se invocará esencialmente en cada línea de código Python ejecutada, pero es fácil identificar las llamadas a funciones: consulte la documentación vinculada.

Puede que también esté interesado en el módulo trace, aunque no hace exactamente lo que usted solicitó. Asegúrese de mirar la opción --trackcalls.

+0

Sí, 'sys.settrace()', junto con la función de seguimiento sugerida de @ kindall, funcionaba como un amuleto. :) El módulo 'trace' también parece muy útil ... lo tendré en cuenta para futuros proyectos de depuración. – James

2

Puede usar settrace, como se describe aquí: Tracing python code. Use la versión cerca del final de la página. Pegué el código de esa página en mi código para ver exactamente qué líneas se ejecutan cuando se está ejecutando mi código. También puede filtrar para que solo vea los nombres de las funciones llamadas.

5

Otra buena herramienta para tener en cuenta es el módulo 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' es útil, pero no pude encontrar cómo producir la salida solicitada por OP: '-l' solo muestra cada función una vez,' -t' muestra cada línea. –

Cuestiones relacionadas