[Python-checkins] cpython (merge 3.4 -> default): Merge 3.4 (asyncio)

victor.stinner python-checkins at python.org
Thu Jan 29 00:56:38 CET 2015


https://hg.python.org/cpython/rev/08c67d4f8e36
changeset:   94361:08c67d4f8e36
parent:      94356:e1eb09421b50
parent:      94360:1b35bef31bf8
user:        Victor Stinner <victor.stinner at gmail.com>
date:        Thu Jan 29 00:55:46 2015 +0100
summary:
  Merge 3.4 (asyncio)

files:
  Lib/asyncio/proactor_events.py                |   2 +-
  Lib/asyncio/selector_events.py                |  31 ++++---
  Lib/asyncio/sslproto.py                       |  20 +++-
  Lib/asyncio/unix_events.py                    |   4 +-
  Lib/test/test_asyncio/test_selector_events.py |  16 ++-
  Lib/test/test_asyncio/test_sslproto.py        |  40 +++++++--
  6 files changed, 77 insertions(+), 36 deletions(-)


diff --git a/Lib/asyncio/proactor_events.py b/Lib/asyncio/proactor_events.py
--- a/Lib/asyncio/proactor_events.py
+++ b/Lib/asyncio/proactor_events.py
@@ -38,7 +38,7 @@
             self._server._attach()
         self._loop.call_soon(self._protocol.connection_made, self)
         if waiter is not None:
-            # wait until protocol.connection_made() has been called
+            # only wake up the waiter when connection_made() has been called
             self._loop.call_soon(waiter._set_result_unless_cancelled, None)
 
     def __repr__(self):
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
@@ -578,10 +578,12 @@
         self._eof = False
         self._paused = False
 
-        self._loop.add_reader(self._sock_fd, self._read_ready)
         self._loop.call_soon(self._protocol.connection_made, self)
+        # only start reading when connection_made() has been called
+        self._loop.call_soon(self._loop.add_reader,
+                             self._sock_fd, self._read_ready)
         if waiter is not None:
-            # wait until protocol.connection_made() has been called
+            # only wake up the waiter when connection_made() has been called
             self._loop.call_soon(waiter._set_result_unless_cancelled, None)
 
     def pause_reading(self):
@@ -732,6 +734,16 @@
             start_time = None
         self._on_handshake(start_time)
 
+    def _wakeup_waiter(self, exc=None):
+        if self._waiter is None:
+            return
+        if not self._waiter.cancelled():
+            if exc is not None:
+                self._waiter.set_exception(exc)
+            else:
+                self._waiter.set_result(None)
+        self._waiter = None
+
     def _on_handshake(self, start_time):
         try:
             self._sock.do_handshake()
@@ -750,8 +762,7 @@
             self._loop.remove_reader(self._sock_fd)
             self._loop.remove_writer(self._sock_fd)
             self._sock.close()
-            if self._waiter is not None and not self._waiter.cancelled():
-                self._waiter.set_exception(exc)
+            self._wakeup_waiter(exc)
             if isinstance(exc, Exception):
                 return
             else:
@@ -774,9 +785,7 @@
                                        "on matching the hostname",
                                        self, exc_info=True)
                     self._sock.close()
-                    if (self._waiter is not None
-                    and not self._waiter.cancelled()):
-                        self._waiter.set_exception(exc)
+                    self._wakeup_waiter(exc)
                     return
 
         # Add extra info that becomes available after handshake.
@@ -789,10 +798,8 @@
         self._write_wants_read = False
         self._loop.add_reader(self._sock_fd, self._read_ready)
         self._loop.call_soon(self._protocol.connection_made, self)
-        if self._waiter is not None:
-            # wait until protocol.connection_made() has been called
-            self._loop.call_soon(self._waiter._set_result_unless_cancelled,
-                                 None)
+        # only wake up the waiter when connection_made() has been called
+        self._loop.call_soon(self._wakeup_waiter)
 
         if self._loop.get_debug():
             dt = self._loop.time() - start_time
