Python: difference between ValueError and Exception? - python

I am trying to understand what is a difference between raising a ValueError and an Exception. I have tried both in the same code (even in the same branch) and the result was the same - I got an error message.
I have made a research on this question on SO, but found no discussion on this. Then I read the documentation of exceptions, and found the following definition of ValueError:
Raised when a built-in operation or function receives an argument that has the right type but an inappropriate value, and the situation is not described by a more precise exception such as IndexError.
So as I understand, an Exception is a more general term, and ValueError can be applied in some specific cases. But since the results of raising both things are the same, I want to understand, what is the practical meaning of differentiating between a ValueError and an Exception. Python version should be here not relevant. Thank you!
EDIT:
Thanks to your answers I got it, what is the difference between both terms in try-exception construct. But how do they differ in case of just raising them, not excepting?
raise Exception('blah')
and
raise ValueError('blah')
Answering to #PeterWood: in both cases I just got the error message "blah", but in one case it was "Exception: blah", and in the second: "ValueError: blah". And I see in this case no practical difference between them both.

ValueError inherits from Exception. You can decide to trap either only ValueError, or Exception, that's what exception inheritance is for.
In this example:
try:
a=12+"xxx"
except Exception:
# exception is trapped (TypeError)
exception is trapped, all exceptions (except BaseException exceptions) are trapped by the except statement.
In this other example:
try:
a=12+"xxx"
except ValueError:
# not trapped
Here, exception is NOT trapped (TypeError is not ValueError and does not inherit)
You generally use specific exceptions to trap only the ones that are likely to occur (best example is IOError when handling files), and leave the rest untrapped. The danger of catching all exceptions is to get a piece of code that does not crash, but does nothing.
(editing the answer in response to your edit:) when you raise an exception: you're creating an instance of Exception which will be filtered out by future except ValueError: statements. the message is different because the representation of the exception (when printed) includes the exception class name.

You said it, ValueError is a specific Exception. A short example :
try:
print int("hello world")
except ValueError:
print "A short description for ValueError"
If you change "hello world" with an int, print int(42), you will not raise the exception.
You can see doc about exceptions here.

