Are there any legitimate situations where exceptions are not thrown and caught, but simply returned from a method and then passed around as error objects?
If it is never thrown then it's not an exception. It is an object derived
from an Exception class, though it does not follow the behavior. Calling it an Exception is purely semantics at this point, but I see nothing wrong with not throwing it. From my perspective, not throwing an exception is the exact same thing as an inner-function catching it and preventing it from propagating.
Is it valid for a function to return exception objects?
Absolutely. Here is a short list of examples where this may be appropriate:
- An exception factory.
- A context object that reports if there was a previous error as a ready to use exception.
- A function that keeps a previously caught exception.
- A third-party API that creates an exception of an inner type.
Is not throwing it bad?
Throwing an exception is kind of like: "Go to jail. Do not pass Go!" in the board game Monopoly. It tells a compiler to jump over all source code up to the catch without executing any of that source code. It doesn't have anything to do with errors, reporting or stopping bugs. I like to think of throwing an exception as a "super return" statement for a function. It returns execution of the code to somewhere much higher up than the original caller.
The important thing here is to understand that the true value of exceptions is in the try/catch
pattern, and not the instantiation of the exception object. The exception object is just a state message.
In your question you seem to be confusing the usage of these two things: a jumping to a handler of the exception, and the error state the exception represents. Just because you took an error state and wrapped it in an exception does not mean you are following the try/catch
pattern or its benefits.
The recommendation in Python is to use exceptions to indicate failure. This is true even if you expect failure on a regular basis.
Look at it from the perspective of the caller of your code:
my_status = get_abe_status(my_url)
What if we return None? If the caller doesn't specifically handle the case that get_abe_status failed, it will simply try to continue on with my_stats being None. That may produce a difficult to diagnose bug later on. Even if you do check for None, this code has no clue why get_abe_status() failed.
But what if we raise an exception? If the caller doesn't specifically handle the case, the exception will propagate upward eventually hitting the default exception handler. That may not be the what you want, but its better then introducing a subtle bug elsewhere in the program. Additionally, the exception gives information about what went wrong which is lost in the first version.
From the caller's perspective, its simply more convenient to get an exception than a return value. And that's the python style, to use exceptions to indicate failure conditions not return values.
Some will take a different perspective and argue that you should only use exceptions for cases you never really expect to happen. They argue that normally running running could should not raise any exceptions. One reason that is given for this is that exceptions are grossly inefficient, but that's not actually true for Python.
A couple of points on your code:
try:
hits[0]
except IndexError:
raise NotFoundError("No mentions found.")
That's a really confusing way to check for an empty list. Don't induce an exception just to check something. Use an if.
# say we expect four hits...
if len(hits) != 4:
raise Warning("An unexpected number of hits.")
logger.warning("An unexpected number of hits.")
You do realize that the logger.warning line will never run right?
Best Answer
Throwing an exception is simply an additional way of making a method return a value. The caller can check for a return value just as easily as catch an exception and check that. Therefore, deciding between
throw
andreturn
requires other criteria.Throwing exceptions should often be avoided if it endangers the efficiency of your program (constructing an exception object and unwinding the call stack is much more work for the computer than just pushing a value onto it). But if the purpose of your method is to upload a file, then the bottleneck is always going to be the network and file system I/O, so it's pointless to optimize the return method.
It's also a bad idea to throw exceptions for what should be a simple control flow (e.g. when a search succeeds by finding its value), because that violates the expectations of API users. But a method failing to fulfill its purpose is an exceptional case (or at least it should be), so I see no reason for not throwing an exception. And if you do that, you might just as well make it a custom, more informative exception (but it's a good idea to make it a subclass of a standard, more general exception like
IOException
).