@@ -924,7 +931,7 @@
         self._loop.add_reader(self._sock_fd, self._read_ready)
         self._loop.call_soon(self._protocol.connection_made, self)
         if waiter is not None:
-            # wait until protocol.connection_made() has been called
+            # only wake up the waiter when connection_made() has been called
             self._loop.call_soon(waiter._set_result_unless_cancelled, None)
 
     def get_write_buffer_size(self):
diff --git a/Lib/asyncio/sslproto.py b/Lib/asyncio/sslproto.py
--- a/Lib/asyncio/sslproto.py
+++ b/Lib/asyncio/sslproto.py
@@ -418,6 +418,16 @@
         self._in_shutdown = False
         self._transport = None
 
+    def _wakeup_waiter(self, exc=None):
+        if self._waiter is None:
+            return
+        if not self._waiter.cancelled():
+            if exc is not None:
+                self._waiter.set_exception(exc)
+            else:
+                self._waiter.set_result(None)
+        self._waiter = None
+
     def connection_made(self, transport):
         """Called when the low-level connection is made.
 
@@ -489,6 +499,9 @@
         try:
             if self._loop.get_debug():
                 logger.debug("%r received EOF", self)
+
+            self._wakeup_waiter(ConnectionResetError)
+
             if not self._in_handshake:
                 keep_open = self._app_protocol.eof_received()
                 if keep_open:
@@ -552,8 +565,7 @@
                                    self, exc_info=True)
             self._transport.close()
             if isinstance(exc, Exception):
-                if self._waiter is not None and not self._waiter.cancelled():
-                    self._waiter.set_exception(exc)
+                self._wakeup_waiter(exc)
                 return
             else:
                 raise
@@ -568,9 +580,7 @@
                            compression=sslobj.compression(),
                            )
         self._app_protocol.connection_made(self._app_transport)
-        if self._waiter is not None:
-            # wait until protocol.connection_made() has been called
-            self._waiter._set_result_unless_cancelled(None)
+        self._wakeup_waiter()
         self._session_established = True
         # In case transport.write() was already called. Don't call
         # immediatly _process_write_backlog(), but schedule it:
diff --git a/Lib/asyncio/unix_events.py b/Lib/asyncio/unix_events.py
--- a/Lib/asyncio/unix_events.py
+++ b/Lib/asyncio/unix_events.py
@@ -301,7 +301,7 @@
         self._loop.add_reader(self._fileno, self._read_ready)
         self._loop.call_soon(self._protocol.connection_made, self)
         if waiter is not None:
-            # wait until protocol.connection_made() has been called
+            # only wake up the waiter when connection_made() has been called
             self._loop.call_soon(waiter._set_result_unless_cancelled, None)
 
     def __repr__(self):
@@ -409,7 +409,7 @@
 
         self._loop.call_soon(self._protocol.connection_made, self)
         if waiter is not None:
-            # wait until protocol.connection_made() has been called
+            # only wake up the waiter when connection_made() has been called
             self._loop.call_soon(waiter._set_result_unless_cancelled, None)
 
     def __repr__(self):
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
@@ -59,6 +59,7 @@
     def test_make_socket_transport(self):
         m = mock.Mock()
         self.loop.add_reader = mock.Mock()
+        self.loop.add_reader._is_coroutine = False
         transport = self.loop._make_socket_transport(m, asyncio.Protocol())
         self.assertIsInstance(transport, _SelectorSocketTransport)
         close_transport(transport)
@@ -67,6 +68,7 @@
     def test_make_ssl_transport(self):
         m = mock.Mock()
         self.loop.add_reader = mock.Mock()
+        self.loop.add_reader._is_coroutine = False
         self.loop.add_writer = mock.Mock()
         self.loop.remove_reader = mock.Mock()
         self.loop.remove_writer = mock.Mock()
@@ -770,20 +772,24 @@
         return transport
 
     def test_ctor(self):
-        tr = self.socket_transport()
+        waiter = asyncio.Future(loop=self.loop)
+        tr = self.socket_transport(waiter=waiter)
+        self.loop.run_until_complete(waiter)
+
         self.loop.assert_reader(7, tr._read_ready)
         test_utils.run_briefly(self.loop)
         self.protocol.connection_made.assert_called_with(tr)
 
     def test_ctor_with_waiter(self):
-        fut = asyncio.Future(loop=self.loop)
+        waiter = asyncio.Future(loop=self.loop)
+        self.socket_transport(waiter=waiter)
+        self.loop.run_until_complete(waiter)
 
-        self.socket_transport(waiter=fut)
-        test_utils.run_briefly(self.loop)
-        self.assertIsNone(fut.result())
+        self.assertIsNone(waiter.result())
 
     def test_pause_resume_reading(self):
         tr = self.socket_transport()
+        test_utils.run_briefly(self.loop)
         self.assertFalse(tr._paused)
         self.loop.assert_reader(7, tr._read_ready)
         tr.pause_reading()
diff --git a/Lib/test/test_asyncio/test_sslproto.py b/Lib/test/test_asyncio/test_sslproto.py
--- a/Lib/test/test_asyncio/test_sslproto.py
+++ b/Lib/test/test_asyncio/test_sslproto.py
@@ -12,21 +12,36 @@
 from asyncio import test_utils
 
 
+ at unittest.skipIf(ssl is None, 'No ssl module')
 class SslProtoHandshakeTests(test_utils.TestCase):
 
     def setUp(self):
         self.loop = asyncio.new_event_loop()
         self.set_event_loop(self.loop)
 
-    @unittest.skipIf(ssl is None, 'No ssl module')
+    def ssl_protocol(self, waiter=None):
+        sslcontext = test_utils.dummy_ssl_context()
+        app_proto = asyncio.Protocol()
+        return sslproto.SSLProtocol(self.loop, app_proto, sslcontext, waiter)
+
+    def connection_made(self, ssl_proto, do_handshake=None):
+        transport = mock.Mock()
+        sslpipe = mock.Mock()
+        sslpipe.shutdown.return_value = b''
+        if do_handshake:
+            sslpipe.do_handshake.side_effect = do_handshake
+        else:
+            def mock_handshake(callback):
+                return []
+            sslpipe.do_handshake.side_effect = mock_handshake
+        with mock.patch('asyncio.sslproto._SSLPipe', return_value=sslpipe):
+            ssl_proto.connection_made(transport)
+
     def test_cancel_handshake(self):
         # Python issue #23197: cancelling an handshake must not raise an
         # exception or log an error, even if the handshake failed
-        sslcontext = test_utils.dummy_ssl_context()
-        app_proto = asyncio.Protocol()
         waiter = asyncio.Future(loop=self.loop)
-        ssl_proto = sslproto.SSLProtocol(self.loop, app_proto, sslcontext,
-                                         waiter)
+        ssl_proto = self.ssl_protocol(waiter)
         handshake_fut = asyncio.Future(loop=self.loop)
 
         def do_handshake(callback):
@@ -36,12 +51,7 @@
             return []
 
         waiter.cancel()
-        transport = mock.Mock()
-        sslpipe = mock.Mock()
-        sslpipe.shutdown.return_value = b''
-        sslpipe.do_handshake.side_effect = do_handshake
-        with mock.patch('asyncio.sslproto._SSLPipe', return_value=sslpipe):
-            ssl_proto.connection_made(transport)
+        self.connection_made(ssl_proto, do_handshake)
 
         with test_utils.disable_logger():
             self.loop.run_until_complete(handshake_fut)
@@ -49,6 +59,14 @@
         # Close the transport
         ssl_proto._app_transport.close()
 
+    def test_eof_received_waiter(self):
+        waiter = asyncio.Future(loop=self.loop)
+        ssl_proto = self.ssl_protocol(waiter)
+        self.connection_made(ssl_proto)
+        ssl_proto.eof_received()
+        test_utils.run_briefly(self.loop)
+        self.assertIsInstance(waiter.exception(), ConnectionResetError)
+
 
 if __name__ == '__main__':
     unittest.main()

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list