A very picky ‘except’ in Python

George Shuklin
Python Pandemonium
Published in
2 min readApr 24, 2017

I just have fixed a bug which forced me to learn how Python processes exception lists in except clause.

Background

I decided to add to my application different exit codes for different errors. All errors inside application are exceptions. So I wrote a rather innocent code:

sad_table = {
config.ConfigError: 10,
config.NotFoundInConfigError: 11,
osclient.CredNotFound: 12,
glanceclient_exceptions.HTTPNotFound: 50
}
try:
app.run()
except sad_table.keys() as e:
return sad[e.__class__]
except Exception as e:
print("Unhandled exception: %s" % e)
raise

To my amusement all my exceptions were ‘Unhandled’.

Can we use variables to store exceptions?

That was my first question. I assumed if I could write except (Exc1, Exc2):, that means I can put any iterable there.

Simple check that we can do it with list:

l = [ValueError, IndexError]
try:
raise ValueError
except l:
print("Got")

Nah. We can’t. But python documentation insists that we can. They says we should use tuples…

l = (ValueError, IndexError)
try:
raise ValueError
except l:
print("Got")

It works! That’s strange… I’ve tried to check other iterables: generators are N.G., iterators are N.G., sets and lists are N.G. Tuples only.

Python3

In Python 2 that code (with list of exceptions) silently worked wrongly. Python3 gives us a better report: TypeError: catching classes that do not inherit from BaseException is not allowed.

That explains what happened with python2: He really tried to catch not ‘any of exceptions in a list’ but the list object itself. Nevertheless we can’t raise such a thing:

>>> try:
... raise [IOError, ValueError]
... except [IOError, ValueError]:
... print "oh..."
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
TypeError: exceptions must be old-style classes or derived from BaseException, not list

This is rather inconsistent. We can’t raise lists, but we can try to catch them.

Moreover, at this point Python stops been ‘ducktyped’ and starts to require a very specific inheritance of type of the object, not just any iterable, as one may assume. I find this a bit annoying. I understand that python 2 had had too much fluke around exceptions. But python3 has clarified it. Why it catches only from tuple, not from other iterables?

Conclusion

You may catch few exceptions using variable in except, but they should be stored only in tuple. Any other iterable will:

  • Cause silent bug in Python 2
  • Cause TypeError in Python 3

--

--

George Shuklin
Python Pandemonium

I work at Servers.com, most of my stories are about Ansible, Ceph, Python, Openstack and Linux. My hobby is Rust.