Source code for openmdao.util.shell_proc

import os.path
import signal
import subprocess
import sys
import time

PIPE = subprocess.PIPE
STDOUT = subprocess.STDOUT
DEV_NULL = 'nul:' if sys.platform == 'win32' else '/dev/null'


[docs]class CalledProcessError(subprocess.CalledProcessError): """ :class:`subprocess.CalledProcessError` plus `errormsg` attribute. """ def __init__(self, returncode, cmd, errormsg): super(CalledProcessError, self).__init__(returncode, cmd) self.errormsg = errormsg def __str__(self): return 'Command %r returned non-zero exit status %d: %s' \ % (self.cmd, self.returncode, self.errormsg)
[docs]class ShellProc(subprocess.Popen): """ A slight modification to :class:`subprocess.Popen`. If `args` is a string, then the ``shell`` argument is set True. Updates a copy of ``os.environ`` with `env` and opens files for any stream which is a :class:`str`. args: string or list If a string, then this is the command line to execute and the :class:`subprocess.Popen` ``shell`` argument is set True. Otherwise, this is a list of arguments; the first is the command to execute. stdin, stdout, stderr: string, file, or int Specify handling of corresponding stream. If a string, a file of that name is opened. Otherwise, see the :mod:`subprocess` documentation. env: dict Environment variables for the command. """ def __init__(self, args, stdin=None, stdout=None, stderr=None, env=None, universal_newlines=False): environ = os.environ.copy() if env: environ.update(env) self._stdin_arg = stdin self._stdout_arg = stdout self._stderr_arg = stderr if isinstance(stdin, str): self._inp = open(stdin, 'r') else: self._inp = stdin if isinstance(stdout, str): self._out = open(stdout, 'w') else: self._out = stdout if isinstance(stderr, str): self._err = open(stderr, 'w') else: self._err = stderr shell = isinstance(args, str) try: subprocess.Popen.__init__(self, args, stdin=self._inp, stdout=self._out, stderr=self._err, shell=shell, env=environ, universal_newlines=universal_newlines) except Exception: self.close_files() raise
[docs] def close_files(self): """ Closes files that were implicitly opened. """ if isinstance(self._stdin_arg, str): self._inp.close() if isinstance(self._stdout_arg, str): self._out.close() if isinstance(self._stderr_arg, str): self._err.close()
[docs] def terminate(self, timeout=None): """ Stop child process. If `timeout` is specified, then :meth:`wait` will be called to wait for the process to terminate. timeout: float (seconds) Maximum time to wait for the process to stop. A value of zero implies an infinite maximum wait. """ super(ShellProc, self).terminate() if timeout is not None: return self.wait(timeout=timeout)
[docs] def wait(self, poll_delay=0., timeout=0.): """ Polls for command completion or timeout. Closes any files implicitly opened. Returns ``(return_code, error_msg)``. poll_delay: float (seconds) Time to delay between polling for command completion. A value of zero uses an internal default. timeout: float (seconds) Maximum time to wait for command completion. A value of zero implies an infinite maximum wait. """ return_code = None try: if poll_delay <= 0: poll_delay = max(0.1, timeout/100.) poll_delay = min(10., poll_delay) npolls = int(timeout / poll_delay) + 1 time.sleep(poll_delay) return_code = self.poll() while return_code is None: npolls -= 1 if (timeout > 0) and (npolls < 0): self.terminate() break time.sleep(poll_delay) return_code = self.poll() finally: self.close_files() # self.returncode set by self.poll(). if return_code is not None: self.errormsg = self.error_message(return_code) else: self.errormsg = 'Timed out' return (return_code, self.errormsg)
[docs] def error_message(self, return_code): """ Return error message for `return_code`. The error messages are derived from the operating system definitions. Some programs don't necessarily return exit codes conforming to these definitions. return_code: int Return code from :meth:`poll`. """ error_msg = '' if return_code: if return_code > 0: error_msg = ': %s' % os.strerror(return_code) elif sys.platform != 'win32': sig = -return_code if sig < signal.NSIG: for item in signal.__dict__.keys(): if item.startswith('SIG'): if getattr(signal, item) == sig: error_msg = ': %s' % item break return error_msg
[docs]def call(args, stdin=None, stdout=None, stderr=None, env=None, poll_delay=0., timeout=0.): """ Run command with arguments. Returns ``(return_code, error_msg)``. args: string or list If a string, then this is the command line to execute and the :class:`subprocess.Popen` ``shell`` argument is set True. Otherwise, this is a list of arguments; the first is the command to execute. stdin, stdout, stderr: string, file, or int Specify handling of corresponding stream. If a string, a file of that name is opened. Otherwise, see the :mod:`subprocess` documentation. env: dict Environment variables for the command. poll_delay: float (seconds) Time to delay between polling for command completion. A value of zero uses an internal default. timeout: float (seconds) Maximum time to wait for command completion. A value of zero implies an infinite maximum wait. """ process = ShellProc(args, stdin, stdout, stderr, env) return process.wait(poll_delay, timeout)
[docs]def check_call(args, stdin=None, stdout=None, stderr=None, env=None, poll_delay=0., timeout=0.): """ Run command with arguments. If non-zero, `return_code` raises :class:`CalledProcessError`. args: string or list If a string, then this is the command line to execute, and the :class:`subprocess.Popen` ``shell`` argument is set True. Otherwise, this is a list of arguments; the first is the command to execute. stdin, stdout, stderr: string, file, or int Specify handling of corresponding stream. If a string, a file of that name is opened. Otherwise, see the :mod:`subprocess` documentation. env: dict Environment variables for the command. poll_delay: float (seconds) Time to delay between polling for command completion. A value of zero uses an internal default. timeout: float (seconds) Maximum time to wait for command completion. A value of zero implies an infinite maximum wait. """ process = ShellProc(args, stdin, stdout, stderr, env) return_code, error_msg = process.wait(poll_delay, timeout) if return_code: raise CalledProcessError(return_code, args, error_msg)