From 6b4883dec0b7f6c5ede45dca861f5dc0e4ff2be7 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Wed, 12 Oct 2011 02:54:14 +0200 Subject: [PATCH] PEP 3151 / issue #12555: reworking the OS and IO exception hierarchy. --- Include/pyerrors.h | 42 ++- 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, 689 insertions(+), 454 deletions(-) create mode 100644 Lib/test/test_pep3151.py diff --git a/Include/pyerrors.h b/Include/pyerrors.h index 6ca833d4fce..44eb3d9fc22 100644 --- a/Include/pyerrors.h +++ b/Include/pyerrors.h @@ -45,18 +45,18 @@ typedef struct { PyObject *myerrno; PyObject *strerror; PyObject *filename; -} PyEnvironmentErrorObject; - #ifdef MS_WINDOWS -typedef struct { - PyException_HEAD - PyObject *myerrno; - PyObject *strerror; - PyObject *filename; PyObject *winerror; -} PyWindowsErrorObject; #endif + Py_ssize_t written; /* only for BlockingIOError, -1 otherwise */ +} PyOSErrorObject; + +/* Compatibility typedefs */ +typedef PyOSErrorObject PyEnvironmentErrorObject; +#ifdef MS_WINDOWS +typedef PyOSErrorObject PyWindowsErrorObject; #endif +#endif /* !Py_LIMITED_API */ /* Error handling definitions */ @@ -132,10 +132,9 @@ PyAPI_DATA(PyObject *) PyExc_LookupError; 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_UnicodeDecodeError; 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_WindowsError; 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 index a9c31d59eef..0611bd6f820 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -23,16 +23,8 @@ DEFAULT_BUFFER_SIZE = 8 * 1024 # bytes # 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 index 13d3d777f2e..0c9695830ea 100644 --- a/Lib/multiprocessing/connection.py +++ b/Lib/multiprocessing/connection.py @@ -321,7 +321,7 @@ if win32: 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 @@ if sys.platform == 'win32': 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 @@ if sys.platform == 'win32': 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 index 5037b335d97..1c1f69f36b3 100644 --- a/Lib/test/exception_hierarchy.txt +++ b/Lib/test/exception_hierarchy.txt @@ -11,11 +11,6 @@ BaseException +-- AssertionError +-- AttributeError +-- BufferError - +-- EnvironmentError - | +-- IOError - | +-- OSError - | +-- WindowsError (Windows) - | +-- VMSError (VMS) +-- EOFError +-- ImportError +-- LookupError @@ -24,6 +19,22 @@ BaseException +-- 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 index 78a9906d345..7522a54acbb 100644 --- a/Lib/test/test_concurrent_futures.py +++ b/Lib/test/test_concurrent_futures.py @@ -34,7 +34,7 @@ PENDING_FUTURE = create_future(state=PENDING) 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 @@ class FutureTests(unittest.TestCase): '') self.assertRegex( repr(EXCEPTION_FUTURE), - '') + '') self.assertRegex( repr(SUCCESSFUL_FUTURE), '') @@ -512,7 +512,7 @@ class FutureTests(unittest.TestCase): 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 @@ class FutureTests(unittest.TestCase): 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 @@ class FutureTests(unittest.TestCase): 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 @@ class FutureTests(unittest.TestCase): 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 index 9be6958820c..a7683ac946f 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -46,8 +46,8 @@ class ExceptionTests(unittest.TestCase): 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 @@ class ExceptionTests(unittest.TestCase): 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 @@ class ExceptionTests(unittest.TestCase): '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 index 41e0dfde330..a35ec95a698 100644 --- a/Lib/test/test_http_cookiejar.py +++ b/Lib/test/test_http_cookiejar.py @@ -248,18 +248,19 @@ class FileCookieJarTests(unittest.TestCase): 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 index a9e3c37c719..0debc8062fb 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -2643,12 +2643,6 @@ class MiscIOTest(unittest.TestCase): 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 index 1cfcee67c54..2230028f5b7 100644 --- a/Lib/test/test_mmap.py +++ b/Lib/test/test_mmap.py @@ -563,8 +563,7 @@ class MmapTests(unittest.TestCase): 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 index 00000000000..9d92425ac29 --- /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 index 40c22917c07..4dd8d224959 100644 --- a/Lib/test/test_xml_etree.py +++ b/Lib/test/test_xml_etree.py @@ -1339,7 +1339,7 @@ def xinclude_loader(href, parse="xml", encoding=None): 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 @@ def xinclude(): >>> 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 @@ def bug_xmltoolkit55(): class ExceptionFile: def read(self, x): - raise IOError + raise OSError def xmltoolkit60(): """ @@ -1619,7 +1619,7 @@ def xmltoolkit60(): 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 index 671ab6894b5..a947608b117 100644 --- a/Lib/urllib/request.py +++ b/Lib/urllib/request.py @@ -1547,6 +1547,8 @@ class URLopener: 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 index 9a8e46c59e3..3b0d973ed45 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,8 @@ What's New in Python 3.3 Alpha 1? 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 index f264756f3d3..9061a41f4e2 100644 --- a/Modules/_io/_iomodule.c +++ b/Modules/_io/_iomodule.c @@ -90,89 +90,6 @@ PyDoc_STRVAR(module_doc, ); -/* - * 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 */ @@ -694,9 +611,11 @@ PyInit__io(void) 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 index 4e97dd12e51..b3a8471bc57 100644 --- a/Modules/_io/_iomodule.h +++ b/Modules/_io/_iomodule.h @@ -60,15 +60,6 @@ extern Py_ssize_t _PyIO_find_line_ending( #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 index 6ef2c206a23..c72bfb9096f 100644 --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -622,14 +622,14 @@ static Py_ssize_t * _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 index f4a80c23792..35d61ba940a 100644 --- a/Modules/mmapmodule.c +++ b/Modules/mmapmodule.c @@ -78,8 +78,6 @@ my_getpagesize(void) # define MAP_ANONYMOUS MAP_ANON #endif -static PyObject *mmap_module_error; - typedef enum { ACCESS_DEFAULT, @@ -459,7 +457,7 @@ mmap_size_method(mmap_object *self, { 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 @@ mmap_resize_method(mmap_object *self, 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 @@ mmap_resize_method(mmap_object *self, #endif if (newmap == (void *)-1) { - PyErr_SetFromErrno(mmap_module_error); + PyErr_SetFromErrno(PyExc_OSError); return NULL; } self->data = newmap; @@ -605,7 +603,7 @@ mmap_flush_method(mmap_object *self, PyObject *args) /* 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 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict) 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 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict) 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 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict) 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 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict) 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 @@ PyInit_mmap(void) 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 index e594b51f23e..6071144cac3 100644 --- a/Modules/selectmodule.c +++ b/Modules/selectmodule.c @@ -54,8 +54,6 @@ extern void bzero(void *, int); # endif #endif -static PyObject *SelectError; - /* list of Python objects and their file descriptor */ typedef struct { PyObject *obj; /* owned reference */ @@ -274,11 +272,11 @@ select_select(PyObject *self, PyObject *args) #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 @@ poll_modify(pollObject *self, PyObject *args) 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 @@ poll_poll(pollObject *self, PyObject *args) Py_END_ALLOW_THREADS if (poll_result < 0) { - PyErr_SetFromErrno(SelectError); + PyErr_SetFromErrno(PyExc_OSError); return NULL; } @@ -764,7 +762,7 @@ newPyEpoll_Object(PyTypeObject *type, int sizehint, SOCKET fd) } if (self->epfd < 0) { Py_DECREF(self); - PyErr_SetFromErrno(PyExc_IOError); + PyErr_SetFromErrno(PyExc_OSError); return NULL; } return (PyObject *)self; @@ -797,7 +795,7 @@ pyepoll_close(pyEpoll_Object *self) { 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 @@ pyepoll_internal_ctl(int epfd, int op, PyObject *pfd, unsigned int events) } if (result < 0) { - PyErr_SetFromErrno(PyExc_IOError); + PyErr_SetFromErrno(PyExc_OSError); return NULL; } Py_RETURN_NONE; @@ -914,7 +912,7 @@ pyepoll_register(pyEpoll_Object *self, PyObject *args, PyObject *kwds) 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 @@ pyepoll_poll(pyEpoll_Object *self, PyObject *args, PyObject *kwds) 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 @@ newKqueue_Object(PyTypeObject *type, SOCKET fd) } if (self->kqfd < 0) { Py_DECREF(self); - PyErr_SetFromErrno(PyExc_IOError); + PyErr_SetFromErrno(PyExc_OSError); return NULL; } return (PyObject *)self; @@ -1436,7 +1434,7 @@ kqueue_queue_close(kqueue_queue_Object *self) { 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 @@ PyInit_select(void) 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 index 6d0a44c171a..2056b61a9b2 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -457,7 +457,6 @@ dup_socket(SOCKET handle) /* 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 PyTypeObject sock_type; 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 @@ set_error(void) 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 @@ set_error(void) } 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 @@ set_error(void) } #endif - return PyErr_SetFromErrno(socket_error); + return PyErr_SetFromErrno(PyExc_OSError); } @@ -883,13 +882,13 @@ setipaddr(char *name, struct sockaddr *addr_ret, size_t addr_ret_size, int af) #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 @@ setipaddr(char *name, struct sockaddr *addr_ret, size_t addr_ret_size, int af) if (name[0] == '<' && strcmp(name, "") == 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 @@ setipaddr(char *name, struct sockaddr *addr_ret, size_t addr_ret_size, int af) return 16; #endif default: - PyErr_SetString(socket_error, "unknown address family"); + PyErr_SetString(PyExc_OSError, "unknown address family"); return -1; } } @@ -1009,7 +1008,7 @@ setbdaddr(char *name, bdaddr_t *bdaddr) 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 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args, 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 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args, { /* 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 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args, _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 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args, _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 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args, _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 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args, #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 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args, 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 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args, } #endif default: - PyErr_SetString(socket_error, "getsockaddrarg: unknown Bluetooth protocol"); + PyErr_SetString(PyExc_OSError, "getsockaddrarg: unknown Bluetooth protocol"); return 0; } } @@ -1633,7 +1632,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args, 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 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args, return 1; } default: - PyErr_SetString(socket_error, + PyErr_SetString(PyExc_OSError, "getsockaddrarg: unsupported CAN protocol"); return 0; } @@ -1656,7 +1655,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args, /* More cases here... */ default: - PyErr_SetString(socket_error, "getsockaddrarg: bad family"); + PyErr_SetString(PyExc_OSError, "getsockaddrarg: bad family"); return 0; } @@ -1722,7 +1721,7 @@ getsockaddrlen(PySocketSockObject *s, socklen_t *len_ret) return 1; #endif default: - PyErr_SetString(socket_error, "getsockaddrlen: " + PyErr_SetString(PyExc_OSError, "getsockaddrlen: " "unknown BT protocol"); return 0; @@ -1757,7 +1756,7 @@ getsockaddrlen(PySocketSockObject *s, socklen_t *len_ret) /* More cases here... */ default: - PyErr_SetString(socket_error, "getsockaddrlen: bad family"); + PyErr_SetString(PyExc_OSError, "getsockaddrlen: bad family"); return 0; } @@ -2098,7 +2097,7 @@ sock_getsockopt(PySocketSockObject *s, PyObject *args) #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 @@ sock_recvmsg_guts(PySocketSockObject *s, struct iovec *iov, int iovlen, 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 @@ sock_recvmsg_into(PySocketSockObject *s, PyObject *args) 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 @@ sock_sendmsg(PySocketSockObject *s, PyObject *args) 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 @@ sock_sendmsg(PySocketSockObject *s, PyObject *args) #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 @@ sock_sendmsg(PySocketSockObject *s, PyObject *args) #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 @@ gethost_common(struct hostent *h, struct sockaddr *addr, int alen, int af) 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 @@ gethost_common(struct hostent *h, struct sockaddr *addr, int alen, int af) #endif default: /* can't happen */ - PyErr_SetString(socket_error, + PyErr_SetString(PyExc_OSError, "unsupported address family"); return NULL; } @@ -4239,7 +4238,7 @@ socket_gethostbyaddr(PyObject *self, PyObject *args) 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 @@ socket_getservbyname(PyObject *self, PyObject *args) 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 @@ socket_getservbyport(PyObject *self, PyObject *args) 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 @@ socket_getprotobyname(PyObject *self, PyObject *args) 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 @@ socket_inet_aton(PyObject *self, PyObject *args) 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 @@ socket_inet_aton(PyObject *self, PyObject *args) 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 @@ socket_inet_ntoa(PyObject *self, PyObject *args) } 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 @@ socket_inet_pton(PyObject *self, PyObject *args) #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 @@ socket_inet_pton(PyObject *self, PyObject *args) 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 @@ socket_inet_pton(PyObject *self, PyObject *args) 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 @@ socket_inet_ntop(PyObject *self, PyObject *args) 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 @@ socket_getaddrinfo(PyObject *self, PyObject *args, PyObject* kwargs) } 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 @@ socket_getnameinfo(PyObject *self, PyObject *args) 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 @@ socket_getnameinfo(PyObject *self, PyObject *args) 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 @@ socket_if_nameindex(PyObject *self, PyObject *arg) ni = if_nameindex(); if (ni == NULL) { - PyErr_SetFromErrno(socket_error); + PyErr_SetFromErrno(PyExc_OSError); return NULL; } @@ -5100,7 +5099,7 @@ socket_if_nametoindex(PyObject *self, PyObject *args) 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 @@ socket_if_indextoname(PyObject *self, PyObject *arg) return NULL; if (if_indextoname(index, name) == NULL) { - PyErr_SetFromErrno(socket_error); + PyErr_SetFromErrno(PyExc_OSError); return NULL; } @@ -5403,27 +5402,24 @@ PyInit__socket(void) 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 index 60b340b9a32..ad618ff06b1 100644 --- 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 @@ static PyTypeObject _PyExc_ ## EXCNAME = { \ 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 @@ static PyTypeObject _PyExc_ ## EXCNAME = { \ 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 @@ static PyMemberDef SystemExit_members[] = { }; 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 @@ SimpleExtendsException(PyExc_Exception, ImportError, /* - * 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 @@ EnvironmentError_reduce(PyEnvironmentErrorObject *self) 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 @@ static PyMemberDef SyntaxError_members[] = { }; ComplexExtendsException(PyExc_Exception, SyntaxError, SyntaxError, - SyntaxError_dealloc, 0, SyntaxError_members, + 0, 0, SyntaxError_members, 0, SyntaxError_str, "Invalid syntax."); @@ -1100,7 +1174,7 @@ KeyError_str(PyBaseExceptionObject *self) } 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 @@ PyObject *PyExc_RecursionErrorInst = NULL; 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 +#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 @@ _PyExc_Init(void) 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 @@ _PyExc_Init(void) 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 @@ _PyExc_Init(void) 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 @@ _PyExc_Init(void) 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 @@ _PyExc_Fini(void) { Py_CLEAR(PyExc_RecursionErrorInst); free_preallocated_memerrors(); + Py_CLEAR(errnomap); } diff --git a/Python/errors.c b/Python/errors.c index 02f84b46ac2..6e69d23c305 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -496,10 +496,11 @@ PyObject *PyErr_SetExcFromWindowsErrWithFilenameObject( 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) {