[Python-checkins] cpython: PEP 3151 / issue #12555: reworking the OS and IO exception hierarchy.

antoine.pitrou python-checkins at python.org
Wed Oct 12 03:00:49 CEST 2011


http://hg.python.org/cpython/rev/41a1de81ef2b
changeset:   72879:41a1de81ef2b
user:        Antoine Pitrou <solipsis at pitrou.net>
date:        Wed Oct 12 02:54:14 2011 +0200
summary:
  PEP 3151 / issue #12555: reworking the OS and IO exception hierarchy.

files:
  Include/pyerrors.h                  |   44 +-
  Lib/_pyio.py                        |   12 +-
  Lib/multiprocessing/connection.py   |    8 +-
  Lib/test/exception_hierarchy.txt    |   21 +-
  Lib/test/test_concurrent_futures.py |   14 +-
  Lib/test/test_exceptions.py         |   43 +-
  Lib/test/test_http_cookiejar.py     |   11 +-
  Lib/test/test_io.py                 |    6 -
  Lib/test/test_mmap.py               |    3 +-
  Lib/test/test_pep3151.py            |  128 +++
  Lib/test/test_xml_etree.py          |    8 +-
  Lib/urllib/request.py               |    2 +
  Misc/NEWS                           |    2 +
  Modules/_io/_iomodule.c             |   91 +--
  Modules/_io/_iomodule.h             |    9 -
  Modules/_io/bufferedio.c            |    4 +-
  Modules/mmapmodule.c                |   26 +-
  Modules/selectmodule.c              |   29 +-
  Modules/socketmodule.c              |  116 +-
  Objects/exceptions.c                |  559 ++++++++++-----
  Python/errors.c                     |    9 +-
  21 files changed, 690 insertions(+), 455 deletions(-)


diff --git a/Include/pyerrors.h b/Include/pyerrors.h
--- a/Include/pyerrors.h
+++ b/Include/pyerrors.h
@@ -45,18 +45,18 @@
     PyObject *myerrno;
     PyObject *strerror;
     PyObject *filename;
-} PyEnvironmentErrorObject;
+#ifdef MS_WINDOWS
+    PyObject *winerror;
+#endif
+    Py_ssize_t written;   /* only for BlockingIOError, -1 otherwise */
+} PyOSErrorObject;
 
+/* Compatibility typedefs */
+typedef PyOSErrorObject PyEnvironmentErrorObject;
 #ifdef MS_WINDOWS
-typedef struct {
-    PyException_HEAD
-    PyObject *myerrno;
-    PyObject *strerror;
-    PyObject *filename;
-    PyObject *winerror;
-} PyWindowsErrorObject;
+typedef PyOSErrorObject PyWindowsErrorObject;
 #endif
-#endif
+#endif /* !Py_LIMITED_API */
 
 /* Error handling definitions */
 
@@ -132,10 +132,9 @@
 
 PyAPI_DATA(PyObject *) PyExc_AssertionError;
 PyAPI_DATA(PyObject *) PyExc_AttributeError;
+PyAPI_DATA(PyObject *) PyExc_BufferError;
 PyAPI_DATA(PyObject *) PyExc_EOFError;
 PyAPI_DATA(PyObject *) PyExc_FloatingPointError;
-PyAPI_DATA(PyObject *) PyExc_EnvironmentError;
-PyAPI_DATA(PyObject *) PyExc_IOError;
 PyAPI_DATA(PyObject *) PyExc_OSError;
 PyAPI_DATA(PyObject *) PyExc_ImportError;
 PyAPI_DATA(PyObject *) PyExc_IndexError;
@@ -160,6 +159,27 @@
 PyAPI_DATA(PyObject *) PyExc_UnicodeTranslateError;
 PyAPI_DATA(PyObject *) PyExc_ValueError;
 PyAPI_DATA(PyObject *) PyExc_ZeroDivisionError;
+
+PyAPI_DATA(PyObject *) PyExc_BlockingIOError;
+PyAPI_DATA(PyObject *) PyExc_BrokenPipeError;
+PyAPI_DATA(PyObject *) PyExc_ChildProcessError;
+PyAPI_DATA(PyObject *) PyExc_ConnectionError;
+PyAPI_DATA(PyObject *) PyExc_ConnectionAbortedError;
+PyAPI_DATA(PyObject *) PyExc_ConnectionRefusedError;
+PyAPI_DATA(PyObject *) PyExc_ConnectionResetError;
+PyAPI_DATA(PyObject *) PyExc_FileExistsError;
+PyAPI_DATA(PyObject *) PyExc_FileNotFoundError;
+PyAPI_DATA(PyObject *) PyExc_InterruptedError;
+PyAPI_DATA(PyObject *) PyExc_IsADirectoryError;
+PyAPI_DATA(PyObject *) PyExc_NotADirectoryError;
+PyAPI_DATA(PyObject *) PyExc_PermissionError;
+PyAPI_DATA(PyObject *) PyExc_ProcessLookupError;
+PyAPI_DATA(PyObject *) PyExc_TimeoutError;
+
+
+/* Compatibility aliases */
+PyAPI_DATA(PyObject *) PyExc_EnvironmentError;
+PyAPI_DATA(PyObject *) PyExc_IOError;
 #ifdef MS_WINDOWS
 PyAPI_DATA(PyObject *) PyExc_WindowsError;
 #endif
@@ -167,8 +187,6 @@
 PyAPI_DATA(PyObject *) PyExc_VMSError;
 #endif
 
-PyAPI_DATA(PyObject *) PyExc_BufferError;
-
 PyAPI_DATA(PyObject *) PyExc_RecursionErrorInst;
 
 /* Predefined warning categories */
diff --git a/Lib/_pyio.py b/Lib/_pyio.py
--- a/Lib/_pyio.py
+++ b/Lib/_pyio.py
@@ -23,16 +23,8 @@
 # defined in io.py. We don't use real inheritance though, because we don't
 # want to inherit the C implementations.
 
-
-class BlockingIOError(IOError):
-
-    """Exception raised when I/O would block on a non-blocking I/O stream."""
-
-    def __init__(self, errno, strerror, characters_written=0):
-        super().__init__(errno, strerror)
-        if not isinstance(characters_written, int):
-            raise TypeError("characters_written must be a integer")
-        self.characters_written = characters_written
+# Rebind for compatibility
+BlockingIOError = BlockingIOError
 
 
 def open(file, mode="r", buffering=-1, encoding=None, errors=None,
diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py
--- a/Lib/multiprocessing/connection.py
+++ b/Lib/multiprocessing/connection.py
@@ -321,7 +321,7 @@
                     firstchunk = overlapped.getbuffer()
                     assert lenfirstchunk == len(firstchunk)
                 except IOError as e:
-                    if e.errno == win32.ERROR_BROKEN_PIPE:
+                    if e.winerror == win32.ERROR_BROKEN_PIPE:
                         raise EOFError
                     raise
                 buf.write(firstchunk)
@@ -669,7 +669,7 @@
             try:
                 win32.ConnectNamedPipe(handle, win32.NULL)
             except WindowsError as e:
-                if e.args[0] != win32.ERROR_PIPE_CONNECTED:
+                if e.winerror != win32.ERROR_PIPE_CONNECTED:
                     raise
             return PipeConnection(handle)
 
@@ -692,8 +692,8 @@
                     0, win32.NULL, win32.OPEN_EXISTING, 0, win32.NULL
                     )
             except WindowsError as e:
-                if e.args[0] not in (win32.ERROR_SEM_TIMEOUT,
-                                     win32.ERROR_PIPE_BUSY) or _check_timeout(t):
+                if e.winerror not in (win32.ERROR_SEM_TIMEOUT,
+                                      win32.ERROR_PIPE_BUSY) or _check_timeout(t):
                     raise
             else:
                 break
diff --git a/Lib/test/exception_hierarchy.txt b/Lib/test/exception_hierarchy.txt
--- a/Lib/test/exception_hierarchy.txt
+++ b/Lib/test/exception_hierarchy.txt
@@ -11,11 +11,6 @@
       +-- AssertionError
       +-- AttributeError
       +-- BufferError
-      +-- EnvironmentError
-      |    +-- IOError
-      |    +-- OSError
-      |         +-- WindowsError (Windows)
-      |         +-- VMSError (VMS)
       +-- EOFError
       +-- ImportError
       +-- LookupError
@@ -24,6 +19,22 @@
       +-- MemoryError
       +-- NameError
       |    +-- UnboundLocalError
+      +-- OSError
+      |    +-- BlockingIOError
+      |    +-- ChildProcessError
+      |    +-- ConnectionError
+      |    |    +-- BrokenPipeError
+      |    |    +-- ConnectionAbortedError
+      |    |    +-- ConnectionRefusedError
+      |    |    +-- ConnectionResetError
+      |    +-- FileExistsError
+      |    +-- FileNotFoundError
+      |    +-- InterruptedError
+      |    +-- IsADirectoryError
+      |    +-- NotADirectoryError
+      |    +-- PermissionError
+      |    +-- ProcessLookupError
+      |    +-- TimeoutError
       +-- ReferenceError
       +-- RuntimeError
       |    +-- NotImplementedError
diff --git a/Lib/test/test_concurrent_futures.py b/Lib/test/test_concurrent_futures.py
--- a/Lib/test/test_concurrent_futures.py
+++ b/Lib/test/test_concurrent_futures.py
@@ -34,7 +34,7 @@
 RUNNING_FUTURE = create_future(state=RUNNING)
 CANCELLED_FUTURE = create_future(state=CANCELLED)
 CANCELLED_AND_NOTIFIED_FUTURE = create_future(state=CANCELLED_AND_NOTIFIED)
-EXCEPTION_FUTURE = create_future(state=FINISHED, exception=IOError())
+EXCEPTION_FUTURE = create_future(state=FINISHED, exception=OSError())
 SUCCESSFUL_FUTURE = create_future(state=FINISHED, result=42)
 
 
@@ -501,7 +501,7 @@
                          '<Future at 0x[0-9a-f]+ state=cancelled>')
         self.assertRegex(
                 repr(EXCEPTION_FUTURE),
-                '<Future at 0x[0-9a-f]+ state=finished raised IOError>')
+                '<Future at 0x[0-9a-f]+ state=finished raised OSError>')
         self.assertRegex(
                 repr(SUCCESSFUL_FUTURE),
                 '<Future at 0x[0-9a-f]+ state=finished returned int>')
@@ -512,7 +512,7 @@
         f2 = create_future(state=RUNNING)
         f3 = create_future(state=CANCELLED)
         f4 = create_future(state=CANCELLED_AND_NOTIFIED)
