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)
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.
Lets assume there is a method, and I want this method to be called only at certain conditions of a class variable, and otherwise I want to throw an exception:
def foo(self):
if not self.somecondition:
raise ????
else:
#do the thing it supposed to do
Now I am looking for a correct exception for that. I went through them all at python built-in Exceptions docs and found nothing suitable. The classic valueerror is not quite suitable because there is not necessarily a problem with the values but rather with the method being called in this
circumstances.
What exception should I use then? Do I need to create my own in this case or does python has a built in answer?
Suppose I have a simple function. For example:
def if_a_float(string):
try:
float(string)
except ValueError:
return False
else:
return True
Should I include the Raises: ValueError statement into my docstring or should I avoid it as the error was already handled in the code? Is it done for any error (caught/uncaught)? I do understand that it probably depends on the style, so let's say I am using the Google Docstring style(though I guess it doesn't matter that much)
You should document the exception raised explicitly, as well as those that may be relevant to the interface, as per the Google Style Guidelines (the same document you mention yourself).
This code does not raise an exception explicitly (there is no raise), and you do not need to mention that you are catching one.
Actually, this code cannot even accidentally raise one (you are catching the only line that could) and therefore it would be misleading if you were to document that the if_a_float() was raising a ValueError.
You should only document the exceptions that callers need to be aware of and may want to catch. If the function catches an exception itself and doesn't raise it to the caller, it's an internal implementation detail that callers don't need to be aware of, so it doesn't need to be documented.
According to a given protocol (which I cannot change, only implement), some function initialize_foo() is supposed to be called only once:
def initialize_foo():
"""
...
Note:
You must call this function exactly once.
"""
I would like to recognize a protocol abuse where it is called twice, and raise an exception:
_foo_initialized = False
def initialize_foo():
"""
...
Note:
You must call this function exactly once.
"""
if _foo_initialized:
raise <what>?
...
_foo_initialized = True
The problem is what class's object to raise. Looking at the standard exceptions, I can't find anything to subclass except Exception, which seems too general.
What is the general practice in this case?
I'd use RuntimeError.
It is often used for that sort of stuff, even in the standard library. You can find an example very similar to your use case in the warnings module:
if self._entered:
raise RuntimeError("Cannot enter %r twice" % self)
Another example is in threading:
if self._started.is_set():
raise RuntimeError("threads can only be started once")
You can also consider raising an ad-hoc exception (possibly a subclass of RuntimeError) if that error is supposed to be caught and if you feel that RuntimeError may be ambiguous.
I would recommend you to subclass a warning, instead of having an exception, since I have a feeling that a lot of times you'd rather continue running after this happens.
There is a simple scenario that I seem to encounter quite often: I invoke a function that can raise any number of exceptions. I won't do anything different if it is one exception versus another, I just want to log the exception information and either re-raise the exception or indicate in some other way that something didn't go as planned (such as returning None), otherwise proceed normally. So I use some form of the exception handling shown below.
Please note:
Imagine his code is running in a daemon that processes messages, so it needs to keep running, even if one of the messages causes some kind of exception.
I am aware that there is a rule of thumb that it is not generally advisable to catch a generic Exception because that may hide specfic errors that should be handled differently. (This is true in other languages as well.) This case is different because I don't care what exception is raised, the handling is the same.
Is there a better way?
def my_func(p1):
retval = None
try:
valx = other_func1(p1)
except Exception as ex:
log.error('other_func1 failed. {}: {}'.format(type(ex).__name__, ex))
else:
retval = ...
return retval
Is there a better way?
Doubt it, Python has these built-in Base Exception Classes so creating something on your own is really just being redundant. If you handle everything in the same way, generalizing in your except with Exception is most likely the best way to tackle this.
Small caveat here: Exception isn't the most general you can get, from the documentation:
All built-in, non-system-exiting exceptions are derived from this class. All user-defined exceptions should also be derived from this class.
So, it won't catch all exceptions:
In [4]: try:
...: raise SystemExit
...: except Exception as b:
...: print("Catch All")
To exit: use 'exit', 'quit', or Ctrl-D.
An exception has occurred, use %tb to see the full traceback.
SystemExit
Which, do note, is of course something you should want. A SystemExit should exit. But, if some edge case requires it, to also catch system-exiting exceptions you can use BaseException which is as loose as you can get with exception matching:
In [2]: try:
...: raise SystemExit
...: except BaseException as b:
...: print("Catch All")
Catch All
Use it at your own discretion but, it probably makes zero sense to actually use it, and this case does not seem to require it. I just mentioned it because it is the most general you can get. I believe the way you have done it is more than sufficient.
That looks like a fine way to catch them if you're handling them all the same way. If you want to check what kind of exception was raised, you can use the built-in function type and compare the result to an exception class (for example, one from the list of built-in exception types):
try:
f()
except Exception as ex:
if type(ex)==ValueError:
handle_valueerror()
else:
handle_other_exception()
If you're handling them differently, use except <SpecificExceptionClass>. I'm not sure what I was thinking before.