web-dev-qa-db-fra.com

Relancez l'exception Python et conservez la trace de la pile

J'essaie d'attraper une exception dans un thread et de la relancer dans le thread principal:

import threading
import sys

class FailingThread(threading.Thread):
    def run(self):
        try:
            raise ValueError('x')
        except ValueError:
            self.exc_info = sys.exc_info()

failingThread = FailingThread()
failingThread.start()
failingThread.join()

print failingThread.exc_info
raise failingThread.exc_info[1]

Cela fonctionne fondamentalement et donne la sortie suivante:

(<type 'exceptions.ValueError'>, ValueError('x',), <traceback object at 0x1004cc320>)
Traceback (most recent call last):
  File "test.py", line 16, in <module>
    raise failingThread.exc_info[1]

Toutefois, la source de l'exception pointe vers la ligne 16, où la nouvelle augmentation a eu lieu. L'exception originale provient de la ligne 7. Comment dois-je modifier le thread main pour que le résultat soit comme suit:

Traceback (most recent call last):
  File "test.py", line 7, in <module>
52
roskakori

En Python 2, vous devez utiliser les trois arguments pour soulever:

raise failingThread.exc_info[0], failingThread.exc_info[1], failingThread.exc_info[2]

le passage de l'objet traceback en tant que troisième argument préserve la pile.

De help('raise'):

Si un troisième objet est présent et non pas None, il doit s'agir d'un traceback object (voir la section La hiérarchie des types standard), et il s’agit de substitué à la place de l'emplacement actuel en tant qu'endroit où le Il y a une exception. Si le troisième objet est présent et pas un traceback object ou None, une exception TypeError est déclenchée. Le La forme à trois expressions de raise est utile pour relancer une exception de manière transparente dans une clause except, mais raise sans expression devrait être préféré si l’exception à ré-augmenter était le plus exception récemment active dans la portée actuelle.

Dans ce cas particulier, vous ne pouvez pas utiliser la version sans expression.

Pour Python 3 (selon les commentaires):

raise failingThread.exc_info[1].with_traceback(failingThread.exc_info[2])

ou vous pouvez simplement chaîner les exceptions en utilisant raise ... from ..., mais cela soulève une exception chaînée avec le contexte d'origine attaché dans l'attribut cause et cela peut être ou ne pas être ce que vous voulez.

51
Duncan

Cet extrait de code fonctionne à la fois en python 2 et 3:

      1 try:
----> 2     raise KeyError('Default key error message')
      3 except KeyError as e:
      4     e.args = ('Custom message when get re-raised',) #The comma is not a typo, it's there to indicate that we're replacing the Tuple that e.args pointing to with another Tuple that contain the custom message.
      5     raise
1
Steven Than

Pourriez-vous l'écrire un peu comme ceci:

try:
    raise ValueError('x')
except ValueError as ex:
    self.exc_info = ex

et ensuite utiliser le stacktrace de l'exception?

0
Ivaylo Petrov