diff --git a/Lib/asyncio/selector_events.py b/Lib/asyncio/selector_events.py --- a/Lib/asyncio/selector_events.py +++ b/Lib/asyncio/selector_events.py @@ -565,6 +565,7 @@ self._loop.remove_reader(self._sock_fd) if not self._buffer: self._conn_lost += 1 + self._loop.remove_writer(self._sock_fd) self._loop.call_soon(self._call_connection_lost, None) # On Python 3.3 and older, objects with a destructor part of a reference @@ -659,6 +660,8 @@ logger.debug("%r resumes reading", self) def _read_ready(self): + if self._conn_lost: + return try: data = self._sock.recv(self.max_size) except (BlockingIOError, InterruptedError): @@ -718,6 +721,8 @@ def _write_ready(self): assert self._buffer, 'Data should not be empty' + if self._conn_lost: + return try: n = self._sock.send(self._buffer) except (BlockingIOError, InterruptedError): @@ -888,6 +893,8 @@ logger.debug("%r resumes reading", self) def _read_ready(self): + if self._conn_lost: + return if self._write_wants_read: self._write_wants_read = False self._write_ready() @@ -920,6 +927,8 @@ self.close() def _write_ready(self): + if self._conn_lost: + return if self._read_wants_write: self._read_wants_write = False self._read_ready() @@ -997,6 +1006,8 @@ return sum(len(data) for data, _ in self._buffer) def _read_ready(self): + if self._conn_lost: + return try: data, addr = self._sock.recvfrom(self.max_size) except (BlockingIOError, InterruptedError): diff --git a/Lib/test/test_asyncio/test_selector_events.py b/Lib/test/test_asyncio/test_selector_events.py --- a/Lib/test/test_asyncio/test_selector_events.py +++ b/Lib/test/test_asyncio/test_selector_events.py @@ -1077,17 +1077,6 @@ err, 'Fatal write error on socket transport') - @mock.patch('asyncio.base_events.logger') - def test_write_ready_exception_and_close(self, m_log): - self.sock.send.side_effect = OSError() - remove_writer = self.loop.remove_writer = mock.Mock() - - transport = self.socket_transport() - transport.close() - transport._buffer.extend(b'data') - transport._write_ready() - remove_writer.assert_called_with(self.sock_fd) - def test_write_eof(self): tr = self.socket_transport() self.assertTrue(tr.can_write_eof()) @@ -1111,6 +1100,14 @@ self.sock.shutdown.assert_called_with(socket.SHUT_WR) tr.close() + @mock.patch('asyncio.base_events.logger') + def test_transport_close_remove_writer(self, m_log): + remove_writer = self.loop.remove_writer = mock.Mock() + + transport = self.socket_transport() + transport.close() + remove_writer.assert_called_with(self.sock_fd) + @unittest.skipIf(ssl is None, 'No ssl module') class SelectorSslTransportTests(test_utils.TestCase): @@ -1165,7 +1162,7 @@ self.sslsock.do_handshake.side_effect = exc with test_utils.disable_logger(): waiter = asyncio.Future(loop=self.loop) - transport = self.ssl_transport(waiter=waiter) + self.ssl_transport(waiter=waiter) self.assertTrue(waiter.done()) self.assertIs(exc, waiter.exception()) self.assertTrue(self.sslsock.close.called) @@ -1364,20 +1361,19 @@ def test_write_ready_send_closing(self): self.sslsock.send.return_value = 4 transport = self._make_one() + transport._buffer = list_to_buffer([b'data']) transport.close() - transport._buffer = list_to_buffer([b'data']) transport._write_ready() - self.assertFalse(self.loop.writers) self.protocol.connection_lost.assert_called_with(None) def test_write_ready_send_closing_empty_buffer(self): self.sslsock.send.return_value = 4 + call_soon = self.loop.call_soon = mock.Mock() transport = self._make_one() + transport._buffer = list_to_buffer() transport.close() - transport._buffer = list_to_buffer() transport._write_ready() - self.assertFalse(self.loop.writers) - self.protocol.connection_lost.assert_called_with(None) + call_soon.assert_called_with(transport._call_connection_lost, None) def test_write_ready_send_retry(self): transport = self._make_one()