Many years ago I wrote about some things that irritated me about Python 2's
socket
module . One of my complaints was that many instances of
socket.error
should actually be
IOError
or
OSError
instead of a separate type, because that's what they really were. Today I was reading AdamW’s Debugging Adventures: Python 3 Porting 201 ( via ), where I discovered in a passing mention that in Python 3,
socket.error
is a deprecated alias of
OSError
.
(Well, from Python 3.3 onwards, due to PEP 3151 .)
On the one hand, this is a change that I cautiously approve of. Many socket errors are just operating system errors, especially on Unix. On the other hand, in some ways this makes
socket.herror
and
socket.gaierror
feel worse. Both of these violate the rule of leaving IOError and OSError alone , because they are subclasses of
OSError
that do not have authentic
errno
values and are not quite genuine OS errors in the same way (they are errors from the C library, but they don't come from
errno
). They do have
errno
and
strerror
fields, which is something, but then I think all subclasses of
OSError
do these days.
Somewhat to my surprise, when I looked at the Python 2
socket
module I discovered that
socket.error
is now a subclass of
IOError
(since Python 2.6, which in practice means 'on any system with Python 2 that you actually want to use'). Python 2 also has the same issue where
socket.herror
and
socket.gaierror
are subclasses of
socket.error
but are not real operating system errors.
Unfortunately for my feelings about leaving OSError alone, the current situation in the
socket
module is probably the best pragmatic tradeoff. Since the module has high level interfaces that can fail in multiple ways that result in different types of errors, in practice people want to be able to just catch one overall error and be done with it, which means that
socket.gaierror
really needs to be a subclass of
socket.error
. When you combine this with
socket.error
really being some form of
OSError
, you arrive at the current state of affairs.
I've decided that I don't have a strong opinion on
socket.error
changing from being a subclass of IOError/OSError to being an alias for it. I can imagine Python code that might want to use
try
at a high level, call both socket functions and other OS functions within that high level
try
, and distinguish between the two sources of errors, which is now impossible in Python 3, but I'm not sure that this is a desirable pattern. I don't think I have anything like this in my own Python code, but it's something that I should keep an eye out for as I convert things over to Python 3.
(I do have some Python 2 code that catches both
socket.error
and
EnvironmentError
, but fortunately it treats them the same.)