You could do what you want in the exception’s __new__ or __init__ method, which is usually but not always executed right before the exception is thrown. Your solution of creating a function that performs the side effect and then raises the exception also works. Personally I might prefer that, as it is a bit of a code smell to have side effects in the constructor.
... print("will be printed just before the exception is raised")
will be printed just before the exception is raised
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
ZeroDivisionError: division by zero
It works if you call the method manually…but my idea was to provide a call directly from raise…
Or better; raise keyword calls the dunder method __throw__ (or __finally__) by default so that it always executes something before throwing the exception, and if the method is not defined, it just throws the exception…
# Does works
# Doesn't works
I don’t think this is a common enough need to warrant changes to the standard Exception class. The workarounds I am suggesting are ways to do what you want in your own code, without requiring language changes.
Just manually include the necessary code before raising the exception:
print("Need to do this before raising")
If it needs to happen whenever an exception reaches a particular part of the code, have that code catch the exception, do the work and re-raise:
except MyException as e:
print("Doing something before the stack trace shows up")
If it needs to happen whether or not the exception is raised, use finally:
print("this happens even if MyException doesn't occur")
print("if the exception does occur, this happens first")
If it needs to happen whenever the exception is raised, use a function to wrap the process of raising it:
print("Doing something before raising a serious exception")
That won’t work, and is not the suggestion.
The idea is that in normal code, the exception object would only be created immediately before raising it. So if you always need to e.g. write to a file before raising that specific exception, do it as part of the process of creating the exception. Thus, the last idea is:
Do it in the MyException constructor:
def __init__(self, arg):
print("This happens when creating the exception, therefore, before it is raised")
However, this is a code smell, and it would mess up code that creates exceptions to throw later (although that’s probably itself a code smell).
Thanks so much for your suggest, @kknechtel.
In fact, your latest proposal is the solution I was looking for. In fact, since the constructor is not defined in BaseException I can modify the behavior of what happens before when the object itself is constructed.
Thank you all for your support and sorry for the impertinence of my proposal.
You are a great community!
I feel sure I’ve encountered this use case somewhere. I can’t remember where, but I do remember what I did about it.
@kknechtel’s excellent list of suggestions doesn’t include the solution I chose, though, so here you go, in case it fits your needs:
Make a function that runs your extra code and then returns the (either created or passed in) exception.
pass # just a normal exception subclass
def do_extra_things_a() -> Exception:
ex = MyException()
def do_extra_things_b(ex: MyException) -> Exception:
Making do_extra_things_* raise the exception itself doesn’t make it clear at the call site that an exception will be raised.
Making the exception’s constructor do the extra things doesn’t make it clear at the raise statement that the extra things will happen.
Doing the extra things on the line before the raise statement doesn’t make it clear that those extra things are specifically to do with raising the exception, so if someone needs to do the same thing later, they might not remember to include both steps (the extra things, and the raise), or those two steps might get split up during refactoring.
So, by returning the exception instance from the function, it’s made as clear as possible that the two steps belong together and shouldn’t be separated.