Est-il possible de dire s'il y avait une exception une fois que vous êtes dans la clause finally
? Quelque chose comme:
try:
funky code
finally:
if ???:
print('the funky code raised')
Je cherche à rendre quelque chose comme ça plus SEC:
try:
funky code
except HandleThis:
# handle it
raised = True
except DontHandleThis:
raised = True
raise
else:
raised = False
finally:
logger.info('funky code raised %s', raised)
Je n'aime pas que cela nécessite d'attraper une exception, que vous n'avez pas l'intention de gérer, juste pour définir un indicateur.
Puisque certains commentaires demandent moins de "M" dans le MCVE, voici un peu plus d'informations sur le cas d'utilisation. Le problème réel concerne l'escalade des niveaux de journalisation.
logger.exception
dans un bloc except n'est pas utile ici.Par conséquent, le code s'exécute dans un contexte de capture de journal (qui configure des gestionnaires personnalisés pour intercepter les enregistrements de journal) et certaines informations de débogage sont retranscrites rétrospectivement:
try:
with LogCapture() as log:
funky_code() # <-- third party badness
finally:
mylog = mylogger.WARNING if <there was exception> else mylogger.DEBUG
for record in log.captured:
mylog(record.msg, record.args)
raised = True
try:
funky code
raised = False
except HandleThis:
# handle it
finally:
logger.info('funky code raised %s', raised)
Étant donné les informations de base supplémentaires ajoutées à la question sur la sélection d'un niveau de journal, cela semble très facilement adapté au cas d'utilisation prévu:
mylog = WARNING
try:
funky code
mylog = DEBUG
except HandleThis:
# handle it
finally:
mylog(...)
Vous pouvez utiliser un gestionnaire de contexte personnalisé, par exemple:
class DidWeRaise:
__slots__ = ('exception_happened', ) # instances will take less memory
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
# If no exception happened the `exc_type` is None
self.exception_happened = exc_type is not None
Et puis utilisez cela dans le try
:
try:
with DidWeRaise() as error_state:
# funky code
finally:
if error_state.exception_happened:
print('the funky code raised')
C'est toujours une variable supplémentaire mais elle est probablement beaucoup plus facile à réutiliser si vous souhaitez l'utiliser à plusieurs endroits. Et vous n'avez pas besoin de le basculer vous-même.
Dans le cas où vous ne voulez pas du gestionnaire de contexte, j'inverserais la logique du déclencheur et le basculerais seulement au cas où non une exception s'est produite. De cette façon, vous n'avez pas besoin d'un cas except
pour les exceptions que vous ne voulez pas gérer. L'endroit le plus approprié serait la clause else
qui est entrée au cas où le try
n'a pas levé d'exception:
exception_happened = True
try:
# funky code
except HandleThis:
# handle this kind of exception
else:
exception_happened = False
finally:
if exception_happened:
print('the funky code raised')
Et comme déjà indiqué au lieu d'avoir une variable "bascule", vous pouvez la remplacer (dans ce cas) par la fonction de journalisation souhaitée:
mylog = mylogger.WARNING
try:
with LogCapture() as log:
funky_code()
except HandleThis:
# handle this kind of exception
else:
# In case absolutely no exception was thrown in the try we can log on debug level
mylog = mylogger.DEBUG
finally:
for record in log.captured:
mylog(record.msg, record.args)
Bien sûr, cela fonctionnerait aussi si vous le mettez à la fin de votre try
(comme d'autres réponses suggérées ici) mais je préfère la clause else
car elle a plus de sens ("ce code est destiné à exécuter uniquement s'il n'y avait pas d'exception dans le bloc try
") et peut être plus facile à gérer à long terme. Bien qu'il soit toujours plus à gérer que le gestionnaire de contexte, car la variable est définie et basculée à différents endroits.
sys.exc_info
(fonctionne uniquement pour les exceptions non gérées)La dernière approche que je veux mentionner n'est probablement pas utile pour vous mais peut-être utile pour les futurs lecteurs qui veulent seulement savoir s'il y a une exception non gérée (une exception qui n'était pas pris dans un bloc except
ou a été soulevé dans un bloc except
). Dans ce cas, vous pouvez utiliser sys.exc_info
:
import sys
try:
# funky code
except HandleThis:
pass
finally:
if sys.exc_info()[0] is not None:
# only entered if there's an *unhandled* exception, e.g. NOT a HandleThis exception
print('funky code raised')
D'accord, donc à quoi cela ressemble, vous voulez juste modifier votre gestionnaire de contexte existant ou utiliser une approche similaire: logbook
a en fait quelque chose appelé FingersCrossedHandler
qui ferait exactement ce que vous voulez. Mais vous pouvez le faire vous-même, comme:
@contextmanager
def LogCapture():
# your existing buffer code here
level = logging.WARN
try:
yield
except UselessException:
level = logging.DEBUG
raise # Or don't, if you just want it to go away
finally:
# emit logs here
Réponse d'origine
Vous y pensez un peu de côté.
Vous faites avez l'intention de gérer l'exception - vous la gérez en définissant un indicateur. Peut-être que vous ne vous souciez de rien d'autre (ce qui semble être une mauvaise idée), mais si vous vous souciez de faire quelque chose lorsque an exception est levée, alors vous voulez être explicite à ce sujet.
Le fait que vous définissiez une variable, mais que vous souhaitez que l'exception continue, signifie que ce que vous vraiment voulez, c'est lever votre propre exception spécifique, à partir de l'exception qui a été levée:
class MyPkgException(Exception): pass
class MyError(PyPkgException): pass # If there's another exception type, you can also inherit from that
def do_the_badness():
try:
raise FileNotFoundError('Or some other code that raises an error')
except FileNotFoundError as e:
raise MyError('File was not found, doh!') from e
finally:
do_some_cleanup()
try:
do_the_badness()
except MyError as e:
print('The error? Yeah, it happened')
Cela résout:
MyPkgException
pour intercepter toutes vos exceptions afin qu'il puisse consigner quelque chose et quitter avec un statut Nice au lieu d'une trace de pile laideVous pouvez facilement affecter votre exception interceptée à une variable et l'utiliser dans le bloc finally, par exemple:
>>> x = 1
>>> error = None
>>> try:
... x.foo()
... except Exception as e:
... error = e
... finally:
... if error is not None:
... print(error)
...
'int' object has no attribute 'foo'