Rastreo de ruta y número de línea

Estoy usando el módulo de trace de Python para rastrear algo de código. Cuando trazo el código de esta manera, puedo obtener uno de los siguientes dos resultados:

Llamar

 tracer = trace.Trace(count=False, trace=True, ignoredirs=[sys.prefix, sys.exec_prefix]) r = tracer.run('run()') tracer.results().write_results(show_missing=True) 

Resultado :

 ():  

Llamada [ cita ]:

 tracer = trace.Trace(count=False, trace=True, ignoredirs=[sys.prefix, sys.exec_prefix], countfuncs=True) r = tracer.run('run()') tracer.results().write_results(show_missing=True) 

Resultado :

     filename:, modulename:, funcname:  

    Lo que realmente necesito es un rastro que me da esto:

       

    Parecería que podría usar la información anterior e intercalarlos para obtener lo que necesito, pero tal bash fallaría en el siguiente caso de uso:

    • sys.path contiene el directorio A y el directorio B
    • Hay dos archivos A/foo.py y B/foo.py
    • Tanto A/foo.py como B/foo.py contienen la bar funciones, definida en las líneas 100 – 120
    • Hay algunas diferencias menores entre A/foo.py y B/foo.py

    En este escenario, usar ese intercalado para identificar correctamente a qué bar se llama es imposible (corríjame si me equivoco) sin analizar de forma estática el código dentro de cada bar , lo que a su vez es muy difícil para funciones no triviales.

    Entonces, ¿cómo puedo obtener el resultado de rastreo correcto que necesito?

    Con un poco de parches de mono, esto es realmente bastante fácil. Buscando en el código fuente del módulo de trace , parece que las devoluciones de llamada se utilizan para informar sobre cada paso de ejecución. La funcionalidad básica de Trace.run , muy simplificada, es:

     sys.settrace(globaltrace) # Set the trace callback function exec function # Execute the function, invoking the callback as necessary sys.settrace(None) # Reset the trace 

    globaltrace se define en Trace.__init__ dependiendo de los argumentos pasados. Específicamente, con los argumentos en su primer ejemplo, Trace.globaltrace_lt se usa como callback global, que llama Trace.localtrace_trace para cada línea de ejecución. Trace.localtrace es simplemente un caso de modificación de Trace.localtrace , para obtener el resultado que desea:

     import trace import sys import time import linecache class Trace(trace.Trace): def localtrace_trace(self, frame, why, arg): if why == "line": # record the file name and line number of every trace filename = frame.f_code.co_filename lineno = frame.f_lineno if self.start_time: print '%.2f' % (time.time() - self.start_time), print "%s (%d): %s" % (filename, lineno, linecache.getline(filename, lineno)), return self.localtrace tracer = Trace(count=False, trace=True, ignoredirs=[sys.prefix, sys.exec_prefix]) r = tracer.run('run()') 

    Hay una diferencia entre los dos ejemplos que das; si la primera salida se imprime durante la llamada Trace.run , en la segunda se imprime durante write_results . El código que he dado anteriormente sigue el patrón del anterior, por lo que tracer.results().write_results() no es necesario. Sin embargo, si desea manipular esta salida en su lugar, se puede lograr parcheando el método trace.CoverageResults.write_results de una manera similar.