Commit 23b4b697 authored by Andrew Svetlov's avatar Andrew Svetlov Committed by Miss Islington (bot)

bpo-36889: Merge asyncio streams (GH-13251)



https://bugs.python.org/issue36889
parent 6f6ff8a5
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
# flake8: noqa # flake8: noqa
import sys import sys
import warnings
# This relies on each of the submodules having an __all__ variable. # This relies on each of the submodules having an __all__ variable.
from .base_events import * from .base_events import *
...@@ -43,3 +44,40 @@ if sys.platform == 'win32': # pragma: no cover ...@@ -43,3 +44,40 @@ if sys.platform == 'win32': # pragma: no cover
else: else:
from .unix_events import * # pragma: no cover from .unix_events import * # pragma: no cover
__all__ += unix_events.__all__ __all__ += unix_events.__all__
__all__ += ('StreamReader', 'StreamWriter', 'StreamReaderProtocol') # deprecated
def __getattr__(name):
global StreamReader, StreamWriter, StreamReaderProtocol
if name == 'StreamReader':
warnings.warn("StreamReader is deprecated since Python 3.8 "
"in favor of Stream, and scheduled for removal "
"in Python 3.10",
DeprecationWarning,
stacklevel=2)
from .streams import StreamReader as sr
StreamReader = sr
return StreamReader
if name == 'StreamWriter':
warnings.warn("StreamWriter is deprecated since Python 3.8 "
"in favor of Stream, and scheduled for removal "
"in Python 3.10",
DeprecationWarning,
stacklevel=2)
from .streams import StreamWriter as sw
StreamWriter = sw
return StreamWriter
if name == 'StreamReaderProtocol':
warnings.warn("Using asyncio internal class StreamReaderProtocol "
"is deprecated since Python 3.8 "
" and scheduled for removal "
"in Python 3.10",
DeprecationWarning,
stacklevel=2)
from .streams import StreamReaderProtocol as srp
StreamReaderProtocol = srp
return StreamReaderProtocol
raise AttributeError(f"module {__name__} has no attribute {name}")
This diff is collapsed.
...@@ -27,6 +27,8 @@ class SubprocessStreamProtocol(streams.FlowControlMixin, ...@@ -27,6 +27,8 @@ class SubprocessStreamProtocol(streams.FlowControlMixin,
self._process_exited = False self._process_exited = False
self._pipe_fds = [] self._pipe_fds = []
self._stdin_closed = self._loop.create_future() self._stdin_closed = self._loop.create_future()
self._stdout_closed = self._loop.create_future()
self._stderr_closed = self._loop.create_future()
def __repr__(self): def __repr__(self):
info = [self.__class__.__name__] info = [self.__class__.__name__]
...@@ -40,30 +42,35 @@ class SubprocessStreamProtocol(streams.FlowControlMixin, ...@@ -40,30 +42,35 @@ class SubprocessStreamProtocol(streams.FlowControlMixin,
def connection_made(self, transport): def connection_made(self, transport):
self._transport = transport self._transport = transport
stdout_transport = transport.get_pipe_transport(1) stdout_transport = transport.get_pipe_transport(1)
if stdout_transport is not None: if stdout_transport is not None:
self.stdout = streams.StreamReader(limit=self._limit, self.stdout = streams.Stream(mode=streams.StreamMode.READ,
loop=self._loop, transport=stdout_transport,
_asyncio_internal=True) protocol=self,
limit=self._limit,
loop=self._loop,
_asyncio_internal=True)
self.stdout.set_transport(stdout_transport) self.stdout.set_transport(stdout_transport)
self._pipe_fds.append(1) self._pipe_fds.append(1)
stderr_transport = transport.get_pipe_transport(2) stderr_transport = transport.get_pipe_transport(2)
if stderr_transport is not None: if stderr_transport is not None:
self.stderr = streams.StreamReader(limit=self._limit, self.stderr = streams.Stream(mode=streams.StreamMode.READ,
loop=self._loop, transport=stderr_transport,
_asyncio_internal=True) protocol=self,
limit=self._limit,
loop=self._loop,
_asyncio_internal=True)
self.stderr.set_transport(stderr_transport) self.stderr.set_transport(stderr_transport)
self._pipe_fds.append(2) self._pipe_fds.append(2)
stdin_transport = transport.get_pipe_transport(0) stdin_transport = transport.get_pipe_transport(0)
if stdin_transport is not None: if stdin_transport is not None:
self.stdin = streams.StreamWriter(stdin_transport, self.stdin = streams.Stream(mode=streams.StreamMode.WRITE,
protocol=self, transport=stdin_transport,
reader=None, protocol=self,
loop=self._loop, loop=self._loop,
_asyncio_internal=True) _asyncio_internal=True)
def pipe_data_received(self, fd, data): def pipe_data_received(self, fd, data):
if fd == 1: if fd == 1:
...@@ -114,6 +121,10 @@ class SubprocessStreamProtocol(streams.FlowControlMixin, ...@@ -114,6 +121,10 @@ class SubprocessStreamProtocol(streams.FlowControlMixin,
def _get_close_waiter(self, stream): def _get_close_waiter(self, stream):
if stream is self.stdin: if stream is self.stdin:
return self._stdin_closed return self._stdin_closed
elif stream is self.stdout:
return self._stdout_closed
elif stream is self.stderr:
return self._stderr_closed
class Process: class Process:
......
...@@ -607,7 +607,7 @@ class IocpProactor: ...@@ -607,7 +607,7 @@ class IocpProactor:
# ConnectPipe() failed with ERROR_PIPE_BUSY: retry later # ConnectPipe() failed with ERROR_PIPE_BUSY: retry later
delay = min(delay * 2, CONNECT_PIPE_MAX_DELAY) delay = min(delay * 2, CONNECT_PIPE_MAX_DELAY)
await tasks.sleep(delay, loop=self._loop) await tasks.sleep(delay)
return windows_utils.PipeHandle(handle) return windows_utils.PipeHandle(handle)
......
...@@ -30,21 +30,27 @@ class AllTest(unittest.TestCase): ...@@ -30,21 +30,27 @@ class AllTest(unittest.TestCase):
raise NoAll(modname) raise NoAll(modname)
names = {} names = {}
with self.subTest(module=modname): with self.subTest(module=modname):
try: with support.check_warnings(
exec("from %s import *" % modname, names) ("", DeprecationWarning),
except Exception as e: ("", ResourceWarning),
# Include the module name in the exception string quiet=True):
self.fail("__all__ failure in {}: {}: {}".format( try:
modname, e.__class__.__name__, e)) exec("from %s import *" % modname, names)
if "__builtins__" in names: except Exception as e:
del names["__builtins__"] # Include the module name in the exception string
if '__annotations__' in names: self.fail("__all__ failure in {}: {}: {}".format(
del names['__annotations__'] modname, e.__class__.__name__, e))
keys = set(names) if "__builtins__" in names:
all_list = sys.modules[modname].__all__ del names["__builtins__"]
all_set = set(all_list) if '__annotations__' in names:
self.assertCountEqual(all_set, all_list, "in module {}".format(modname)) del names['__annotations__']
self.assertEqual(keys, all_set, "in module {}".format(modname)) if "__warningregistry__" in names:
del names["__warningregistry__"]
keys = set(names)
all_list = sys.modules[modname].__all__
all_set = set(all_list)
self.assertCountEqual(all_set, all_list, "in module {}".format(modname))
self.assertEqual(keys, all_set, "in module {}".format(modname))
def walk_modules(self, basedir, modpath): def walk_modules(self, basedir, modpath):
for fn in sorted(os.listdir(basedir)): for fn in sorted(os.listdir(basedir)):
......
...@@ -1152,8 +1152,9 @@ class BaseEventLoopWithSelectorTests(test_utils.TestCase): ...@@ -1152,8 +1152,9 @@ class BaseEventLoopWithSelectorTests(test_utils.TestCase):
@unittest.skipUnless(hasattr(socket, 'AF_INET6'), 'no IPv6 support') @unittest.skipUnless(hasattr(socket, 'AF_INET6'), 'no IPv6 support')
def test_create_server_ipv6(self): def test_create_server_ipv6(self):
async def main(): async def main():
srv = await asyncio.start_server( with self.assertWarns(DeprecationWarning):
lambda: None, '::1', 0, loop=self.loop) srv = await asyncio.start_server(
lambda: None, '::1', 0, loop=self.loop)
try: try:
self.assertGreater(len(srv.sockets), 0) self.assertGreater(len(srv.sockets), 0)
finally: finally:
......
...@@ -58,9 +58,10 @@ class BaseTestBufferedProtocol(func_tests.FunctionalTestCaseMixin): ...@@ -58,9 +58,10 @@ class BaseTestBufferedProtocol(func_tests.FunctionalTestCaseMixin):
writer.close() writer.close()
await writer.wait_closed() await writer.wait_closed()
srv = self.loop.run_until_complete( with self.assertWarns(DeprecationWarning):
asyncio.start_server( srv = self.loop.run_until_complete(
on_server_client, '127.0.0.1', 0)) asyncio.start_server(
on_server_client, '127.0.0.1', 0))
addr = srv.sockets[0].getsockname() addr = srv.sockets[0].getsockname()
self.loop.run_until_complete( self.loop.run_until_complete(
......
...@@ -94,7 +94,9 @@ class StreamReaderTests(BaseTest): ...@@ -94,7 +94,9 @@ class StreamReaderTests(BaseTest):
def test_readline(self): def test_readline(self):
DATA = b'line1\nline2\nline3' DATA = b'line1\nline2\nline3'
stream = asyncio.StreamReader(loop=self.loop, _asyncio_internal=True) stream = asyncio.Stream(mode=asyncio.StreamMode.READ,
loop=self.loop,
_asyncio_internal=True)
stream.feed_data(DATA) stream.feed_data(DATA)
stream.feed_eof() stream.feed_eof()
......
...@@ -46,8 +46,9 @@ class BaseStartServer(func_tests.FunctionalTestCaseMixin): ...@@ -46,8 +46,9 @@ class BaseStartServer(func_tests.FunctionalTestCaseMixin):
async with srv: async with srv:
await srv.serve_forever() await srv.serve_forever()
srv = self.loop.run_until_complete(asyncio.start_server( with self.assertWarns(DeprecationWarning):
serve, support.HOSTv4, 0, loop=self.loop, start_serving=False)) srv = self.loop.run_until_complete(asyncio.start_server(
serve, support.HOSTv4, 0, loop=self.loop, start_serving=False))
self.assertFalse(srv.is_serving()) self.assertFalse(srv.is_serving())
...@@ -102,8 +103,9 @@ class SelectorStartServerTests(BaseStartServer, unittest.TestCase): ...@@ -102,8 +103,9 @@ class SelectorStartServerTests(BaseStartServer, unittest.TestCase):
await srv.serve_forever() await srv.serve_forever()
with test_utils.unix_socket_path() as addr: with test_utils.unix_socket_path() as addr:
srv = self.loop.run_until_complete(asyncio.start_unix_server( with self.assertWarns(DeprecationWarning):
serve, addr, loop=self.loop, start_serving=False)) srv = self.loop.run_until_complete(asyncio.start_unix_server(
serve, addr, loop=self.loop, start_serving=False))
main_task = self.loop.create_task(main(srv)) main_task = self.loop.create_task(main(srv))
......
...@@ -649,12 +649,13 @@ class BaseStartTLS(func_tests.FunctionalTestCaseMixin): ...@@ -649,12 +649,13 @@ class BaseStartTLS(func_tests.FunctionalTestCaseMixin):
sock.close() sock.close()
async def client(addr): async def client(addr):
reader, writer = await asyncio.open_connection( with self.assertWarns(DeprecationWarning):
*addr, reader, writer = await asyncio.open_connection(
ssl=client_sslctx, *addr,
server_hostname='', ssl=client_sslctx,
loop=self.loop, server_hostname='',
ssl_handshake_timeout=1.0) loop=self.loop,
ssl_handshake_timeout=1.0)
with self.tcp_server(server, with self.tcp_server(server,
max_clients=1, max_clients=1,
...@@ -688,12 +689,13 @@ class BaseStartTLS(func_tests.FunctionalTestCaseMixin): ...@@ -688,12 +689,13 @@ class BaseStartTLS(func_tests.FunctionalTestCaseMixin):
sock.close() sock.close()
async def client(addr): async def client(addr):
reader, writer = await asyncio.open_connection( with self.assertWarns(DeprecationWarning):
*addr, reader, writer = await asyncio.open_connection(
ssl=client_sslctx, *addr,
server_hostname='', ssl=client_sslctx,
loop=self.loop, server_hostname='',
ssl_handshake_timeout=1.0) loop=self.loop,
ssl_handshake_timeout=1.0)
with self.tcp_server(server, with self.tcp_server(server,
max_clients=1, max_clients=1,
...@@ -724,11 +726,12 @@ class BaseStartTLS(func_tests.FunctionalTestCaseMixin): ...@@ -724,11 +726,12 @@ class BaseStartTLS(func_tests.FunctionalTestCaseMixin):
sock.close() sock.close()
async def client(addr): async def client(addr):
reader, writer = await asyncio.open_connection( with self.assertWarns(DeprecationWarning):
*addr, reader, writer = await asyncio.open_connection(
ssl=client_sslctx, *addr,
server_hostname='', ssl=client_sslctx,
loop=self.loop) server_hostname='',
loop=self.loop)
self.assertEqual(await reader.readline(), b'A\n') self.assertEqual(await reader.readline(), b'A\n')
writer.write(b'B') writer.write(b'B')
......
This diff is collapsed.
...@@ -17,6 +17,7 @@ import _winapi ...@@ -17,6 +17,7 @@ import _winapi
import asyncio import asyncio
from asyncio import windows_events from asyncio import windows_events
from asyncio.streams import _StreamProtocol
from test.test_asyncio import utils as test_utils from test.test_asyncio import utils as test_utils
from test.support.script_helper import spawn_python from test.support.script_helper import spawn_python
...@@ -100,16 +101,16 @@ class ProactorTests(test_utils.TestCase): ...@@ -100,16 +101,16 @@ class ProactorTests(test_utils.TestCase):
clients = [] clients = []
for i in range(5): for i in range(5):
stream_reader = asyncio.StreamReader(loop=self.loop, stream = asyncio.Stream(mode=asyncio.StreamMode.READ,
_asyncio_internal=True) loop=self.loop, _asyncio_internal=True)
protocol = asyncio.StreamReaderProtocol(stream_reader, protocol = _StreamProtocol(stream,
loop=self.loop, loop=self.loop,
_asyncio_internal=True) _asyncio_internal=True)
trans, proto = await self.loop.create_pipe_connection( trans, proto = await self.loop.create_pipe_connection(
lambda: protocol, ADDRESS) lambda: protocol, ADDRESS)
self.assertIsInstance(trans, asyncio.Transport) self.assertIsInstance(trans, asyncio.Transport)
self.assertEqual(protocol, proto) self.assertEqual(protocol, proto)
clients.append((stream_reader, trans)) clients.append((stream, trans))
for i, (r, w) in enumerate(clients): for i, (r, w) in enumerate(clients):
w.write('lower-{}\n'.format(i).encode()) w.write('lower-{}\n'.format(i).encode())
...@@ -118,6 +119,7 @@ class ProactorTests(test_utils.TestCase): ...@@ -118,6 +119,7 @@ class ProactorTests(test_utils.TestCase):
response = await r.readline() response = await r.readline()
self.assertEqual(response, 'LOWER-{}\n'.format(i).encode()) self.assertEqual(response, 'LOWER-{}\n'.format(i).encode())
w.close() w.close()
await r.close()
server.close() server.close()
......
Introduce :class:`asyncio.Stream` class that merges :class:`asyncio.StreamReader` and :class:`asyncio.StreamWriter` functionality.
:class:`asyncio.Stream` can work in readonly, writeonly and readwrite modes.
Provide :func:`asyncio.connect`, :func:`asyncio.connect_unix`, :func:`asyncio.connect_read_pipe` and :func:`asyncio.connect_write_pipe` factories to open :class:`asyncio.Stream` connections. Provide :class:`asyncio.StreamServer` and :class:`UnixStreamServer` to serve servers with asyncio.Stream API.
Modify :func:`asyncio.create_subprocess_shell` and :func:`asyncio.create_subprocess_exec` to use :class:`asyncio.Stream` instead of deprecated :class:`StreamReader` and :class:`StreamWriter`.
Deprecate :class:`asyncio.StreamReader` and :class:`asyncio.StreamWriter`.
Deprecate usage of private classes, e.g. :class:`asyncio.FlowControlMixing` and :class:`asyncio.StreamReaderProtocol` outside of asyncio package.
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