¿Cómo guardar los valores de traceback / sys.exc_info () en una variable?

127

Quiero guardar el nombre del error y los detalles de rastreo en una variable. Aquí está mi intento.

import sys
try:
    try:
        print x
    except Exception, ex:
        raise NameError
except Exception, er:
    print "0", sys.exc_info()[0]
    print "1", sys.exc_info()[1]
    print "2", sys.exc_info()[2]

Salida:

0 <type 'exceptions.NameError'>
1 
2 <traceback object at 0xbd5fc8>

Salida deseada:

0 NameError
1
2 Traceback (most recent call last):
  File "exception.py", line 6, in <module>
    raise NameError

PD: Sé que esto se puede hacer fácilmente usando el módulo de rastreo, pero quiero saber el uso del objeto sys.exc_info () [2] aquí.

codificadores del oscuro
fuente
¿Intentó imprimir sys.exc_info () [x] .__ str __ ()?
zmbq
3
Es posible que haya entendido mal lo que está sucediendo en su programa: a lo que se refiere como "objeto sys.exc_info () [2]" es una instancia del objeto de rastreo (= ya está utilizando el módulo de rastreo). Ahora, puede manipular ese objeto sin usar las funciones de ayuda en el módulo de rastreo, pero eso no cambia el hecho de que todavía lo está usando. :)
mac
1
Entonces @mac, por favor, ayúdame a usar el acceso al valor de este objeto con o sin usar la función auxiliar.
codersofthedark
1
@dragosrsupercool: como mencioné en mi respuesta a continuación, debe consultar la documentación de rastreo . Proporcioné un ejemplo de cómo recuperar los datos textualmente, pero hay otros métodos del objeto que le permiten extraer el nombre de la excepción, la fila del código, etc. El correcto depende realmente de cómo desee manipular el objeto. valor después ...
mac
1
Mi respuesta a otra pregunta puede ayudar a ilustrar los detalles, ¡con enlaces! Para cadenas enlatadas, el módulo de rastreo de biblioteca estándar parece estar bien. Si desea obtener los detalles, lea la fuente ( <python install path>/Lib/traceback.py) para obtener más información.
pythonlarry

Respuestas:

180

Así es como lo hago:

>>> import traceback
>>> try:
...   int('k')
... except:
...   var = traceback.format_exc()
... 
>>> print var
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ValueError: invalid literal for int() with base 10: 'k'

Sin embargo, debe echar un vistazo a la documentación de rastreo , ya que puede encontrar métodos más adecuados, dependiendo de cómo desee procesar su variable después ...

Mac
fuente
1
Estaba buscando un método sin usar el módulo de rastreo. ¿Hay alguna manera de que podamos imprimir detalles de rastreo de esta referencia de objeto? sys.exc_info () [2]
codersofthedark
Correcto, es por eso que pensé que podíamos hacer algo como sys.exc_info () [2] .format_exc (), pero esto no funciona. Por lo tanto, me pregunto cómo puedo extraer valor del objeto de rastreo sys.exc_info () [2] ¿Alguna idea?
codersofthedark
1
sys.exc_info () [2] .tb_text da error de seguimiento -> AttributeError: el objeto 'traceback' no tiene atributo 'tb_text'
codersofthedark
44
@dragosrsupercool - sys.exc_info()[2].tb_frame.f_code.co_names[3], pero no tiene ningún sentido ... Si hay un módulo llamado tracebacken la biblioteca estándar, hay una razón para ello ... :)
mac
2
@codersofthedark traceback.format_exception(*sys.exc_info())es la forma de hacerlo. Pero eso es funcionalmente equivalente a traceback.format_exc().
wizzwizz4
25

sys.exc_info () devuelve una tupla con tres valores (tipo, valor, rastreo).

  1. Aquí el tipo obtiene el tipo de excepción de la excepción que se está manejando
  2. valor son los argumentos que se pasan al constructor de la clase de excepción
  3. el rastreo contiene la información de la pila, como dónde ocurrió la excepción, etc.

Por ejemplo, en el siguiente programa

try:

    a = 1/0

except Exception,e:

    exc_tuple = sys.exc_info()

