mirror of
https://github.com/python/cpython.git
synced 2025-08-03 16:39:00 +00:00
asyncio: Add support for running subprocesses on Windows with the IOCP event loop (Richard Oudkerk).
This commit is contained in:
parent
90fb914b4b
commit
5969128a86
7 changed files with 137 additions and 195 deletions
|
@ -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())
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue