summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorVictor Stinner <victor.stinner@gmail.com>2015-02-10 13:49:32 (GMT)
committerVictor Stinner <victor.stinner@gmail.com>2015-02-10 13:49:32 (GMT)
commit8e36812e27f70bd6e4b3b85c9e9e858b0ac0df5e (patch)
treeed88c714f3995dd3e85e3f8fef26097e04a6e285 /Lib
parent832dd5f0d65d3a0ebd7d7c7a3a4c80ab5170cd08 (diff)
downloadcpython-8e36812e27f70bd6e4b3b85c9e9e858b0ac0df5e.zip
cpython-8e36812e27f70bd6e4b3b85c9e9e858b0ac0df5e.tar.gz
cpython-8e36812e27f70bd6e4b3b85c9e9e858b0ac0df5e.tar.bz2
asyncio: BaseSubprocessTransport.close() doesn't try to kill the process if it
already finished
Diffstat (limited to 'Lib')
-rw-r--r--Lib/asyncio/base_subprocess.py7
-rw-r--r--Lib/test/test_asyncio/test_subprocess.py55
2 files changed, 61 insertions, 1 deletions
diff --git a/Lib/asyncio/base_subprocess.py b/Lib/asyncio/base_subprocess.py
index 02b9e89..5458ab1 100644
--- a/Lib/asyncio/base_subprocess.py
+++ b/Lib/asyncio/base_subprocess.py
@@ -93,7 +93,12 @@ class BaseSubprocessTransport(transports.SubprocessTransport):
continue
proto.pipe.close()
- if self._proc is not None and self._returncode is None:
+ if (self._proc is not None
+ # the child process finished?
+ and self._returncode is None
+ # the child process finished but the transport was not notified yet?
+ and self._proc.poll() is None
+ ):
if self._loop.get_debug():
logger.warning('Close running child process: kill %r', self)
diff --git a/Lib/test/test_asyncio/test_subprocess.py b/Lib/test/test_asyncio/test_subprocess.py
index b467b04..de0b08a 100644
--- a/Lib/test/test_asyncio/test_subprocess.py
+++ b/Lib/test/test_asyncio/test_subprocess.py
@@ -349,6 +349,61 @@ class SubprocessMixin:
self.loop.run_until_complete(cancel_make_transport())
test_utils.run_briefly(self.loop)
+ def test_close_kill_running(self):
+ @asyncio.coroutine
+ def kill_running():
+ create = self.loop.subprocess_exec(asyncio.SubprocessProtocol,
+ *PROGRAM_BLOCKED)
+ transport, protocol = yield from create
+ proc = transport.get_extra_info('subprocess')
+ proc.kill = mock.Mock()
+ returncode = transport.get_returncode()
+ transport.close()
+ return (returncode, proc.kill.called)
+
+ # Ignore "Close running child process: kill ..." log
+ with test_utils.disable_logger():
+ returncode, killed = self.loop.run_until_complete(kill_running())
+ self.assertIsNone(returncode)
+
+ # transport.close() must kill the process if it is still running
+ self.assertTrue(killed)
+ test_utils.run_briefly(self.loop)
+
+ def test_close_dont_kill_finished(self):
+ @asyncio.coroutine
+ def kill_running():
+ create = self.loop.subprocess_exec(asyncio.SubprocessProtocol,
+ *PROGRAM_BLOCKED)
+ transport, protocol = yield from create
+ proc = transport.get_extra_info('subprocess')
+
+ # kill the process (but asyncio is not notified immediatly)
+ proc.kill()
+ proc.wait()
+
+ proc.kill = mock.Mock()
+ proc_returncode = proc.poll()
+ transport_returncode = transport.get_returncode()
+ transport.close()
+ return (proc_returncode, transport_returncode, proc.kill.called)
+
+ # Ignore "Unknown child process pid ..." log of SafeChildWatcher,
+ # emitted because the test already consumes the exit status:
+ # proc.wait()
+ with test_utils.disable_logger():
+ result = self.loop.run_until_complete(kill_running())
+ test_utils.run_briefly(self.loop)
+
+ proc_returncode, transport_return_code, killed = result
+
+ self.assertIsNotNone(proc_returncode)
+ self.assertIsNone(transport_return_code)
+
+ # transport.close() must not kill the process if it finished, even if
+ # the transport was not notified yet
+ self.assertFalse(killed)
+
if sys.platform != 'win32':
# Unix