Commit 6b4883de authored by Antoine Pitrou's avatar Antoine Pitrou

PEP 3151 / issue #12555: reworking the OS and IO exception hierarchy.

parent 983b1434
...@@ -45,18 +45,18 @@ typedef struct { ...@@ -45,18 +45,18 @@ typedef struct {
PyObject *myerrno; PyObject *myerrno;
PyObject *strerror; PyObject *strerror;
PyObject *filename; PyObject *filename;
} PyEnvironmentErrorObject;
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
typedef struct {
PyException_HEAD
PyObject *myerrno;
PyObject *strerror;
PyObject *filename;
PyObject *winerror; PyObject *winerror;
} PyWindowsErrorObject;
#endif #endif
Py_ssize_t written; /* only for BlockingIOError, -1 otherwise */
} PyOSErrorObject;
/* Compatibility typedefs */
typedef PyOSErrorObject PyEnvironmentErrorObject;
#ifdef MS_WINDOWS
typedef PyOSErrorObject PyWindowsErrorObject;
#endif #endif
#endif /* !Py_LIMITED_API */
/* Error handling definitions */ /* Error handling definitions */
...@@ -132,10 +132,9 @@ PyAPI_DATA(PyObject *) PyExc_LookupError; ...@@ -132,10 +132,9 @@ PyAPI_DATA(PyObject *) PyExc_LookupError;
PyAPI_DATA(PyObject *) PyExc_AssertionError; PyAPI_DATA(PyObject *) PyExc_AssertionError;
PyAPI_DATA(PyObject *) PyExc_AttributeError; PyAPI_DATA(PyObject *) PyExc_AttributeError;
PyAPI_DATA(PyObject *) PyExc_BufferError;
PyAPI_DATA(PyObject *) PyExc_EOFError; PyAPI_DATA(PyObject *) PyExc_EOFError;
PyAPI_DATA(PyObject *) PyExc_FloatingPointError; PyAPI_DATA(PyObject *) PyExc_FloatingPointError;
PyAPI_DATA(PyObject *) PyExc_EnvironmentError;
PyAPI_DATA(PyObject *) PyExc_IOError;
PyAPI_DATA(PyObject *) PyExc_OSError; PyAPI_DATA(PyObject *) PyExc_OSError;
PyAPI_DATA(PyObject *) PyExc_ImportError; PyAPI_DATA(PyObject *) PyExc_ImportError;
PyAPI_DATA(PyObject *) PyExc_IndexError; PyAPI_DATA(PyObject *) PyExc_IndexError;
...@@ -160,6 +159,27 @@ PyAPI_DATA(PyObject *) PyExc_UnicodeDecodeError; ...@@ -160,6 +159,27 @@ PyAPI_DATA(PyObject *) PyExc_UnicodeDecodeError;
PyAPI_DATA(PyObject *) PyExc_UnicodeTranslateError; PyAPI_DATA(PyObject *) PyExc_UnicodeTranslateError;
PyAPI_DATA(PyObject *) PyExc_ValueError; PyAPI_DATA(PyObject *) PyExc_ValueError;
PyAPI_DATA(PyObject *) PyExc_ZeroDivisionError; 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 #ifdef MS_WINDOWS
PyAPI_DATA(PyObject *) PyExc_WindowsError; PyAPI_DATA(PyObject *) PyExc_WindowsError;
#endif #endif
...@@ -167,8 +187,6 @@ PyAPI_DATA(PyObject *) PyExc_WindowsError; ...@@ -167,8 +187,6 @@ PyAPI_DATA(PyObject *) PyExc_WindowsError;
PyAPI_DATA(PyObject *) PyExc_VMSError; PyAPI_DATA(PyObject *) PyExc_VMSError;
#endif #endif
PyAPI_DATA(PyObject *) PyExc_BufferError;
PyAPI_DATA(PyObject *) PyExc_RecursionErrorInst; PyAPI_DATA(PyObject *) PyExc_RecursionErrorInst;
/* Predefined warning categories */ /* Predefined warning categories */
......
...@@ -23,16 +23,8 @@ DEFAULT_BUFFER_SIZE = 8 * 1024 # bytes ...@@ -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 # defined in io.py. We don't use real inheritance though, because we don't
# want to inherit the C implementations. # want to inherit the C implementations.
# Rebind for compatibility
class BlockingIOError(IOError): BlockingIOError = BlockingIOError
"""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
def open(file, mode="r", buffering=-1, encoding=None, errors=None, def open(file, mode="r", buffering=-1, encoding=None, errors=None,
......
...@@ -321,7 +321,7 @@ if win32: ...@@ -321,7 +321,7 @@ if win32:
firstchunk = overlapped.getbuffer() firstchunk = overlapped.getbuffer()
assert lenfirstchunk == len(firstchunk) assert lenfirstchunk == len(firstchunk)
except IOError as e: except IOError as e:
if e.errno == win32.ERROR_BROKEN_PIPE: if e.winerror == win32.ERROR_BROKEN_PIPE:
raise EOFError raise EOFError
raise raise
buf.write(firstchunk) buf.write(firstchunk)
...@@ -669,7 +669,7 @@ if sys.platform == 'win32': ...@@ -669,7 +669,7 @@ if sys.platform == 'win32':
try: try:
win32.ConnectNamedPipe(handle, win32.NULL) win32.ConnectNamedPipe(handle, win32.NULL)
except WindowsError as e: except WindowsError as e:
if e.args[0] != win32.ERROR_PIPE_CONNECTED: if e.winerror != win32.ERROR_PIPE_CONNECTED:
raise raise
return PipeConnection(handle) return PipeConnection(handle)
...@@ -692,7 +692,7 @@ if sys.platform == 'win32': ...@@ -692,7 +692,7 @@ if sys.platform == 'win32':
0, win32.NULL, win32.OPEN_EXISTING, 0, win32.NULL 0, win32.NULL, win32.OPEN_EXISTING, 0, win32.NULL
) )
except WindowsError as e: except WindowsError as e:
if e.args[0] not in (win32.ERROR_SEM_TIMEOUT, if e.winerror not in (win32.ERROR_SEM_TIMEOUT,
win32.ERROR_PIPE_BUSY) or _check_timeout(t): win32.ERROR_PIPE_BUSY) or _check_timeout(t):
raise raise
else: else:
......
...@@ -11,11 +11,6 @@ BaseException ...@@ -11,11 +11,6 @@ BaseException
+-- AssertionError +-- AssertionError
+-- AttributeError +-- AttributeError
+-- BufferError +-- BufferError
+-- EnvironmentError
| +-- IOError
| +-- OSError
| +-- WindowsError (Windows)
| +-- VMSError (VMS)
+-- EOFError +-- EOFError
+-- ImportError +-- ImportError
+-- LookupError +-- LookupError
...@@ -24,6 +19,22 @@ BaseException ...@@ -24,6 +19,22 @@ BaseException
+-- MemoryError +-- MemoryError
+-- NameError +-- NameError
| +-- UnboundLocalError | +-- UnboundLocalError
+-- OSError
| +-- BlockingIOError
| +-- ChildProcessError
| +-- ConnectionError
| | +-- BrokenPipeError
| | +-- ConnectionAbortedError
| | +-- ConnectionRefusedError
| | +-- ConnectionResetError
| +-- FileExistsError
| +-- FileNotFoundError
| +-- InterruptedError
| +-- IsADirectoryError
| +-- NotADirectoryError
| +-- PermissionError
| +-- ProcessLookupError
| +-- TimeoutError
+-- ReferenceError +-- ReferenceError
+-- RuntimeError +-- RuntimeError
| +-- NotImplementedError | +-- NotImplementedError
......
...@@ -34,7 +34,7 @@ PENDING_FUTURE = create_future(state=PENDING) ...@@ -34,7 +34,7 @@ PENDING_FUTURE = create_future(state=PENDING)
RUNNING_FUTURE = create_future(state=RUNNING) RUNNING_FUTURE = create_future(state=RUNNING)
CANCELLED_FUTURE = create_future(state=CANCELLED) CANCELLED_FUTURE = create_future(state=CANCELLED)
CANCELLED_AND_NOTIFIED_FUTURE = create_future(state=CANCELLED_AND_NOTIFIED) 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) SUCCESSFUL_FUTURE = create_future(state=FINISHED, result=42)
...@@ -501,7 +501,7 @@ class FutureTests(unittest.TestCase): ...@@ -501,7 +501,7 @@ class FutureTests(unittest.TestCase):
'<Future at 0x[0-9a-f]+ state=cancelled>') '<Future at 0x[0-9a-f]+ state=cancelled>')
self.assertRegex( self.assertRegex(
repr(EXCEPTION_FUTURE), 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( self.assertRegex(
repr(SUCCESSFUL_FUTURE), repr(SUCCESSFUL_FUTURE),
'<Future at 0x[0-9a-f]+ state=finished returned int>') '<Future at 0x[0-9a-f]+ state=finished returned int>')
...@@ -512,7 +512,7 @@ class FutureTests(unittest.TestCase): ...@@ -512,7 +512,7 @@ class FutureTests(unittest.TestCase):
f2 = create_future(state=RUNNING) f2 = create_future(state=RUNNING)
f3 = create_future(state=CANCELLED) f3 = create_future(state=CANCELLED)
f4 = create_future(state=CANCELLED_AND_NOTIFIED) 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) f6 = create_future(state=FINISHED, result=5)
self.assertTrue(f1.cancel()) self.assertTrue(f1.cancel())
...@@ -566,7 +566,7 @@ class FutureTests(unittest.TestCase): ...@@ -566,7 +566,7 @@ class FutureTests(unittest.TestCase):
CANCELLED_FUTURE.result, timeout=0) CANCELLED_FUTURE.result, timeout=0)
self.assertRaises(futures.CancelledError, self.assertRaises(futures.CancelledError,
CANCELLED_AND_NOTIFIED_FUTURE.result, timeout=0) 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) self.assertEqual(SUCCESSFUL_FUTURE.result(timeout=0), 42)
def test_result_with_success(self): def test_result_with_success(self):
...@@ -605,7 +605,7 @@ class FutureTests(unittest.TestCase): ...@@ -605,7 +605,7 @@ class FutureTests(unittest.TestCase):
self.assertRaises(futures.CancelledError, self.assertRaises(futures.CancelledError,
CANCELLED_AND_NOTIFIED_FUTURE.exception, timeout=0) CANCELLED_AND_NOTIFIED_FUTURE.exception, timeout=0)
self.assertTrue(isinstance(EXCEPTION_FUTURE.exception(timeout=0), self.assertTrue(isinstance(EXCEPTION_FUTURE.exception(timeout=0),
IOError)) OSError))
self.assertEqual(SUCCESSFUL_FUTURE.exception(timeout=0), None) self.assertEqual(SUCCESSFUL_FUTURE.exception(timeout=0), None)
def test_exception_with_success(self): def test_exception_with_success(self):
...@@ -614,14 +614,14 @@ class FutureTests(unittest.TestCase): ...@@ -614,14 +614,14 @@ class FutureTests(unittest.TestCase):
time.sleep(1) time.sleep(1)
with f1._condition: with f1._condition:
f1._state = FINISHED f1._state = FINISHED
f1._exception = IOError() f1._exception = OSError()
f1._condition.notify_all() f1._condition.notify_all()
f1 = create_future(state=PENDING) f1 = create_future(state=PENDING)
t = threading.Thread(target=notification) t = threading.Thread(target=notification)
t.start() t.start()
self.assertTrue(isinstance(f1.exception(timeout=5), IOError)) self.assertTrue(isinstance(f1.exception(timeout=5), OSError))
@test.support.reap_threads @test.support.reap_threads
def test_main(): def test_main():
......
...@@ -46,8 +46,8 @@ class ExceptionTests(unittest.TestCase): ...@@ -46,8 +46,8 @@ class ExceptionTests(unittest.TestCase):
fp.close() fp.close()
unlink(TESTFN) unlink(TESTFN)
self.raise_catch(IOError, "IOError") self.raise_catch(OSError, "OSError")
self.assertRaises(IOError, open, 'this file does not exist', 'r') self.assertRaises(OSError, open, 'this file does not exist', 'r')
self.raise_catch(ImportError, "ImportError") self.raise_catch(ImportError, "ImportError")
self.assertRaises(ImportError, __import__, "undefined_module") self.assertRaises(ImportError, __import__, "undefined_module")
...@@ -192,11 +192,35 @@ class ExceptionTests(unittest.TestCase): ...@@ -192,11 +192,35 @@ class ExceptionTests(unittest.TestCase):
except NameError: except NameError:
pass pass
else: else:
self.assertEqual(str(WindowsError(1001)), "1001") self.assertIs(WindowsError, OSError)
self.assertEqual(str(WindowsError(1001, "message")), self.assertEqual(str(OSError(1001)), "1001")
"[Error 1001] message") self.assertEqual(str(OSError(1001, "message")),
self.assertEqual(WindowsError(1001, "message").errno, 22) "[Errno 1001] message")
self.assertEqual(WindowsError(1001, "message").winerror, 1001) # 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): def testAttributes(self):
# test that exception attributes are happy # test that exception attributes are happy
...@@ -274,11 +298,12 @@ class ExceptionTests(unittest.TestCase): ...@@ -274,11 +298,12 @@ class ExceptionTests(unittest.TestCase):
'start' : 0, 'end' : 1}), 'start' : 0, 'end' : 1}),
] ]
try: try:
# More tests are in test_WindowsError
exceptionList.append( exceptionList.append(
(WindowsError, (1, 'strErrorStr', 'filenameStr'), (WindowsError, (1, 'strErrorStr', 'filenameStr'),
{'args' : (1, 'strErrorStr'), {'args' : (1, 'strErrorStr'),
'strerror' : 'strErrorStr', 'winerror' : 1, 'strerror' : 'strErrorStr', 'winerror' : None,
'errno' : 22, 'filename' : 'filenameStr'}) 'errno' : 1, 'filename' : 'filenameStr'})
) )
except NameError: except NameError:
pass pass
......
...@@ -248,18 +248,19 @@ class FileCookieJarTests(unittest.TestCase): ...@@ -248,18 +248,19 @@ class FileCookieJarTests(unittest.TestCase):
self.assertEqual(c._cookies["www.acme.com"]["/"]["boo"].value, None) self.assertEqual(c._cookies["www.acme.com"]["/"]["boo"].value, None)
def test_bad_magic(self): 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 filename = test.support.TESTFN
for cookiejar_class in LWPCookieJar, MozillaCookieJar: for cookiejar_class in LWPCookieJar, MozillaCookieJar:
c = cookiejar_class() c = cookiejar_class()
try: try:
c.load(filename="for this test to work, a file with this " c.load(filename="for this test to work, a file with this "
"filename should not exist") "filename should not exist")
except IOError as exc: except OSError as exc:
# exactly IOError, not LoadError # an OSError subclass (likely FileNotFoundError), but not
self.assertIs(exc.__class__, IOError) # LoadError
self.assertIsNot(exc.__class__, LoadError)
else: else:
self.fail("expected IOError for invalid filename") self.fail("expected OSError for invalid filename")
# Invalid contents of cookies file (eg. bad magic string) # Invalid contents of cookies file (eg. bad magic string)
# causes a LoadError. # causes a LoadError.
try: try:
......
...@@ -2643,12 +2643,6 @@ class MiscIOTest(unittest.TestCase): ...@@ -2643,12 +2643,6 @@ class MiscIOTest(unittest.TestCase):
def test_blockingioerror(self): def test_blockingioerror(self):
# Various BlockingIOError issues # 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): class C(str):
pass pass
c = C("") c = C("")
......
...@@ -563,8 +563,7 @@ class MmapTests(unittest.TestCase): ...@@ -563,8 +563,7 @@ class MmapTests(unittest.TestCase):
f.close() f.close()
def test_error(self): def test_error(self):
self.assertTrue(issubclass(mmap.error, EnvironmentError)) self.assertIs(mmap.error, OSError)
self.assertIn("mmap.error", str(mmap.error))
def test_io_methods(self): def test_io_methods(self):
data = b"0123456789" data = b"0123456789"
......
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()
...@@ -1339,7 +1339,7 @@ def xinclude_loader(href, parse="xml", encoding=None): ...@@ -1339,7 +1339,7 @@ def xinclude_loader(href, parse="xml", encoding=None):
try: try:
data = XINCLUDE[href] data = XINCLUDE[href]
except KeyError: except KeyError:
raise IOError("resource not found") raise OSError("resource not found")
if parse == "xml": if parse == "xml":
from xml.etree.ElementTree import XML from xml.etree.ElementTree import XML
return XML(data) return XML(data)
...@@ -1404,7 +1404,7 @@ def xinclude(): ...@@ -1404,7 +1404,7 @@ def xinclude():
>>> document = xinclude_loader("C5.xml") >>> document = xinclude_loader("C5.xml")
>>> ElementInclude.include(document, xinclude_loader) >>> ElementInclude.include(document, xinclude_loader)
Traceback (most recent call last): Traceback (most recent call last):
IOError: resource not found OSError: resource not found
>>> # print(serialize(document)) # C5 >>> # print(serialize(document)) # C5
""" """
...@@ -1611,7 +1611,7 @@ def bug_xmltoolkit55(): ...@@ -1611,7 +1611,7 @@ def bug_xmltoolkit55():
class ExceptionFile: class ExceptionFile:
def read(self, x): def read(self, x):
raise IOError raise OSError
def xmltoolkit60(): def xmltoolkit60():
""" """
...@@ -1619,7 +1619,7 @@ def xmltoolkit60(): ...@@ -1619,7 +1619,7 @@ def xmltoolkit60():
Handle crash in stream source. Handle crash in stream source.
>>> tree = ET.parse(ExceptionFile()) >>> tree = ET.parse(ExceptionFile())
Traceback (most recent call last): Traceback (most recent call last):
IOError OSError
""" """
......
...@@ -1547,6 +1547,8 @@ class URLopener: ...@@ -1547,6 +1547,8 @@ class URLopener:
return getattr(self, name)(url) return getattr(self, name)(url)
else: else:
return getattr(self, name)(url, data) return getattr(self, name)(url, data)
except HTTPError:
raise
except socket.error as msg: except socket.error as msg:
raise IOError('socket error', msg).with_traceback(sys.exc_info()[2]) raise IOError('socket error', msg).with_traceback(sys.exc_info()[2])
......
...@@ -10,6 +10,8 @@ What's New in Python 3.3 Alpha 1? ...@@ -10,6 +10,8 @@ What's New in Python 3.3 Alpha 1?
Core and Builtins Core and Builtins
----------------- -----------------
- PEP 3151 / issue #12555: reworking the OS and IO exception hierarchy.
- Add internal API for static strings (_Py_identifier et.al.). - Add internal API for static strings (_Py_identifier et.al.).
- Issue #13063: the Windows error ERROR_NO_DATA (numbered 232 and described - Issue #13063: the Windows error ERROR_NO_DATA (numbered 232 and described
......
...@@ -90,89 +90,6 @@ PyDoc_STRVAR(module_doc, ...@@ -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 * The main open() function
*/ */
...@@ -694,9 +611,11 @@ PyInit__io(void) ...@@ -694,9 +611,11 @@ PyInit__io(void)
state->unsupported_operation) < 0) state->unsupported_operation) < 0)
goto fail; goto fail;
/* BlockingIOError */ /* BlockingIOError, for compatibility */
_PyExc_BlockingIOError.tp_base = (PyTypeObject *) PyExc_IOError; Py_INCREF(PyExc_BlockingIOError);
ADD_TYPE(&_PyExc_BlockingIOError, "BlockingIOError"); if (PyModule_AddObject(m, "BlockingIOError",
(PyObject *) PyExc_BlockingIOError) < 0)
goto fail;
/* Concrete base types of the IO ABCs. /* Concrete base types of the IO ABCs.
(the ABCs themselves are declared through inheritance in io.py) (the ABCs themselves are declared through inheritance in io.py)
......
...@@ -60,15 +60,6 @@ extern Py_ssize_t _PyIO_find_line_ending( ...@@ -60,15 +60,6 @@ extern Py_ssize_t _PyIO_find_line_ending(
#define DEFAULT_BUFFER_SIZE (8 * 1024) /* bytes */ #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. * Offset type for positioning.
*/ */
......
...@@ -622,14 +622,14 @@ static Py_ssize_t * ...@@ -622,14 +622,14 @@ static Py_ssize_t *
_buffered_check_blocking_error(void) _buffered_check_blocking_error(void)
{ {
PyObject *t, *v, *tb; PyObject *t, *v, *tb;
PyBlockingIOErrorObject *err; PyOSErrorObject *err;
PyErr_Fetch(&t, &v, &tb); PyErr_Fetch(&t, &v, &tb);
if (v == NULL || !PyErr_GivenExceptionMatches(v, PyExc_BlockingIOError)) { if (v == NULL || !PyErr_GivenExceptionMatches(v, PyExc_BlockingIOError)) {
PyErr_Restore(t, v, tb); PyErr_Restore(t, v, tb);
return NULL; return NULL;
} }
err = (PyBlockingIOErrorObject *) v; err = (PyOSErrorObject *) v;
/* TODO: sanity check (err->written >= 0) */ /* TODO: sanity check (err->written >= 0) */
PyErr_Restore(t, v, tb); PyErr_Restore(t, v, tb);
return &err->written; return &err->written;
......
...@@ -78,8 +78,6 @@ my_getpagesize(void) ...@@ -78,8 +78,6 @@ my_getpagesize(void)
# define MAP_ANONYMOUS MAP_ANON # define MAP_ANONYMOUS MAP_ANON
#endif #endif
static PyObject *mmap_module_error;
typedef enum typedef enum
{ {
ACCESS_DEFAULT, ACCESS_DEFAULT,
...@@ -459,7 +457,7 @@ mmap_size_method(mmap_object *self, ...@@ -459,7 +457,7 @@ mmap_size_method(mmap_object *self,
{ {
struct stat buf; struct stat buf;
if (-1 == fstat(self->fd, &buf)) { if (-1 == fstat(self->fd, &buf)) {
PyErr_SetFromErrno(mmap_module_error); PyErr_SetFromErrno(PyExc_OSError);
return NULL; return NULL;
} }
#ifdef HAVE_LARGEFILE_SUPPORT #ifdef HAVE_LARGEFILE_SUPPORT
...@@ -549,7 +547,7 @@ mmap_resize_method(mmap_object *self, ...@@ -549,7 +547,7 @@ mmap_resize_method(mmap_object *self,
void *newmap; void *newmap;
if (ftruncate(self->fd, self->offset + new_size) == -1) { if (ftruncate(self->fd, self->offset + new_size) == -1) {
PyErr_SetFromErrno(mmap_module_error); PyErr_SetFromErrno(PyExc_OSError);
return NULL; return NULL;
} }
...@@ -564,7 +562,7 @@ mmap_resize_method(mmap_object *self, ...@@ -564,7 +562,7 @@ mmap_resize_method(mmap_object *self,
#endif #endif
if (newmap == (void *)-1) if (newmap == (void *)-1)
{ {
PyErr_SetFromErrno(mmap_module_error); PyErr_SetFromErrno(PyExc_OSError);
return NULL; return NULL;
} }
self->data = newmap; self->data = newmap;
...@@ -605,7 +603,7 @@ mmap_flush_method(mmap_object *self, PyObject *args) ...@@ -605,7 +603,7 @@ mmap_flush_method(mmap_object *self, PyObject *args)
/* XXX semantics of return value? */ /* XXX semantics of return value? */
/* XXX flags for msync? */ /* XXX flags for msync? */
if (-1 == msync(self->data + offset, size, MS_SYNC)) { if (-1 == msync(self->data + offset, size, MS_SYNC)) {
PyErr_SetFromErrno(mmap_module_error); PyErr_SetFromErrno(PyExc_OSError);
return NULL; return NULL;
} }
return PyLong_FromLong(0); return PyLong_FromLong(0);
...@@ -1205,7 +1203,7 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict) ...@@ -1205,7 +1203,7 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
fd = devzero = open("/dev/zero", O_RDWR); fd = devzero = open("/dev/zero", O_RDWR);
if (devzero == -1) { if (devzero == -1) {
Py_DECREF(m_obj); Py_DECREF(m_obj);
PyErr_SetFromErrno(mmap_module_error); PyErr_SetFromErrno(PyExc_OSError);
return NULL; return NULL;
} }
#endif #endif
...@@ -1213,7 +1211,7 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict) ...@@ -1213,7 +1211,7 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
m_obj->fd = dup(fd); m_obj->fd = dup(fd);
if (m_obj->fd == -1) { if (m_obj->fd == -1) {
Py_DECREF(m_obj); Py_DECREF(m_obj);
PyErr_SetFromErrno(mmap_module_error); PyErr_SetFromErrno(PyExc_OSError);
return NULL; return NULL;
} }
} }
...@@ -1229,7 +1227,7 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict) ...@@ -1229,7 +1227,7 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
if (m_obj->data == (char *)-1) { if (m_obj->data == (char *)-1) {
m_obj->data = NULL; m_obj->data = NULL;
Py_DECREF(m_obj); Py_DECREF(m_obj);
PyErr_SetFromErrno(mmap_module_error); PyErr_SetFromErrno(PyExc_OSError);
return NULL; return NULL;
} }
m_obj->access = (access_mode)access; m_obj->access = (access_mode)access;
...@@ -1310,12 +1308,12 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict) ...@@ -1310,12 +1308,12 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
if (fileno != -1 && fileno != 0) { if (fileno != -1 && fileno != 0) {
/* Ensure that fileno is within the CRT's valid range */ /* Ensure that fileno is within the CRT's valid range */
if (_PyVerify_fd(fileno) == 0) { if (_PyVerify_fd(fileno) == 0) {
PyErr_SetFromErrno(mmap_module_error); PyErr_SetFromErrno(PyExc_OSError);
return NULL; return NULL;
} }
fh = (HANDLE)_get_osfhandle(fileno); fh = (HANDLE)_get_osfhandle(fileno);
if (fh==(HANDLE)-1) { if (fh==(HANDLE)-1) {
PyErr_SetFromErrno(mmap_module_error); PyErr_SetFromErrno(PyExc_OSError);
return NULL; return NULL;
} }
/* Win9x appears to need us seeked to zero */ /* Win9x appears to need us seeked to zero */
...@@ -1469,11 +1467,7 @@ PyInit_mmap(void) ...@@ -1469,11 +1467,7 @@ PyInit_mmap(void)
dict = PyModule_GetDict(module); dict = PyModule_GetDict(module);
if (!dict) if (!dict)
return NULL; return NULL;
mmap_module_error = PyErr_NewException("mmap.error", PyDict_SetItemString(dict, "error", PyExc_OSError);
PyExc_EnvironmentError , NULL);
if (mmap_module_error == NULL)
return NULL;
PyDict_SetItemString(dict, "error", mmap_module_error);
PyDict_SetItemString(dict, "mmap", (PyObject*) &mmap_object_type); PyDict_SetItemString(dict, "mmap", (PyObject*) &mmap_object_type);
#ifdef PROT_EXEC #ifdef PROT_EXEC
setint(dict, "PROT_EXEC", PROT_EXEC); setint(dict, "PROT_EXEC", PROT_EXEC);
......
...@@ -54,8 +54,6 @@ extern void bzero(void *, int); ...@@ -54,8 +54,6 @@ extern void bzero(void *, int);
# endif # endif
#endif #endif
static PyObject *SelectError;
/* list of Python objects and their file descriptor */ /* list of Python objects and their file descriptor */
typedef struct { typedef struct {
PyObject *obj; /* owned reference */ PyObject *obj; /* owned reference */
...@@ -274,11 +272,11 @@ select_select(PyObject *self, PyObject *args) ...@@ -274,11 +272,11 @@ select_select(PyObject *self, PyObject *args)
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
if (n == SOCKET_ERROR) { if (n == SOCKET_ERROR) {
PyErr_SetExcFromWindowsErr(SelectError, WSAGetLastError()); PyErr_SetExcFromWindowsErr(PyExc_OSError, WSAGetLastError());
} }
#else #else
if (n < 0) { if (n < 0) {
PyErr_SetFromErrno(SelectError); PyErr_SetFromErrno(PyExc_OSError);
} }
#endif #endif
else { else {
...@@ -425,7 +423,7 @@ poll_modify(pollObject *self, PyObject *args) ...@@ -425,7 +423,7 @@ poll_modify(pollObject *self, PyObject *args)
return NULL; return NULL;
if (PyDict_GetItem(self->dict, key) == NULL) { if (PyDict_GetItem(self->dict, key) == NULL) {
errno = ENOENT; errno = ENOENT;
PyErr_SetFromErrno(PyExc_IOError); PyErr_SetFromErrno(PyExc_OSError);
return NULL; return NULL;
} }
value = PyLong_FromLong(events); value = PyLong_FromLong(events);
...@@ -524,7 +522,7 @@ poll_poll(pollObject *self, PyObject *args) ...@@ -524,7 +522,7 @@ poll_poll(pollObject *self, PyObject *args)
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if (poll_result < 0) { if (poll_result < 0) {
PyErr_SetFromErrno(SelectError); PyErr_SetFromErrno(PyExc_OSError);
return NULL; return NULL;
} }
...@@ -764,7 +762,7 @@ newPyEpoll_Object(PyTypeObject *type, int sizehint, SOCKET fd) ...@@ -764,7 +762,7 @@ newPyEpoll_Object(PyTypeObject *type, int sizehint, SOCKET fd)
} }
if (self->epfd < 0) { if (self->epfd < 0) {
Py_DECREF(self); Py_DECREF(self);
PyErr_SetFromErrno(PyExc_IOError); PyErr_SetFromErrno(PyExc_OSError);
return NULL; return NULL;
} }
return (PyObject *)self; return (PyObject *)self;
...@@ -797,7 +795,7 @@ pyepoll_close(pyEpoll_Object *self) ...@@ -797,7 +795,7 @@ pyepoll_close(pyEpoll_Object *self)
{ {
errno = pyepoll_internal_close(self); errno = pyepoll_internal_close(self);
if (errno < 0) { if (errno < 0) {
PyErr_SetFromErrno(PyExc_IOError); PyErr_SetFromErrno(PyExc_OSError);
return NULL; return NULL;
} }
Py_RETURN_NONE; Py_RETURN_NONE;
...@@ -890,7 +888,7 @@ pyepoll_internal_ctl(int epfd, int op, PyObject *pfd, unsigned int events) ...@@ -890,7 +888,7 @@ pyepoll_internal_ctl(int epfd, int op, PyObject *pfd, unsigned int events)
} }
if (result < 0) { if (result < 0) {
PyErr_SetFromErrno(PyExc_IOError); PyErr_SetFromErrno(PyExc_OSError);
return NULL; return NULL;
} }
Py_RETURN_NONE; Py_RETURN_NONE;
...@@ -914,7 +912,7 @@ pyepoll_register(pyEpoll_Object *self, PyObject *args, PyObject *kwds) ...@@ -914,7 +912,7 @@ pyepoll_register(pyEpoll_Object *self, PyObject *args, PyObject *kwds)
PyDoc_STRVAR(pyepoll_register_doc, PyDoc_STRVAR(pyepoll_register_doc,
"register(fd[, eventmask]) -> None\n\ "register(fd[, eventmask]) -> None\n\
\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\ fd is the target file descriptor of the operation.\n\
events is a bit set composed of the various EPOLL constants; the default\n\ events is a bit set composed of the various EPOLL constants; the default\n\
is EPOLL_IN | EPOLL_OUT | EPOLL_PRI.\n\ is EPOLL_IN | EPOLL_OUT | EPOLL_PRI.\n\
...@@ -1013,7 +1011,7 @@ pyepoll_poll(pyEpoll_Object *self, PyObject *args, PyObject *kwds) ...@@ -1013,7 +1011,7 @@ pyepoll_poll(pyEpoll_Object *self, PyObject *args, PyObject *kwds)
nfds = epoll_wait(self->epfd, evs, maxevents, timeout); nfds = epoll_wait(self->epfd, evs, maxevents, timeout);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if (nfds < 0) { if (nfds < 0) {
PyErr_SetFromErrno(PyExc_IOError); PyErr_SetFromErrno(PyExc_OSError);
goto error; goto error;
} }
...@@ -1404,7 +1402,7 @@ newKqueue_Object(PyTypeObject *type, SOCKET fd) ...@@ -1404,7 +1402,7 @@ newKqueue_Object(PyTypeObject *type, SOCKET fd)
} }
if (self->kqfd < 0) { if (self->kqfd < 0) {
Py_DECREF(self); Py_DECREF(self);
PyErr_SetFromErrno(PyExc_IOError); PyErr_SetFromErrno(PyExc_OSError);
return NULL; return NULL;
} }
return (PyObject *)self; return (PyObject *)self;
...@@ -1436,7 +1434,7 @@ kqueue_queue_close(kqueue_queue_Object *self) ...@@ -1436,7 +1434,7 @@ kqueue_queue_close(kqueue_queue_Object *self)
{ {
errno = kqueue_queue_internal_close(self); errno = kqueue_queue_internal_close(self);
if (errno < 0) { if (errno < 0) {
PyErr_SetFromErrno(PyExc_IOError); PyErr_SetFromErrno(PyExc_OSError);
return NULL; return NULL;
} }
Py_RETURN_NONE; Py_RETURN_NONE;
...@@ -1778,9 +1776,8 @@ PyInit_select(void) ...@@ -1778,9 +1776,8 @@ PyInit_select(void)
if (m == NULL) if (m == NULL)
return NULL; return NULL;
SelectError = PyErr_NewException("select.error", NULL, NULL); Py_INCREF(PyExc_OSError);
Py_INCREF(SelectError); PyModule_AddObject(m, "error", PyExc_OSError);
PyModule_AddObject(m, "error", SelectError);
#ifdef PIPE_BUF #ifdef PIPE_BUF
#ifdef HAVE_BROKEN_PIPE_BUF #ifdef HAVE_BROKEN_PIPE_BUF
......
This diff is collapsed.
This diff is collapsed.
...@@ -496,10 +496,11 @@ PyObject *PyErr_SetExcFromWindowsErrWithFilenameObject( ...@@ -496,10 +496,11 @@ PyObject *PyErr_SetExcFromWindowsErrWithFilenameObject(
return NULL; return NULL;
} }
if (filenameObject != NULL) if (filenameObject == NULL)
v = Py_BuildValue("(iOO)", err, message, filenameObject); filenameObject = Py_None;
else /* This is the constructor signature for passing a Windows error code.
v = Py_BuildValue("(iO)", err, message); The POSIX translation will be figured out by the constructor. */
v = Py_BuildValue("(iOOi)", 0, message, filenameObject, err);
Py_DECREF(message); Py_DECREF(message);
if (v != NULL) { if (v != NULL) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment