2012-01-06 6 views
48

Próbuję złapać wyjątek w wątku i ponownie podnieść go w głównym wątku:Re-raise wyjątek Python i zachować ślad stosu

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] 

to w zasadzie działa i daje następujący wynik:

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

Źródło wyjątku wskazuje jednak na linię 16, w której nastąpiło ponowne przebicie. Oryginalny wyjątek pochodzi z linii 7. Jak mam zmodyfikować główny nitkę tak, że wyjście brzmi:

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

niesamowite, byłem ponownie podnosząc wyjątki od innych wątków też, ale nigdy nie poszedł tak daleko chcesz :) –

+0

możliwy duplikat ["Wewnętrzny wyjątek" (z tracebackiem) w Pythonie?] (Http://stackoverflow.com/questions/1350671/inner-exception-with-traceback-in-python) –

Odpowiedz

50

Trzeba wykorzystać wszystkie trzy argumenty podnieść:

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

przekazując obiekt Traceback jako trzeci argument zachowuje stos.

Z help('raise'):

Jeżeli Trzecim celem jest obecne i nie None musi być traceback przedmiot (patrz standardowy typ hierarchii) i jest podstawiony zamiast bieżącego lokalizacja jako miejsce, w którym wystąpił wyjątek . Jeśli trzeci obiekt jest obecny, a nie obiekt śledzenia trasy lub None, zgłoszony zostanie wyjątek TypeError. Postać trzy ekspresja raise jest użyteczny do ponownego podniesienia wyjątek przejrzysty w sposób poza punkt, ale zawartość raise bez ekspresji powinny być korzystne, gdy wyjątek ponownie podniósł się najbardziej ostatnio aktywny wyjątek prąd zakres.

W tym szczególnym przypadku nie można użyć wersji bez ekspresji.

+3

Uwaga: to nie działa w pythonie 3. –

+4

@AndyHayden Rzeczywiście, w Pythonie 3 [podniesienie] (http://docs.python.org/3/reference/simple_stmts.html#the-raise-statement) musiałoby zostać wywołane w sposób podobny do 'raise failingThread.exc_info [0 ] (failingThread.exc_info [1]). with_traceback (failin gThread.exc_info [2]) '. Chociaż w Pythonie 3, 'raise AnotherError from stored_exception' może zapewnić jeszcze lepszą korekcję wyjściową –

+0

, pierwszą z nich powinno być' raise failingThread.exc_info [1] .with_traceback (failingThread.exc_info [2]) 'jak [this comment] (http://stackoverflow.com/questions/18188563/how-to-re-raise-an-exception-in-nested-try-except-blocks/18188660?noredirect=1#comment26654548_18188660) –

0

można napisać go trochę tak:

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

a następnie użyć stos stosu od wyjątku?

1

Ten fragment kodu działa zarówno 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

Dlaczego spadamy? – pkoch

+1

Mogę zgadnąć, dlaczego spadł. To rozwiązanie jest poprawne tylko wtedy, gdy nie było żadnych wyjątków. Wyobraź sobie, że po wychwyceniu wyjątku próbujesz zaktualizować status w db, a to również się nie powiedzie. W takim przypadku wyjątek, który zostanie podniesiony, jest ostatni (transakcja db nie powiodła się), a nie ta, którą początkowo złapaliśmy. – odedfos