Safely running code in a process, redirect stdout in multithreading.Process Safely running code in a process, redirect stdout in multithreading.Process multithreading multithreading

Safely running code in a process, redirect stdout in multithreading.Process


Communicating with running process is not straightforward in Python. For some reason you can only do it once in subprocess life cycle. From my experience, it is best to run a thread that starts a process and after timeout gets its output and terminates the subprocess.

Something like:

def subprocess_with_timeout(cmd, timeout_sec, stdin_data=None):    """Execute `cmd` in a subprocess and enforce timeout `timeout_sec` seconds.    Send `stdin_data` to the subprocess.    Return subprocess exit code and outputs on natural completion of the subprocess.    Raise an exception if timeout expires before subprocess completes."""    proc = os.subprocess.Popen(cmd,                        stdin=subprocess.PIPE,                        stdout=subprocess.PIPE,                        stderr=subprocess.PIPE)    timer = threading.Timer(timeout_sec, proc.kill)    # this will terminate subprocess after timeout    timer.start()    # you will be blocked here until process terminates (by itself or by timeout death switch)    stdoutdata, stderrdata = proc.communicate(stdin_data)     if timer.is_alive():        # Process completed naturally - cancel timer and return exit code        timer.cancel()        return proc.returncode, stdoutdata, stderrdata    # Process killed by timer - raise exception    raise TimeoutError('Process #%d killed after %f seconds' % (proc.pid, timeout_sec))

So, run a threaded executioner that calls for subprocess_with_timeout. It should handle the inputs and save the results.

Another idea is using a webserver to do the IPC. See this link


What about subprocess.check_output? You could call python -c {snippet} with it, or if it is longer, just write the snippet to a temporary .py file. check_output (and indeed, all the other functions in subprocess) has a timeout parameter.

The general idea is then:

import subprocessimport sysdef execCode(code):    try:        output = subprocess.check_output([sys.executable, '-c', code],                                         stdin=subprocess.PIPE,                                         stderr=subprocess.PIPE,                                         timeout=0.5)        return True, output    except subprocess.TimeoutExpired as te:        return False, 'run time exceeded'    except subprocess.CalledProcessError as cpe:        return False, cpe.stderr

Example runs in IPython:

In [18]: execCode('import os\nprint(" ".join(os.listdir()))')Out[18]:(True, b'contents of directory\n')In [19]: execCode('import time\ntime.sleep(1)')Out[19]: (False, 'run time exceeded')In [20]: execCode('import os\nprint("\t".join(os.listdi))')Out[20]: (False, b'Traceback (most recent call last):\n  File "<string>", line 2, in <module>\nAttributeError: module \'os\' has no attribute \'listdi\'\n')

Note that check_output returns a bytes sequence, so you will have to convert it into str. Or you can use the encoding parameter of check_output.