-        f5 = create_future(state=FINISHED, exception=IOError())
+        f5 = create_future(state=FINISHED, exception=OSError())
         f6 = create_future(state=FINISHED, result=5)
 
         self.assertTrue(f1.cancel())
@@ -566,7 +566,7 @@
                           CANCELLED_FUTURE.result, timeout=0)
         self.assertRaises(futures.CancelledError,
                           CANCELLED_AND_NOTIFIED_FUTURE.result, timeout=0)
-        self.assertRaises(IOError, EXCEPTION_FUTURE.result, timeout=0)
+        self.assertRaises(OSError, EXCEPTION_FUTURE.result, timeout=0)
         self.assertEqual(SUCCESSFUL_FUTURE.result(timeout=0), 42)
 
     def test_result_with_success(self):
@@ -605,7 +605,7 @@
         self.assertRaises(futures.CancelledError,
                           CANCELLED_AND_NOTIFIED_FUTURE.exception, timeout=0)
         self.assertTrue(isinstance(EXCEPTION_FUTURE.exception(timeout=0),
-                                   IOError))
+                                   OSError))
         self.assertEqual(SUCCESSFUL_FUTURE.exception(timeout=0), None)
 
     def test_exception_with_success(self):
@@ -614,14 +614,14 @@
             time.sleep(1)
             with f1._condition:
                 f1._state = FINISHED
-                f1._exception = IOError()
+                f1._exception = OSError()
                 f1._condition.notify_all()
 
         f1 = create_future(state=PENDING)
         t = threading.Thread(target=notification)
         t.start()
 
-        self.assertTrue(isinstance(f1.exception(timeout=5), IOError))
+        self.assertTrue(isinstance(f1.exception(timeout=5), OSError))
 
 @test.support.reap_threads
 def test_main():
diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py
--- a/Lib/test/test_exceptions.py
+++ b/Lib/test/test_exceptions.py
@@ -46,8 +46,8 @@
             fp.close()
             unlink(TESTFN)
 
-        self.raise_catch(IOError, "IOError")
-        self.assertRaises(IOError, open, 'this file does not exist', 'r')
+        self.raise_catch(OSError, "OSError")
+        self.assertRaises(OSError, open, 'this file does not exist', 'r')
 
         self.raise_catch(ImportError, "ImportError")
         self.assertRaises(ImportError, __import__, "undefined_module")
@@ -192,11 +192,35 @@
         except NameError:
             pass
         else:
-            self.assertEqual(str(WindowsError(1001)), "1001")
-            self.assertEqual(str(WindowsError(1001, "message")),
-                             "[Error 1001] message")
-            self.assertEqual(WindowsError(1001, "message").errno, 22)
-            self.assertEqual(WindowsError(1001, "message").winerror, 1001)
+            self.assertIs(WindowsError, OSError)
+            self.assertEqual(str(OSError(1001)), "1001")
+            self.assertEqual(str(OSError(1001, "message")),
+                             "[Errno 1001] message")
+            # POSIX errno (9 aka EBADF) is untranslated
+            w = OSError(9, 'foo', 'bar')
+            self.assertEqual(w.errno, 9)
+            self.assertEqual(w.winerror, None)
+            self.assertEqual(str(w), "[Errno 9] foo: 'bar'")
+            # ERROR_PATH_NOT_FOUND (win error 3) becomes ENOENT (2)
+            w = OSError(0, 'foo', 'bar', 3)
+            self.assertEqual(w.errno, 2)
+            self.assertEqual(w.winerror, 3)
+            self.assertEqual(w.strerror, 'foo')
+            self.assertEqual(w.filename, 'bar')
+            self.assertEqual(str(w), "[Error 3] foo: 'bar'")
+            # Unknown win error becomes EINVAL (22)
+            w = OSError(0, 'foo', None, 1001)
+            self.assertEqual(w.errno, 22)
+            self.assertEqual(w.winerror, 1001)
+            self.assertEqual(w.strerror, 'foo')
+            self.assertEqual(w.filename, None)
+            self.assertEqual(str(w), "[Error 1001] foo")
+            # Non-numeric "errno"
+            w = OSError('bar', 'foo')
+            self.assertEqual(w.errno, 'bar')
+            self.assertEqual(w.winerror, None)
+            self.assertEqual(w.strerror, 'foo')
+            self.assertEqual(w.filename, None)
 
     def testAttributes(self):
         # test that exception attributes are happy
@@ -274,11 +298,12 @@
                  'start' : 0, 'end' : 1}),
         ]
         try:
+            # More tests are in test_WindowsError
             exceptionList.append(
                 (WindowsError, (1, 'strErrorStr', 'filenameStr'),
                     {'args' : (1, 'strErrorStr'),
-                     'strerror' : 'strErrorStr', 'winerror' : 1,
-                     'errno' : 22, 'filename' : 'filenameStr'})
+                     'strerror' : 'strErrorStr', 'winerror' : None,
+                     'errno' : 1, 'filename' : 'filenameStr'})
             )
         except NameError:
             pass
diff --git a/Lib/test/test_http_cookiejar.py b/Lib/test/test_http_cookiejar.py
--- a/Lib/test/test_http_cookiejar.py
+++ b/Lib/test/test_http_cookiejar.py
@@ -248,18 +248,19 @@
         self.assertEqual(c._cookies["www.acme.com"]["/"]["boo"].value, None)
 
     def test_bad_magic(self):
-        # IOErrors (eg. file doesn't exist) are allowed to propagate
+        # OSErrors (eg. file doesn't exist) are allowed to propagate
         filename = test.support.TESTFN
         for cookiejar_class in LWPCookieJar, MozillaCookieJar:
             c = cookiejar_class()
             try:
                 c.load(filename="for this test to work, a file with this "
                                 "filename should not exist")
-            except IOError as exc:
-                # exactly IOError, not LoadError
-                self.assertIs(exc.__class__, IOError)
+            except OSError as exc:
+                # an OSError subclass (likely FileNotFoundError), but not
+                # LoadError
+                self.assertIsNot(exc.__class__, LoadError)
             else:
-                self.fail("expected IOError for invalid filename")
+                self.fail("expected OSError for invalid filename")
         # Invalid contents of cookies file (eg. bad magic string)
         # causes a LoadError.
         try:
diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py
--- a/Lib/test/test_io.py
+++ b/Lib/test/test_io.py
@@ -2643,12 +2643,6 @@
 
     def test_blockingioerror(self):
         # Various BlockingIOError issues
-        self.assertRaises(TypeError, self.BlockingIOError)
-        self.assertRaises(TypeError, self.BlockingIOError, 1)
-        self.assertRaises(TypeError, self.BlockingIOError, 1, 2, 3, 4)
-        self.assertRaises(TypeError, self.BlockingIOError, 1, "", None)
-        b = self.BlockingIOError(1, "")
-        self.assertEqual(b.characters_written, 0)
         class C(str):
             pass
         c = C("")
diff --git a/Lib/test/test_mmap.py b/Lib/test/test_mmap.py
--- a/Lib/test/test_mmap.py
+++ b/Lib/test/test_mmap.py
@@ -563,8 +563,7 @@
         f.close()
 
     def test_error(self):
-        self.assertTrue(issubclass(mmap.error, EnvironmentError))
-        self.assertIn("mmap.error", str(mmap.error))
+        self.assertIs(mmap.error, OSError)
 
     def test_io_methods(self):
         data = b"0123456789"
diff --git a/Lib/test/test_pep3151.py b/Lib/test/test_pep3151.py
new file mode 100644
--- /dev/null
+++ b/Lib/test/test_pep3151.py
@@ -0,0 +1,128 @@
+import builtins
+import os
+import select
+import socket
+import sys
+import unittest
+import errno
+from errno import EEXIST
+
+from test import support
+
+class SubOSError(OSError):
+    pass
+
+
+class HierarchyTest(unittest.TestCase):
+
+    def test_builtin_errors(self):
+        self.assertEqual(OSError.__name__, 'OSError')
+        self.assertIs(IOError, OSError)
+        self.assertIs(EnvironmentError, OSError)
+
+    def test_socket_errors(self):
+        self.assertIs(socket.error, IOError)
+        self.assertIs(socket.gaierror.__base__, OSError)
+        self.assertIs(socket.herror.__base__, OSError)
+        self.assertIs(socket.timeout.__base__, OSError)
+
+    def test_select_error(self):
+        self.assertIs(select.error, OSError)
+
+    # mmap.error is tested in test_mmap
+
+    _pep_map = """
+        +-- BlockingIOError        EAGAIN, EALREADY, EWOULDBLOCK, EINPROGRESS
+        +-- ChildProcessError                                          ECHILD
+        +-- ConnectionError
+            +-- BrokenPipeError                              EPIPE, ESHUTDOWN
+            +-- ConnectionAbortedError                           ECONNABORTED
+            +-- ConnectionRefusedError                           ECONNREFUSED
+            +-- ConnectionResetError                               ECONNRESET
+        +-- FileExistsError                                            EEXIST
+        +-- FileNotFoundError                                          ENOENT
+        +-- InterruptedError                                            EINTR
+        +-- IsADirectoryError                                          EISDIR
+        +-- NotADirectoryError                                        ENOTDIR
+        +-- PermissionError                                     EACCES, EPERM
+        +-- ProcessLookupError                                          ESRCH
+        +-- TimeoutError                                            ETIMEDOUT
+    """
+    def _make_map(s):
+        _map = {}
+        for line in s.splitlines():
+            line = line.strip('+- ')
+            if not line:
+                continue
+            excname, _, errnames = line.partition(' ')
+            for errname in filter(None, errnames.strip().split(', ')):
+                _map[getattr(errno, errname)] = getattr(builtins, excname)
+        return _map
+    _map = _make_map(_pep_map)
+
+    def test_errno_mapping(self):
+        # The OSError constructor maps errnos to subclasses
+        # A sample test for the basic functionality
+        e = OSError(EEXIST, "Bad file descriptor")
+        self.assertIs(type(e), FileExistsError)
+        # Exhaustive testing
+        for errcode, exc in self._map.items():
+            e = OSError(errcode, "Some message")
+            self.assertIs(type(e), exc)
+        othercodes = set(errno.errorcode) - set(self._map)
+        for errcode in othercodes:
+            e = OSError(errcode, "Some message")
+            self.assertIs(type(e), OSError)
+
+    def test_OSError_subclass_mapping(self):
+        # When constructing an OSError subclass, errno mapping isn't done
+        e = SubOSError(EEXIST, "Bad file descriptor")
+        self.assertIs(type(e), SubOSError)
+
+
+class AttributesTest(unittest.TestCase):
+
+    def test_windows_error(self):
+        if os.name == "nt":
+            self.assertIn('winerror', dir(OSError))
+        else:
+            self.assertNotIn('winerror', dir(OSError))
+
+    def test_posix_error(self):
+        e = OSError(EEXIST, "File already exists", "foo.txt")
+        self.assertEqual(e.errno, EEXIST)
+        self.assertEqual(e.args[0], EEXIST)
+        self.assertEqual(e.strerror, "File already exists")
+        self.assertEqual(e.filename, "foo.txt")
+        if os.name == "nt":
+            self.assertEqual(e.winerror, None)
+
+    @unittest.skipUnless(os.name == "nt", "Windows-specific test")
+    def test_errno_translation(self):
+        # ERROR_ALREADY_EXISTS (183) -> EEXIST
+        e = OSError(0, "File already exists", "foo.txt", 183)
+        self.assertEqual(e.winerror, 183)
+        self.assertEqual(e.errno, EEXIST)
+        self.assertEqual(e.args[0], EEXIST)
+        self.assertEqual(e.strerror, "File already exists")
+        self.assertEqual(e.filename, "foo.txt")
+
+    def test_blockingioerror(self):
+        args = ("a", "b", "c", "d", "e")
+        for n in range(6):
+            e = BlockingIOError(*args[:n])
+            with self.assertRaises(AttributeError):
+                e.characters_written
+        e = BlockingIOError("a", "b", 3)
+        self.assertEqual(e.characters_written, 3)
+        e.characters_written = 5
+        self.assertEqual(e.characters_written, 5)
+
+    # XXX VMSError not tested
+
+
+def test_main():
+    support.run_unittest(__name__)
+
+if __name__=="__main__":
+    test_main()
diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py
--- a/Lib/test/test_xml_etree.py
+++ b/Lib/test/test_xml_etree.py
@@ -1339,7 +1339,7 @@
     try:
         data = XINCLUDE[href]
     except KeyError:
