summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorAntoine Pitrou <solipsis@pitrou.net>2011-10-12 00:54:14 (GMT)
committerAntoine Pitrou <solipsis@pitrou.net>2011-10-12 00:54:14 (GMT)
commit6b4883dec0b7f6c5ede45dca861f5dc0e4ff2be7 (patch)
treee731290ba41ff9208385bebbe402282ecbbd682f /Lib
parent983b1434bda1819864fb5d82248f249358a4c22d (diff)
downloadcpython-6b4883dec0b7f6c5ede45dca861f5dc0e4ff2be7.zip
cpython-6b4883dec0b7f6c5ede45dca861f5dc0e4ff2be7.tar.gz
cpython-6b4883dec0b7f6c5ede45dca861f5dc0e4ff2be7.tar.bz2
PEP 3151 / issue #12555: reworking the OS and IO exception hierarchy.
Diffstat (limited to 'Lib')
-rw-r--r--Lib/_pyio.py12
-rw-r--r--Lib/multiprocessing/connection.py8
-rw-r--r--Lib/test/exception_hierarchy.txt21
-rw-r--r--Lib/test/test_concurrent_futures.py14
-rw-r--r--Lib/test/test_exceptions.py43
-rw-r--r--Lib/test/test_http_cookiejar.py11
-rw-r--r--Lib/test/test_io.py6
-rw-r--r--Lib/test/test_mmap.py3
-rw-r--r--Lib/test/test_pep3151.py128
-rw-r--r--Lib/test/test_xml_etree.py8
-rw-r--r--Lib/urllib/request.py2
11 files changed, 204 insertions, 52 deletions
diff --git a/Lib/_pyio.py b/Lib/_pyio.py
index a9c31d5..0611bd6 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 13d3d77..0c96958 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 5037b33..1c1f69f 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 78a9906..7522a54 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):
'<Future at 0x[0-9a-f]+ state=cancelled>')
self.assertRegex(
repr(EXCEPTION_FUTURE),
- '<Future at 0x[0-9a-f]+ state=finished raised IOError>')
+ '<Future at 0x[0-9a-f]+ state=finished raised OSError>')
self.assertRegex(
repr(SUCCESSFUL_FUTURE),
'<Future at 0x[0-9a-f]+ state=finished returned int>')
@@ -512,7 +512,7 @@ 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 9be6958..a7683ac 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 41e0dfd..a35ec95 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 a9e3c37..0debc80 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 1cfcee6..2230028 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 0000000..9d92425
--- /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 40c2291..4dd8d22 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 671ab68..a947608 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])