2012-01-06 16 views
48

Sto cercando di catturare un'eccezione in un thread e re-raise nel thread principale:Re-raise eccezione Python e conservare traccia dello stack

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] 

Questo funziona in pratica e produce il seguente output:

(<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] 

Tuttavia, la fonte dell'eccezione punta alla riga 16, dove si è verificato il controrilancio. L'eccezione originale viene dalla linea 7. Come devo modificare il principale filo modo che l'uscita si legge:

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

impressionante, ho ri-Sollevare eccezioni da altri thread troppo, ma mai andato, per quanto si vuole :) –

+0

possibile duplicato ["Eccezione interna" (con traceback) in Python?] (Http://stackoverflow.com/questions/1350671/inner-exception-with-traceback-in-python) –

risposta

50

è necessario utilizzare tutti i tre argomenti per sollevare:

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

passando l'oggetto traceback come terzo argomento conserva la pila.

Da help('raise'):

Se un terzo oggetto è presente e non None, deve essere un oggetto traceback (vedere la sezione Il tipo Hierarchy Standard), ed è sostituito al posto della corrente posizione come il luogo in cui si è verificata l'eccezione . Se il terzo oggetto è presente e non un oggetto traceback o None, viene sollevata un'eccezione TypeError. La forma tre espressione di raise è utile per rilanciare un'eccezione trasparente in una clausola except, ma raise senza espressioni dovrebbe essere preferito se l'eccezione di essere ri-sollevata era il più eccezione recentemente attivo nella corrente scopo.

In questo caso particolare non è possibile utilizzare la versione senza espressione.

+3

Nota: questo non funziona in python 3. –

+4

@AndyHayden Infatti, in Python 3 [raise] (http://docs.python.org/3/reference/simple_stmts.html#the-raise-statement) dovrebbe essere chiamato in qualche modo come "raise failingThread.exc_info [0 ] (failingThread.exc_info [1]). with_traceback (failin gThread.exc_info [2]) '. Sebbene in Python 3 il 'raise AnotherError from stored_exception' possa fornire una correzione ancora migliore –

+0

, il primo dovrebbe essere" raise failingThread.exc_info [1] .with_traceback (failingThread.exc_info [2]) 'come da [questo commento] (http://stackoverflow.com/questions/18188563/how-to-re-raise-an-exception-in-nested-try-cept-blocks/18188660?noredirect=1#comment26654548_18188660) –

0

Potrebbe scrivere un po 'come questo:

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

e quindi utilizzare lo stacktrace dall'eccezione?

1

Questo frammento di codice funziona sia in 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

Perché il downvote? – pkoch

+1

Posso indovinare perché il downvote. Questa soluzione è valida solo se non sono state gestite eccezioni. Immagina che al rilevamento dell'eccezione proverai ad aggiornare uno stato nel db e anche questo fallisce. In tal caso, l'eccezione che verrà sollevata è l'ultima (transazione db non riuscita) e non quella che abbiamo inizialmente rilevato. – odedfos

Problemi correlati