"Another mistake that many libraries make is that they make a generic 'FooLibraryException' and subclass all their exceptions from there, instead of from where it makes sense (IoError, KeyError, MemoryError ...)"
Actually, this isn't black and white. When I need to create more than one exception class for a library, I prefer to avoid subclassing standard exceptions to prevent that kind of confusing situation:
If my_library was returning a IOError, you would not know if the problem came from your code or the library code. Moreover, this would leak implementation detail.
Of course, you can always get around this by wrapping each library call within a try/catch block, but that does not scale well if you make many calls interspersed with your code.
Well, at least from a maintainability perspective it's more clear to do
try:
afile = open('myfile.txt')
content = afile.read()
except XXX:
(handle exception)
else:
(continue here using content)
If you want to capture the exceptions from a certain call. Otherwise, my_library.do_something might start raising a IOError somewhere in its bowels later on, and you have a confusing debug process.
When you find that you have a lot of similar or even repeated try/catch blocks, using 'with' context managers might be a good idea.
My point was that an IOError is an IOError, wether it's a MongoDBIOError or a MySQLIOError. Yes, you leak some implementation detail (namely, that I/O is used) but do do useful diagnostics you need some (abstract) implementation knowledge... network error: is your router plugged in? io error: is the disk full? keyerror: implementation bug? and so on.
Hm maybe this would be a case for multi-inheritance exceptions? MongoDBIOException is both and IOException as a MongoDBException. Don't even know if it's possible, or wise :)
Actually, this isn't black and white. When I need to create more than one exception class for a library, I prefer to avoid subclassing standard exceptions to prevent that kind of confusing situation:
If my_library was returning a IOError, you would not know if the problem came from your code or the library code. Moreover, this would leak implementation detail.Of course, you can always get around this by wrapping each library call within a try/catch block, but that does not scale well if you make many calls interspersed with your code.