J'écris un module et je souhaite avoir une hiérarchie d'exceptions unifiée pour les exceptions qu'il peut soulever (par exemple, hériter d'une FooError
classe abstraite pour toutes les foo
exceptions spécifiques du module). Cela permet aux utilisateurs du module d'attraper ces exceptions particulières et de les gérer distinctement, si nécessaire. Mais la plupart des exceptions levées à partir du module sont déclenchées à cause d'une autre exception; par exemple, échec d'une tâche à cause d'une erreur OSError sur un fichier.
Ce dont j'ai besoin, c'est «d'envelopper» l'exception capturée de telle sorte qu'elle ait un type et un message différents , de sorte que les informations soient disponibles plus haut dans la hiérarchie de propagation par tout ce qui intercepte l'exception. Mais je ne veux pas perdre le type, le message et la trace de pile existants; ce sont toutes des informations utiles pour quelqu'un qui essaie de déboguer le problème. Un gestionnaire d'exceptions de niveau supérieur n'est pas bon, car j'essaie de décorer l'exception avant qu'elle ne monte plus haut dans la pile de propagation, et le gestionnaire de niveau supérieur est trop tard.
Ceci est en partie résolu en dérivant les foo
types d'exceptions spécifiques de mon module à partir du type existant (par exemple class FooPermissionError(OSError, FooError)
), mais cela ne facilite pas l'encapsulation de l'instance d'exception existante dans un nouveau type, ni ne modifie le message.
Le PEP 3134 de Python «Chaînage d'exceptions et traçages intégrés» traite d'un changement accepté dans Python 3.0 pour le «chaînage» des objets d'exception, pour indiquer qu'une nouvelle exception a été déclenchée lors de la gestion d'une exception existante.
Ce que j'essaie de faire est lié: j'en ai besoin aussi pour les versions antérieures de Python, et j'en ai besoin non pas pour le chaînage, mais uniquement pour le polymorphisme. Quel est le bon moyen de le faire?
la source
except Exception as e
->raise type(e), type(e)(e.message + custom_message), sys.exc_info()[2]
-> cette solution provient d'une autre question SO . Ce n'est pas joli mais fonctionnel.Réponses:
Python 3 a introduit le chaînage d'exceptions (comme décrit dans PEP 3134 ). Cela permet, lors de la levée d'une exception, de citer une exception existante comme «cause»:
L'exception interceptée devient ainsi une partie de (est la «cause de») la nouvelle exception, et est disponible pour tout code qui intercepte la nouvelle exception.
En utilisant cette fonctionnalité, l'
__cause__
attribut est défini. Le gestionnaire d'exceptions intégré sait également comment signaler la «cause» et le «contexte» de l'exception avec le traçage.Dans Python 2 , il semble que ce cas d'utilisation n'ait pas de bonne réponse (comme décrit par Ian Bicking et Ned Batchelder ). Bummer.
la source
try: return 2 / 0 except ZeroDivisionError as e: raise ValueError(e)
Vous pouvez utiliser sys.exc_info () pour obtenir le traçage, et lever votre nouvelle exception avec ledit traçage (comme le PEP le mentionne). Si vous souhaitez conserver l'ancien type et le message, vous pouvez le faire sur l'exception, mais ce n'est utile que si ce qui intercepte votre exception le recherche.
Par exemple
Bien sûr, ce n'est vraiment pas si utile. Si c'était le cas, nous n'aurions pas besoin de ce PEP. Je ne recommanderais pas de le faire.
la source
sys.exc_info()
@Devin. Il dit: «L'affectation de la valeur de retour de trace à une variable locale dans une fonction qui gère une exception provoquera une référence circulaire». Cependant, une note suivante indique que depuis Python 2.2, le cycle peut être nettoyé, mais il est plus efficace de l'éviter.Vous pouvez créer votre propre type d'exception qui étend l'exception que vous avez interceptée.
Mais la plupart du temps, je pense qu'il serait plus simple d'attraper l'exception, de la gérer et de
raise
l'exception d'origine (et de conserver le retraçage) ouraise NewException()
. Si j'appelais votre code et que je recevais l'une de vos exceptions personnalisées, je m'attendrais à ce que votre code ait déjà géré toute exception que vous avez dû intercepter. Je n'ai donc pas besoin d'y accéder moi-même.Edit: J'ai trouvé cette analyse des moyens de lever votre propre exception et de conserver l'exception d'origine. Pas de jolies solutions.
la source
J'ai également constaté que plusieurs fois, j'ai besoin d'un "emballage" aux erreurs soulevées.
Cela incluait à la fois la portée d'une fonction et n'enveloppait parfois que quelques lignes à l'intérieur d'une fonction.
Créé un wrapper à utiliser a
decorator
etcontext manager
:la mise en oeuvre
Exemples d'utilisation
décorateur
lors de l'appel de la
do
méthode, ne vous inquiétez pasIndexError
, justeMyError
gestionnaire de contexte
à l'intérieur
do2
, dans lecontext manager
, siIndexError
est soulevé, il sera enveloppé et soulevéMyError
la source
La solution la plus simple à vos besoins devrait être la suivante:
De cette façon, vous pouvez imprimer ultérieurement votre message et l'erreur spécifique générée par la fonction de téléchargement
la source