Skip to content

Commit

Permalink
Add a new option (and parameter) --kill-children
Browse files Browse the repository at this point in the history
This option force simpleflow to kill child processes in simpleflow.execute, on exit.
  • Loading branch information
benjastudio committed Aug 23, 2017
1 parent 5787f26 commit 3dbdc54
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 3 deletions.
30 changes: 27 additions & 3 deletions simpleflow/execute.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

import time

import psutil

try:
import subprocess32 as subprocess
except ImportError:
Expand Down Expand Up @@ -172,7 +174,7 @@ def wait_subprocess(process, timeout=None, command_info=None):
return process.wait()


def python(interpreter='python', logger_name=__name__, timeout=None):
def python(interpreter='python', logger_name=__name__, timeout=None, kill_children=False):
"""
Execute a callable as an external Python program.
Expand Down Expand Up @@ -202,6 +204,8 @@ def execute(*args, **kwargs):
'--result-fd={}'.format(dup_result_fd),
'--error-fd={}'.format(dup_error_fd),
]
if kill_children:
full_command.append('--kill-children')
if compat.PY2: # close_fds doesn't work with python2 (using its C _posixsubprocess helper)
close_fds = False
pass_fds = ()
Expand All @@ -214,9 +218,7 @@ def execute(*args, **kwargs):
close_fds=close_fds,
pass_fds=pass_fds,
)

rc = wait_subprocess(process, timeout=timeout, command_info=full_command)

os.close(dup_result_fd)
os.close(dup_error_fd)
if rc:
Expand Down Expand Up @@ -409,7 +411,25 @@ def main():
metavar='N',
help='error file descriptor',
)
parser.add_argument(
'--kill-children',
action='store_const',
const=True,
default=False,
help='kill child processes on exit',
)
cmd_arguments = parser.parse_args()

def kill_child_processes():
process = psutil.Process(os.getpid())
children = process.children(recursive=True)

for child in children:
child.terminate()
time.sleep(0.3)
for child in children:
child.kill()

funcname = cmd_arguments.funcname
try:
arguments = format.decode(cmd_arguments.funcargs)
Expand Down Expand Up @@ -447,6 +467,8 @@ def main():
if not compat.PY2:
details = details.encode('utf-8')
os.write(cmd_arguments.error_fd, details)
if cmd_arguments.kill_children:
kill_child_processes()
sys.exit(1)

if cmd_arguments.result_fd == 1: # stdout (legacy)
Expand All @@ -456,6 +478,8 @@ def main():
if not compat.PY2:
result = result.encode('utf-8')
os.write(cmd_arguments.result_fd, result)
if cmd_arguments.kill_children:
kill_child_processes()


if __name__ == '__main__':
Expand Down
22 changes: 22 additions & 0 deletions tests/test_simpleflow/test_execute.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@
import platform
import threading

import psutil
import pytest
import time

import subprocess

from simpleflow import execute
from simpleflow.exceptions import ExecutionError, ExecutionTimeoutError
from simpleflow.execute import get_name


@execute.program(path='ls')
Expand Down Expand Up @@ -287,3 +291,21 @@ def test_timeout_execute_from_thread():
t = threading.Thread(target=test_timeout_execute)
t.start()
t.join()


def create_subprocess_and_raise():
pid = subprocess.Popen(['sleep', '600']).pid
return pid


def test_execute_dont_kill_children():
pid = execute.python()(create_subprocess_and_raise)()
subprocess = psutil.Process(pid)
assert subprocess.status() == 'sleeping'
subprocess.terminate() # cleanup


def test_execute_kill_children():
pid = execute.python(kill_children=True)(create_subprocess_and_raise)()
with pytest.raises(psutil.NoSuchProcess):
psutil.Process(pid)

0 comments on commit 3dbdc54

Please sign in to comment.