Skip to content

Commit 346ffb3

Browse files
committed
Issue python#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").
1 parent 83d1ab4 commit 346ffb3

File tree

6 files changed

+64
-38
lines changed

6 files changed

+64
-38
lines changed

asyncio/proactor_events.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,10 @@ def close(self):
5353
if self._read_fut is not None:
5454
self._read_fut.cancel()
5555

56-
def _fatal_error(self, exc):
56+
def _fatal_error(self, exc, message='Fatal error on pipe transport'):
5757
if not isinstance(exc, (BrokenPipeError, ConnectionResetError)):
5858
self._loop.call_exception_handler({
59-
'message': 'Fatal transport error',
59+
'message': message,
6060
'exception': exc,
6161
'transport': self,
6262
'protocol': self._protocol,
@@ -151,11 +151,11 @@ def _loop_reading(self, fut=None):
151151
self._read_fut = self._loop._proactor.recv(self._sock, 4096)
152152
except ConnectionAbortedError as exc:
153153
if not self._closing:
154-
self._fatal_error(exc)
154+
self._fatal_error(exc, 'Fatal read error on pipe transport')
155155
except ConnectionResetError as exc:
156156
self._force_close(exc)
157157
except OSError as exc:
158-
self._fatal_error(exc)
158+
self._fatal_error(exc, 'Fatal read error on pipe transport')
159159
except futures.CancelledError:
160160
if not self._closing:
161161
raise
@@ -246,7 +246,7 @@ def _loop_writing(self, f=None, data=None):
246246
except ConnectionResetError as exc:
247247
self._force_close(exc)
248248
except OSError as exc:
249-
self._fatal_error(exc)
249+
self._fatal_error(exc, 'Fatal write error on pipe transport')
250250

251251
def can_write_eof(self):
252252
return True

asyncio/selector_events.py

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -377,11 +377,11 @@ def close(self):
377377
self._conn_lost += 1
378378
self._loop.call_soon(self._call_connection_lost, None)
379379

380-
def _fatal_error(self, exc):
380+
def _fatal_error(self, exc, message='Fatal error on transport'):
381381
# Should be called from exception handler only.
382382
if not isinstance(exc, (BrokenPipeError, ConnectionResetError)):
383383
self._loop.call_exception_handler({
384-
'message': 'Fatal transport error',
384+
'message': message,
385385
'exception': exc,
386386
'transport': self,
387387
'protocol': self._protocol,
@@ -452,7 +452,7 @@ def _read_ready(self):
452452
except (BlockingIOError, InterruptedError):
453453
pass
454454
except Exception as exc:
455-
self._fatal_error(exc)
455+
self._fatal_error(exc, 'Fatal read error on socket transport')
456456
else:
457457
if data:
458458
self._protocol.data_received(data)
@@ -488,7 +488,7 @@ def write(self, data):
488488
except (BlockingIOError, InterruptedError):
489489
pass
490490
except Exception as exc:
491-
self._fatal_error(exc)
491+
self._fatal_error(exc, 'Fatal write error on socket transport')
492492
return
493493
else:
494494
data = data[n:]
@@ -511,7 +511,7 @@ def _write_ready(self):
511511
except Exception as exc:
512512
self._loop.remove_writer(self._sock_fd)
513513
self._buffer.clear()
514-
self._fatal_error(exc)
514+
self._fatal_error(exc, 'Fatal write error on socket transport')
515515
else:
516516
if n:
517517
del self._buffer[:n]
@@ -678,7 +678,7 @@ def _read_ready(self):
678678
self._loop.remove_reader(self._sock_fd)
679679
self._loop.add_writer(self._sock_fd, self._write_ready)
680680
except Exception as exc:
681-
self._fatal_error(exc)
681+
self._fatal_error(exc, 'Fatal read error on SSL transport')
682682
else:
683683
if data:
684684
self._protocol.data_received(data)
@@ -712,7 +712,7 @@ def _write_ready(self):
712712
except Exception as exc:
713713
self._loop.remove_writer(self._sock_fd)
714714
self._buffer.clear()
715-
self._fatal_error(exc)
715+
self._fatal_error(exc, 'Fatal write error on SSL transport')
716716
return
717717

718718
if n:
@@ -770,7 +770,7 @@ def _read_ready(self):
770770
except OSError as exc:
771771
self._protocol.error_received(exc)
772772
except Exception as exc:
773-
self._fatal_error(exc)
773+
self._fatal_error(exc, 'Fatal read error on datagram transport')
774774
else:
775775
self._protocol.datagram_received(data, addr)
776776

@@ -805,7 +805,8 @@ def sendto(self, data, addr=None):
805805
self._protocol.error_received(exc)
806806
return
807807
except Exception as exc:
808-
self._fatal_error(exc)
808+
self._fatal_error(exc,
809+
'Fatal write error on datagram transport')
809810
return
810811

811812
# Ensure that what we buffer is immutable.
@@ -827,7 +828,8 @@ def _sendto_ready(self):
827828
self._protocol.error_received(exc)
828829
return
829830
except Exception as exc:
830-
self._fatal_error(exc)
831+
self._fatal_error(exc,
832+
'Fatal write error on datagram transport')
831833
return
832834

833835
self._maybe_resume_protocol() # May append to buffer.

asyncio/unix_events.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ def _read_ready(self):
271271
except (BlockingIOError, InterruptedError):
272272
pass
273273
except OSError as exc:
274-
self._fatal_error(exc)
274+
self._fatal_error(exc, 'Fatal read error on pipe transport')
275275
else:
276276
if data:
277277
self._protocol.data_received(data)
@@ -291,11 +291,11 @@ def close(self):
291291
if not self._closing:
292292
self._close(None)
293293

294-
def _fatal_error(self, exc):
294+
def _fatal_error(self, exc, message='Fatal error on pipe transport'):
295295
# should be called by exception handler only
296296
if not (isinstance(exc, OSError) and exc.errno == errno.EIO):
297297
self._loop.call_exception_handler({
298-
'message': 'Fatal transport error',
298+
'message': message,
299299
'exception': exc,
300300
'transport': self,
301301
'protocol': self._protocol,
@@ -381,7 +381,7 @@ def write(self, data):
381381
n = 0
382382
except Exception as exc:
383383
self._conn_lost += 1
384-
self._fatal_error(exc)
384+
self._fatal_error(exc, 'Fatal write error on pipe transport')
385385
return
386386
if n == len(data):
387387
return
@@ -406,7 +406,7 @@ def _write_ready(self):
406406
# Remove writer here, _fatal_error() doesn't it
407407
# because _buffer is empty.
408408
self._loop.remove_writer(self._fileno)
409-
self._fatal_error(exc)
409+
self._fatal_error(exc, 'Fatal write error on pipe transport')
410410
else:
411411
if n == len(data):
412412
self._loop.remove_writer(self._fileno)
@@ -443,11 +443,11 @@ def close(self):
443443
def abort(self):
444444
self._close(None)
445445

446-
def _fatal_error(self, exc):
446+
def _fatal_error(self, exc, message='Fatal error on pipe transport'):
447447
# should be called by exception handler only
448448
if not isinstance(exc, (BrokenPipeError, ConnectionResetError)):
449449
self._loop.call_exception_handler({
450-
'message': 'Fatal transport error',
450+
'message': message,
451451
'exception': exc,
452452
'transport': self,
453453
'protocol': self._protocol,

tests/test_proactor_events.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,9 @@ def test_loop_reading_aborted(self):
6969
tr = _ProactorSocketTransport(self.loop, self.sock, self.protocol)
7070
tr._fatal_error = unittest.mock.Mock()
7171
tr._loop_reading()
72-
tr._fatal_error.assert_called_with(err)
72+
tr._fatal_error.assert_called_with(
73+
err,
74+
'Fatal read error on pipe transport')
7375

7476
def test_loop_reading_aborted_closing(self):
7577
self.loop._proactor.recv.side_effect = ConnectionAbortedError()
@@ -105,7 +107,9 @@ def test_loop_reading_exception(self):
105107
tr = _ProactorSocketTransport(self.loop, self.sock, self.protocol)
106108
tr._fatal_error = unittest.mock.Mock()
107109
tr._loop_reading()
108-
tr._fatal_error.assert_called_with(err)
110+
tr._fatal_error.assert_called_with(
111+
err,
112+
'Fatal read error on pipe transport')
109113

110114
def test_write(self):
111115
tr = _ProactorSocketTransport(self.loop, self.sock, self.protocol)
@@ -142,7 +146,9 @@ def test_loop_writing_err(self, m_log):
142146
tr._fatal_error = unittest.mock.Mock()
143147
tr._buffer = [b'da', b'ta']
144148
tr._loop_writing()
145-
tr._fatal_error.assert_called_with(err)
149+
tr._fatal_error.assert_called_with(
150+
err,
151+
'Fatal write error on pipe transport')
146152
tr._conn_lost = 1
147153

148154
tr.write(b'data')

tests/test_selector_events.py

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,7 @@ def test_fatal_error(self, m_exc):
655655

656656
m_exc.assert_called_with(
657657
test_utils.MockPattern(
658-
'Fatal transport error\nprotocol:.*\ntransport:.*'),
658+
'Fatal error on transport\nprotocol:.*\ntransport:.*'),
659659
exc_info=(OSError, MOCK_ANY, MOCK_ANY))
660660

661661
tr._force_close.assert_called_with(exc)
@@ -785,7 +785,9 @@ def test_read_ready_err(self, m_exc):
785785
transport._fatal_error = unittest.mock.Mock()
786786
transport._read_ready()
787787

788-
transport._fatal_error.assert_called_with(err)
788+
transport._fatal_error.assert_called_with(
789+
err,
790+
'Fatal read error on socket transport')
789791

790792
def test_write(self):
791793
data = b'data'
@@ -898,7 +900,9 @@ def test_write_exception(self, m_log):
898900
self.loop, self.sock, self.protocol)
899901
transport._fatal_error = unittest.mock.Mock()
900902
transport.write(data)
901-
transport._fatal_error.assert_called_with(err)
903+
transport._fatal_error.assert_called_with(
904+
err,
905+
'Fatal write error on socket transport')
902906
transport._conn_lost = 1
903907

904908
self.sock.reset_mock()
@@ -1001,7 +1005,9 @@ def test_write_ready_exception(self):
10011005
transport._fatal_error = unittest.mock.Mock()
10021006
transport._buffer.extend(b'data')
10031007
transport._write_ready()
1004-
transport._fatal_error.assert_called_with(err)
1008+
transport._fatal_error.assert_called_with(
1009+
err,
1010+
'Fatal write error on socket transport')
10051011

10061012
@unittest.mock.patch('asyncio.base_events.logger')
10071013
def test_write_ready_exception_and_close(self, m_log):
@@ -1237,7 +1243,9 @@ def test_read_ready_recv_exc(self):
12371243
transport = self._make_one()
12381244
transport._fatal_error = unittest.mock.Mock()
12391245
transport._read_ready()
1240-
transport._fatal_error.assert_called_with(err)
1246+
transport._fatal_error.assert_called_with(
1247+
err,
1248+
'Fatal read error on SSL transport')
12411249

12421250
def test_write_ready_send(self):
12431251
self.sslsock.send.return_value = 4
@@ -1319,7 +1327,9 @@ def test_write_ready_send_exc(self):
13191327
transport._buffer = list_to_buffer([b'data'])
13201328
transport._fatal_error = unittest.mock.Mock()
13211329
transport._write_ready()
1322-
transport._fatal_error.assert_called_with(err)
1330+
transport._fatal_error.assert_called_with(
1331+
err,
1332+
'Fatal write error on SSL transport')
13231333
self.assertEqual(list_to_buffer(), transport._buffer)
13241334

13251335
def test_write_ready_read_wants_write(self):
@@ -1407,7 +1417,9 @@ def test_read_ready_err(self):
14071417
transport._fatal_error = unittest.mock.Mock()
14081418
transport._read_ready()
14091419

1410-
transport._fatal_error.assert_called_with(err)
1420+
transport._fatal_error.assert_called_with(
1421+
err,
1422+
'Fatal read error on datagram transport')
14111423

14121424
def test_read_ready_oserr(self):
14131425
transport = _SelectorDatagramTransport(
@@ -1517,7 +1529,9 @@ def test_sendto_exception(self, m_log):
15171529
transport.sendto(data, ())
15181530

15191531
self.assertTrue(transport._fatal_error.called)
1520-
transport._fatal_error.assert_called_with(err)
1532+
transport._fatal_error.assert_called_with(
1533+
err,
1534+
'Fatal write error on datagram transport')
15211535
transport._conn_lost = 1
15221536

15231537
transport._address = ('123',)
@@ -1633,7 +1647,9 @@ def test_sendto_ready_exception(self):
16331647
transport._buffer.append((b'data', ()))
16341648
transport._sendto_ready()
16351649

1636-
transport._fatal_error.assert_called_with(err)
1650+
transport._fatal_error.assert_called_with(
1651+
err,
1652+
'Fatal write error on datagram transport')
16371653

16381654
def test_sendto_ready_error_received(self):
16391655
self.sock.sendto.side_effect = ConnectionRefusedError
@@ -1667,7 +1683,7 @@ def test_fatal_error_connected(self, m_exc):
16671683
self.assertFalse(self.protocol.error_received.called)
16681684
m_exc.assert_called_with(
16691685
test_utils.MockPattern(
1670-
'Fatal transport error\nprotocol:.*\ntransport:.*'),
1686+
'Fatal error on transport\nprotocol:.*\ntransport:.*'),
16711687
exc_info=(ConnectionRefusedError, MOCK_ANY, MOCK_ANY))
16721688

16731689

tests/test_unix_events.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ def test__read_ready_error(self, m_read, m_logexc):
365365
tr._close.assert_called_with(err)
366366
m_logexc.assert_called_with(
367367
test_utils.MockPattern(
368-
'Fatal transport error\nprotocol:.*\ntransport:.*'),
368+
'Fatal read error on pipe transport\nprotocol:.*\ntransport:.*'),
369369
exc_info=(OSError, MOCK_ANY, MOCK_ANY))
370370

371371
@unittest.mock.patch('os.read')
@@ -558,7 +558,9 @@ def test_write_err(self, m_write, m_log):
558558
m_write.assert_called_with(5, b'data')
559559
self.assertFalse(self.loop.writers)
560560
self.assertEqual([], tr._buffer)
561-
tr._fatal_error.assert_called_with(err)
561+
tr._fatal_error.assert_called_with(
562+
err,
563+
'Fatal write error on pipe transport')
562564
self.assertEqual(1, tr._conn_lost)
563565

564566
tr.write(b'data')
@@ -660,7 +662,7 @@ def test__write_ready_err(self, m_write, m_logexc):
660662
self.assertTrue(tr._closing)
661663
m_logexc.assert_called_with(
662664
test_utils.MockPattern(
663-
'Fatal transport error\nprotocol:.*\ntransport:.*'),
665+
'Fatal write error on pipe transport\nprotocol:.*\ntransport:.*'),
664666
exc_info=(OSError, MOCK_ANY, MOCK_ANY))
665667
self.assertEqual(1, tr._conn_lost)
666668
test_utils.run_briefly(self.loop)

0 commit comments

Comments
 (0)