Ahora, si imprimimos la tupla, los valores serán este.

  1. El valor exc_tuple [0] será " ZeroDivisionError "
  2. El valor exc_tuple [1] será " división entera o módulo por cero " (Cadena pasada como parámetro a la clase de excepción)
  3. El valor exc_tuple [2] será " objeto trackback en (alguna dirección de memoria) "

Los detalles anteriores también se pueden obtener simplemente imprimiendo la excepción en formato de cadena.

print str(e)
keya
fuente
Para Python3, exc_tuple [1] (también conocido como valor) es la instancia de la excepción, no la "Cadena pasada como parámetro". Ver: docs.python.org/3/library/sys.html#sys.exc_info
Jinghao Shi
¿No debería ser "excepto Excepción como e:"?
Henrik
20

Úselo traceback.extract_stack()si desea un acceso conveniente a los nombres de módulos y funciones y números de línea.

Úselo ''.join(traceback.format_stack())si solo desea una cadena que se parezca a la traceback.print_stack()salida.

Tenga en cuenta que incluso con ''.join()usted obtendrá una cadena de varias líneas, ya que los elementos de format_stack()contienen \n. Ver salida a continuación.

Recuerde import traceback.

Aquí está la salida de traceback.extract_stack(). Formato agregado para facilitar la lectura.

>>> traceback.extract_stack()
[
   ('<string>', 1, '<module>', None),
   ('C:\\Python\\lib\\idlelib\\run.py', 126, 'main', 'ret = method(*args, **kwargs)'),
   ('C:\\Python\\lib\\idlelib\\run.py', 353, 'runcode', 'exec(code, self.locals)'),
   ('<pyshell#1>', 1, '<module>', None)
]

Aquí está la salida de ''.join(traceback.format_stack()). Formato agregado para facilitar la lectura.

>>> ''.join(traceback.format_stack())
'  File "<string>", line 1, in <module>\n
   File "C:\\Python\\lib\\idlelib\\run.py", line 126, in main\n
       ret = method(*args, **kwargs)\n
   File "C:\\Python\\lib\\idlelib\\run.py", line 353, in runcode\n
       exec(code, self.locals)\n  File "<pyshell#2>", line 1, in <module>\n'
Nathan
fuente
4

Tenga cuidado al sacar el objeto de excepción o el objeto de rastreo del manejador de excepciones, ya que esto causa referencias circulares y gc.collect()no se podrá recopilar. Esto parece ser un problema particular en el entorno portátil ipython / jupyter donde el objeto de rastreo no consigue aclaró en el momento adecuado, e incluso una llamada explícita a gc.collect()en finallyla sección no hace nada. Y ese es un gran problema si tiene algunos objetos enormes que no recuperan su memoria debido a eso (p. Ej., Excepciones de CUDA sin memoria que sin esta solución requieren un reinicio completo del núcleo para recuperarse).

En general, si desea guardar el objeto de rastreo, debe borrarlo de las referencias a locals(), así:

import sys, traceback, gc
type, val, tb = None, None, None
try:
    myfunc()
except:
    type, val, tb = sys.exc_info()
    traceback.clear_frames(tb)
# some cleanup code
gc.collect()
# and then use the tb:
if tb:
    raise type(val).with_traceback(tb)

En el caso del cuaderno jupyter, debe hacerlo al menos dentro del controlador de excepciones:

try:
    myfunc()
except:
    type, val, tb = sys.exc_info()
    traceback.clear_frames(tb)
    raise type(val).with_traceback(tb)
finally:
    # cleanup code in here
    gc.collect()

Probado con python 3.7.

ps el problema con ipython o jupyter notebook env es que tiene %tbmagia que guarda el rastreo y lo hace disponible en cualquier momento posterior. Y como resultado, ninguno locals()de los fotogramas que participan en el rastreo no se liberará hasta que el notebook salga u otra excepción sobrescribirá el rastreo almacenado anteriormente. Esto es muy problemático. No debe almacenar el rastreo sin limpiar sus marcos. Arreglo enviado aquí .

stason
fuente
3

El objeto se puede usar como parámetro en la Exception.with_traceback()función:

except Exception as e:
    tb = sys.exc_info()
    print(e.with_traceback(tb[2]))
Hansen D'Silva
fuente