Mon expérience est en C # et j'ai récemment commencé à programmer en Python. Lorsqu'une exception est levée, je veux généralement l'encapsuler dans une autre exception qui ajoute plus d'informations, tout en affichant la trace complète de la pile. C'est assez facile en C #, mais comment faire en Python?
Par exemple. en C # je ferais quelque chose comme ça:
try
{
ProcessFile(filePath);
}
catch (Exception ex)
{
throw new ApplicationException("Failed to process file " + filePath, ex);
}
En Python je peux faire quelque chose de similaire:
try:
ProcessFile(filePath)
except Exception as e:
raise Exception('Failed to process file ' + filePath, e)
... mais cela perd la trace de l'exception intérieure!
Edit: J'aimerais voir les messages d'exception et les traces de pile et corréler les deux. Autrement dit, je veux voir dans la sortie que l'exception X s'est produite ici, puis l'exception Y là-bas - comme je le ferais en C #. Est-ce possible en Python 2.6? On dirait que le mieux que je puisse faire jusqu'à présent (basé sur la réponse de Glenn Maynard) est:
try:
ProcessFile(filePath)
except Exception as e:
raise Exception('Failed to process file' + filePath, e), None, sys.exc_info()[2]
Cela inclut à la fois les messages et les traces, mais cela ne montre pas quelle exception s'est produite à l'endroit où dans la trace.
C'est simple; passez le traceback comme troisième argument à soulever.
import sys
class MyException(Exception): pass
try:
raise TypeError("test")
except TypeError, e:
raise MyException(), None, sys.exc_info()[2]
Faites toujours cela lorsque vous attrapez une exception et que vous en relancez une autre.
Dans python 3, vous pouvez effectuer les opérations suivantes:
try:
raise MyExceptionToBeWrapped("I have twisted my ankle")
except MyExceptionToBeWrapped as e:
raise MyWrapperException("I'm not in a good shape") from e
Cela produira quelque chose comme ceci:
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 a la clause raise
... from
pour enchaîner les exceptions. Réponse de Glenn est idéal pour Python 2.7, mais il n'utilise que le retraçage de l'exception d'origine et jette le message d'erreur et d'autres détails. Voici quelques exemples dans Python 2.7 qui ajoute des informations de contexte de la portée actuelle dans le message d'erreur de l'exception d'origine, mais garde les autres détails intacts.
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
Cette saveur de instruction raise
prend le type d'exception comme première expression, les arguments du constructeur de classe d'exception dans un tuple comme deuxième expression et le traceback comme troisième expression. Si vous exécutez avant Python 2.2, consultez les avertissements sur sys.exc_info()
.
Voici un autre exemple plus général si vous ne savez pas quel type d'exceptions votre code pourrait avoir à intercepter. L'inconvénient est qu'il perd le type d'exception et déclenche simplement un RuntimeError. Vous devez importer le module traceback
.
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
Voici une autre option si le type d'exception vous permet d'y ajouter du contexte. Vous pouvez modifier le message de l'exception, puis le relancer.
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
Cela génère la trace de pile suivante:
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']
Vous pouvez voir qu'il affiche la ligne où check_output()
a été appelée, mais le message d'exception inclut désormais la ligne de commande.
Dans Python 3.x :
raise Exception('Failed to process file ' + filePath).with_traceback(e.__traceback__)
ou simplement
except Exception:
raise MyException()
qui propagera MyException
mais affichera les deux exceptions si elle n'est pas gérée.
Dans Python 2.x :
raise Exception, 'Failed to process file ' + filePath, e
Vous pouvez empêcher l'impression des deux exceptions en supprimant le __context__
attribut. Ici, j'écris un gestionnaire de contexte qui utilise cela pour attraper et changer votre exception à la volée: (voir http://docs.python.org/3.1/library/stdtypes.html pour une explication de leur fonctionnement )
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
Je ne pense pas que vous puissiez le faire dans Python 2.x, mais quelque chose de similaire à cette fonctionnalité fait partie de Python 3. De PEP 3134 :
Dans l'implémentation actuelle de Python, les exceptions sont composées de trois parties: le type, la valeur et le suivi. Le module 'sys', expose l'exception actuelle dans trois variables parallèles, exc_type, exc_value, et exc_traceback, la fonction sys.exc_info () renvoie un Tuple de ces trois parties, et l'instruction 'raise' a une forme à trois arguments acceptant ces trois parties. La manipulation des exceptions nécessite souvent de passer ces trois choses en parallèle, ce qui peut être fastidieux et sujette aux erreurs. En outre, l'instruction "except" ne peut donner accès qu'à la valeur, pas au traceback. Ajout de l'attribut " traceback " à l'exception valeurs rend toutes les informations d'exception accessibles à partir d'un seul endroit.
Comparaison avec C #:
Les exceptions en C # contiennent une propriété 'InnerException' en lecture seule qui peut pointer vers une autre exception. Sa documentation [10] indique que "Lorsqu'une exception X est levée comme résultat direct d'une exception Y précédente, la propriété InnerException de X doit contenir une référence à Y." Cette propriété n'est pas définie par la VM automatiquement; tous les constructeurs d'exceptions acceptent plutôt un argument 'innerException' facultatif pour la définir explicitement. La cause ' 'attribut remplit le même but que InnerException, mais ce PEP propose une nouvelle forme de' lever 'plutôt que d'étendre les constructeurs de toutes les exceptions. C # fournit également une méthode GetBaseException qui saute directement à la fin de la Chaîne InnerException; ce PEP ne propose aucun analogue.
Notez également que Java, Ruby et Perl 5 ne supportent pas ce type de chose non plus. Citant encore:
Comme pour les autres langages, Java et Ruby les deux rejettent l'exception d'origine lorsqu'une autre exception se produit dans un 'catch'/'rescue' ou 'finalement'/' La clause assure ". Perl 5 manque de gestion intégrée des exceptions structurées. Pour Perl 6, le numéro RFC 88 [9] propose un mécanisme d'exception qui conserve implicitement les exceptions chaînées dans un tableau nommé @@.
Pour une compatibilité maximale entre Python 2 et 3, vous pouvez utiliser raise_from
dans la bibliothèque six
. https://six.readthedocs.io/#six.raise_from . Voici votre exemple (légèrement modifié pour plus de clarté):
import six
try:
ProcessFile(filePath)
except Exception as e:
six.raise_from(IOError('Failed to process file ' + repr(filePath)), e)
Vous pouvez utiliser ma classe CausedException pour enchaîner les exceptions dans Python 2.x (et même dans Python 3 cela peut être utile dans si vous voulez donner plus d'une exception interceptée comme cause à une nouvelle exception levée. Peut-être que cela peut vous aider.
En supposant:
raise ... from
Solution)vous pouvez utiliser une solution simple à partir de la documentation 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
Le résultat:
An exception flew by!
Traceback (most recent call last):
File "<stdin>", line 2, in ?
NameError: HiThere
Il semble que l'élément clé soit le mot-clé "augmenter" simplifié qui est autonome. Cela relèvera l'exception dans le bloc except.
Vous pourriez peut-être saisir les informations pertinentes et les transmettre? Je pense à quelque chose comme:
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