Commit c813c7f5 authored by Victor Stinner's avatar Victor Stinner

asyncio, Tulip issue 139: Improve error messages on "fatal errors"

Mention if the error was caused by a read or a write, and be more specific on
the object (ex: "pipe transport" instead of "transport").
parent fd6d6f81
...@@ -53,10 +53,10 @@ class _ProactorBasePipeTransport(transports._FlowControlMixin, ...@@ -53,10 +53,10 @@ class _ProactorBasePipeTransport(transports._FlowControlMixin,
if self._read_fut is not None: if self._read_fut is not None:
self._read_fut.cancel() self._read_fut.cancel()
def _fatal_error(self, exc): def _fatal_error(self, exc, message='Fatal error on pipe transport'):
if not isinstance(exc, (BrokenPipeError, ConnectionResetError)): if not isinstance(exc, (BrokenPipeError, ConnectionResetError)):
self._loop.call_exception_handler({ self._loop.call_exception_handler({
'message': 'Fatal transport error', 'message': message,
'exception': exc, 'exception': exc,
'transport': self, 'transport': self,
'protocol': self._protocol, 'protocol': self._protocol,
...@@ -151,11 +151,11 @@ class _ProactorReadPipeTransport(_ProactorBasePipeTransport, ...@@ -151,11 +151,11 @@ class _ProactorReadPipeTransport(_ProactorBasePipeTransport,
self._read_fut = self._loop._proactor.recv(self._sock, 4096) self._read_fut = self._loop._proactor.recv(self._sock, 4096)
except ConnectionAbortedError as exc: except ConnectionAbortedError as exc:
if not self._closing: if not self._closing:
self._fatal_error(exc) self._fatal_error(exc, 'Fatal read error on pipe transport')
except ConnectionResetError as exc: except ConnectionResetError as exc:
self._force_close(exc) self._force_close(exc)
except OSError as exc: except OSError as exc:
self._fatal_error(exc) self._fatal_error(exc, 'Fatal read error on pipe transport')
except futures.CancelledError: except futures.CancelledError:
if not self._closing: if not self._closing:
raise raise
...@@ -246,7 +246,7 @@ class _ProactorBaseWritePipeTransport(_ProactorBasePipeTransport, ...@@ -246,7 +246,7 @@ class _ProactorBaseWritePipeTransport(_ProactorBasePipeTransport,
except ConnectionResetError as exc: except ConnectionResetError as exc:
self._force_close(exc) self._force_close(exc)
except OSError as exc: except OSError as exc:
self._fatal_error(exc) self._fatal_error(exc, 'Fatal write error on pipe transport')
def can_write_eof(self): def can_write_eof(self):
return True return True
......
...@@ -377,11 +377,11 @@ class _SelectorTransport(transports._FlowControlMixin, ...@@ -377,11 +377,11 @@ class _SelectorTransport(transports._FlowControlMixin,
self._conn_lost += 1 self._conn_lost += 1
self._loop.call_soon(self._call_connection_lost, None) self._loop.call_soon(self._call_connection_lost, None)
def _fatal_error(self, exc): def _fatal_error(self, exc, message='Fatal error on transport'):
# Should be called from exception handler only. # Should be called from exception handler only.
if not isinstance(exc, (BrokenPipeError, ConnectionResetError)): if not isinstance(exc, (BrokenPipeError, ConnectionResetError)):
self._loop.call_exception_handler({ self._loop.call_exception_handler({
'message': 'Fatal transport error', 'message': message,
'exception': exc, 'exception': exc,
'transport': self, 'transport': self,
'protocol': self._protocol, 'protocol': self._protocol,
...@@ -452,7 +452,7 @@ class _SelectorSocketTransport(_SelectorTransport): ...@@ -452,7 +452,7 @@ class _SelectorSocketTransport(_SelectorTransport):
except (BlockingIOError, InterruptedError): except (BlockingIOError, InterruptedError):
pass pass
except Exception as exc: except Exception as exc:
self._fatal_error(exc) self._fatal_error(exc, 'Fatal read error on socket transport')
else: else:
if data: if data:
self._protocol.data_received(data) self._protocol.data_received(data)
...@@ -488,7 +488,7 @@ class _SelectorSocketTransport(_SelectorTransport): ...@@ -488,7 +488,7 @@ class _SelectorSocketTransport(_SelectorTransport):
except (BlockingIOError, InterruptedError): except (BlockingIOError, InterruptedError):
pass pass
except Exception as exc: except Exception as exc:
self._fatal_error(exc) self._fatal_error(exc, 'Fatal write error on socket transport')
return return
else: else:
data = data[n:] data = data[n:]
...@@ -511,7 +511,7 @@ class _SelectorSocketTransport(_SelectorTransport): ...@@ -511,7 +511,7 @@ class _SelectorSocketTransport(_SelectorTransport):
except Exception as exc: except Exception as exc:
self._loop.remove_writer(self._sock_fd) self._loop.remove_writer(self._sock_fd)
self._buffer.clear() self._buffer.clear()
self._fatal_error(exc) self._fatal_error(exc, 'Fatal write error on socket transport')
else: else:
if n: if n:
del self._buffer[:n] del self._buffer[:n]
...@@ -678,7 +678,7 @@ class _SelectorSslTransport(_SelectorTransport): ...@@ -678,7 +678,7 @@ class _SelectorSslTransport(_SelectorTransport):
self._loop.remove_reader(self._sock_fd) self._loop.remove_reader(self._sock_fd)
self._loop.add_writer(self._sock_fd, self._write_ready) self._loop.add_writer(self._sock_fd, self._write_ready)
except Exception as exc: except Exception as exc:
self._fatal_error(exc) self._fatal_error(exc, 'Fatal read error on SSL transport')
else: else:
if data: if data:
self._protocol.data_received(data) self._protocol.data_received(data)
...@@ -712,7 +712,7 @@ class _SelectorSslTransport(_SelectorTransport): ...@@ -712,7 +712,7 @@ class _SelectorSslTransport(_SelectorTransport):
except Exception as exc: except Exception as exc:
self._loop.remove_writer(self._sock_fd) self._loop.remove_writer(self._sock_fd)
self._buffer.clear() self._buffer.clear()
self._fatal_error(exc) self._fatal_error(exc, 'Fatal write error on SSL transport')
return return
if n: if n:
...@@ -770,7 +770,7 @@ class _SelectorDatagramTransport(_SelectorTransport): ...@@ -770,7 +770,7 @@ class _SelectorDatagramTransport(_SelectorTransport):
except OSError as exc: except OSError as exc:
self._protocol.error_received(exc) self._protocol.error_received(exc)
except Exception as exc: except Exception as exc:
self._fatal_error(exc) self._fatal_error(exc, 'Fatal read error on datagram transport')
else: else:
self._protocol.datagram_received(data, addr) self._protocol.datagram_received(data, addr)
...@@ -805,7 +805,8 @@ class _SelectorDatagramTransport(_SelectorTransport): ...@@ -805,7 +805,8 @@ class _SelectorDatagramTransport(_SelectorTransport):
self._protocol.error_received(exc) self._protocol.error_received(exc)
return return
except Exception as exc: except Exception as exc:
self._fatal_error(exc) self._fatal_error(exc,
'Fatal write error on datagram transport')
return return
# Ensure that what we buffer is immutable. # Ensure that what we buffer is immutable.
...@@ -827,7 +828,8 @@ class _SelectorDatagramTransport(_SelectorTransport): ...@@ -827,7 +828,8 @@ class _SelectorDatagramTransport(_SelectorTransport):
self._protocol.error_received(exc) self._protocol.error_received(exc)
return return
except Exception as exc: except Exception as exc:
self._fatal_error(exc) self._fatal_error(exc,
'Fatal write error on datagram transport')
return return
self._maybe_resume_protocol() # May append to buffer. self._maybe_resume_protocol() # May append to buffer.
......
...@@ -271,7 +271,7 @@ class _UnixReadPipeTransport(transports.ReadTransport): ...@@ -271,7 +271,7 @@ class _UnixReadPipeTransport(transports.ReadTransport):
except (BlockingIOError, InterruptedError): except (BlockingIOError, InterruptedError):
pass pass
except OSError as exc: except OSError as exc:
self._fatal_error(exc) self._fatal_error(exc, 'Fatal read error on pipe transport')
else: else:
if data: if data:
self._protocol.data_received(data) self._protocol.data_received(data)
...@@ -291,11 +291,11 @@ class _UnixReadPipeTransport(transports.ReadTransport): ...@@ -291,11 +291,11 @@ class _UnixReadPipeTransport(transports.ReadTransport):
if not self._closing: if not self._closing:
self._close(None) self._close(None)
def _fatal_error(self, exc): def _fatal_error(self, exc, message='Fatal error on pipe transport'):
# should be called by exception handler only # should be called by exception handler only
if not (isinstance(exc, OSError) and exc.errno == errno.EIO): if not (isinstance(exc, OSError) and exc.errno == errno.EIO):
self._loop.call_exception_handler({ self._loop.call_exception_handler({
'message': 'Fatal transport error', 'message': message,
'exception': exc, 'exception': exc,
'transport': self, 'transport': self,
'protocol': self._protocol, 'protocol': self._protocol,
...@@ -381,7 +381,7 @@ class _UnixWritePipeTransport(transports._FlowControlMixin, ...@@ -381,7 +381,7 @@ class _UnixWritePipeTransport(transports._FlowControlMixin,
n = 0 n = 0
except Exception as exc: except Exception as exc:
self._conn_lost += 1 self._conn_lost += 1
self._fatal_error(exc) self._fatal_error(exc, 'Fatal write error on pipe transport')
return return
if n == len(data): if n == len(data):
return return
...@@ -406,7 +406,7 @@ class _UnixWritePipeTransport(transports._FlowControlMixin, ...@@ -406,7 +406,7 @@ class _UnixWritePipeTransport(transports._FlowControlMixin,
# Remove writer here, _fatal_error() doesn't it # Remove writer here, _fatal_error() doesn't it
# because _buffer is empty. # because _buffer is empty.
self._loop.remove_writer(self._fileno) self._loop.remove_writer(self._fileno)
self._fatal_error(exc) self._fatal_error(exc, 'Fatal write error on pipe transport')
else: else:
if n == len(data): if n == len(data):
self._loop.remove_writer(self._fileno) self._loop.remove_writer(self._fileno)
...@@ -443,11 +443,11 @@ class _UnixWritePipeTransport(transports._FlowControlMixin, ...@@ -443,11 +443,11 @@ class _UnixWritePipeTransport(transports._FlowControlMixin,
def abort(self): def abort(self):
self._close(None) self._close(None)
def _fatal_error(self, exc): def _fatal_error(self, exc, message='Fatal error on pipe transport'):
# should be called by exception handler only # should be called by exception handler only
if not isinstance(exc, (BrokenPipeError, ConnectionResetError)): if not isinstance(exc, (BrokenPipeError, ConnectionResetError)):
self._loop.call_exception_handler({ self._loop.call_exception_handler({
'message': 'Fatal transport error', 'message': message,
'exception': exc, 'exception': exc,
'transport': self, 'transport': self,
'protocol': self._protocol, 'protocol': self._protocol,
......
...@@ -69,7 +69,9 @@ class ProactorSocketTransportTests(unittest.TestCase): ...@@ -69,7 +69,9 @@ class ProactorSocketTransportTests(unittest.TestCase):
tr = _ProactorSocketTransport(self.loop, self.sock, self.protocol) tr = _ProactorSocketTransport(self.loop, self.sock, self.protocol)
tr._fatal_error = unittest.mock.Mock() tr._fatal_error = unittest.mock.Mock()
tr._loop_reading() tr._loop_reading()
tr._fatal_error.assert_called_with(err) tr._fatal_error.assert_called_with(
err,
'Fatal read error on pipe transport')
def test_loop_reading_aborted_closing(self): def test_loop_reading_aborted_closing(self):
self.loop._proactor.recv.side_effect = ConnectionAbortedError() self.loop._proactor.recv.side_effect = ConnectionAbortedError()
...@@ -105,7 +107,9 @@ class ProactorSocketTransportTests(unittest.TestCase): ...@@ -105,7 +107,9 @@ class ProactorSocketTransportTests(unittest.TestCase):
tr = _ProactorSocketTransport(self.loop, self.sock, self.protocol) tr = _ProactorSocketTransport(self.loop, self.sock, self.protocol)
tr._fatal_error = unittest.mock.Mock() tr._fatal_error = unittest.mock.Mock()
tr._loop_reading() tr._loop_reading()
tr._fatal_error.assert_called_with(err) tr._fatal_error.assert_called_with(
err,
'Fatal read error on pipe transport')
def test_write(self): def test_write(self):
tr = _ProactorSocketTransport(self.loop, self.sock, self.protocol) tr = _ProactorSocketTransport(self.loop, self.sock, self.protocol)
...@@ -142,7 +146,9 @@ class ProactorSocketTransportTests(unittest.TestCase): ...@@ -142,7 +146,9 @@ class ProactorSocketTransportTests(unittest.TestCase):
tr._fatal_error = unittest.mock.Mock() tr._fatal_error = unittest.mock.Mock()
tr._buffer = [b'da', b'ta'] tr._buffer = [b'da', b'ta']
tr._loop_writing() tr._loop_writing()
tr._fatal_error.assert_called_with(err) tr._fatal_error.assert_called_with(
err,
'Fatal write error on pipe transport')
tr._conn_lost = 1 tr._conn_lost = 1
tr.write(b'data') tr.write(b'data')
......
...@@ -655,7 +655,7 @@ class SelectorTransportTests(unittest.TestCase): ...@@ -655,7 +655,7 @@ class SelectorTransportTests(unittest.TestCase):
m_exc.assert_called_with( m_exc.assert_called_with(
test_utils.MockPattern( test_utils.MockPattern(
'Fatal transport error\nprotocol:.*\ntransport:.*'), 'Fatal error on transport\nprotocol:.*\ntransport:.*'),
exc_info=(OSError, MOCK_ANY, MOCK_ANY)) exc_info=(OSError, MOCK_ANY, MOCK_ANY))
tr._force_close.assert_called_with(exc) tr._force_close.assert_called_with(exc)
...@@ -785,7 +785,9 @@ class SelectorSocketTransportTests(unittest.TestCase): ...@@ -785,7 +785,9 @@ class SelectorSocketTransportTests(unittest.TestCase):
transport._fatal_error = unittest.mock.Mock() transport._fatal_error = unittest.mock.Mock()
transport._read_ready() transport._read_ready()
transport._fatal_error.assert_called_with(err) transport._fatal_error.assert_called_with(
err,
'Fatal read error on socket transport')
def test_write(self): def test_write(self):
data = b'data' data = b'data'
...@@ -898,7 +900,9 @@ class SelectorSocketTransportTests(unittest.TestCase): ...@@ -898,7 +900,9 @@ class SelectorSocketTransportTests(unittest.TestCase):
self.loop, self.sock, self.protocol) self.loop, self.sock, self.protocol)
transport._fatal_error = unittest.mock.Mock() transport._fatal_error = unittest.mock.Mock()
transport.write(data) transport.write(data)
transport._fatal_error.assert_called_with(err) transport._fatal_error.assert_called_with(
err,
'Fatal write error on socket transport')
transport._conn_lost = 1 transport._conn_lost = 1
self.sock.reset_mock() self.sock.reset_mock()
...@@ -1001,7 +1005,9 @@ class SelectorSocketTransportTests(unittest.TestCase): ...@@ -1001,7 +1005,9 @@ class SelectorSocketTransportTests(unittest.TestCase):
transport._fatal_error = unittest.mock.Mock() transport._fatal_error = unittest.mock.Mock()
transport._buffer.extend(b'data') transport._buffer.extend(b'data')
transport._write_ready() transport._write_ready()
transport._fatal_error.assert_called_with(err) transport._fatal_error.assert_called_with(
err,
'Fatal write error on socket transport')
@unittest.mock.patch('asyncio.base_events.logger') @unittest.mock.patch('asyncio.base_events.logger')
def test_write_ready_exception_and_close(self, m_log): def test_write_ready_exception_and_close(self, m_log):
...@@ -1237,7 +1243,9 @@ class SelectorSslTransportTests(unittest.TestCase): ...@@ -1237,7 +1243,9 @@ class SelectorSslTransportTests(unittest.TestCase):
transport = self._make_one() transport = self._make_one()
transport._fatal_error = unittest.mock.Mock() transport._fatal_error = unittest.mock.Mock()
transport._read_ready() transport._read_ready()
transport._fatal_error.assert_called_with(err) transport._fatal_error.assert_called_with(
err,
'Fatal read error on SSL transport')
def test_write_ready_send(self): def test_write_ready_send(self):
self.sslsock.send.return_value = 4 self.sslsock.send.return_value = 4
...@@ -1319,7 +1327,9 @@ class SelectorSslTransportTests(unittest.TestCase): ...@@ -1319,7 +1327,9 @@ class SelectorSslTransportTests(unittest.TestCase):
transport._buffer = list_to_buffer([b'data']) transport._buffer = list_to_buffer([b'data'])
transport._fatal_error = unittest.mock.Mock() transport._fatal_error = unittest.mock.Mock()
transport._write_ready() transport._write_ready()
transport._fatal_error.assert_called_with(err) transport._fatal_error.assert_called_with(
err,
'Fatal write error on SSL transport')
self.assertEqual(list_to_buffer(), transport._buffer) self.assertEqual(list_to_buffer(), transport._buffer)
def test_write_ready_read_wants_write(self): def test_write_ready_read_wants_write(self):
...@@ -1407,7 +1417,9 @@ class SelectorDatagramTransportTests(unittest.TestCase): ...@@ -1407,7 +1417,9 @@ class SelectorDatagramTransportTests(unittest.TestCase):
transport._fatal_error = unittest.mock.Mock() transport._fatal_error = unittest.mock.Mock()
transport._read_ready() transport._read_ready()
transport._fatal_error.assert_called_with(err) transport._fatal_error.assert_called_with(
err,
'Fatal read error on datagram transport')
def test_read_ready_oserr(self): def test_read_ready_oserr(self):
transport = _SelectorDatagramTransport( transport = _SelectorDatagramTransport(
...@@ -1517,7 +1529,9 @@ class SelectorDatagramTransportTests(unittest.TestCase): ...@@ -1517,7 +1529,9 @@ class SelectorDatagramTransportTests(unittest.TestCase):
transport.sendto(data, ()) transport.sendto(data, ())
self.assertTrue(transport._fatal_error.called) self.assertTrue(transport._fatal_error.called)
transport._fatal_error.assert_called_with(err) transport._fatal_error.assert_called_with(
err,
'Fatal write error on datagram transport')
transport._conn_lost = 1 transport._conn_lost = 1
transport._address = ('123',) transport._address = ('123',)
...@@ -1633,7 +1647,9 @@ class SelectorDatagramTransportTests(unittest.TestCase): ...@@ -1633,7 +1647,9 @@ class SelectorDatagramTransportTests(unittest.TestCase):
transport._buffer.append((b'data', ())) transport._buffer.append((b'data', ()))
transport._sendto_ready() transport._sendto_ready()
transport._fatal_error.assert_called_with(err) transport._fatal_error.assert_called_with(
err,
'Fatal write error on datagram transport')
def test_sendto_ready_error_received(self): def test_sendto_ready_error_received(self):
self.sock.sendto.side_effect = ConnectionRefusedError self.sock.sendto.side_effect = ConnectionRefusedError
...@@ -1667,7 +1683,7 @@ class SelectorDatagramTransportTests(unittest.TestCase): ...@@ -1667,7 +1683,7 @@ class SelectorDatagramTransportTests(unittest.TestCase):
self.assertFalse(self.protocol.error_received.called) self.assertFalse(self.protocol.error_received.called)
m_exc.assert_called_with( m_exc.assert_called_with(
test_utils.MockPattern( test_utils.MockPattern(
'Fatal transport error\nprotocol:.*\ntransport:.*'), 'Fatal error on transport\nprotocol:.*\ntransport:.*'),
exc_info=(ConnectionRefusedError, MOCK_ANY, MOCK_ANY)) exc_info=(ConnectionRefusedError, MOCK_ANY, MOCK_ANY))
......
...@@ -365,7 +365,7 @@ class UnixReadPipeTransportTests(unittest.TestCase): ...@@ -365,7 +365,7 @@ class UnixReadPipeTransportTests(unittest.TestCase):
tr._close.assert_called_with(err) tr._close.assert_called_with(err)
m_logexc.assert_called_with( m_logexc.assert_called_with(
test_utils.MockPattern( test_utils.MockPattern(
'Fatal transport error\nprotocol:.*\ntransport:.*'), 'Fatal read error on pipe transport\nprotocol:.*\ntransport:.*'),
exc_info=(OSError, MOCK_ANY, MOCK_ANY)) exc_info=(OSError, MOCK_ANY, MOCK_ANY))
@unittest.mock.patch('os.read') @unittest.mock.patch('os.read')
...@@ -558,7 +558,9 @@ class UnixWritePipeTransportTests(unittest.TestCase): ...@@ -558,7 +558,9 @@ class UnixWritePipeTransportTests(unittest.TestCase):
m_write.assert_called_with(5, b'data') m_write.assert_called_with(5, b'data')
self.assertFalse(self.loop.writers) self.assertFalse(self.loop.writers)
self.assertEqual([], tr._buffer) self.assertEqual([], tr._buffer)
tr._fatal_error.assert_called_with(err) tr._fatal_error.assert_called_with(
err,
'Fatal write error on pipe transport')
self.assertEqual(1, tr._conn_lost) self.assertEqual(1, tr._conn_lost)
tr.write(b'data') tr.write(b'data')
...@@ -660,7 +662,7 @@ class UnixWritePipeTransportTests(unittest.TestCase): ...@@ -660,7 +662,7 @@ class UnixWritePipeTransportTests(unittest.TestCase):
self.assertTrue(tr._closing) self.assertTrue(tr._closing)
m_logexc.assert_called_with( m_logexc.assert_called_with(
test_utils.MockPattern( test_utils.MockPattern(
'Fatal transport error\nprotocol:.*\ntransport:.*'), 'Fatal write error on pipe transport\nprotocol:.*\ntransport:.*'),
exc_info=(OSError, MOCK_ANY, MOCK_ANY)) exc_info=(OSError, MOCK_ANY, MOCK_ANY))
self.assertEqual(1, tr._conn_lost) self.assertEqual(1, tr._conn_lost)
test_utils.run_briefly(self.loop) test_utils.run_briefly(self.loop)
......
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