Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python - check if currently handling an exception

How to check if the current code is being executed while processing an exception? By 'while processing an exception' I mean within an except block, functions called from it, or if catching a specific exception type, the optional issubclass override within the exception's metaclass.

def sth():
    handling_exception = # ???
    if handling_exception:
        print 'handling an exception'
    else:
        print 'not handling an exception'


try:
    1 / 0
except:
    sth()  # prints 'handling an exception'

sth()  # prints 'not handling an exception'

Checking sys.exc_info() is not enough because exceptions tend to remain there - they are not cleared automatically. And enforcing sys.exc_clear() is impractical.

like image 572
Michał Pawłowski Avatar asked Aug 10 '15 13:08

Michał Pawłowski


1 Answers

In Python 2, sys.exc_info() is cleared when you exit the frame where it was caught. So if you put the try..except in a function, exiting the function clears sys.exc_info(). In Python 3, the information is cleared when you exit the exception handler.

As such it can be used to detect if an exception was raised in this frame or any calling frame. If you need to somehow detect if an exception is being handled within the same frame as the try..except you can call sys.exc_clear() earlier to facilitate that.

There are no other methods of detecting if an exception was raised and handled.

Demo in Python 2:

>>> import sys
>>> from __future__ import print_function
>>> def baz():
...     print('Child frame', sys.exc_info())
... 
>>> def foo(clear=False):
...     bar(clear)
...     print('Calling frame', sys.exc_info())
... 
>>> def bar(clear=False):
...     try:
...         raise ValueError('bar')
...     except ValueError:
...         print('Handling an exception', sys.exc_info())
...         baz()
...     if clear: sys.exc_clear()
...     print('Frame that handled exception', sys.exc_info())
...     baz()
... 
>>> def baz():
...     print('Child frame', sys.exc_info())
... 
>>> foo()
Handling an exception (<type 'exceptions.ValueError'>, ValueError('bar',), <traceback object at 0x10075f6c8>)
Child frame (<type 'exceptions.ValueError'>, ValueError('bar',), <traceback object at 0x10075f6c8>)
Frame that handled exception (<type 'exceptions.ValueError'>, ValueError('bar',), <traceback object at 0x10075f6c8>)
Child frame (<type 'exceptions.ValueError'>, ValueError('bar',), <traceback object at 0x10075f6c8>)
Calling frame (None, None, None)
>>> foo(True)
Handling an exception (<type 'exceptions.ValueError'>, ValueError('bar',), <traceback object at 0x10075f290>)
Child frame (<type 'exceptions.ValueError'>, ValueError('bar',), <traceback object at 0x10075f290>)
Frame that handled exception (None, None, None)
Child frame (None, None, None)
Calling frame (None, None, None)

and in Python 3 (same function definitions):

>>> foo()
Handling an exception (<class 'ValueError'>, ValueError('bar',), <traceback object at 0x10f740e88>)
Child frame (<class 'ValueError'>, ValueError('bar',), <traceback object at 0x10f740e88>)
Frame that handled exception (None, None, None)
Child frame (None, None, None)
Calling frame (None, None, None)
like image 131
Martijn Pieters Avatar answered Nov 24 '22 03:11

Martijn Pieters