webentwicklung-frage-antwort-db.com.de

"Innere Ausnahme" (mit Traceback) in Python?

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.

128
EMP

Python 2

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.

132
Glenn Maynard

Python 3

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")
203
Alexei Tenitski

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.

Bekannter Ausnahmetyp

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() .

Beliebiger Ausnahmetyp

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

Ändern Sie die Nachricht

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.

18
Don Kirkby

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
10
ilya n.

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.

5
ire_and_curses

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)
3
LexieHankins

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.

3
Alfe

Vorausgesetzt:

  • du brauchst eine lösung, die funktioniert für Python 2 (für pure Python 3 siehe raise ... from Lösung)
  • ich möchte nur die Fehlermeldung anreichern, z. Bereitstellung eines zusätzlichen Kontexts
  • brauche den vollständigen Stack-Trace

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.

2
atreat

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
2
brool