From 02757ea7e9bd3e3ea7c4cdb3f1c2b7f76fa2b294 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 10 Jan 2014 13:30:04 -0800 Subject: [PATCH] asyncio: Minimal pty support in UNIX read pipe, by Jonathan Slenders. --- Lib/asyncio/unix_events.py | 7 +++-- Lib/test/test_asyncio/test_events.py | 42 ++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/Lib/asyncio/unix_events.py b/Lib/asyncio/unix_events.py index 80a98f806ef..24da3274f8d 100644 --- a/Lib/asyncio/unix_events.py +++ b/Lib/asyncio/unix_events.py @@ -190,7 +190,9 @@ class _UnixReadPipeTransport(transports.ReadTransport): self._pipe = pipe self._fileno = pipe.fileno() mode = os.fstat(self._fileno).st_mode - if not (stat.S_ISFIFO(mode) or stat.S_ISSOCK(mode)): + if not (stat.S_ISFIFO(mode) or + stat.S_ISSOCK(mode) or + stat.S_ISCHR(mode)): raise ValueError("Pipe transport is for pipes/sockets only.") _set_nonblocking(self._fileno) self._protocol = protocol @@ -228,7 +230,8 @@ class _UnixReadPipeTransport(transports.ReadTransport): def _fatal_error(self, exc): # should be called by exception handler only - logger.exception('Fatal error for %s', self) + if not (isinstance(exc, OSError) and exc.errno == errno.EIO): + logger.exception('Fatal error for %s', self) self._close(exc) def _close(self, exc): diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py index 9545dd137a7..2e1dfebf34a 100644 --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -132,6 +132,8 @@ class MyReadPipeProto(protocols.Protocol): self.state.append('EOF') def connection_lost(self, exc): + if 'EOF' not in self.state: + self.state.append('EOF') # It is okay if EOF is missed. assert self.state == ['INITIAL', 'CONNECTED', 'EOF'], self.state self.state.append('CLOSED') if self.done: @@ -953,6 +955,46 @@ class EventLoopTestsMixin: # extra info is available self.assertIsNotNone(proto.transport.get_extra_info('pipe')) + @unittest.skipUnless(sys.platform != 'win32', + "Don't support pipes for Windows") + def test_read_pty_output(self): + proto = None + + def factory(): + nonlocal proto + proto = MyReadPipeProto(loop=self.loop) + return proto + + master, slave = os.openpty() + master_read_obj = io.open(master, 'rb', 0) + + @tasks.coroutine + def connect(): + t, p = yield from self.loop.connect_read_pipe(factory, + master_read_obj) + self.assertIs(p, proto) + self.assertIs(t, proto.transport) + self.assertEqual(['INITIAL', 'CONNECTED'], proto.state) + self.assertEqual(0, proto.nbytes) + + self.loop.run_until_complete(connect()) + + os.write(slave, b'1') + test_utils.run_until(self.loop, lambda: proto.nbytes) + self.assertEqual(1, proto.nbytes) + + os.write(slave, b'2345') + test_utils.run_until(self.loop, lambda: proto.nbytes >= 5) + self.assertEqual(['INITIAL', 'CONNECTED'], proto.state) + self.assertEqual(5, proto.nbytes) + + os.close(slave) + self.loop.run_until_complete(proto.done) + self.assertEqual( + ['INITIAL', 'CONNECTED', 'EOF', 'CLOSED'], proto.state) + # extra info is available + self.assertIsNotNone(proto.transport.get_extra_info('pipe')) + @unittest.skipUnless(sys.platform != 'win32', "Don't support pipes for Windows") def test_write_pipe(self):