I have just learned about exception chaining in Python. What I see is, that there are two ways - inplicit and explicit. If I don't do anything special and raise caught exception. Py will automatically save info about previous exception (if I understand it correctly to __context__ attribute). If the second exception is not caught I'll get nice stack containing both exceptions. I can also make this explicitly using from keyword. In this case only difference I see is, that instead of using __context__ Py uses __cause__. And stack message changes from During handling of the above exception, another exception occurred: to The above exception was the direct cause of the following exception:. So why is there explicit exception chaining? If I don't do anything, I'll get the same thing.
Related
I have code that raises exceptions from other exceptions so that we can see details about eveything that went wrong. In the example below we include information about what we are processing and what specific thing in the processing went wrong.
def process(thing):
try:
process_widgets(thing)
except Exception as e:
raise CouldNotProcess(thing) from e
def process_widgets(thing):
for widget in get_widgets(thing):
raise CouldNotProcessWidget(widget)
def do_processing():
for thing in things_to_process():
process(thing)
I am trying to change this so that process_widgets can raise a specific type of exception and do_processing can change its behaviour based on this exception. However the raise from in process is masking this exception which makes this impossible. Is there a good to let do_processing know about what went wrong in process_widgets while also doing raise from.
Ideas:
Python 3.11 has exception groups. So perhaps there is a way of adding exceptions to group and catching them with the likely confusing except* syntax.
There is a dirty trick where I do raise e from CouldNoPorcess(thing) to get both the helpful logging.
Apparently internally exception chaining works by adding __cause__ property (forming a linked list) so I could manually look through causes in the top most exception to manually implement behaviour like except* with exception groups.
def raised_from(e, type):
while e is not None:
if isinstance(e, Specific):
return True
e = e.__cause__
return False
...
try:
do_processing()
except CouldNotProcess as e:
if raised_from(e, CouldNotProcessWidget):
do_stuff()
You see this pattern quite a lot with status_codes from http.
I could use logging rather than adding information to exceptions. This hides information from the exception handling code, but works for logging. I think this is the work around that I'll use at the moment.
It's noticeable that the PEP says that exception chaining isn't quite designed for adding information to exceptions.
Update
python 3.11 has an add_note method and notes property which can be used to add information - which works for some use cases.
For this use case exception groups might be the way to go, though I am concerned that this might be a little confusing.
I have certain scenarios where I need to raise an exception in my Python code. When I looked at certain examples , I see both raise Exception() and raise RuntimeException() being used. When should I use one over the other?
Exception is a superclass for all other normal exception classes. Choosing this class doesn't convey much information to the caller; you should try to use something more specific.
There is no RuntimeException, but there is RuntimeError. Its documentation states:
Raised when an error is detected that doesn’t fall in any of the other categories.
("The other categories" being all of the other builtin exception classes)
Suppose I have the following custom exception.
class CustomException(TypeError):
def __init__(message, code):
super().__init__(f'{code}: message')
self.code = code
How does python know when to catch my exception in the following code?
try:
x = doSomething(a, b, c)
except CustomException:
raise
when I implement the doSomething() function, must it explicitly throw a CustomException in order for it to be caught? Like, for built-in exception classes, code can throw an exception like a KeyError and we don't have to explicitly say raise KeyError whenever we do something with a dictionary.
Any code that raises an exception has done so explicitly, including KeyError. No special handling is needed for custom exceptions versus the built-in types. A try...except can only catch an exception if one has been raised by code executed by any statement inside the try. This includes any further function calls, calls chain together into a callstack.
In the following example
>>> d = {}
>>> d['foo']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'foo'
the KeyError doesn't spring out of nothingness, the Python dict implementation raises that exception explicitly. This may not always be obvious because native code (code implemented in C here) doesn't show up in the Python traceback.
For the d[...] subscription operation the dict_subscript() function calls _PyErr_SetKeyError(key);, a helper function that uses PyErr_SetObject(), the C equivalent of raise to raise the KeyError(key) exception.
Catching exceptions works the same for all exception types, custom exceptions are not special here. When an exception is raised the normal code flow is interrupted, and the callstack is unwound until an active try statement is encountered, and then any exception handlers are tested, in order of definition in the source code, with isinstance(active_exception, ExceptionClassBeingHandled).
If nothing catches the exception, Python continues unwinding the callstack until it reaches the last stack frame, at which point Python would exit with a stack trace if nothing caught the exception.
In Java, getting the message of an exception is as easy as always calling a certain method.
But in Python, it seems to be impossible. Sometimes it works by doing this:
try:
# Code
pass
except Exception as e:
print(e.message)
But sometimes capturing an exception like that ends up by raising another exception because the message attribute doesn't exist. Ironically sad. Trying to control a error produces another one...
Sometimes it works by doing this:
print(e.msg)
But sometimes it also raises missing attribute exception.
Sometimes this works as well:
print(str(e))
But sometimes it prints an empty string so it is simply useless.
I've even heard that it depends on the library you're using, on the concrete Exception implementation. That seems really stupid for me. How can I handle an error for printing what has happened if I never know what attributes does it have for retrieving the error message?
But sometimes it prints an empty string so it is simply useless.
Yeah, that's what happens when someone raises an exception without a message. Blame authors (of the lib you are using) for that.
Generally you can use repr which is supposed to be unambiguous and if not overriden contains at least information about the exception's type:
try:
0/0
except Exception as exc:
print(repr(exc))
raise
If you need whole traceback you can use
import traceback
try:
0/0
except Exception:
print(traceback.format_exc())
raise
The following code terminates abnormally as no object is explicitly thrown. What is thrown by throw statement in the following code?
int main()
{
try{
cout<<"try";
throw ;
}
catch(...){
cout<<"catch";
}
return 0;
}
throw without an argument should only be used inside a catch statement, to rethrow the caught exception object. You code tries to use it outside the catch statement - instead you should pick a type to throw, if in doubt it's not unreasonable to start with std::runtime_error. For more options, see here. You can also throw your own types, but it's usually a good idea to derive them from one of the Standard-library provided types so client code has a better chance at specifying appropriate handling for all logically similar errors, rather than having to catch and handle them separately and being constantly updated for each new possible error.
FWIW, the Standard says in 15.1/9:
If no exception is presently being handled, executing a throw-expression with no operand calls std::terminate().
So very explicitly, the answer to "What is thrown..." is that no throwing is done, and std::terminate is called instead.
So the question is: "What happens when I throw outside a catch block?" The answer to this can be found in its documentation:
Rethrows the currently handled exception. Abandons the execution of the current catch block and passes control to the next matching exception handler (but not to another catch clause after the same try block: its compound-statement is considered to have been 'exited'), reusing the existing exception object: no new objects are made. This form is only allowed when an exception is presently being handled (it calls std::terminate if used otherwise). The catch clause associated with a function-try-block must exit via rethrowing if used on a constructor.
Emphasize mine.