My conjecture is that if you feel a need for such a helper, you're probably catching too many exceptions in the first place. What useful action can you take on an exception? You either know the cause of the error from the context (that is, you expect the exception) and can provide a reasonable fall-back strategy, then your generic handler won't help. Or you don't know what to do with the exception, then you should simply let it bubble up until somebody eventually will be able to act upon it in a reasonable way. As a last resort, you might have a “catch all” handler in your main function that says “sorry”, logs the exception and terminates the program. For many unexpected errors, this is the appropriate reaction.
Python is a little different from most other languages I know in that it encourages you to use exceptions much more as a form of ordinary control flow but I don't see how that would change the overall picture. Just as you don't have a generic helper function to handle return values, you probably don't want a generic exception handler.
Let me illustrate this with some example.
This is a reasonable use of catching an exception that can't really be automated away.
def get_foo_factor(fallback=10):
try:
return int(os.getenv('FOO_FACTOR'))
except TypeError | ValueError:
# Environment variable not set or not an integer.
return fallback
This one is completely ridiculous.
def append_to_file(filename, message):
try:
with open(filename, 'a') as ostr:
print(message, file=ostr)
except TypeError:
print("Not a string?", file=sys.stderr)
except AttributeError:
print("This should never happen!", file=sys.stderr)
If your code has a lot of such except
cascades, the best refactoring would probably be to simply delete them. For those few that remain, you'd probably find that your automated handler won't be of much use. The language already has a specialized syntax for making exception handling as convenient as possible, after all.
The only use-case for a handler I can see is if you're interfacing with a library that throws various exceptions and you want to translate them into your own scheme. In this case, something like the following might be appropriate.
def translate_libfoo_exception(exception):
try:
raise exception
except FooErrorOne as e:
return MyError(e)
except FooErrorTwo as e:
return MyOtherError(e)
except FooErrorThree as e:
return MyOtherError(e)
except FooError as e:
return MyGenericError(e)
except Exception as e:
return e
It could then be used like this.
try:
foo.bar()
except FooErrorThree:
# Needs special handling
pass
except Exception as e:
# Cannot be handled, translate and re-raise
raise translate_libfoo_exception(e)