2012-01-06 7 views
48

Estoy tratando de capturar una excepción en un hilo y volver a subir en el hilo principal:volver a subir a excepción de Python y preservar seguimiento de la pila

import threading 
import sys 

class FailingThread(threading.Thread): 
    def run(self): 
     try: 
      raise ValueError('x') 
     except ValueError: 
      self.exc_info = sys.exc_info() 

failingThread = FailingThread() 
failingThread.start() 
failingThread.join() 

print failingThread.exc_info 
raise failingThread.exc_info[1] 

Esto básicamente funciona y da el siguiente resultado:

(<type 'exceptions.ValueError'>, ValueError('x',), <traceback object at 0x1004cc320>) 
Traceback (most recent call last): 
    File "test.py", line 16, in <module> 
    raise failingThread.exc_info[1] 

Sin embargo, la fuente de la excepción apunta a la línea 16, donde ocurrió la reposición. La excepción original proviene de la línea 7. ¿Cómo hacer que tengo que modificar el principal hilo para que la salida se lee:

Traceback (most recent call last): 
    File "test.py", line 7, in <module> 
+0

increíble, he estado re-criando excepciones de otros temas también, pero nunca fue tan lejos quieres :) –

+0

posible duplicado de ["Excepción interna" (con traceback) en Python?] (Http://stackoverflow.com/questions/1350671/inner-exception-with-traceback-in-python) –

Respuesta

50

Es necesario utilizar los tres argumentos para levantar:

raise failingThread.exc_info[0], failingThread.exc_info[1], failingThread.exc_info[2] 

pasando el objeto de rastreo en el que el tercer argumento conserva la pila.

De help('raise'):

Si un tercer objeto está presente y no None, debe ser un objeto de rastreo (ver sección La jerarquía de tipo estándar), y es sustituido en lugar del actual ubicación como el lugar donde se produjo la excepción . Si el tercer objeto está presente y no es un objeto de seguimiento o None, se genera una excepción TypeError. La forma tres-expresión de raise es útil para volver a lanzar una excepción transparente en una cláusula except, pero raise sin expresiones debe preferirse si la excepción a ser re-levantó fue el más excepción activo recientemente en el actual alcance.

En este caso particular, no puede usar la versión sin expresión.

+3

Nota: esto no funciona en Python 3. –

+4

@AndyHayden De hecho, en Python 3 [subir] (http://docs.python.org/3/reference/simple_stmts.html#the-raise-statement) tendría que llamarse de alguna manera como 'raise failingThread.exc_info [0 ] (failingThread.exc_info [1]). with_traceback (failin gThread.exc_info [2]) '. Aunque en Python 3 el 'raise AnotherError from stored_exception' podría proporcionar una salida aún mejor –

+0

corrección, la primera debería ser' raise failingThread.exc_info [1] .with_traceback (failingThread.exc_info [2]) 'según [este comentario] (http://stackoverflow.com/questions/18188563/how-to-re-raise-an-exception-in-nested-try-except-blocks/18188660?noredirect=1#comment26654548_18188660) –

0

Podría escribir algo como esto:

try: 
    raise ValueError('x') 
except ValueError as ex: 
    self.exc_info = ex 

y luego usar la stacktrace de la excepción?

1

Este fragmento de código funciona tanto en Python 2 & 3:

 1 try: 
----> 2  raise KeyError('Default key error message') 
     3 except KeyError as e: 
     4  e.args = ('Custom message when get re-raised',) #The comma is not a typo, it's there to indicate that we're replacing the tuple that e.args pointing to with another tuple that contain the custom message. 
     5  raise 
+0

¿Por qué el voto a favor? – pkoch

+1

Puedo adivinar por qué el voto a favor. Esta solución solo es válida si no hubo ningún manejo de excepciones. Imagine que al detectar la excepción intenta actualizar un estado en la base de datos y eso también falla. En ese caso, la excepción que se planteará es la última (la transacción db falló) y no la que capturamos inicialmente. – odedfos

Cuestiones relacionadas