Source code for railgun.common.osutil
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# @file: railgun/common/osutil.py
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# This file is released under BSD 2-clause license.
import os
import time
import math
import signal
import subprocess
[docs]class ProcessTimeout(Exception):
"""Indicate that the timeout was reached when executing a process."""
pass
[docs]def is_running(pid):
"""Check whether the process with given `pid` is still running.
:param pid: The process id.
:type pid: :class:`int`
:return: :data:`True` if the process is still alive, :data:`False`
otherwise.
"""
try:
os.kill(pid, 0)
return True
except OSError:
return False
[docs]def execute(cmd, timeout=None, **kwargs):
"""Execute a command, read the output and return it back.
:param cmd: Command to execute.
:type cmd: :class:`str`
:param timeout: Process timeout in seconds.
:type timeout: :class:`int`
:param kwargs: Named arguments for `subprocess.Popen`.
:return: (stdout, stderr, exit code)
:rtype: :class:`tuple`
:raises: :class:`OSError` on missing command or any other OS errors.
:raises: :class:`ProcessTimeout` if a timeout was reached.
"""
ph_out = None # process output
ph_err = None # stderr
ph_ret = None # return code
p = subprocess.Popen(cmd, shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
**kwargs)
# if timeout is not set wait for process to complete
if not timeout:
ph_ret = p.wait()
else:
fin_time = math.ceil(time.time() + timeout)
while p.poll() is None and fin_time > time.time():
time.sleep(1)
# if timeout reached, raise an exception
if fin_time < time.time():
# starting 2.6 subprocess has a kill() method which is preferable
# p.kill()
if is_running(p.pid):
os.kill(p.pid, signal.SIGKILL)
raise ProcessTimeout("Process timeout has been reached.")
ph_ret = p.returncode
ph_out, ph_err = p.communicate()
return (ph_ret, ph_out, ph_err)