To add extra detail onto the above answers, you can chain the except statements in the try clause. So you might first check for ValueError, or another type, then lastly you can check for Exception (anything that wasn't already caught by ValueError).
As mentioned above, ValueError inherits from Exception, so it is a more specific type of Exception. docs: https://docs.python.org/3/library/exceptions.html
Example:
Assuming mycheck() function passes back some exception text, we can access it with the below variable 'e'.
try:
value = mycheck(value)
except ValueError as e:
print(f"'{value}' did not pass mycheck ValueCheck: {e}")
except Exception as e:
print(f"'{value}' did not pass mycheck validation: {e}")
You asked about the difference of raising ValueError versus raising Exception. Both behave similarly. It's a matter of how specific you want to be. It would be recommended generally to raise the most specific type of error in order to provide the most useful error feedback to the user.
If you look at how error trapping can be chained in my example, then you might see how your choice of which type of error to raise would affect the output to the user.

Related

In python 3, re-raise an error with a shorter traceback

I'm trying to create a try-except block that re-raises an arbitrary exception, but only includes the final block in the traceback stack.
Something like this:
import traceback
def my_func(shorten_tracebacks=True):
try:
# Do stuff here
except Exception as e:
if shorten_tracebacks:
raise TheSameTypeOfError, e, traceback.print_exc(limit=1)
else:
raise(e)
Is there a preferred way to do this?
In case it matters, I'm doing this for convenience in debugging certain APIs that are often used in jupyter notebooks---they tend to generate really long stack traces where only the last block is informative. This forces the user to scroll a lot. If you don't want to shorten the traceback, you can always set shorten_tracebacks=False
My preference is to create a new exception without a context (if an exception has a context python will print both the exception and the exception which caused that exception, and the exception which caused that exception, and so on...)
try:
...
except:
raise Exception("Can't ramistat the foo, is foo installed?") from None
Some best practices:
Include relevant debugging information in the exception message.
Use a custom exception type, so callers can catch the new exception.
Catch only the specific error types you're expecting, to let unexpected errors fall through with the extended traceback.
The downside to this approach is that if your exception catching is overly broad, you can end up suppressing useful context which is important to debugging the exception. An alternate pattern might look like this:
try:
...
except Exception as e:
if "ramistat not found" in e.message:
# The ramistat is missing. This is a common kind of error.
# Create a more helpful and shorter message
raise Exception("Can't ramistat the foo, is foo installed?") from None
else:
# Some other kind of problem
raise e
Essentially, you check the exception, and replace it with a custom message if it's a kind of error you know how to deal with. Otherwise, you re-raise the original exception, and let the user figure out what to do.

Raising an exception, Handling it. Only to raise it again; Python

I'm reading the Python Tutorial.
I came across a part that says:
'''
If you need to determine whether an exception was raised but don’t intend to handle it, a simpler form of the raise statement allows you to re-raise the exception:
'''
try:
raise NameError('HiThere')
except NameError:
print('An exception flew by!')
raise
I don't understand why would one raise an exception, handle it and then re-raise it??
If the programmer doesn't want to handle the exception they shouldn't use a try..except statement in the first place??
It helps if you don't think of exceptions as "mistakes" that have to be caught/corrected, and instead think of them as a way of communicating information.
Sometimes when you catch an exception it's expected, or you can find a way around it. Other times, the exception means you can't do the thing you were trying to do, and so you want to raise an exception to your own caller to let them know why it's not going to work out.
When you're in that situation, sometimes you'll catch the lower-level exception that thwarted your plans, and then raise your own exception (maybe with a more specific type) to communicate your own failure to the caller. Sometimes it might work just as well to simply raise the same exception back to the caller. In the situation where you just want the lower level exception to go all the way up to the caller, you might opt to just not catch it, but what if you want to log a message or clean up some piece of internal state before you go down in flames? That's where the except/raise pattern is useful.
begin_complicated_process()
try:
do_risky_thing()
except ShenanigansError:
log("Did someone say shenanigans?!")
unwind_complicated_process()
raise
complete_complicated_process()

How to raise exception within a try/except block properly

right now I have a problem where I want to raise a specific TypeError if there is one. However, what ends up happening is the interpreter sees the first error, and then in the middle of handling it it raises the other one as well saying "During handling of the above exception, another exception occurred:"
this is what I have
def function(dictionary)
try:
value = max(dictionary.values())
except TypeError:
raise TypeError("some error")
I plug in the following into the shell:
function({1:'a', 2:3})
How can I approach this?
If you want to discard the exception context, you can explicitly discard it using from None, e.g.:
try:
value = max(dictionary.values())
except TypeError:
raise TypeError("some error") from None
That said, it's usually best to leave the context in place; the only time you'll see it is if the exception is uncaught and the default logging occurs, or you try to log the exception (e.g. with logger.exception). That additional information is often useful, especially for extremely broad exception types like TypeError and ValueError (where you intend to catch specific known subtypes, and unexpectedly catch one caused in a completely different way).
To be clear, this only works on Python 3, but then, exception context chaining only exists on Python 3; on Python 2, the context is lost automatically.
Since you are raising the exception while handling it, the exception is sent back to the caller function.
If you just want to handle it and print the error and move on with rest of the execution, you can do sth like this
except TypeError as t:
print ("Error", t)

In Python, how do I inspect and then re-raise an exception while maintaining the original call stack?

I've got a situation where I'm catching a specific exception type, inspecting the exception's message to check if it's actually an exception I want to catch, and then re-raising the exception if not:
try:
# do something exception-prone
except FooException as e:
if e.message == 'Something I want to handle':
# handle the exception
else:
raise e
This works fine, with one problem. In the case I re-raise the exception, that exception now occurs at the line I re-raised it (i.e. at raise e), rather than at the location the exception originally occurred. This isn't ideal for debugging, where you want to know where the original exception happened.
Thus my question: is there any way to re-raise or otherwise "pass on" an exception after catching it while maintaining the original exception location?
NOTE: In case you are wondering what the actual situation is: I'm dynamically importing some modules using __import__. I'm catching ImportError to gracefully deal with the case that any of these modules do not exist. However, in the case that any of these modules themselves contain an import statement that raises ImportError, I want those "real" (from the point of view of my application) exceptions to be raised -- and at the original location as far as debugging tools are concerned.
Just do:
raise
instead of raise e. See the tutorial section on raising exceptions, and also the language reference on raise statements:
If no expressions are present, raise re-raises the last exception that was active in the current scope. If no exception is active in the current scope, a TypeError exception is raised indicating that this is an error (if running under IDLE, a Queue.Empty exception is raised instead).

Deciding which exceptions to catch in Python

Suppose that I am using a library X that specifies for example that exception.BaseError is the base class for all exceptions of X.
Now, there is another exception, say X.FooError, which of course inherits from exception.BaseError but is more generalized, let's say that it handles invalid input. Let's suppose there are many other such classes, inherting from BaseError but all for generalized cases.
X
|
BaseError
|
FooError
So I want to then check for invalid input. So which exception should I catch? Of course, catching each individual exception is not possible, so I catch the X.BaseError and then print an error message. Or I can catch the X.FooError specifically but then I miss out all the other error cases.
Is this the standard way of doing it -- catch the base exception? If yes, then why do the other exceptions exist? For the generalized case when we want to catch a specific exception?
Catch only the exceptions you can handle. If you can handle both the base exception and the derived exception then catch both. But make sure to put the derived exception first, since the first exception handler found that matches is the one used.
try:
X.foo()
except X.FooError:
pass
except X.BaseError:
pass
As usual, there is good advice in PEP-8, Style Guide for Python Code:
When catching exceptions, mention specific exceptions whenever possible instead of using a bare except: clause.
There's lots more in there, but it's pointless me reproducing it here.
In this case, I would catch the specifics, were I to handle them differently to a BaseError and the BaseError for those that require more general handling. I would stop well short of catching the built-in Exception, however.
You catch a specfic exception by defining it in the except clause, thus:
try:
#do stuff
except X.FooError:
# handle the error
except (X.AnotherError, x.YetAnotherError), exc:
print 'I'm handling %s' % exc
Notice you can handle multiple exception classes by listing them in a tuple.

Categories