In light of yesterday's entry , here is one of my new rules for Python modules, especially extension modules written in C:
If you're going to raise an exception because a system call failed, don't make up a new exception for it. Raise either
IOErrororOSErrorthemselves.
Every deviation from this causes annoyances for Python programmers. This is especially visible in the situation with signals and
EINTR
, where I really do not want to be rewriting the same code over and over again with different exception classes (or worse, writing different code because you decided to hide
errno
somewhere new in your exception).
A corollary to this is that if for some reason you absolutely have to raise a new exception, you should make it duck typing compatible with
OSError
(see here for what that requires). But really, you don't have to, especially because Python code can raise real
OSError
exceptions.
(Please do not get creative with the error message for
OSError
instances that you create yourself in Python code. If it is not exactly the
strerror()
of the
errno
, you are doing it wrong.)
Extension modules written in C have no excuse, because the Python C API makes it very easy to do the right thing (and reusing
OSError
saves you from having to create your own C level exception object). Alas, the standard Python exception modules are just overflowing with bad examples, where people make up a new exception instead of reusing
OSError
or
IOError
.
(This is sort of the reverse corollary of not using
IOError
for things that aren't system call errors .)
Sidebar: to wrap or not to wrap
I'm aware that this looks inconsistent with my views on not wrapping exceptions . The difference to me is if you are essentially wrapping a system call and directly exposing
errno
, you should be using the standard way of doing that, which is an EnvironmentError exception (or should be). If the system call's failure is just an internal implementation detail, you should wrap the problem up in your own exception to expose the high level issue.
(This is the difference between 'cannot resolve hostname, nameserver not available' and '
sendto()
failed'. A DNS module that returned the latter would be technically correct but not useful.)