[Python-checkins] bpo-34666: Implement stream.awrite() and stream.aclose() (GH-9274)

Andrew Svetlov webhook-mailer at python.org
Thu Sep 13 19:53:54 EDT 2018


https://github.com/python/cpython/commit/11194c877c902a6c3b769d85be887c2272e0a541
commit: 11194c877c902a6c3b769d85be887c2272e0a541
branch: master
author: Andrew Svetlov <andrew.svetlov at gmail.com>
committer: GitHub <noreply at github.com>
date: 2018-09-13T16:53:49-07:00
summary:

bpo-34666: Implement stream.awrite() and stream.aclose() (GH-9274)

files:
A Misc/NEWS.d/next/Library/2018-09-13-11-49-52.bpo-34666.3uLtWv.rst
M Doc/library/asyncio-stream.rst
M Lib/asyncio/streams.py
M Lib/test/test_asyncio/test_streams.py

diff --git a/Doc/library/asyncio-stream.rst b/Doc/library/asyncio-stream.rst
index 0cfecda91e99..80b76253d065 100644
--- a/Doc/library/asyncio-stream.rst
+++ b/Doc/library/asyncio-stream.rst
@@ -20,13 +20,13 @@ streams::
             '127.0.0.1', 8888)
 
         print(f'Send: {message!r}')
-        writer.write(message.encode())
+        await writer.awrite(message.encode())
 
         data = await reader.read(100)
         print(f'Received: {data.decode()!r}')
 
         print('Close the connection')
-        writer.close()
+        await writer.aclose()
 
     asyncio.run(tcp_echo_client('Hello World!'))
 
@@ -229,14 +229,57 @@ StreamWriter
    directly; use :func:`open_connection` and :func:`start_server`
    instead.
 
+   .. coroutinemethod:: awrite(data)
+
+      Write *data* to the stream.
+
+      The method respects control-flow, execution is paused if write
+      buffer reaches high-water limit.
+
+      .. versionadded:: 3.8
+
+   .. coroutinemethod:: aclose()
+
+      Close the stream.
+
+      Wait for finishing all closing actions, e.g. SSL shutdown for
+      secure sockets.
+
+      .. versionadded:: 3.8
+
+   .. method:: can_write_eof()
+
+      Return *True* if the underlying transport supports
+      the :meth:`write_eof` method, *False* otherwise.
+
+   .. method:: write_eof()
+
+      Close the write end of the stream after the buffered write
+      data is flushed.
+
+   .. attribute:: transport
+
+      Return the underlying asyncio transport.
+
+   .. method:: get_extra_info(name, default=None)
+
+      Access optional transport information; see
+      :meth:`BaseTransport.get_extra_info` for details.
+
    .. method:: write(data)
 
       Write *data* to the stream.
 
+      This method doesn't apply control-flow. The call should be
+      followed by :meth:`drain`.
+
    .. method:: writelines(data)
 
       Write a list (or any iterable) of bytes to the stream.
 
+      This method doesn't apply control-flow. The call should be
+      followed by :meth:`drain`.
+
    .. coroutinemethod:: drain()
 
       Wait until it is appropriate to resume writing to the stream.
@@ -272,25 +315,6 @@ StreamWriter
 
       .. versionadded:: 3.7
 
-   .. method:: can_write_eof()
-
-      Return *True* if the underlying transport supports
-      the :meth:`write_eof` method, *False* otherwise.
-
-   .. method:: write_eof()
-
-      Close the write end of the stream after the buffered write
-      data is flushed.
-
-   .. attribute:: transport
-
-      Return the underlying asyncio transport.
-
-   .. method:: get_extra_info(name, default=None)
-
-      Access optional transport information; see
-      :meth:`BaseTransport.get_extra_info` for details.
-
 
 Examples
 ========
diff --git a/Lib/asyncio/streams.py b/Lib/asyncio/streams.py
index e7fb22ee5d1a..0afc66a473d4 100644
--- a/Lib/asyncio/streams.py
+++ b/Lib/asyncio/streams.py
@@ -348,7 +348,7 @@ def close(self):
         # a reader can be garbage collected
         # after connection closing
         self._protocol._untrack_reader()
-        return self._transport.close()
+        self._transport.close()
 
     def is_closing(self):
         return self._transport.is_closing()
@@ -381,6 +381,14 @@ def get_extra_info(self, name, default=None):
             await sleep(0, loop=self._loop)
         await self._protocol._drain_helper()
 
+    async def aclose(self):
+        self.close()
+        await self.wait_closed()
+
+    async def awrite(self, data):
+        self.write(data)
+        await self.drain()
+
 
 class StreamReader:
 
diff --git a/Lib/test/test_asyncio/test_streams.py b/Lib/test/test_asyncio/test_streams.py
index 67ac9d91a0b1..d8e371510dea 100644
--- a/Lib/test/test_asyncio/test_streams.py
+++ b/Lib/test/test_asyncio/test_streams.py
@@ -964,6 +964,28 @@ def test_del_stream_before_connection_made(self):
                          'call "stream.close()" explicitly.',
                          messages[0]['message'])
 
+    def test_async_writer_api(self):
+        messages = []
+        self.loop.set_exception_handler(lambda loop, ctx: messages.append(ctx))
+
+        with test_utils.run_test_server() as httpd:
+            rd, wr = self.loop.run_until_complete(
+                asyncio.open_connection(*httpd.address,
+                                        loop=self.loop))
+
+            f = wr.awrite(b'GET / HTTP/1.0\r\n\r\n')
+            self.loop.run_until_complete(f)
+            f = rd.readline()
+            data = self.loop.run_until_complete(f)
+            self.assertEqual(data, b'HTTP/1.0 200 OK\r\n')
+            f = rd.read()
+            data = self.loop.run_until_complete(f)
+            self.assertTrue(data.endswith(b'\r\n\r\nTest message'))
+            f = wr.aclose()
+            self.loop.run_until_complete(f)
+
+        self.assertEqual(messages, [])
+
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/Misc/NEWS.d/next/Library/2018-09-13-11-49-52.bpo-34666.3uLtWv.rst b/Misc/NEWS.d/next/Library/2018-09-13-11-49-52.bpo-34666.3uLtWv.rst
new file mode 100644
index 000000000000..be82cfed7f13
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2018-09-13-11-49-52.bpo-34666.3uLtWv.rst
@@ -0,0 +1,3 @@
+Implement ``asyncio.StreamWriter.awrite`` and
+``asyncio.StreamWriter.aclose()`` coroutines.  Methods are needed for
+providing a consistent stream API with control flow switched on by default.



More information about the Python-checkins mailing list