This should cover most of the bases and allows for non-builtin exceptions. Basically, get the exception class name and look up the corresponding class object up from a pre-computed dictionary of classnames to Exception subclasses.
from django.core.exceptions import ImproperlyConfigured
class MyCustomExc(Exception):
pass
def raiser(di : dict):
di = di.copy()
cls = None
def get_excs(di : dict):
di_exc = {k:v for k,v in di.items() if type(v) == type and issubclass(v, Exception)}
return di_exc
di_exc = get_excs(vars(__builtins__))
di_exc.update(**get_excs(globals()))
dflt_exc = RuntimeError
errorType = di.pop("errorType", dflt_exc.__name__)
cls = di_exc.get(errorType)
if not cls:
di = di.copy()
di["unknown_exception"] = errorType
cls = dflt_exc
raise cls(di)
#the alternative version which does not raise
return cls, di
inputs = [
{'errorMessage': "name 'foo' is not defined", 'errorType': 'NameError'},
{'errorMessage': "custom stuff", 'errorType': 'MyCustomExc'},
{'errorMessage': "Django stuff", 'errorType': 'ImproperlyConfigured'},
{'errorMessage': "Django stuff", 'errorType': 'UnknownExc'},
]
for inp in inputs:
try:
dummy = raiser(inp)
print(dummy)
except (Exception,) as got:
# breakpoint()
msg = f"{str(inp):60.60} => {repr(got)}"
print(msg)
output:
{'errorMessage': "name 'foo' is not defined", 'errorType': ' => NameError({'errorMessage': "name 'foo' is not defined"})
{'errorMessage': 'custom stuff', 'errorType': 'MyCustomExc'} => MyCustomExc({'errorMessage': 'custom stuff'})
{'errorMessage': 'Django stuff', 'errorType': 'ImproperlyCon => ImproperlyConfigured({'errorMessage': 'Django stuff'})
{'errorMessage': 'Django stuff', 'errorType': 'UnknownExc'} => RuntimeError({'errorMessage': 'Django stuff', 'unknown_exception': 'UnknownExc'})
One thing I would probably do however is to just get the bits you need from the function rather than raise-ing in it (that way the stacktrace shows the actual location of your call, not raiser
). I would also pre-compute the exceptions dictionary from builtins and globals and pass it into raiser
rather than re-computing each time.
result = getResult(foo)
cls_exc, exc_data = raiser(result)
if cls_exc:
raise cls_exc(exc_data)
The conditional allows handling when you don't get an error.