Last active
January 3, 2021 23:16
-
-
Save outofmbufs/906178bab109ac7e4cab210c27687ea7 to your computer and use it in GitHub Desktop.
Python decorator to convert exception into function return value
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import functools | |
__IGX_SENTINEL = object() # sentinel for a "default argument" see below | |
def IgnoreExceptionsDecorator(excepts, *, exception_result=__IGX_SENTINEL): | |
"""Decorator - catch exceptions and convert to a return value. | |
'excepts' - exceptions (as a tuple, or a single exception) to catch | |
'exception_result' - if specified, the return value to use when | |
any of the 'excepts' happens. | |
If no exception_result is given, then the exception info itself | |
is returned if one of the 'excepts' occurs. | |
""" | |
# determine whether an explicit exception return was given or not... | |
return_the_exception = exception_result is __IGX_SENTINEL | |
# _deco is the outer/callable decorator object that will be called | |
# to decorate the function, and will (at that time) return the inner | |
# _ig() function which represents the wrapped/decorated function. | |
def _deco(f): | |
@functools.wraps(f) | |
def _ig(*args, **kwargs): | |
try: | |
return f(*args, **kwargs) | |
except excepts as e: | |
return e if return_the_exception else exception_result | |
return _ig | |
return _deco | |
# test code and example of how to use | |
if __name__ == "__main__": | |
import unittest | |
import math | |
class Foo: | |
pass | |
@IgnoreExceptionsDecorator(AttributeError, exception_result=42) | |
def return_bar(x): | |
return x.bar | |
@IgnoreExceptionsDecorator(AttributeError) | |
def return_e(x): | |
return x.bar | |
@IgnoreExceptionsDecorator((TypeError, ZeroDivisionError), | |
exception_result=math.nan) | |
def divideby(a, b): | |
return a/b | |
# the point here is to show that uncaught exceptions still work | |
@IgnoreExceptionsDecorator(AttributeError) | |
def div0(): | |
return 1 / 0 | |
class TestMethods(unittest.TestCase): | |
def test_1(self): | |
f = Foo() | |
self.assertEqual(return_bar(f), 42) | |
def test_2(self): | |
f = Foo() | |
f.bar = 17 | |
self.assertEqual(return_bar(f), 17) | |
def test_3(self): | |
f = Foo() | |
e = return_e(f) | |
self.assertTrue(isinstance(e, AttributeError)) | |
def test_4(self): | |
f = Foo() | |
f.bar = 17 | |
e = return_e(f) | |
self.assertEqual(e, 17) | |
def test_5(self): | |
self.assertTrue(math.isnan(divideby(6, 0))) | |
def test_6(self): | |
self.assertTrue(math.isnan(divideby(6, '!'))) | |
def test_7(self): | |
self.assertRaises(ZeroDivisionError, div0) | |
unittest.main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment