Mein Hintergrund ist in C # und ich habe gerade erst begonnen, in Python zu programmieren. Wenn eine Ausnahme ausgelöst wird, möchte ich sie normalerweise in eine andere Ausnahme einschließen, die weitere Informationen hinzufügt und dennoch die vollständige Stapelverfolgung anzeigt. In C # ist das ganz einfach, aber wie mache ich das in Python?
Z.B. in c # würde ich so etwas machen:
try
{
ProcessFile(filePath);
}
catch (Exception ex)
{
throw new ApplicationException("Failed to process file " + filePath, ex);
}
In Python kann ich etwas Ähnliches machen:
try:
ProcessFile(filePath)
except Exception as e:
raise Exception('Failed to process file ' + filePath, e)
... aber dies verliert die Rückverfolgung der inneren Ausnahme!
Edit: Ich möchte beide Ausnahmemeldungen und beide Stack-Traces sehen und die beiden korrelieren. Das heißt, ich möchte in der Ausgabe sehen, dass die Ausnahme X hier und dann die Ausnahme Y dort aufgetreten ist - genau wie in C #. Ist dies in Python 2.6? Das Beste, was ich bisher tun kann (basierend auf Glenn Maynards Antwort), ist:
try:
ProcessFile(filePath)
except Exception as e:
raise Exception('Failed to process file' + filePath, e), None, sys.exc_info()[2]
Dies schließt sowohl die Nachrichten als auch die Tracebacks ein, zeigt jedoch nicht an, welche Ausnahme wo im Traceback aufgetreten ist.
Es ist einfach; Übergeben Sie den Traceback als drittes Argument.
import sys
class MyException(Exception): pass
try:
raise TypeError("test")
except TypeError, e:
raise MyException(), None, sys.exc_info()[2]
Tun Sie dies immer, wenn Sie eine Ausnahme abfangen und eine andere erneut auslösen.
In python 3 können Sie Folgendes tun:
try:
raise MyExceptionToBeWrapped("I have twisted my ankle")
except MyExceptionToBeWrapped as e:
raise MyWrapperException("I'm not in a good shape") from e
Dies wird ungefähr so aussehen:
Traceback (most recent call last):
...
MyExceptionToBeWrapped: ("I have twisted my ankle")
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
...
MyWrapperException: ("I'm not in a good shape")
Python 3 hat die raise
... from
-Klausel , um Ausnahmen zu verketten. Glenns Antwort eignet sich hervorragend für Python 2.7, verwendet jedoch nur das Traceback der ursprünglichen Ausnahme und verwirft die Fehlermeldung und andere Details. Hier einige Beispiele in Python 2.7, das der Fehlermeldung der ursprünglichen Ausnahme Kontextinformationen aus dem aktuellen Bereich hinzufügt, andere Details jedoch intakt hält.
try:
sock_common = xmlrpclib.ServerProxy(rpc_url+'/common')
self.user_id = sock_common.login(self.dbname, username, self.pwd)
except IOError:
_, ex, traceback = sys.exc_info()
message = "Connecting to '%s': %s." % (config['connection'],
ex.strerror)
raise IOError, (ex.errno, message), traceback
Diese Variante von raise
statement verwendet den Ausnahmetyp als ersten Ausdruck, die Argumente des Ausnahmeklassenkonstruktors in einem Tuple als zweiten Ausdruck und den Traceback als dritten Ausdruck. Wenn Sie früher als Python 2.2 ausführen, lesen Sie die Warnungen unter sys.exc_info()
.
Das folgende Beispiel ist allgemeiner, wenn Sie nicht wissen, welche Ausnahmen Ihr Code möglicherweise abfangen muss. Der Nachteil ist, dass es den Ausnahmetyp verliert und nur einen RuntimeError auslöst. Sie müssen das traceback
Modul importieren.
except Exception:
extype, ex, tb = sys.exc_info()
formatted = traceback.format_exception_only(extype, ex)[-1]
message = "Importing row %d, %s" % (rownum, formatted)
raise RuntimeError, message, tb
Hier ist eine weitere Option, wenn Sie mit dem Ausnahmetyp Kontext hinzufügen können. Sie können die Nachricht der Ausnahme ändern und sie dann erneut ausgeben.
import subprocess
try:
final_args = ['lsx', '/home']
s = subprocess.check_output(final_args)
except OSError as ex:
ex.strerror += ' for command {}'.format(final_args)
raise
Das generiert den folgenden Stack-Trace:
Traceback (most recent call last):
File "/mnt/data/don/workspace/scratch/scratch.py", line 5, in <module>
s = subprocess.check_output(final_args)
File "/usr/lib/python2.7/subprocess.py", line 566, in check_output
process = Popen(stdout=PIPE, *popenargs, **kwargs)
File "/usr/lib/python2.7/subprocess.py", line 710, in __init__
errread, errwrite)
File "/usr/lib/python2.7/subprocess.py", line 1327, in _execute_child
raise child_exception
OSError: [Errno 2] No such file or directory for command ['lsx', '/home']
Sie können sehen, dass es die Zeile zeigt, in der check_output()
aufgerufen wurde, aber die Ausnahmemeldung enthält jetzt die Befehlszeile.
In Python 3.x:
raise Exception('Failed to process file ' + filePath).with_traceback(e.__traceback__)
oder einfach
except Exception:
raise MyException()
das wird MyException
verbreiten, aber both Ausnahmen ausgeben, wenn es nicht behandelt wird.
In Python 2.x:
raise Exception, 'Failed to process file ' + filePath, e
Sie können das Drucken beider Ausnahmen verhindern, indem Sie das Attribut __context__
Beenden. Hier schreibe ich einen Kontextmanager, der das verwendet, um Ihre Ausnahmebedingung sofort zu erfassen und zu ändern: (Siehe http: //docs.python.org/3.1/library/stdtypes.html zur Erläuterung, wie Sie arbeiten)
try: # Wrap the whole program into the block that will kill __context__.
class Catcher(Exception):
'''This context manager reraises an exception under a different name.'''
def __init__(self, name):
super().__init__('Failed to process code in {!r}'.format(name))
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type is not None:
self.__traceback__ = exc_tb
raise self
...
with Catcher('class definition'):
class a:
def spam(self):
# not really pass, but you get the idea
pass
lut = [1,
3,
17,
[12,34],
5,
_spam]
assert a().lut[-1] == a.spam
...
except Catcher as e:
e.__context__ = None
raise
Ich glaube nicht, dass Sie dies in Python= 2.x tun können, aber etwas Ähnliches wie diese Funktionalität ist Teil von Python 3. From PEP 3134 :
In der heutigen Implementierung von Python bestehen Ausnahmen aus drei Teilen: dem Typ, dem Wert und dem Traceback. Das Modul 'sys' macht die aktuelle Ausnahme in drei parallelen Variablen verfügbar, exc_type, exc_value, und exc_traceback, die Funktion sys.exc_info () gibt einen Tupel dieser drei Teile zurück, und die 'raise'-Anweisung besteht aus drei Argumenten, wobei diese drei Teile akzeptiert werden Außerdem kann mit der Anweisung 'except' nur auf den Wert und nicht auf den Traceback zugegriffen werden. Durch Hinzufügen des Attributs ' traceback' zu den Ausnahmewerten werden alle Ausnahmeinformationen über a zugänglich einzelner Ort.
Vergleich mit C #:
Ausnahmen in C # enthalten eine schreibgeschützte 'InnerException'-Eigenschaft, die möglicherweise auf eine andere Ausnahme verweist. In der Dokumentation [10] heißt es: "Wenn eine Ausnahme X als direktes Ergebnis einer vorherigen Ausnahme Y ausgelöst wird, sollte die InnerException-Eigenschaft von X einen Verweis auf Y enthalten." Diese Eigenschaft wird nicht automatisch vom VM; vielmehr verwenden alle Ausnahmekonstruktoren ein optionales Argument 'innerException', um sie explizit festzulegen. Das Attribut ' cause' erfüllt den gleichen Zweck wie InnerException, aber dieses PEP schlägt eine neue Form der Erhöhung vor, anstatt die Konstruktoren aller Ausnahmen zu erweitern. C # bietet auch eine GetBaseException-Methode, die direkt zum Ende der InnerException-Kette springt. Dieses PEP schlägt kein Analogon vor.
Beachten Sie auch, dass Java, Ruby und Perl 5 diese Art von Dingen ebenfalls nicht unterstützen.
Wie bei anderen Sprachen verwerfen Java und Ruby beide die ursprüngliche Ausnahme, wenn eine andere Ausnahme in einem 'catch'/'rescue' oder 'finally'/'auftritt. In Perl 5 fehlt eine integrierte strukturierte Ausnahmebehandlung. In Perl 6 schlägt RFC-Nummer 88 [9] einen Ausnahmemechanismus vor, der verkettete Ausnahmen implizit in einem Array mit dem Namen @@ beibehält.
Für maximale Kompatibilität zwischen Python 2 und 3 können Sie raise_from
In der Bibliothek six
verwenden. https://six.readthedocs.io /#six.raise_from . Hier ist Ihr Beispiel (aus Gründen der Übersichtlichkeit leicht modifiziert):
import six
try:
ProcessFile(filePath)
except Exception as e:
six.raise_from(IOError('Failed to process file ' + repr(filePath)), e)
Sie können meine CausedException-Klasse verwenden, um Ausnahmen in Python 2.x (und sogar in Python 3) zu verketten Falls Sie mehr als eine gefangene Ausnahme als Ursache für eine neu ausgelöste Ausnahme angeben möchten, kann dies möglicherweise hilfreich sein.
Vorausgesetzt:
raise ... from
Lösung)sie können eine einfache Lösung aus den Dokumenten verwenden https://docs.python.org/3/tutorial/errors.html#raising-exceptions :
try:
raise NameError('HiThere')
except NameError:
print 'An exception flew by!' # print or log, provide details about context
raise # reraise the original exception, keeping full stack trace
Die Ausgabe:
An exception flew by!
Traceback (most recent call last):
File "<stdin>", line 2, in ?
NameError: HiThere
Es sieht so aus, als wäre das Schlüsselstück das vereinfachte "Raise" -Schlüsselwort, das für sich steht. Dadurch wird die Exception im except-Block erneut ausgelöst.
Vielleicht könnten Sie die relevanten Informationen aufgreifen und weitergeben? Ich denke so etwas wie:
import traceback
import sys
import StringIO
class ApplicationError:
def __init__(self, value, e):
s = StringIO.StringIO()
traceback.print_exc(file=s)
self.value = (value, s.getvalue())
def __str__(self):
return repr(self.value)
try:
try:
a = 1/0
except Exception, e:
raise ApplicationError("Failed to process file", e)
except Exception, e:
print e