Jak poprawnie uzyskać komunikat o wyjątku w Pythonie

104

Jaki jest najlepszy sposób uzyskiwania komunikatów o wyjątkach ze składników biblioteki standardowej w Pythonie?

Zauważyłem, że w niektórych przypadkach można to uzyskać za pośrednictwem messagetakiego pola:

try:
  pass
except Exception as ex:
  print(ex.message)

ale w niektórych przypadkach (na przykład w przypadku błędów gniazda) musisz zrobić coś takiego:

try:
  pass
except socket.error as ex:
  print(ex)

Zastanawiałem się, czy istnieje standardowy sposób na opisanie większości tych sytuacji?

Zamrożone serce
źródło
1
Łączysz dwie różne rzeczy - except Foo as bar:jest to to samo, co except Foo, bar:(z wyjątkiem tego, że pierwsza jest nowsza i będzie działać w 3.x), niezależnie od tego, czy błąd występuje z messageatrybutem, czy nie.
jonrsharpe
1
@FrozenHeart Czy pytasz, czy dostęp .messagew przypadku błędu jest standardowy, czy nie?
Anand S Kumar
Twój drugi przykład z print(msg)to tylko skrót doprint(str(msg))
Salo
@Anand S Kumar Yep. Czy to zadziała w każdym przypadku?
FrozenHeart
5
Uważam, że dostęp messagejest przestarzały.
Jonathon Reinhart

Odpowiedzi:

102

Jeśli przejrzysz dokumentację dotyczącą błędów wbudowanych , zobaczysz, że większość Exceptionklas przypisuje pierwszy argument jako messageatrybut. Nie wszyscy jednak.

Warto zauważyć, że EnvironmentError(z podklasami IOErrori OSError) ma pierwszy argument errno, drugi z strerror. Nie ma message... strerrorjest mniej więcej analogiczne do tego, co normalnie byłoby message.

Mówiąc bardziej ogólnie, podklasy Exceptionmogą robić, co chcą. Mogą mieć messageatrybut lub nie . Przyszłe wbudowane elementy Exceptionmogą nie mieć messageatrybutu. Żadna Exceptionpodklasa zaimportowana z bibliotek innych firm lub kod użytkownika może nie mieć messageatrybutu.

Myślę, że właściwym sposobem radzenia sobie z tym jest zidentyfikowanie konkretnych Exceptionpodklas, które chcesz wyłapać, a następnie wyłapanie tylko tych, a nie wszystkiego za pomocą znaku except Exception, a następnie wykorzystanie dowolnych atrybutów, które określona podklasa definiuje, jak chcesz.

Jeśli musisz printcoś, myślę, że drukowanie złapanego Exceptionsamego siebie najprawdopodobniej zrobi to, co chcesz, bez względu na to, czy ma messageatrybut, czy nie.

Możesz również sprawdzić atrybut wiadomości, jeśli chcesz, w ten sposób, ale tak naprawdę nie sugerowałbym tego, ponieważ wydaje się po prostu niechlujny:

try:
    pass
except Exception as e:
    # Just print(e) is cleaner and more likely what you want,
    # but if you insist on printing message specifically whenever possible...
    if hasattr(e, 'message'):
        print(e.message)
    else:
        print(e)
ArtOfWarfare
źródło
Dziękuję za odpowiedź. Czy jest jakiś szczególny powód, aby używać str(ex)zamiast po prostu ex?
FrozenHeart
4
@FrozenHeart - print()automatycznie dzwoni str()do Ciebie. Nie ma powodu, aby ręcznie wywoływać to samo, chociaż jest to nieszkodliwe, ponieważ wywołanie str()łańcucha po prostu zwróci sam ciąg.
ArtOfWarfare
1
@ArtOfWarfare Myślę, że mogą wystąpić problemy z str()typem wiadomości unicode.
kratenko
39

