2013-07-16 14 views
9

Chciałbym dodać kontekst wyjątek tak:Jak mogę dodać kontekst do wyjątku w Pythonie

def process(vals): 
    for key in vals: 
     try: 
      do_something(vals[key]) 
     except Exception as ex: # base class. Not sure what to expect. 
      raise # with context regarding the key that was being processed. 

znalazłem sposób, który jest nietypowo długo zdyszany dla Pythona. Czy istnieje lepszy sposób niż to?

try: 
    do_something(vals[key]) 
except Exception as ex: 
    args = list(ex.args) 
    if len(args) > 1: 
     args[0] = "{}: {}".format(key, args[0]) 
     ex.args = tuple(args) 
    raise # Will re-trhow ValueError with new args[0] 
+0

wewnątrz wyjątkiem bloku 'ex.args = (klucz,) + ex.args' jest nieco czystsze? –

+0

@SteveAllison: Można to zrobić w ten sposób, ale wiadomość będzie prezentowana jako krotka, na przykład 'ZeroDivisionError: ('0:', 'dzielenie przez zero')'. – unutbu

Odpowiedz

2

można po prostu podnieść nowy wyjątek:

def process(vals): 
    for key in vals: 
     try: 
      do_something(vals[key]) 
     except Exception as ex: 
      raise Error(key, context=ex) 

W Pythonie 3 nie trzeba dostarczyć starą wyjątek wyraźnie, będzie ona dostępna jako __context__ atrybutu nowego obiektu wyjątku i obsługi wyjątków domyślny będzie go zgłosić się automatycznie:

def process(vals): 
    for key in vals: 
     try: 
      do_something(vals[key]) 
     except Exception: 
      raise Error(key) 

W Tobie W takim przypadku powinieneś prawdopodobnie użyć jawnej składni raise Error(key) from ex, która ustawia atrybut __cause__ na nowym wyjściu, patrz Exception Chaining and Embedded Tracebacks.


Jeśli jedynym problemem jest szczegółowość kodu zmieniającego wiadomość w pytaniu; można ująć go w funkcji:

try: 
    do_something(vals[key]) 
except Exception: 
    reraise_with_context(key=key) # reraise with extra info 

gdzie:

import inspect 
import sys 

def reraise_with_context(**context): 
    ex = sys.exc_info()[1] 
    if not context: # use locals from the caller scope 
     context = inspect.currentframe().f_back.f_locals 
    extra_info = ", ".join("%s=%s" % item for item in context.items()) 
    amend_message(ex, extra_info) 
    raise 

def amend_message(ex, extra): 
    msg = '{} with context: {}'.format(ex.args[0], extra) if ex.args else extra 
    ex.args = (msg,) + ex.args[1:] 
+0

Dziękuję J.F. Nie myślałem o łańcuchowym wyjątku, aby rozwiązać mój problem. Problem z tym rozwiązaniem polega na tym, że domyślny program obsługi wyjątków Pythona wyświetla oba ślady stosów jeden nad drugim, więc aby zrozumieć kontekst, trzeba trochę przewinąć (gdy stos jest duży). Naprawdę mam nadzieję, że właśnie poprawię wiadomość. – elmotec

+0

@elmotec: Dodałem rozwiązanie "encapsulate in a function". – jfs

+0

Powraca do prawie tego samego co @unubtu. Niestety, mogę wybrać tylko jedną odpowiedź, więc wybieram tę, ponieważ pokazuje ona zarówno zmiany ex.args, jak i łańcuchy wyjątków. Dziękuję obojgu. – elmotec

4

Pierwszy element w ex.args jest zawsze wiadomością - jeśli jest. (Uwaga dla pewnymi wyjątkami, takimi jak ten podniesiony przez assert False, ex.args jest pusta krotka.)

nie wiem od czystszego sposób zmodyfikować komunikat niż realokacja nowej krotki do ex.args. (Nie możemy modyfikować krotki, ponieważ krotki są niezmienne).

Poniższy kod jest podobny do Twojego, oprócz tego, że konstruuje krotki bez korzystania z listy pośrednią, obsługuje przypadek, gdy ex.args jest pusta, i aby kod był bardziej czytelny, to ukrywa boilerplate wewnątrz menedżera kontekstowego:

import contextlib 

def process(val): 
    with context(val): 
     do_something(val) 

def do_something(val): 
    # assert False 
    return 1/val 

@contextlib.contextmanager 
def context(msg): 
    try: 
     yield 
    except Exception as ex: 
     msg = '{}: {}'.format(msg, ex.args[0]) if ex.args else str(msg) 
     ex.args = (msg,) + ex.args[1:] 
     raise 

process(0) 

daje ślad stosu z tego jako ostatecznego wiadomości:

ZeroDivisionError: 0: division by zero 
+0

To zdecydowanie czystsze niż moja pierwsza próba. Choć jest tak gadatliwy, wykonuje proste zadanie zmiany wiadomości. Dzięki. – elmotec

Powiązane problemy