In python is it true that except Exception as ex or except BaseException as ex is the the same as except: but you get a reference to the exception?
From what I understand BaseException is the newer default catch-all.
Aside from that why would you ever want just an except: clause?
The difference between the three is:
bare except catches everything, including system-exiting things like KeyboardInterrupt;
except Exception[ as ex] will catch any subclass of Exception, which should be all your user-defined exceptions and everything built-in that is non-system-exiting; and
except BaseException[ as ex] will, like bare except, catch absolutely everything.
Generally, I would recommend using 2. (ideally as a fallback, after you have caught specific/"expected" errors), as this allows those system-exiting exceptions to percolate up to the top level. As you say, the as ex part for 2. and 3. lets you inspect the error while handling it.
There is a useful article on "the evils of except" here.
There are several differences, apart from Pokémon exception handling* being a bad idea.
Neither except Exception: nor except BaseException: will catch old-style class exceptions (Python 2 only):
>>> class Foo(): pass
...
>>> try:
... raise Foo()
... except Exception:
... print 'Caught'
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
__main__.Foo: <__main__.Foo instance at 0x10ef566c8>
>>> try:
... raise Foo()
... except BaseException:
... print 'Caught'
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
__main__.Foo: <__main__.Foo instance at 0x10ef56680>
>>> try:
... raise Foo()
... except:
... print 'Caught'
...
Caught
because the old-style object is not derived from BaseException or Exception. This is a good reason to never use custom exceptions that do not derive from Exception, in any case.
Next, there are three exceptions that derive from BaseException, but not from Exception; in most cases you don't want to catch those. SystemExit, KeyboardInterrupt and GeneratorExit are not exceptions you would want to catch in the normal course of exception handling. If you use except BaseException: you do catch these, except Exception will not.
* Pokémon exception handling because you gotta catch em all.
If you truly do not care what the reason or message of the failure was, you can use a bare except:. Sometimes this is useful if you are trying to access some functionality which may or may not be present or working, and if it fails you plan to degrade gracefully to some other code path. In that case, what the error type or string was does not affect what you're going to do.
It's not quite the case, no.
If you have a look at the Python documentation on built-in exceptions (specifically this bit) you see what exceptions inherit from where. If you use raw except: it will catch every exception thrown which even includes KeyboardInterrupt which you almost certainly don't want to catch; the same will happen if you catch BaseException with except BaseException as exp: since all exceptions inherit from it.
If you want to catch all program runtime exceptions it's proper to use except Exception as exp: since it won't catch the type of exceptions that you want to end the program (like KeyboardInterrupt).
Now, people will tell you it's a bad idea to catch all exceptions in this way, and generally they're right; but if for instance you have a program processing a large batch of data you may rightfully want it to not exit in case of an exception. So long as you handle the exception properly (ie, log it and make sure the user sees an exception has occurred) but never just pass; if your program produces errors you're unaware of, it will do strange things indeed!
Aside from that why would you ever want just an except: clause?
Short answer: You don't want that.
Longer answer: Using a bare except: takes away the ability to distinguish between exceptions, and even getting a hand on the exception object is a bit harder. So you normally always use the form except ExceptionType as e:.
Related
I know that I can do:
try:
# do something that may fail
except:
# do this if ANYTHING goes wrong
I can also do this:
try:
# do something that may fail
except IDontLikeYouException:
# say please
except YouAreTooShortException:
# stand on a ladder
But if I want to do the same thing inside two different exceptions, the best I can think of right now is to do this:
try:
# do something that may fail
except IDontLikeYouException:
# say please
except YouAreBeingMeanException:
# say please
Is there any way that I can do something like this (since the action to take in both exceptions is to say please):
try:
# do something that may fail
except IDontLikeYouException, YouAreBeingMeanException:
# say please
Now this really won't work, as it matches the syntax for:
try:
# do something that may fail
except Exception, e:
# say please
So, my effort to catch the two distinct exceptions doesn't exactly come through.
Is there a way to do this?
From Python Documentation:
An except clause may name multiple exceptions as a parenthesized tuple, for example
except (IDontLikeYouException, YouAreBeingMeanException) as e:
pass
Or, for Python 2 only:
except (IDontLikeYouException, YouAreBeingMeanException), e:
pass
Separating the exception from the variable with a comma will still work in Python 2.6 and 2.7, but is now deprecated and does not work in Python 3; now you should be using as.
How do I catch multiple exceptions in one line (except block)
Do this:
try:
may_raise_specific_errors():
except (SpecificErrorOne, SpecificErrorTwo) as error:
handle(error) # might log or have some other default behavior...
The parentheses are required due to older syntax that used the commas to assign the error object to a name. The as keyword is used for the assignment. You can use any name for the error object, I prefer error personally.
Best Practice
To do this in a manner currently and forward compatible with Python, you need to separate the Exceptions with commas and wrap them with parentheses to differentiate from earlier syntax that assigned the exception instance to a variable name by following the Exception type to be caught with a comma.
Here's an example of simple usage:
import sys
try:
mainstuff()
except (KeyboardInterrupt, EOFError): # the parens are necessary
sys.exit(0)
I'm specifying only these exceptions to avoid hiding bugs, which if I encounter I expect the full stack trace from.
This is documented here: https://docs.python.org/tutorial/errors.html
You can assign the exception to a variable, (e is common, but you might prefer a more verbose variable if you have long exception handling or your IDE only highlights selections larger than that, as mine does.) The instance has an args attribute. Here is an example:
import sys
try:
mainstuff()
except (KeyboardInterrupt, EOFError) as err:
print(err)
print(err.args)
sys.exit(0)
Note that in Python 3, the err object falls out of scope when the except block is concluded.
Deprecated
You may see code that assigns the error with a comma. This usage, the only form available in Python 2.5 and earlier, is deprecated, and if you wish your code to be forward compatible in Python 3, you should update the syntax to use the new form:
import sys
try:
mainstuff()
except (KeyboardInterrupt, EOFError), err: # don't do this in Python 2.6+
print err
print err.args
sys.exit(0)
If you see the comma name assignment in your codebase, and you're using Python 2.5 or higher, switch to the new way of doing it so your code remains compatible when you upgrade.
The suppress context manager
The accepted answer is really 4 lines of code, minimum:
try:
do_something()
except (IDontLikeYouException, YouAreBeingMeanException) as e:
pass
The try, except, pass lines can be handled in a single line with the suppress context manager, available in Python 3.4:
from contextlib import suppress
with suppress(IDontLikeYouException, YouAreBeingMeanException):
do_something()
So when you want to pass on certain exceptions, use suppress.
From Python documentation -> 8.3 Handling Exceptions:
A try statement may have more than one except clause, to specify
handlers for different exceptions. At most one handler will be
executed. Handlers only handle exceptions that occur in the
corresponding try clause, not in other handlers of the same try
statement. An except clause may name multiple exceptions as a
parenthesized tuple, for example:
except (RuntimeError, TypeError, NameError):
pass
Note that the parentheses around this tuple are required, because
except ValueError, e: was the syntax used for what is normally
written as except ValueError as e: in modern Python (described
below). The old syntax is still supported for backwards compatibility.
This means except RuntimeError, TypeError is not equivalent to
except (RuntimeError, TypeError): but to except RuntimeError as
TypeError: which is not what you want.
If you frequently use a large number of exceptions, you can pre-define a tuple, so you don't have to re-type them many times.
#This example code is a technique I use in a library that connects with websites to gather data
ConnectErrs = (URLError, SSLError, SocketTimeoutError, BadStatusLine, ConnectionResetError)
def connect(url, data):
#do connection and return some data
return(received_data)
def some_function(var_a, var_b, ...):
try: o = connect(url, data)
except ConnectErrs as e:
#do the recovery stuff
blah #do normal stuff you would do if no exception occurred
NOTES:
If you, also, need to catch other exceptions than those in the
pre-defined tuple, you will need to define another except block.
If you just cannot tolerate a global variable, define it in main()
and pass it around where needed...
One of the way to do this is..
try:
You do your operations here;
......................
except(Exception1[, Exception2[,...ExceptionN]]]):
If there is any exception from the given exception list,
then execute this block.
......................
else:
If there is no exception then execute this block.
and another way is to create method which performs task executed by except block and call it through all of the except block that you write..
try:
You do your operations here;
......................
except Exception1:
functionname(parameterList)
except Exception2:
functionname(parameterList)
except Exception3:
functionname(parameterList)
else:
If there is no exception then execute this block.
def functionname( parameters ):
//your task..
return [expression]
I know that second one is not the best way to do this, but i'm just showing number of ways to do this thing.
As of Python 3.11 you can take advantage of the except* clause that is used to handle multiple exceptions.
PEP-654 introduced a new standard exception type called ExceptionGroup that corresponds to a group of exceptions that are being propagated together. The ExceptionGroup can be handled using a new except* syntax. The * symbol indicates that multiple exceptions can be handled by each except* clause.
For example, you can handle multiple exceptions
try:
raise ExceptionGroup('Example ExceptionGroup', (
TypeError('Example TypeError'),
ValueError('Example ValueError'),
KeyError('Example KeyError'),
AttributeError('Example AttributeError')
))
except* TypeError:
...
except* ValueError as e:
...
except* (KeyError, AttributeError) as e:
...
For more details see PEP-654.
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
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.
I was taking a look at the hierarchy of the built-in python exceptions, and I noticed that StopIteration and GeneratorExit have different base classes:
BaseException
+-- SystemExit
+-- KeyboardInterrupt
+-- GeneratorExit
+-- Exception
+-- StopIteration
+-- StandardError
+-- Warning
Or in code:
>>> GeneratorExit.__bases__
(<type 'exceptions.BaseException'>,)
>>> StopIteration.__bases__
(<type 'exceptions.Exception'>,)
When I go to the specific description of each exception, I can read following:
https://docs.python.org/2/library/exceptions.html#exceptions.GeneratorExit
exception GeneratorExit
Raised when a generator‘s close() method is called. It directly inherits from BaseException instead of StandardError since it is technically not an error.
https://docs.python.org/2/library/exceptions.html#exceptions.StopIteration
exception StopIteration
Raised by an iterator‘s next() method to signal that there are no further values. This is derived from Exception rather than StandardError, since this is not considered an error in its normal application.
Which is not very clear to me. Both are similar in the sense that they do not notify errors, but an "event" to change the flow of the code. So, they are not technically errors, and I understand that they should be separated from the rest of the exceptions... but why is one a subclass of BaseException and the other one a subclass of Exception?.
In general I considered always that Exception subclasses are errors, and when I write a blind try: except: (for instance calling third party code), I always tried to catch Exception, but maybe that is wrong and I should be catching StandardError.
It is quite common to use try: ... except Exception: ... blocks.
If GeneratorExit would inherit from Exception you would get the following issue:
def get_next_element(alist):
for element in alist:
try:
yield element
except BaseException: # except Exception
pass
for element in get_next_element([0,1,2,3,4,5,6,7,8,9]):
if element == 3:
break
else:
print(element)
0
1
2
Exception ignored in: <generator object get_next_element at 0x7fffed7e8360>
RuntimeError: generator ignored GeneratorExit
This example is quite simple but imagine in the try block a more complex operation which, in case of failure, would simply ignore the issue (or print a message) and get to the next iteration.
If you would catch the generic Exception, you would end up preventing the user of your generator from breaking the loop without getting a RuntimeError.
A better explanation is here.
EDIT: answering here as it was too long for a comment.
I'd rather say the opposite. GeneratorExit should inherit from Exception rather than BaseException. When you catch Exception you basically want to catch almost everything. BaseException as PEP-352 states, is for those exceptions which need to be "excepted" in order to allow the user to escape from code that would otherwise catch them. In this way you can, for example, still CTRL-C running code. GeneratorExit falls into that category in order to break loops. An interesting conversation about it on comp.lang.python.
I came here to find the answer myself and found it somewhere else.
There are 3 "special" Exceptions that inherit directly from BaseException not Exception:
SystemExit
KeyboardInterrupt
GeneratorExit
These 3 are conceptually different from "normal exceptions". They are not an error, but an unexpected external event which you probably don't want to catch. You expect to exit. terminate, stop!
SystemExit, and KeyboardInterrupt are obvious. sys.exit() generates a SystemExit, and ctrl-C a KeyboardInterrupt
GeneratorExit kills a generator that has not iterated all the way through when it's being deleted or garbage collected.But only the generator dies not the whole program.
So what about StopIteration? It's not an error either. Well it is sort of. You asked for next and there is no next. And it obviously must be caught. Otherwise any for loop would generate an exit.
It might seem a bit unfair to call it an error because waiting for it is the only way to determine loop end. But to be brutally bureaucratic: You asked for something you couldn't have. That's an error.
I am writing a class in Python and part of the code deals with a server. Therefore I need to deal with exceptions like ExpiredSession or ConnectionError.
Instead of writing exception handling code for every try/except block, I have a single function in the class to deal with the exceptions. something like this (inside the class definition)
def job_a(self):
try:
do something
except Exception as e:
#maybe print the error on screen.
self.exception_handling(e)
def job_b(self):
try:
do something else
except Exception as e:
#maybe print the error on screen.
self.exception_handling(e)
def exception_handling(self,e):
if isInstanceOf(e,ExpiredSession):
#deal with expired session.
self.reconnect()
if isInstanceOf(e,ConnectionError):
#deal with connection error
else:
#other exceptions
I am not sure if this kind of code would cause any problem because I haven't seen code do this. like, possible memory leak? (Now I notice the memory usage grows(though slowly) when I have more and more error/exception and eventually I have to restart the process before it eats all my memories). Not sure this is the cause.
Is it a good practice to pass exceptions to a single function?
This is a good use case for a context manager. You can see some examples of using context managers for error handling here. The contextmanager decorator allows you to write context managers concisely in the form of a single function. Here's a simple example:
class Foo(object):
def meth1(self):
with self.errorHandler():
1/0
def meth2(self):
with self.errorHandler():
2 + ""
def meth3(self):
with self.errorHandler():
# an unhandled ("unexpected") kind of exception
""[3]
#contextlib.contextmanager
def errorHandler(self):
try:
yield
except TypeError:
print "A TypeError occurred"
except ZeroDivisionError:
print "Divide by zero occurred"
Then:
>>> x = Foo()
>>> x.meth1()
Divide by zero occurred
>>> x.meth2()
A TypeError occurred
The with statement allows you to "offload" the error handling into a separate function where you catch the exceptions and do what you like with them. In your "real" functions (i.e., the functions that do the work but may raise the exceptions), you just need a with statement instead of an entire block of complicated try/except statements.
An additional advantage of this approach is that if an unforeseen exception is raised, it will propagate up normally with no extra effort:
>>> x.meth3()
Traceback (most recent call last):
File "<pyshell#394>", line 1, in <module>
x.meth3()
File "<pyshell#389>", line 12, in meth3
""[3]
IndexError: string index out of range
In your proposed solution, on the other hand, the exception is already caught in each function and the actual exception object is passed the handler. If the handler gets an unexpected error, it would have to manually reraise it (and can't even use a bare raise to do so). Using a context manager, unexpected exceptions have their ordinary behavior with no extra work required.