Aby poprawić odpowiedź udzieloną przez @artofwarfare , oto, co uważam za lepszy sposób na sprawdzenie messageatrybutu i wydrukowanie go lub wydrukowanie Exceptionobiektu jako rezerwy.

try:
    pass 
except Exception as e:
    print getattr(e, 'message', repr(e))

Wywołanie reprjest opcjonalne, ale uważam, że w niektórych przypadkach jest konieczne.


Aktualizacja nr 1:

Po komentarzu @MadPhysicist , oto dowód, dlaczego wezwanie do reprmoże być konieczne. Spróbuj uruchomić następujący kod w swoim interpreterze:

try:
    raise Exception 
except Exception as e:
    print(getattr(e, 'message', repr(e)))
    print(getattr(e, 'message', str(e)))

Aktualizacja nr 2:

Oto demo ze specyfikacjami dla Pythona 2.7 i 3.5: https://gist.github.com/takwas/3b7a6edddef783f2abddffda1439f533

Tosin Mash
źródło
1
getattrzgłasza wyjątek, jeśli przekazany obiekt nie ma żądanego atrybutu. Jeśli chcesz zrobić coś takiego, myślę, że należy użyć opcjonalnego trzeciego argumentu za getattrcoś takiego: print getattr(e, 'message', e). Zdecydowanie lepsze niż to, co zrobiłem. Inną opcją byłoby print(e.message if hasattr(e, 'message') else e).
ArtOfWarfare
2
Prawie na pewno chciałbyś strzamiast tego repr.
Mad Physicist,
2
W porównaniu z str, reprpo prostu dodaje nazwę klasy. Zamiast używać repr, proponuję użyć print('{e.__class__.__name__}: {e}'.format(e=e)). Wydruk jest czystszy i przylega do wyjścia samoczynnego podnoszenia.
kadee
1
W oparciu o powyższe stwierdziłem, że najbardziej przydatny jest dla mnie'{e.__class__.__module__}.{e.__class__.__name__}: {e}'
shadi
1
Dzięki!! repr(e)było na razie jedynym rozwiązaniem, które pomogło mi wydrukować co najmniej minimalną ilość informacji o błędzie, który wyłapałem w określonych okolicznościach. repr(e)daje KeyError(0,)(co jest tym, czym jest błąd), odpowiednio tylko podczas str(e)lub e.messageprzyniosło, 0albo wcale.
FlorianH
0

Miałem ten sam problem. Myślę, że najlepszym rozwiązaniem jest użycie log.exception, który automatycznie wydrukuje ślad stosu i komunikat o błędzie, taki jak:

try:
    pass
    log.info('Success')
except:
    log.exception('Failed')
Bill Z
źródło
2
Co to jest log? Właśnie wypróbowałem import logPython 2.7.13 i otrzymałem komunikat, że nie ma modułu o takiej nazwie. Czy jest to coś, co dodałeś przez, pipczy też zostało dodane w nowszej wersji Pythona (wiem, wiem, muszę zaktualizować do Pythona 3 ... Zrobię to tak szybko, jak CentOS domyślnie przestanie być dostarczany z Pythonem 2 ...)
ArtOfWarfare
7
Prawdopodobnie używa modułu logowania z Pythona, a następnie tworzy instancję programu rejestrującego jako zmienną dziennika. import logging\ nlog = logging.getLogger(__name__)
Josepas
0

Ja też miałem ten sam problem. Zagłębiając się w to, odkryłem, że klasa Exception ma argsatrybut, który przechwytuje argumenty użyte do utworzenia wyjątku. Jeśli zawęzisz wyjątki, które będą przechwytywać poza, do podzbioru, powinieneś być w stanie określić, w jaki sposób zostały skonstruowane, a tym samym, który argument zawiera wiadomość.

try:
   # do something that may raise an AuthException
except AuthException as ex:
   if ex.args[0] == "Authentication Timeout.":
      # handle timeout
   else:
      # generic handling
user2501097
źródło