Let's say I have a special exception that does some neat and wonderful things - Solving world hunger, good will toward men, logging, etc:
class SpecialException(Exception):
# Does something really neat here.
pass
Now let's say that an exception may be raised, but we don't know what type of exception we'll encounter:
def crashAndBurn():
try:
import random
choice = random.choice([1,2])
if choice == 1:
int('asdf') # Can't cast string to int: ValueError.
else:
x # Variable `x` doesn't exist: NameError.
except Exception as e:
# Code to wrap `e` into `SpecialException` class
raise e
When that unknown type of exception is raised, we want to catch it, wrap it in our SpecialException
class, and raise it so it can be caught either by the original type of exception thrown, or by catching SpecialException
:
try:
crashAndBurn()
except ValueError as e:
print('This time it was a ValueError, and we want to handle this differently for some reason')
except SpecialException as e:
print('Handle this more generically')
Any recommendations on how to (reasonably) solve this?
In summary, we need:
- Ability to catch exception originally raised, and ability to catch
SpecialException
- Retain traceback for easy debugging
- Contain the original exception's error message
What I've Tried:
Tried using raise SpecialException from e
. While we are able to view the error message and traceback from the originally raised exception, we are no longer able to catch it by the type of exception originally thrown... Eg: We can catch SpecialException
, but can't catch ValueError
:
def crashAndBurn():
try:
int('asdf') # ValueError
except Exception as e:
raise SpecialException('Special Exception Encountered').with_traceback(e.__traceback__) from e
try:
crashAndBurn()
except ValueError as e:
print('This will never be encountered')
except SpecialException as e:
print('This will be encountered, when we wanted it to be handled above')
The closest we've gotten technically fulfills our needs, however:
- While the exception raised can be caught as either
SpecialException
orValueError
, it's actually raised as another, one-time use class:DynamicSpecialException
- It's really gross and seemingly very un-pythonic
def crashAndBurn():
try:
int('asdf') # ValueError
except Exception as e:
class DynamicSpecialException(SpecialException, e.__class__):
pass # I feel dirty
raise DynamicSpecialException('Special Exception Encountered').with_traceback(e.__traceback__)
try:
crashAndBurn()
except ValueError as e:
print('Caught as a ValueError!')
try:
crashAndBurn()
except SpecialException as e:
print('Caught as SpecialException!')
What I was really expecting to find was something similar to raise e.extend(SpecialException)
or raise SpecialException.from(e)
- rather than this rabbit hole I've seemingly wiggled my way down today! :)