asyncio: Add support for running subprocesses on Windows with the IOCP event loop (Richard Oudkerk).

This commit is contained in:
Guido van Rossum 2013-10-30 14:52:03 -07:00
parent 90fb914b4b
commit 5969128a86
7 changed files with 137 additions and 195 deletions

View file

@ -955,8 +955,23 @@ class EventLoopTestsMixin:
r.close()
w.close()
@unittest.skipIf(sys.platform == 'win32',
"Don't support subprocess for Windows yet")
class SubprocessTestsMixin:
def check_terminated(self, returncode):
if sys.platform == 'win32':
self.assertIsInstance(returncode, int)
self.assertNotEqual(0, returncode)
else:
self.assertEqual(-signal.SIGTERM, returncode)
def check_killed(self, returncode):
if sys.platform == 'win32':
self.assertIsInstance(returncode, int)
self.assertNotEqual(0, returncode)
else:
self.assertEqual(-signal.SIGKILL, returncode)
def test_subprocess_exec(self):
proto = None
transp = None
@ -980,11 +995,9 @@ class EventLoopTestsMixin:
self.loop.run_until_complete(proto.got_data[1].wait())
transp.close()
self.loop.run_until_complete(proto.completed)
self.assertEqual(-signal.SIGTERM, proto.returncode)
self.check_terminated(proto.returncode)
self.assertEqual(b'Python The Winner', proto.data[1])
@unittest.skipIf(sys.platform == 'win32',
"Don't support subprocess for Windows yet")
def test_subprocess_interactive(self):
proto = None
transp = None
@ -1017,10 +1030,8 @@ class EventLoopTestsMixin:
transp.close()
self.loop.run_until_complete(proto.completed)
self.assertEqual(-signal.SIGTERM, proto.returncode)
self.check_terminated(proto.returncode)
@unittest.skipIf(sys.platform == 'win32',
"Don't support subprocess for Windows yet")
def test_subprocess_shell(self):
proto = None
transp = None
@ -1030,7 +1041,7 @@ class EventLoopTestsMixin:
nonlocal proto, transp
transp, proto = yield from self.loop.subprocess_shell(
functools.partial(MySubprocessProtocol, self.loop),
'echo "Python"')
'echo Python')
self.assertIsInstance(proto, MySubprocessProtocol)
self.loop.run_until_complete(connect())
@ -1040,10 +1051,9 @@ class EventLoopTestsMixin:
self.loop.run_until_complete(proto.completed)
self.assertEqual(0, proto.returncode)
self.assertTrue(all(f.done() for f in proto.disconnects.values()))
self.assertEqual({1: b'Python\n', 2: b''}, proto.data)
self.assertEqual(proto.data[1].rstrip(b'\r\n'), b'Python')
self.assertEqual(proto.data[2], b'')
@unittest.skipIf(sys.platform == 'win32',
"Don't support subprocess for Windows yet")
def test_subprocess_exitcode(self):
proto = None
@ -1059,8 +1069,6 @@ class EventLoopTestsMixin:
self.loop.run_until_complete(proto.completed)
self.assertEqual(7, proto.returncode)
@unittest.skipIf(sys.platform == 'win32',
"Don't support subprocess for Windows yet")
def test_subprocess_close_after_finish(self):
proto = None
transp = None
@ -1081,8 +1089,6 @@ class EventLoopTestsMixin:
self.assertEqual(7, proto.returncode)
self.assertIsNone(transp.close())
@unittest.skipIf(sys.platform == 'win32',
"Don't support subprocess for Windows yet")
def test_subprocess_kill(self):
proto = None
transp = None
@ -1102,10 +1108,30 @@ class EventLoopTestsMixin:
transp.kill()
self.loop.run_until_complete(proto.completed)
self.assertEqual(-signal.SIGKILL, proto.returncode)
self.check_killed(proto.returncode)
@unittest.skipIf(sys.platform == 'win32',
"Don't support subprocess for Windows yet")
def test_subprocess_terminate(self):
proto = None
transp = None
prog = os.path.join(os.path.dirname(__file__), 'echo.py')
@tasks.coroutine
def connect():
nonlocal proto, transp
transp, proto = yield from self.loop.subprocess_exec(
functools.partial(MySubprocessProtocol, self.loop),
sys.executable, prog)
self.assertIsInstance(proto, MySubprocessProtocol)
self.loop.run_until_complete(connect())
self.loop.run_until_complete(proto.connected)
transp.terminate()
self.loop.run_until_complete(proto.completed)
self.check_terminated(proto.returncode)
@unittest.skipIf(sys.platform == 'win32', "Don't have SIGHUP")
def test_subprocess_send_signal(self):
proto = None
transp = None
@ -1127,8 +1153,6 @@ class EventLoopTestsMixin:
self.loop.run_until_complete(proto.completed)
self.assertEqual(-signal.SIGHUP, proto.returncode)
@unittest.skipIf(sys.platform == 'win32',
"Don't support subprocess for Windows yet")
def test_subprocess_stderr(self):
proto = None
transp = None
@ -1156,8 +1180,6 @@ class EventLoopTestsMixin:
self.assertTrue(proto.data[2].startswith(b'ERR:test'), proto.data[2])
self.assertEqual(0, proto.returncode)
@unittest.skipIf(sys.platform == 'win32',
"Don't support subprocess for Windows yet")
def test_subprocess_stderr_redirect_to_stdout(self):
proto = None
transp = None
@ -1188,8 +1210,6 @@ class EventLoopTestsMixin:
transp.close()
self.assertEqual(0, proto.returncode)
@unittest.skipIf(sys.platform == 'win32',
"Don't support subprocess for Windows yet")
def test_subprocess_close_client_stream(self):
proto = None
transp = None
@ -1217,14 +1237,18 @@ class EventLoopTestsMixin:
self.loop.run_until_complete(proto.disconnects[1])
stdin.write(b'xxx')
self.loop.run_until_complete(proto.got_data[2].wait())
self.assertEqual(b'ERR:BrokenPipeError', proto.data[2])
if sys.platform != 'win32':
self.assertEqual(b'ERR:BrokenPipeError', proto.data[2])
else:
# After closing the read-end of a pipe, writing to the
# write-end using os.write() fails with errno==EINVAL and
# GetLastError()==ERROR_INVALID_NAME on Windows!?! (Using
# WriteFile() we get ERROR_BROKEN_PIPE as expected.)
self.assertEqual(b'ERR:OSError', proto.data[2])
transp.close()
self.loop.run_until_complete(proto.completed)
self.assertEqual(-signal.SIGTERM, proto.returncode)
self.check_terminated(proto.returncode)
@unittest.skipIf(sys.platform == 'win32',
"Don't support subprocess for Windows yet")
def test_subprocess_wait_no_same_group(self):
proto = None
transp = None
@ -1252,7 +1276,10 @@ if sys.platform == 'win32':
def create_event_loop(self):
return windows_events.SelectorEventLoop()
class ProactorEventLoopTests(EventLoopTestsMixin, unittest.TestCase):
class ProactorEventLoopTests(EventLoopTestsMixin,
SubprocessTestsMixin,
unittest.TestCase):
def create_event_loop(self):
return windows_events.ProactorEventLoop()
@ -1283,26 +1310,34 @@ else:
from asyncio import unix_events
if hasattr(selectors, 'KqueueSelector'):
class KqueueEventLoopTests(EventLoopTestsMixin, unittest.TestCase):
class KqueueEventLoopTests(EventLoopTestsMixin,
SubprocessTestsMixin,
unittest.TestCase):
def create_event_loop(self):
return unix_events.SelectorEventLoop(
selectors.KqueueSelector())
if hasattr(selectors, 'EpollSelector'):
class EPollEventLoopTests(EventLoopTestsMixin, unittest.TestCase):
class EPollEventLoopTests(EventLoopTestsMixin,
SubprocessTestsMixin,
unittest.TestCase):
def create_event_loop(self):
return unix_events.SelectorEventLoop(selectors.EpollSelector())
if hasattr(selectors, 'PollSelector'):
class PollEventLoopTests(EventLoopTestsMixin, unittest.TestCase):
class PollEventLoopTests(EventLoopTestsMixin,
SubprocessTestsMixin,
unittest.TestCase):
def create_event_loop(self):
return unix_events.SelectorEventLoop(selectors.PollSelector())
# Should always exist.
class SelectEventLoopTests(EventLoopTestsMixin, unittest.TestCase):
class SelectEventLoopTests(EventLoopTestsMixin,
SubprocessTestsMixin,
unittest.TestCase):
def create_event_loop(self):
return unix_events.SelectorEventLoop(selectors.SelectSelector())