-        raise IOError("resource not found")
+        raise OSError("resource not found")
     if parse == "xml":
         from xml.etree.ElementTree import XML
         return XML(data)
@@ -1404,7 +1404,7 @@
     >>> document = xinclude_loader("C5.xml")
     >>> ElementInclude.include(document, xinclude_loader)
     Traceback (most recent call last):
-    IOError: resource not found
+    OSError: resource not found
     >>> # print(serialize(document)) # C5
     """
 
@@ -1611,7 +1611,7 @@
 
 class ExceptionFile:
     def read(self, x):
-        raise IOError
+        raise OSError
 
 def xmltoolkit60():
     """
@@ -1619,7 +1619,7 @@
     Handle crash in stream source.
     >>> tree = ET.parse(ExceptionFile())
     Traceback (most recent call last):
-    IOError
+    OSError
 
     """
 
diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py
--- a/Lib/urllib/request.py
+++ b/Lib/urllib/request.py
@@ -1547,6 +1547,8 @@
                 return getattr(self, name)(url)
             else:
                 return getattr(self, name)(url, data)
+        except HTTPError:
+            raise
         except socket.error as msg:
             raise IOError('socket error', msg).with_traceback(sys.exc_info()[2])
 
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,8 @@
 Core and Builtins
 -----------------
 
+- PEP 3151 / issue #12555: reworking the OS and IO exception hierarchy.
+
 - Add internal API for static strings (_Py_identifier et.al.).
 
 - Issue #13063: the Windows error ERROR_NO_DATA (numbered 232 and described
diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c
--- a/Modules/_io/_iomodule.c
+++ b/Modules/_io/_iomodule.c
@@ -91,89 +91,6 @@
 
 
 /*
- * BlockingIOError extends IOError
- */
-
-static int
-blockingioerror_init(PyBlockingIOErrorObject *self, PyObject *args,
-                     PyObject *kwds)
-{
-    PyObject *myerrno = NULL, *strerror = NULL;
-    PyObject *baseargs = NULL;
-    Py_ssize_t written = 0;
-
-    assert(PyTuple_Check(args));
-
-    self->written = 0;
-    if (!PyArg_ParseTuple(args, "OO|n:BlockingIOError",
-                          &myerrno, &strerror, &written))
-        return -1;
-
-    baseargs = PyTuple_Pack(2, myerrno, strerror);
-    if (baseargs == NULL)
-        return -1;
-    /* This will take care of initializing of myerrno and strerror members */
-    if (((PyTypeObject *)PyExc_IOError)->tp_init(
-                (PyObject *)self, baseargs, kwds) == -1) {
-        Py_DECREF(baseargs);
-        return -1;
-    }
-    Py_DECREF(baseargs);
-
-    self->written = written;
-    return 0;
-}
-
-static PyMemberDef blockingioerror_members[] = {
-    {"characters_written", T_PYSSIZET, offsetof(PyBlockingIOErrorObject, written), 0},
-    {NULL}  /* Sentinel */
-};
-
-static PyTypeObject _PyExc_BlockingIOError = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    "BlockingIOError", /*tp_name*/
-    sizeof(PyBlockingIOErrorObject), /*tp_basicsize*/
-    0,                          /*tp_itemsize*/
-    0,                          /*tp_dealloc*/
-    0,                          /*tp_print*/
-    0,                          /*tp_getattr*/
-    0,                          /*tp_setattr*/
-    0,                          /*tp_compare */
-    0,                          /*tp_repr*/
-    0,                          /*tp_as_number*/
-    0,                          /*tp_as_sequence*/
-    0,                          /*tp_as_mapping*/
-    0,                          /*tp_hash */
-    0,                          /*tp_call*/
-    0,                          /*tp_str*/
-    0,                          /*tp_getattro*/
-    0,                          /*tp_setattro*/
-    0,                          /*tp_as_buffer*/
-    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
-    PyDoc_STR("Exception raised when I/O would block "
-              "on a non-blocking I/O stream"), /* tp_doc */
-    0,                          /* tp_traverse */
-    0,                          /* tp_clear */
-    0,                          /* tp_richcompare */
-    0,                          /* tp_weaklistoffset */
-    0,                          /* tp_iter */
-    0,                          /* tp_iternext */
-    0,                          /* tp_methods */
-    blockingioerror_members,    /* tp_members */
-    0,                          /* tp_getset */
-    0,                          /* tp_base */
-    0,                          /* tp_dict */
-    0,                          /* tp_descr_get */
-    0,                          /* tp_descr_set */
-    0,                          /* tp_dictoffset */
-    (initproc)blockingioerror_init, /* tp_init */
-    0,                          /* tp_alloc */
-    0,                          /* tp_new */
-};
-PyObject *PyExc_BlockingIOError = (PyObject *)&_PyExc_BlockingIOError;
-
-
-/*
  * The main open() function
  */
 PyDoc_STRVAR(open_doc,
@@ -694,9 +611,11 @@
                            state->unsupported_operation) < 0)
         goto fail;
 
-    /* BlockingIOError */
-    _PyExc_BlockingIOError.tp_base = (PyTypeObject *) PyExc_IOError;
-    ADD_TYPE(&_PyExc_BlockingIOError, "BlockingIOError");
+    /* BlockingIOError, for compatibility */
+    Py_INCREF(PyExc_BlockingIOError);
+    if (PyModule_AddObject(m, "BlockingIOError",
+                           (PyObject *) PyExc_BlockingIOError) < 0)
+        goto fail;
 
     /* Concrete base types of the IO ABCs.
        (the ABCs themselves are declared through inheritance in io.py)
diff --git a/Modules/_io/_iomodule.h b/Modules/_io/_iomodule.h
--- a/Modules/_io/_iomodule.h
+++ b/Modules/_io/_iomodule.h
@@ -60,15 +60,6 @@
 
 #define DEFAULT_BUFFER_SIZE (8 * 1024)  /* bytes */
 
-typedef struct {
-    PyException_HEAD
-    PyObject *myerrno;
-    PyObject *strerror;
-    PyObject *filename; /* Not used, but part of the IOError object */
-    Py_ssize_t written;
-} PyBlockingIOErrorObject;
-PyAPI_DATA(PyObject *) PyExc_BlockingIOError;
-
 /*
  * Offset type for positioning.
  */
diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c
--- a/Modules/_io/bufferedio.c
+++ b/Modules/_io/bufferedio.c
@@ -622,14 +622,14 @@
 _buffered_check_blocking_error(void)
 {
     PyObject *t, *v, *tb;
-    PyBlockingIOErrorObject *err;
+    PyOSErrorObject *err;
 
     PyErr_Fetch(&t, &v, &tb);
     if (v == NULL || !PyErr_GivenExceptionMatches(v, PyExc_BlockingIOError)) {
         PyErr_Restore(t, v, tb);
         return NULL;
     }
-    err = (PyBlockingIOErrorObject *) v;
+    err = (PyOSErrorObject *) v;
     /* TODO: sanity check (err->written >= 0) */
     PyErr_Restore(t, v, tb);
     return &err->written;
diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c
--- a/Modules/mmapmodule.c
+++ b/Modules/mmapmodule.c
@@ -78,8 +78,6 @@
 #  define MAP_ANONYMOUS MAP_ANON
 #endif
 
-static PyObject *mmap_module_error;
-
 typedef enum
 {
     ACCESS_DEFAULT,
@@ -459,7 +457,7 @@
     {
         struct stat buf;
         if (-1 == fstat(self->fd, &buf)) {
-            PyErr_SetFromErrno(mmap_module_error);
+            PyErr_SetFromErrno(PyExc_OSError);
             return NULL;
         }
 #ifdef HAVE_LARGEFILE_SUPPORT
@@ -549,7 +547,7 @@
         void *newmap;
 
         if (ftruncate(self->fd, self->offset + new_size) == -1) {
-            PyErr_SetFromErrno(mmap_module_error);
+            PyErr_SetFromErrno(PyExc_OSError);
             return NULL;
         }
 
@@ -564,7 +562,7 @@
 #endif
         if (newmap == (void *)-1)
         {
-            PyErr_SetFromErrno(mmap_module_error);
+            PyErr_SetFromErrno(PyExc_OSError);
             return NULL;
         }
         self->data = newmap;
@@ -605,7 +603,7 @@
     /* XXX semantics of return value? */
     /* XXX flags for msync? */
     if (-1 == msync(self->data + offset, size, MS_SYNC)) {
-        PyErr_SetFromErrno(mmap_module_error);
+        PyErr_SetFromErrno(PyExc_OSError);
         return NULL;
     }
     return PyLong_FromLong(0);
@@ -1205,7 +1203,7 @@
         fd = devzero = open("/dev/zero", O_RDWR);
         if (devzero == -1) {
             Py_DECREF(m_obj);
-            PyErr_SetFromErrno(mmap_module_error);
+            PyErr_SetFromErrno(PyExc_OSError);
             return NULL;
         }
 #endif
@@ -1213,7 +1211,7 @@
         m_obj->fd = dup(fd);
         if (m_obj->fd == -1) {
             Py_DECREF(m_obj);
-            PyErr_SetFromErrno(mmap_module_error);
+            PyErr_SetFromErrno(PyExc_OSError);
             return NULL;
         }
     }
@@ -1229,7 +1227,7 @@
     if (m_obj->data == (char *)-1) {
         m_obj->data = NULL;
         Py_DECREF(m_obj);
-        PyErr_SetFromErrno(mmap_module_error);
+        PyErr_SetFromErrno(PyExc_OSError);
         return NULL;
     }
     m_obj->access = (access_mode)access;
@@ -1310,12 +1308,12 @@
     if (fileno != -1 && fileno != 0) {
         /* Ensure that fileno is within the CRT's valid range */
         if (_PyVerify_fd(fileno) == 0) {
-            PyErr_SetFromErrno(mmap_module_error);
+            PyErr_SetFromErrno(PyExc_OSError);
             return NULL;
         }
         fh = (HANDLE)_get_osfhandle(fileno);
         if (fh==(HANDLE)-1) {
-            PyErr_SetFromErrno(mmap_module_error);
+            PyErr_SetFromErrno(PyExc_OSError);
             return NULL;
         }
         /* Win9x appears to need us seeked to zero */
@@ -1469,11 +1467,7 @@
     dict = PyModule_GetDict(module);
     if (!dict)
         return NULL;
-    mmap_module_error = PyErr_NewException("mmap.error",
-        PyExc_EnvironmentError , NULL);
-    if (mmap_module_error == NULL)
-        return NULL;
-    PyDict_SetItemString(dict, "error", mmap_module_error);
+    PyDict_SetItemString(dict, "error", PyExc_OSError);
     PyDict_SetItemString(dict, "mmap", (PyObject*) &mmap_object_type);
 #ifdef PROT_EXEC
     setint(dict, "PROT_EXEC", PROT_EXEC);
diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c
--- a/Modules/selectmodule.c
+++ b/Modules/selectmodule.c
@@ -54,8 +54,6 @@
 #  endif
 #endif
 
-static PyObject *SelectError;
-
 /* list of Python objects and their file descriptor */
 typedef struct {
     PyObject *obj;                           /* owned reference */
@@ -274,11 +272,11 @@
 
 #ifdef MS_WINDOWS
     if (n == SOCKET_ERROR) {
-        PyErr_SetExcFromWindowsErr(SelectError, WSAGetLastError());
+        PyErr_SetExcFromWindowsErr(PyExc_OSError, WSAGetLastError());
     }
 #else
     if (n < 0) {
-        PyErr_SetFromErrno(SelectError);
+        PyErr_SetFromErrno(PyExc_OSError);
     }
 #endif
     else {
@@ -425,7 +423,7 @@
         return NULL;
     if (PyDict_GetItem(self->dict, key) == NULL) {
         errno = ENOENT;
-        PyErr_SetFromErrno(PyExc_IOError);
+        PyErr_SetFromErrno(PyExc_OSError);
         return NULL;
     }
     value = PyLong_FromLong(events);
@@ -524,7 +522,7 @@
     Py_END_ALLOW_THREADS
 
     if (poll_result < 0) {
-        PyErr_SetFromErrno(SelectError);
+        PyErr_SetFromErrno(PyExc_OSError);
         return NULL;
     }
 
@@ -764,7 +762,7 @@
     }
     if (self->epfd < 0) {
         Py_DECREF(self);
-        PyErr_SetFromErrno(PyExc_IOError);
+        PyErr_SetFromErrno(PyExc_OSError);
         return NULL;
     }
     return (PyObject *)self;
@@ -797,7 +795,7 @@
 {
     errno = pyepoll_internal_close(self);
     if (errno < 0) {
-        PyErr_SetFromErrno(PyExc_IOError);
+        PyErr_SetFromErrno(PyExc_OSError);
         return NULL;
     }
     Py_RETURN_NONE;
@@ -890,7 +888,7 @@
     }
 
     if (result < 0) {
-        PyErr_SetFromErrno(PyExc_IOError);
+        PyErr_SetFromErrno(PyExc_OSError);
         return NULL;
     }
     Py_RETURN_NONE;
@@ -914,7 +912,7 @@
 PyDoc_STRVAR(pyepoll_register_doc,
 "register(fd[, eventmask]) -> None\n\
 \n\
-Registers a new fd or raises an IOError if the fd is already registered.\n\
+Registers a new fd or raises an OSError if the fd is already registered.\n\
 fd is the target file descriptor of the operation.\n\
 events is a bit set composed of the various EPOLL constants; the default\n\
 is EPOLL_IN | EPOLL_OUT | EPOLL_PRI.\n\
@@ -1013,7 +1011,7 @@
     nfds = epoll_wait(self->epfd, evs, maxevents, timeout);
     Py_END_ALLOW_THREADS
     if (nfds < 0) {
-        PyErr_SetFromErrno(PyExc_IOError);
+        PyErr_SetFromErrno(PyExc_OSError);
         goto error;
     }
 
@@ -1404,7 +1402,7 @@
     }
     if (self->kqfd < 0) {
         Py_DECREF(self);
-        PyErr_SetFromErrno(PyExc_IOError);
+        PyErr_SetFromErrno(PyExc_OSError);
         return NULL;
     }
     return (PyObject *)self;
@@ -1436,7 +1434,7 @@
 {
     errno = kqueue_queue_internal_close(self);
     if (errno < 0) {
-        PyErr_SetFromErrno(PyExc_IOError);
+        PyErr_SetFromErrno(PyExc_OSError);
         return NULL;
     }
     Py_RETURN_NONE;
@@ -1778,9 +1776,8 @@
     if (m == NULL)
         return NULL;
 
-    SelectError = PyErr_NewException("select.error", NULL, NULL);
-    Py_INCREF(SelectError);
-    PyModule_AddObject(m, "error", SelectError);
+    Py_INCREF(PyExc_OSError);
+    PyModule_AddObject(m, "error", PyExc_OSError);
 
 #ifdef PIPE_BUF
 #ifdef HAVE_BROKEN_PIPE_BUF
diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c
--- a/Modules/socketmodule.c
+++ b/Modules/socketmodule.c
@@ -457,7 +457,6 @@
 
 /* Global variable holding the exception type for errors detected
    by this module (but not argument type or memory errors, etc.). */
-static PyObject *socket_error;
 static PyObject *socket_herror;
 static PyObject *socket_gaierror;
 static PyObject *socket_timeout;
@@ -498,7 +497,7 @@
 static PyObject*
 select_error(void)
 {
-    PyErr_SetString(socket_error, "unable to select on socket");
+    PyErr_SetString(PyExc_OSError, "unable to select on socket");
     return NULL;
 }
 
@@ -525,7 +524,7 @@
        recognizes the error codes used by both GetLastError() and
        WSAGetLastError */
     if (err_no)
-        return PyErr_SetExcFromWindowsErr(socket_error, err_no);
+        return PyErr_SetExcFromWindowsErr(PyExc_OSError, err_no);
 #endif
 
 #if defined(PYOS_OS2) && !defined(PYCC_GCC)
@@ -556,7 +555,7 @@
             }
             v = Py_BuildValue("(is)", myerrorcode, outbuf);
             if (v != NULL) {
-                PyErr_SetObject(socket_error, v);
+                PyErr_SetObject(PyExc_OSError, v);
                 Py_DECREF(v);
             }
             return NULL;
@@ -564,7 +563,7 @@
     }
 #endif
 
-    return PyErr_SetFromErrno(socket_error);
+    return PyErr_SetFromErrno(PyExc_OSError);
 }
 
 
@@ -883,13 +882,13 @@
 #endif
         default:
             freeaddrinfo(res);
-            PyErr_SetString(socket_error,
+            PyErr_SetString(PyExc_OSError,
                 "unsupported address family");
             return -1;
         }
         if (res->ai_next) {
             freeaddrinfo(res);
-            PyErr_SetString(socket_error,
+            PyErr_SetString(PyExc_OSError,
                 "wildcard resolved to multiple address");
             return -1;
         }
@@ -902,7 +901,7 @@
     if (name[0] == '<' && strcmp(name, "<broadcast>") == 0) {
         struct sockaddr_in *sin;
         if (af != AF_INET && af != AF_UNSPEC) {
-            PyErr_SetString(socket_error,
+            PyErr_SetString(PyExc_OSError,
                 "address family mismatched");
             return -1;
         }
@@ -960,7 +959,7 @@
         return 16;
 #endif
     default:
-        PyErr_SetString(socket_error, "unknown address family");
+        PyErr_SetString(PyExc_OSError, "unknown address family");
         return -1;
     }
 }
@@ -1009,7 +1008,7 @@
         bdaddr->b[5] = b5;
         return 6;
     } else {
-        PyErr_SetString(socket_error, "bad bluetooth address");
+        PyErr_SetString(PyExc_OSError, "bad bluetooth address");
         return -1;
     }
 }
@@ -1278,7 +1277,7 @@
         if (len > 0 && path[0] == 0) {
             /* Linux abstract namespace extension */
             if (len > sizeof addr->sun_path) {
-                PyErr_SetString(socket_error,
+                PyErr_SetString(PyExc_OSError,
                                 "AF_UNIX path too long");
                 return 0;
             }
@@ -1288,7 +1287,7 @@
         {
             /* regular NULL-terminated string */
             if (len >= sizeof addr->sun_path) {
-                PyErr_SetString(socket_error,
+                PyErr_SetString(PyExc_OSError,
                                 "AF_UNIX path too long");
                 return 0;
             }
@@ -1418,7 +1417,7 @@
             _BT_L2_MEMB(addr, family) = AF_BLUETOOTH;
             if (!PyArg_ParseTuple(args, "si", &straddr,
                                   &_BT_L2_MEMB(addr, psm))) {
-                PyErr_SetString(socket_error, "getsockaddrarg: "
+                PyErr_SetString(PyExc_OSError, "getsockaddrarg: "
                                 "wrong format");
                 return 0;
             }
@@ -1437,7 +1436,7 @@
             _BT_RC_MEMB(addr, family) = AF_BLUETOOTH;
             if (!PyArg_ParseTuple(args, "si", &straddr,
                                   &_BT_RC_MEMB(addr, channel))) {
-                PyErr_SetString(socket_error, "getsockaddrarg: "
+                PyErr_SetString(PyExc_OSError, "getsockaddrarg: "
                                 "wrong format");
                 return 0;
             }
@@ -1455,7 +1454,7 @@
 
                         _BT_HCI_MEMB(addr, family) = AF_BLUETOOTH;
             if (straddr == NULL) {
-                PyErr_SetString(socket_error, "getsockaddrarg: "
+                PyErr_SetString(PyExc_OSError, "getsockaddrarg: "
                     "wrong format");
                 return 0;
             }
@@ -1464,7 +1463,7 @@
 #else
             _BT_HCI_MEMB(addr, family) = AF_BLUETOOTH;
             if (!PyArg_ParseTuple(args, "i", &_BT_HCI_MEMB(addr, dev))) {
-                PyErr_SetString(socket_error, "getsockaddrarg: "
+                PyErr_SetString(PyExc_OSError, "getsockaddrarg: "
                                 "wrong format");
                 return 0;
             }
@@ -1481,7 +1480,7 @@
             addr = (struct sockaddr_sco *)addr_ret;
             _BT_SCO_MEMB(addr, family) = AF_BLUETOOTH;
             if (!PyBytes_Check(args)) {
-                PyErr_SetString(socket_error, "getsockaddrarg: "
+                PyErr_SetString(PyExc_OSError, "getsockaddrarg: "
                                 "wrong format");
                 return 0;
             }
@@ -1494,7 +1493,7 @@
         }
 #endif
         default:
-            PyErr_SetString(socket_error, "getsockaddrarg: unknown Bluetooth protocol");
+            PyErr_SetString(PyExc_OSError, "getsockaddrarg: unknown Bluetooth protocol");
             return 0;
         }
     }
@@ -1633,7 +1632,7 @@
                     return 0;
                 }
             } else {
-                PyErr_SetString(socket_error,
+                PyErr_SetString(PyExc_OSError,
                                 "AF_CAN interface name too long");
                 Py_DECREF(interfaceName);
                 return 0;
@@ -1647,7 +1646,7 @@
             return 1;
         }
         default:
-            PyErr_SetString(socket_error,
+            PyErr_SetString(PyExc_OSError,
                             "getsockaddrarg: unsupported CAN protocol");
             return 0;
         }
@@ -1656,7 +1655,7 @@
     /* More cases here... */
 
     default:
-        PyErr_SetString(socket_error, "getsockaddrarg: bad family");
+        PyErr_SetString(PyExc_OSError, "getsockaddrarg: bad family");
         return 0;
 
     }
@@ -1722,7 +1721,7 @@
             return 1;
 #endif
         default:
-            PyErr_SetString(socket_error, "getsockaddrlen: "
+            PyErr_SetString(PyExc_OSError, "getsockaddrlen: "
                             "unknown BT protocol");
             return 0;
 
@@ -1757,7 +1756,7 @@
     /* More cases here... */
 
     default:
-        PyErr_SetString(socket_error, "getsockaddrlen: bad family");
+        PyErr_SetString(PyExc_OSError, "getsockaddrlen: bad family");
         return 0;
 
     }
@@ -2098,7 +2097,7 @@
 #else
     if (buflen <= 0 || buflen > 1024) {
 #endif
-        PyErr_SetString(socket_error,
+        PyErr_SetString(PyExc_OSError,
                         "getsockopt buflen out of range");
         return NULL;
     }
@@ -2926,7 +2925,7 @@
         if (cmsg_status < 0)
             break;
         if (cmsgdatalen > PY_SSIZE_T_MAX) {
-            PyErr_SetString(socket_error, "control message too long");
+            PyErr_SetString(PyExc_OSError, "control message too long");
             goto err_closefds;
         }
 
@@ -3087,7 +3086,7 @@
         return NULL;
     nitems = PySequence_Fast_GET_SIZE(fast);
     if (nitems > INT_MAX) {
-        PyErr_SetString(socket_error, "recvmsg_into() argument 1 is too long");
+        PyErr_SetString(PyExc_OSError, "recvmsg_into() argument 1 is too long");
         goto finally;
     }
 
@@ -3394,7 +3393,7 @@
         goto finally;
     ndataparts = PySequence_Fast_GET_SIZE(data_fast);
     if (ndataparts > INT_MAX) {
-        PyErr_SetString(socket_error, "sendmsg() argument 1 is too long");
+        PyErr_SetString(PyExc_OSError, "sendmsg() argument 1 is too long");
         goto finally;
     }
     msg.msg_iovlen = ndataparts;
@@ -3426,7 +3425,7 @@
 
 #ifndef CMSG_SPACE
     if (ncmsgs > 1) {
-        PyErr_SetString(socket_error,
+        PyErr_SetString(PyExc_OSError,
                         "sending multiple control messages is not supported "
                         "on this system");
         goto finally;
@@ -3455,12 +3454,12 @@
 #else
         if (!get_CMSG_LEN(bufsize, &space)) {
 #endif
-            PyErr_SetString(socket_error, "ancillary data item too large");
+            PyErr_SetString(PyExc_OSError, "ancillary data item too large");
             goto finally;
         }
         controllen += space;
         if (controllen > SOCKLEN_T_LIMIT || controllen < controllen_last) {
-            PyErr_SetString(socket_error, "too much ancillary data");
+            PyErr_SetString(PyExc_OSError, "too much ancillary data");
             goto finally;
         }
         controllen_last = controllen;
@@ -4000,7 +3999,7 @@
 
     if (h->h_addrtype != af) {
         /* Let's get real error message to return */
-        PyErr_SetString(socket_error,
+        PyErr_SetString(PyExc_OSError,
                         (char *)strerror(EAFNOSUPPORT));
 
         return NULL;
@@ -4085,7 +4084,7 @@
 #endif
 
         default:                /* can't happen */
-            PyErr_SetString(socket_error,
+            PyErr_SetString(PyExc_OSError,
                             "unsupported address family");
             return NULL;
         }
@@ -4239,7 +4238,7 @@
         break;
 #endif
     default:
-        PyErr_SetString(socket_error, "unsupported address family");
+        PyErr_SetString(PyExc_OSError, "unsupported address family");
         goto finally;
     }
     Py_BEGIN_ALLOW_THREADS
@@ -4295,7 +4294,7 @@
     sp = getservbyname(name, proto);
     Py_END_ALLOW_THREADS
     if (sp == NULL) {
-        PyErr_SetString(socket_error, "service/proto not found");
+        PyErr_SetString(PyExc_OSError, "service/proto not found");
         return NULL;
     }
     return PyLong_FromLong((long) ntohs(sp->s_port));
@@ -4332,7 +4331,7 @@
     sp = getservbyport(htons((short)port), proto);
     Py_END_ALLOW_THREADS
     if (sp == NULL) {
-        PyErr_SetString(socket_error, "port/proto not found");
+        PyErr_SetString(PyExc_OSError, "port/proto not found");
         return NULL;
     }
     return PyUnicode_FromString(sp->s_name);
@@ -4361,7 +4360,7 @@
     sp = getprotobyname(name);
     Py_END_ALLOW_THREADS
     if (sp == NULL) {
-        PyErr_SetString(socket_error, "protocol not found");
+        PyErr_SetString(PyExc_OSError, "protocol not found");
         return NULL;
     }
     return PyLong_FromLong((long) sp->p_proto);
@@ -4616,7 +4615,7 @@
         return PyBytes_FromStringAndSize((char *)(&buf),
                                           sizeof(buf));
 
-    PyErr_SetString(socket_error,
+    PyErr_SetString(PyExc_OSError,
                     "illegal IP address string passed to inet_aton");
     return NULL;
 
@@ -4637,7 +4636,7 @@
         packed_addr = inet_addr(ip_addr);
 
         if (packed_addr == INADDR_NONE) {               /* invalid address */
-            PyErr_SetString(socket_error,
+            PyErr_SetString(PyExc_OSError,
                 "illegal IP address string passed to inet_aton");
             return NULL;
         }
@@ -4669,7 +4668,7 @@
     }
 
     if (addr_len != sizeof(packed_addr)) {
-        PyErr_SetString(socket_error,
+        PyErr_SetString(PyExc_OSError,
             "packed IP wrong length for inet_ntoa");
         return NULL;
     }
@@ -4704,7 +4703,7 @@
 
 #if !defined(ENABLE_IPV6) && defined(AF_INET6)
     if(af == AF_INET6) {
-        PyErr_SetString(socket_error,
+        PyErr_SetString(PyExc_OSError,
                         "can't use AF_INET6, IPv6 is disabled");
         return NULL;
     }
@@ -4712,10 +4711,10 @@
 
     retval = inet_pton(af, ip, packed);
     if (retval < 0) {
-        PyErr_SetFromErrno(socket_error);
+        PyErr_SetFromErrno(PyExc_OSError);
         return NULL;
     } else if (retval == 0) {
-        PyErr_SetString(socket_error,
+        PyErr_SetString(PyExc_OSError,
             "illegal IP address string passed to inet_pton");
         return NULL;
     } else if (af == AF_INET) {
@@ -4727,7 +4726,7 @@
                                           sizeof(struct in6_addr));
 #endif
     } else {
-        PyErr_SetString(socket_error, "unknown address family");
+        PyErr_SetString(PyExc_OSError, "unknown address family");
         return NULL;
     }
 }
@@ -4779,7 +4778,7 @@
 
     retval = inet_ntop(af, packed, ip, sizeof(ip));
     if (!retval) {
-        PyErr_SetFromErrno(socket_error);
+        PyErr_SetFromErrno(PyExc_OSError);
         return NULL;
     } else {
         return PyUnicode_FromString(retval);
@@ -4850,7 +4849,7 @@
     } else if (pobj == Py_None) {
         pptr = (char *)NULL;
     } else {
-        PyErr_SetString(socket_error, "Int or String expected");
+        PyErr_SetString(PyExc_OSError, "Int or String expected");
         goto err;
     }
     memset(&hints, 0, sizeof(hints));
@@ -4947,7 +4946,7 @@
         goto fail;
     }
     if (res->ai_next) {
-        PyErr_SetString(socket_error,
+        PyErr_SetString(PyExc_OSError,
             "sockaddr resolved to multiple addresses");
         goto fail;
     }
@@ -4955,7 +4954,7 @@
     case AF_INET:
         {
         if (PyTuple_GET_SIZE(sa) != 2) {
-            PyErr_SetString(socket_error,
+            PyErr_SetString(PyExc_OSError,
                 "IPv4 sockaddr must be 2 tuple");
             goto fail;
         }
@@ -5054,7 +5053,7 @@
 
     ni = if_nameindex();
     if (ni == NULL) {
-        PyErr_SetFromErrno(socket_error);
+        PyErr_SetFromErrno(PyExc_OSError);
         return NULL;
     }
 
@@ -5100,7 +5099,7 @@
     Py_DECREF(oname);
     if (index == 0) {
         /* if_nametoindex() doesn't set errno */
-        PyErr_SetString(socket_error, "no interface with this name");
+        PyErr_SetString(PyExc_OSError, "no interface with this name");
         return NULL;
     }
 
@@ -5123,7 +5122,7 @@
         return NULL;
 
     if (if_indextoname(index, name) == NULL) {
-        PyErr_SetFromErrno(socket_error);
+        PyErr_SetFromErrno(PyExc_OSError);
         return NULL;
     }
 
@@ -5403,27 +5402,24 @@
     if (m == NULL)
         return NULL;
 
-    socket_error = PyErr_NewException("socket.error",
-                                      PyExc_IOError, NULL);
-    if (socket_error == NULL)
-        return NULL;
-    PySocketModuleAPI.error = socket_error;
-    Py_INCREF(socket_error);
-    PyModule_AddObject(m, "error", socket_error);
+    Py_INCREF(PyExc_OSError);
+    PySocketModuleAPI.error = PyExc_OSError;
+    Py_INCREF(PyExc_OSError);
+    PyModule_AddObject(m, "error", PyExc_OSError);
     socket_herror = PyErr_NewException("socket.herror",
-                                       socket_error, NULL);
+                                       PyExc_OSError, NULL);
     if (socket_herror == NULL)
         return NULL;
     Py_INCREF(socket_herror);
     PyModule_AddObject(m, "herror", socket_herror);
-    socket_gaierror = PyErr_NewException("socket.gaierror", socket_error,
+    socket_gaierror = PyErr_NewException("socket.gaierror", PyExc_OSError,
         NULL);
     if (socket_gaierror == NULL)
         return NULL;
     Py_INCREF(socket_gaierror);
     PyModule_AddObject(m, "gaierror", socket_gaierror);
     socket_timeout = PyErr_NewException("socket.timeout",
-                                        socket_error, NULL);
+                                        PyExc_OSError, NULL);
     if (socket_timeout == NULL)
         return NULL;
     PySocketModuleAPI.timeout_error = socket_timeout;
diff --git a/Objects/exceptions.c b/Objects/exceptions.c
--- a/Objects/exceptions.c
+++ b/Objects/exceptions.c
@@ -10,6 +10,20 @@
 #include "osdefs.h"
 
 
+/* Compatibility aliases */
+PyObject *PyExc_EnvironmentError = NULL;
+PyObject *PyExc_IOError = NULL;
+#ifdef MS_WINDOWS
+PyObject *PyExc_WindowsError = NULL;
+#endif
+#ifdef __VMS
+PyObject *PyExc_VMSError = NULL;
+#endif
+
+/* The dict map from errno codes to OSError subclasses */
+static PyObject *errnomap = NULL;
+
+
 /* NOTE: If the exception class hierarchy changes, don't forget to update
  * Lib/test/exception_hierarchy.txt
  */
@@ -433,11 +447,13 @@
     PyDoc_STR(EXCDOC), (traverseproc)EXCSTORE ## _traverse, \
     (inquiry)EXCSTORE ## _clear, 0, 0, 0, 0, 0, 0, 0, &_ ## EXCBASE, \
     0, 0, 0, offsetof(Py ## EXCSTORE ## Object, dict), \
-    (initproc)EXCSTORE ## _init, 0, BaseException_new,\
+    (initproc)EXCSTORE ## _init, 0, 0, \
 }; \
 PyObject *PyExc_ ## EXCNAME = (PyObject *)&_PyExc_ ## EXCNAME
 
-#define ComplexExtendsException(EXCBASE, EXCNAME, EXCSTORE, EXCDEALLOC, EXCMETHODS, EXCMEMBERS, EXCSTR, EXCDOC) \
+#define ComplexExtendsException(EXCBASE, EXCNAME, EXCSTORE, EXCNEW, \
+                                EXCMETHODS, EXCMEMBERS, EXCGETSET, \
+                                EXCSTR, EXCDOC) \
 static PyTypeObject _PyExc_ ## EXCNAME = { \
     PyVarObject_HEAD_INIT(NULL, 0) \
     # EXCNAME, \
@@ -447,9 +463,9 @@
     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, \
     PyDoc_STR(EXCDOC), (traverseproc)EXCSTORE ## _traverse, \
     (inquiry)EXCSTORE ## _clear, 0, 0, 0, 0, EXCMETHODS, \
-    EXCMEMBERS, 0, &_ ## EXCBASE, \
+    EXCMEMBERS, EXCGETSET, &_ ## EXCBASE, \
     0, 0, 0, offsetof(Py ## EXCSTORE ## Object, dict), \
-    (initproc)EXCSTORE ## _init, 0, BaseException_new,\
+    (initproc)EXCSTORE ## _init, 0, EXCNEW,\
 }; \
 PyObject *PyExc_ ## EXCNAME = (PyObject *)&_PyExc_ ## EXCNAME
 
@@ -534,7 +550,7 @@
 };
 
 ComplexExtendsException(PyExc_BaseException, SystemExit, SystemExit,
-                        SystemExit_dealloc, 0, SystemExit_members, 0,
+                        0, 0, SystemExit_members, 0, 0,
                         "Request to exit from the interpreter.");
 
 /*
@@ -552,124 +568,233 @@
 
 
 /*
- *    EnvironmentError extends Exception
+ *    OSError extends Exception
  */
 
+#ifdef MS_WINDOWS
+#include "errmap.h"
+#endif
+
 /* Where a function has a single filename, such as open() or some
  * of the os module functions, PyErr_SetFromErrnoWithFilename() is
  * called, giving a third argument which is the filename.  But, so
  * that old code using in-place unpacking doesn't break, e.g.:
  *
- * except IOError, (errno, strerror):
+ * except OSError, (errno, strerror):
  *
  * we hack args so that it only contains two items.  This also
  * means we need our own __str__() which prints out the filename
  * when it was supplied.
  */
-static int
-EnvironmentError_init(PyEnvironmentErrorObject *self, PyObject *args,
-    PyObject *kwds)
+
+static PyObject *
+OSError_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 {
+    PyOSErrorObject *self = NULL;
+    Py_ssize_t nargs;
+
     PyObject *myerrno = NULL, *strerror = NULL, *filename = NULL;
     PyObject *subslice = NULL;
+#ifdef MS_WINDOWS
+    PyObject *winerror = NULL;
+    long winerrcode = 0;
+#endif
 
-    if (BaseException_init((PyBaseExceptionObject *)self, args, kwds) == -1)
-        return -1;
+    if (!_PyArg_NoKeywords(type->tp_name, kwds))
+        return NULL;
+    Py_INCREF(args);
+    nargs = PyTuple_GET_SIZE(args);
 
-    if (PyTuple_GET_SIZE(args) <= 1 || PyTuple_GET_SIZE(args) > 3) {
-        return 0;
+#ifdef MS_WINDOWS
+    if (nargs >= 2 && nargs <= 4) {
+        if (!PyArg_UnpackTuple(args, "OSError", 2, 4,
+                               &myerrno, &strerror, &filename, &winerror))
+            goto error;
+        if (winerror && PyLong_Check(winerror)) {
+            long errcode;
+            PyObject *newargs;
+            Py_ssize_t i;
+
+            winerrcode = PyLong_AsLong(winerror);
+            if (winerrcode == -1 && PyErr_Occurred())
+                goto error;
+            /* Set errno to the corresponding POSIX errno (overriding
+               first argument).  Windows Socket error codes (>= 10000)
+               have the same value as their POSIX counterparts.
+            */
+            if (winerrcode < 10000)
+                errcode = winerror_to_errno(winerrcode);
+            else
+                errcode = winerrcode;
+            myerrno = PyLong_FromLong(errcode);
+            if (!myerrno)
+                goto error;
+            newargs = PyTuple_New(nargs);
+            if (!newargs)
+                goto error;
+            PyTuple_SET_ITEM(newargs, 0, myerrno);
+            for (i = 1; i < nargs; i++) {
+                PyObject *val = PyTuple_GET_ITEM(args, i);
+                Py_INCREF(val);
+                PyTuple_SET_ITEM(newargs, i, val);
+            }
+            Py_DECREF(args);
+            args = newargs;
+        }
+    }
+#else
+    if (nargs >= 2 && nargs <= 3) {
+        if (!PyArg_UnpackTuple(args, "OSError", 2, 3,
+                               &myerrno, &strerror, &filename))
+            goto error;
+    }
+#endif
+    if (myerrno && PyLong_Check(myerrno) &&
+        errnomap && (PyObject *) type == PyExc_OSError) {
+        PyObject *newtype;
+        newtype = PyDict_GetItem(errnomap, myerrno);
+        if (newtype) {
+            assert(PyType_Check(newtype));
+            type = (PyTypeObject *) newtype;
+        }
+        else if (PyErr_Occurred())
+            goto error;
     }
 
-    if (!PyArg_UnpackTuple(args, "EnvironmentError", 2, 3,
-                           &myerrno, &strerror, &filename)) {
-        return -1;
-    }
-    Py_CLEAR(self->myerrno);       /* replacing */
-    self->myerrno = myerrno;
-    Py_INCREF(self->myerrno);
+    self = (PyOSErrorObject *) type->tp_alloc(type, 0);
+    if (!self)
+        goto error;
 
-    Py_CLEAR(self->strerror);      /* replacing */
-    self->strerror = strerror;
-    Py_INCREF(self->strerror);
+    self->dict = NULL;
+    self->traceback = self->cause = self->context = NULL;
+    self->written = -1;
 
     /* self->filename will remain Py_None otherwise */
-    if (filename != NULL) {
-        Py_CLEAR(self->filename);      /* replacing */
-        self->filename = filename;
-        Py_INCREF(self->filename);
+    if (filename && filename != Py_None) {
+        if ((PyObject *) type == PyExc_BlockingIOError &&
+            PyNumber_Check(filename)) {
+            /* BlockingIOError's 3rd argument can be the number of
+             * characters written.
+             */
+            self->written = PyNumber_AsSsize_t(filename, PyExc_ValueError);
+            if (self->written == -1 && PyErr_Occurred())
+                goto error;
+        }
+        else {
+            Py_INCREF(filename);
+            self->filename = filename;
 
-        subslice = PyTuple_GetSlice(args, 0, 2);
-        if (!subslice)
-            return -1;
+            if (nargs >= 2 && nargs <= 3) {
+                /* filename is removed from the args tuple (for compatibility
+                   purposes, see test_exceptions.py) */
+                subslice = PyTuple_GetSlice(args, 0, 2);
+                if (!subslice)
+                    goto error;
 
-        Py_DECREF(self->args);  /* replacing args */
-        self->args = subslice;
+                Py_DECREF(args);  /* replacing args */
+                args = subslice;
+            }
+        }
     }
+
+    /* Steals the reference to args */
+    self->args = args;
+    args = NULL;
+
+    Py_XINCREF(myerrno);
+    self->myerrno = myerrno;
+
+    Py_XINCREF(strerror);
+    self->strerror = strerror;
+
+#ifdef MS_WINDOWS
+    Py_XINCREF(winerror);
+    self->winerror = winerror;
+#endif
+
+    return (PyObject *) self;
+
+error:
+    Py_XDECREF(args);
+    Py_XDECREF(self);
+    return NULL;
+}
+
+static int
+OSError_init(PySyntaxErrorObject *self, PyObject *args, PyObject *kwds)
+{
+    /* Everything already done in OSError_new */
     return 0;
 }
 
 static int
-EnvironmentError_clear(PyEnvironmentErrorObject *self)
+OSError_clear(PyOSErrorObject *self)
 {
     Py_CLEAR(self->myerrno);
     Py_CLEAR(self->strerror);
     Py_CLEAR(self->filename);
+#ifdef MS_WINDOWS
+    Py_CLEAR(self->winerror);
+#endif
     return BaseException_clear((PyBaseExceptionObject *)self);
 }
 
 static void
-EnvironmentError_dealloc(PyEnvironmentErrorObject *self)
+OSError_dealloc(PyOSErrorObject *self)
 {
     _PyObject_GC_UNTRACK(self);
-    EnvironmentError_clear(self);
+    OSError_clear(self);
     Py_TYPE(self)->tp_free((PyObject *)self);
 }
 
 static int
-EnvironmentError_traverse(PyEnvironmentErrorObject *self, visitproc visit,
+OSError_traverse(PyOSErrorObject *self, visitproc visit,
         void *arg)
 {
     Py_VISIT(self->myerrno);
     Py_VISIT(self->strerror);
     Py_VISIT(self->filename);
+#ifdef MS_WINDOWS
+    Py_VISIT(self->winerror);
+#endif
     return BaseException_traverse((PyBaseExceptionObject *)self, visit, arg);
 }
 
 static PyObject *
-EnvironmentError_str(PyEnvironmentErrorObject *self)
+OSError_str(PyOSErrorObject *self)
 {
+#ifdef MS_WINDOWS
+    /* If available, winerror has the priority over myerrno */
+    if (self->winerror && self->filename)
+        return PyUnicode_FromFormat("[Error %S] %S: %R",
+                                    self->winerror ? self->winerror: Py_None,
+                                    self->strerror ? self->strerror: Py_None,
+                                    self->filename);
+    if (self->winerror && self->strerror)
+        return PyUnicode_FromFormat("[Error %S] %S",
+                                    self->winerror ? self->winerror: Py_None,
+                                    self->strerror ? self->strerror: Py_None);
+#endif
     if (self->filename)
         return PyUnicode_FromFormat("[Errno %S] %S: %R",
                                     self->myerrno ? self->myerrno: Py_None,
                                     self->strerror ? self->strerror: Py_None,
                                     self->filename);
-    else if (self->myerrno && self->strerror)
+    if (self->myerrno && self->strerror)
         return PyUnicode_FromFormat("[Errno %S] %S",
                                     self->myerrno ? self->myerrno: Py_None,
                                     self->strerror ? self->strerror: Py_None);
-    else
-        return BaseException_str((PyBaseExceptionObject *)self);
+    return BaseException_str((PyBaseExceptionObject *)self);
 }
 
-static PyMemberDef EnvironmentError_members[] = {
-    {"errno", T_OBJECT, offsetof(PyEnvironmentErrorObject, myerrno), 0,
-        PyDoc_STR("exception errno")},
-    {"strerror", T_OBJECT, offsetof(PyEnvironmentErrorObject, strerror), 0,
-        PyDoc_STR("exception strerror")},
-    {"filename", T_OBJECT, offsetof(PyEnvironmentErrorObject, filename), 0,
-        PyDoc_STR("exception filename")},
-    {NULL}  /* Sentinel */
-};
-
-
 static PyObject *
-EnvironmentError_reduce(PyEnvironmentErrorObject *self)
+OSError_reduce(PyOSErrorObject *self)
 {
     PyObject *args = self->args;
     PyObject *res = NULL, *tmp;
 
     /* self->args is only the first two real arguments if there was a
-     * file name given to EnvironmentError. */
+     * file name given to OSError. */
     if (PyTuple_GET_SIZE(args) == 2 && self->filename) {
         args = PyTuple_New(3);
         if (!args) return NULL;
@@ -695,144 +820,93 @@
     return res;
 }
 
+static PyObject *
+OSError_written_get(PyOSErrorObject *self, void *context)
+{
+    if (self->written == -1) {
+        PyErr_SetString(PyExc_AttributeError, "characters_written");
+        return NULL;
+    }
+    return PyLong_FromSsize_t(self->written);
+}
 
-static PyMethodDef EnvironmentError_methods[] = {
-    {"__reduce__", (PyCFunction)EnvironmentError_reduce, METH_NOARGS},
+static int
+OSError_written_set(PyOSErrorObject *self, PyObject *arg, void *context)
+{
+    Py_ssize_t n;
+    n = PyNumber_AsSsize_t(arg, PyExc_ValueError);
+    if (n == -1 && PyErr_Occurred())
+        return -1;
+    self->written = n;
+    return 0;
+}
+
+static PyMemberDef OSError_members[] = {
+    {"errno", T_OBJECT, offsetof(PyOSErrorObject, myerrno), 0,
+        PyDoc_STR("POSIX exception code")},
+    {"strerror", T_OBJECT, offsetof(PyOSErrorObject, strerror), 0,
+        PyDoc_STR("exception strerror")},
+    {"filename", T_OBJECT, offsetof(PyOSErrorObject, filename), 0,
+        PyDoc_STR("exception filename")},
+#ifdef MS_WINDOWS
+    {"winerror", T_OBJECT, offsetof(PyOSErrorObject, winerror), 0,
+        PyDoc_STR("Win32 exception code")},
+#endif
+    {NULL}  /* Sentinel */
+};
+
+static PyMethodDef OSError_methods[] = {
+    {"__reduce__", (PyCFunction)OSError_reduce, METH_NOARGS},
     {NULL}
 };
 
-ComplexExtendsException(PyExc_Exception, EnvironmentError,
-                        EnvironmentError, EnvironmentError_dealloc,
-                        EnvironmentError_methods, EnvironmentError_members,
-                        EnvironmentError_str,
+static PyGetSetDef OSError_getset[] = {
+    {"characters_written", (getter) OSError_written_get,
+                           (setter) OSError_written_set, NULL},
+    {NULL}
+};
+
+
+ComplexExtendsException(PyExc_Exception, OSError,
+                        OSError, OSError_new,
+                        OSError_methods, OSError_members, OSError_getset,
+                        OSError_str,
                         "Base class for I/O related errors.");
 
 
 /*
- *    IOError extends EnvironmentError
+ *    Various OSError subclasses
  */
-MiddlingExtendsException(PyExc_EnvironmentError, IOError,
-                         EnvironmentError, "I/O operation failed.");
-
-
-/*
- *    OSError extends EnvironmentError
- */
-MiddlingExtendsException(PyExc_EnvironmentError, OSError,
-                         EnvironmentError, "OS system call failed.");
-
-
-/*
- *    WindowsError extends OSError
- */
-#ifdef MS_WINDOWS
-#include "errmap.h"
-
-static int
-WindowsError_clear(PyWindowsErrorObject *self)
-{
-    Py_CLEAR(self->myerrno);
-    Py_CLEAR(self->strerror);
-    Py_CLEAR(self->filename);
-    Py_CLEAR(self->winerror);
-    return BaseException_clear((PyBaseExceptionObject *)self);
-}
-
-static void
-WindowsError_dealloc(PyWindowsErrorObject *self)
-{
-    _PyObject_GC_UNTRACK(self);
-    WindowsError_clear(self);
-    Py_TYPE(self)->tp_free((PyObject *)self);
-}
-
-static int
-WindowsError_traverse(PyWindowsErrorObject *self, visitproc visit, void *arg)
-{
-    Py_VISIT(self->myerrno);
-    Py_VISIT(self->strerror);
-    Py_VISIT(self->filename);
-    Py_VISIT(self->winerror);
-    return BaseException_traverse((PyBaseExceptionObject *)self, visit, arg);
-}
-
-static int
-WindowsError_init(PyWindowsErrorObject *self, PyObject *args, PyObject *kwds)
-{
-    PyObject *o_errcode = NULL;
-    long errcode;
-    long posix_errno;
-
-    if (EnvironmentError_init((PyEnvironmentErrorObject *)self, args, kwds)
-            == -1)
-        return -1;
-
-    if (self->myerrno == NULL)
-        return 0;
-
-    /* Set errno to the POSIX errno, and winerror to the Win32
-       error code. */
-    errcode = PyLong_AsLong(self->myerrno);
-    if (errcode == -1 && PyErr_Occurred())
-        return -1;
-    posix_errno = winerror_to_errno(errcode);
-
-    Py_CLEAR(self->winerror);
-    self->winerror = self->myerrno;
-
-    o_errcode = PyLong_FromLong(posix_errno);
-    if (!o_errcode)
-        return -1;
-
-    self->myerrno = o_errcode;
-
-    return 0;
-}
-
-
-static PyObject *
-WindowsError_str(PyWindowsErrorObject *self)
-{
-    if (self->filename)
-        return PyUnicode_FromFormat("[Error %S] %S: %R",
-                                    self->winerror ? self->winerror: Py_None,
-                                    self->strerror ? self->strerror: Py_None,
-                                    self->filename);
-    else if (self->winerror && self->strerror)
-        return PyUnicode_FromFormat("[Error %S] %S",
-                                    self->winerror ? self->winerror: Py_None,
-                                    self->strerror ? self->strerror: Py_None);
-    else
-        return EnvironmentError_str((PyEnvironmentErrorObject *)self);
-}
-
-static PyMemberDef WindowsError_members[] = {
-    {"errno", T_OBJECT, offsetof(PyWindowsErrorObject, myerrno), 0,
-        PyDoc_STR("POSIX exception code")},
-    {"strerror", T_OBJECT, offsetof(PyWindowsErrorObject, strerror), 0,
-        PyDoc_STR("exception strerror")},
-    {"filename", T_OBJECT, offsetof(PyWindowsErrorObject, filename), 0,
-        PyDoc_STR("exception filename")},
-    {"winerror", T_OBJECT, offsetof(PyWindowsErrorObject, winerror), 0,
-        PyDoc_STR("Win32 exception code")},
-    {NULL}  /* Sentinel */
-};
-
-ComplexExtendsException(PyExc_OSError, WindowsError, WindowsError,
-                        WindowsError_dealloc, 0, WindowsError_members,
-                        WindowsError_str, "MS-Windows OS system call failed.");
-
-#endif /* MS_WINDOWS */
-
-
-/*
- *    VMSError extends OSError (I think)
- */
-#ifdef __VMS
-MiddlingExtendsException(PyExc_OSError, VMSError, EnvironmentError,
-                         "OpenVMS OS system call failed.");
-#endif
-
+MiddlingExtendsException(PyExc_OSError, BlockingIOError, OSError,
+                         "I/O operation would block.");
+MiddlingExtendsException(PyExc_OSError, ConnectionError, OSError,
+                         "Connection error.");
+MiddlingExtendsException(PyExc_OSError, ChildProcessError, OSError,
+                         "Child process error.");
+MiddlingExtendsException(PyExc_ConnectionError, BrokenPipeError, OSError,
+                         "Broken pipe.");
+MiddlingExtendsException(PyExc_ConnectionError, ConnectionAbortedError, OSError,
+                         "Connection aborted.");
+MiddlingExtendsException(PyExc_ConnectionError, ConnectionRefusedError, OSError,
+                         "Connection refused.");
+MiddlingExtendsException(PyExc_ConnectionError, ConnectionResetError, OSError,
+                         "Connection reset.");
+MiddlingExtendsException(PyExc_OSError, FileExistsError, OSError,
+                         "File already exists.");
+MiddlingExtendsException(PyExc_OSError, FileNotFoundError, OSError,
+                         "File not found.");
+MiddlingExtendsException(PyExc_OSError, IsADirectoryError, OSError,
+                         "Operation doesn't work on directories.");
+MiddlingExtendsException(PyExc_OSError, NotADirectoryError, OSError,
+                         "Operation only works on directories.");
+MiddlingExtendsException(PyExc_OSError, InterruptedError, OSError,
+                         "Interrupted by signal.");
+MiddlingExtendsException(PyExc_OSError, PermissionError, OSError,
+                         "Not enough permissions.");
+MiddlingExtendsException(PyExc_OSError, ProcessLookupError, OSError,
+                         "Process not found.");
+MiddlingExtendsException(PyExc_OSError, TimeoutError, OSError,
+                         "Timeout expired.");
 
 /*
  *    EOFError extends Exception
@@ -1046,7 +1120,7 @@
 };
 
 ComplexExtendsException(PyExc_Exception, SyntaxError, SyntaxError,
-                        SyntaxError_dealloc, 0, SyntaxError_members,
+                        0, 0, SyntaxError_members, 0,
                         SyntaxError_str, "Invalid syntax.");
 
 
@@ -1100,7 +1174,7 @@
 }
 
 ComplexExtendsException(PyExc_LookupError, KeyError, BaseException,
-                        0, 0, 0, KeyError_str, "Mapping key not found.");
+                        0, 0, 0, 0, KeyError_str, "Mapping key not found.");
 
 
 /*
@@ -1977,6 +2051,45 @@
     if (PyDict_SetItemString(bdict, # TYPE, PyExc_ ## TYPE)) \
         Py_FatalError("Module dictionary insertion problem.");
 
+#define INIT_ALIAS(NAME, TYPE) Py_INCREF(PyExc_ ## TYPE); \
+    PyExc_ ## NAME = PyExc_ ## TYPE; \
+    if (PyDict_SetItemString(bdict, # NAME, PyExc_ ## NAME)) \
+        Py_FatalError("Module dictionary insertion problem.");
+
+#define ADD_ERRNO(TYPE, CODE) { \
+    PyObject *_code = PyLong_FromLong(CODE); \
+    assert(_PyObject_RealIsSubclass(PyExc_ ## TYPE, PyExc_OSError)); \
+    if (!_code || PyDict_SetItem(errnomap, _code, PyExc_ ## TYPE)) \
+        Py_FatalError("errmap insertion problem."); \
+    }
+
+#ifdef MS_WINDOWS
+#include <Winsock2.h>
+#if defined(WSAEALREADY) && !defined(EALREADY)
+#define EALREADY WSAEALREADY
+#endif
+#if defined(WSAECONNABORTED) && !defined(ECONNABORTED)
+#define ECONNABORTED WSAECONNABORTED
+#endif
+#if defined(WSAECONNREFUSED) && !defined(ECONNREFUSED)
+#define ECONNREFUSED WSAECONNREFUSED
+#endif
+#if defined(WSAECONNRESET) && !defined(ECONNRESET)
+#define ECONNRESET WSAECONNRESET
+#endif
+#if defined(WSAEINPROGRESS) && !defined(EINPROGRESS)
+#define EINPROGRESS WSAEINPROGRESS
+#endif
+#if defined(WSAESHUTDOWN) && !defined(ESHUTDOWN)
+#define ESHUTDOWN WSAESHUTDOWN
+#endif
+#if defined(WSAETIMEDOUT) && !defined(ETIMEDOUT)
+#define ETIMEDOUT WSAETIMEDOUT
+#endif
+#if defined(WSAEWOULDBLOCK) && !defined(EWOULDBLOCK)
+#define EWOULDBLOCK WSAEWOULDBLOCK
+#endif
+#endif /* MS_WINDOWS */
 
 void
 _PyExc_Init(void)
@@ -1991,15 +2104,7 @@
     PRE_INIT(SystemExit)
     PRE_INIT(KeyboardInterrupt)
     PRE_INIT(ImportError)
-    PRE_INIT(EnvironmentError)
-    PRE_INIT(IOError)
     PRE_INIT(OSError)
-#ifdef MS_WINDOWS
-    PRE_INIT(WindowsError)
-#endif
-#ifdef __VMS
-    PRE_INIT(VMSError)
-#endif
     PRE_INIT(EOFError)
     PRE_INIT(RuntimeError)
     PRE_INIT(NotImplementedError)
@@ -2039,6 +2144,24 @@
     PRE_INIT(BytesWarning)
     PRE_INIT(ResourceWarning)
 
+    /* OSError subclasses */
+    PRE_INIT(ConnectionError);
+
+    PRE_INIT(BlockingIOError);
+    PRE_INIT(BrokenPipeError);
+    PRE_INIT(ChildProcessError);
+    PRE_INIT(ConnectionAbortedError);
+    PRE_INIT(ConnectionRefusedError);
+    PRE_INIT(ConnectionResetError);
+    PRE_INIT(FileExistsError);
+    PRE_INIT(FileNotFoundError);
+    PRE_INIT(IsADirectoryError);
+    PRE_INIT(NotADirectoryError);
+    PRE_INIT(InterruptedError);
+    PRE_INIT(PermissionError);
+    PRE_INIT(ProcessLookupError);
+    PRE_INIT(TimeoutError);
+
     bltinmod = PyImport_ImportModule("builtins");
     if (bltinmod == NULL)
         Py_FatalError("exceptions bootstrapping error.");
@@ -2054,14 +2177,14 @@
     POST_INIT(SystemExit)
     POST_INIT(KeyboardInterrupt)
     POST_INIT(ImportError)
-    POST_INIT(EnvironmentError)
-    POST_INIT(IOError)
     POST_INIT(OSError)
+    INIT_ALIAS(EnvironmentError, OSError)
+    INIT_ALIAS(IOError, OSError)
 #ifdef MS_WINDOWS
-    POST_INIT(WindowsError)
+    INIT_ALIAS(WindowsError, OSError)
 #endif
 #ifdef __VMS
-    POST_INIT(VMSError)
+    INIT_ALIAS(VMSError, OSError)
 #endif
     POST_INIT(EOFError)
     POST_INIT(RuntimeError)
@@ -2102,6 +2225,47 @@
     POST_INIT(BytesWarning)
     POST_INIT(ResourceWarning)
 
+    errnomap = PyDict_New();
+    if (!errnomap)
+        Py_FatalError("Cannot allocate map from errnos to OSError subclasses");
+
+    /* OSError subclasses */
+    POST_INIT(ConnectionError);
+
+    POST_INIT(BlockingIOError);
+    ADD_ERRNO(BlockingIOError, EAGAIN);
+    ADD_ERRNO(BlockingIOError, EALREADY);
+    ADD_ERRNO(BlockingIOError, EINPROGRESS);
+    ADD_ERRNO(BlockingIOError, EWOULDBLOCK);
+    POST_INIT(BrokenPipeError);
+    ADD_ERRNO(BrokenPipeError, EPIPE);
+    ADD_ERRNO(BrokenPipeError, ESHUTDOWN);
+    POST_INIT(ChildProcessError);
+    ADD_ERRNO(ChildProcessError, ECHILD);
+    POST_INIT(ConnectionAbortedError);
+    ADD_ERRNO(ConnectionAbortedError, ECONNABORTED);
+    POST_INIT(ConnectionRefusedError);
+    ADD_ERRNO(ConnectionRefusedError, ECONNREFUSED);
+    POST_INIT(ConnectionResetError);
+    ADD_ERRNO(ConnectionResetError, ECONNRESET);
+    POST_INIT(FileExistsError);
+    ADD_ERRNO(FileExistsError, EEXIST);
+    POST_INIT(FileNotFoundError);
+    ADD_ERRNO(FileNotFoundError, ENOENT);
+    POST_INIT(IsADirectoryError);
+    ADD_ERRNO(IsADirectoryError, EISDIR);
+    POST_INIT(NotADirectoryError);
+    ADD_ERRNO(NotADirectoryError, ENOTDIR);
+    POST_INIT(InterruptedError);
+    ADD_ERRNO(InterruptedError, EINTR);
+    POST_INIT(PermissionError);
+    ADD_ERRNO(PermissionError, EACCES);
+    ADD_ERRNO(PermissionError, EPERM);
+    POST_INIT(ProcessLookupError);
+    ADD_ERRNO(ProcessLookupError, ESRCH);
+    POST_INIT(TimeoutError);
+    ADD_ERRNO(TimeoutError, ETIMEDOUT);
+
     preallocate_memerrors();
 
     PyExc_RecursionErrorInst = BaseException_new(&_PyExc_RuntimeError, NULL, NULL);
@@ -2135,4 +2299,5 @@
 {
     Py_CLEAR(PyExc_RecursionErrorInst);
     free_preallocated_memerrors();
+    Py_CLEAR(errnomap);
 }
diff --git a/Python/errors.c b/Python/errors.c
--- a/Python/errors.c
+++ b/Python/errors.c
@@ -496,10 +496,11 @@
         return NULL;
     }
 
-    if (filenameObject != NULL)
-        v = Py_BuildValue("(iOO)", err, message, filenameObject);
-    else
-        v = Py_BuildValue("(iO)", err, message);
+    if (filenameObject == NULL)
+        filenameObject = Py_None;
+    /* This is the constructor signature for passing a Windows error code.
+       The POSIX translation will be figured out by the constructor. */
+    v = Py_BuildValue("(iOOi)", 0, message, filenameObject, err);
     Py_DECREF(message);
 
     if (v != NULL) {

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list