from __future__ import print_function
from __future__ import unicode_literals
import psutil
import sh
try:
from shlex import quote as shell_quote
except ImportError:
from pipes import quote as shell_quote
[docs]class ShellCommandError(Exception):
"""
Raised when :meth:`.LogMixin.run_shell_command` fails.
"""
[docs]class ShellCommandMixin(object):
"""
Shell command mixin - for classes that need to run shell commands.
Requires :class:`~ievv_opensource.utils.logmixin.LogMixin`.
"""
[docs] def log_shell_command_stdout(self, line):
"""
Called by :meth:`.run_shell_command` each time the shell
command outputs anything to stdout.
"""
self.get_logger().stdout(line.rstrip())
[docs] def log_shell_command_stderr(self, line):
"""
Called by :meth:`.run_shell_command` each time the shell
command outputs anything to stderr.
"""
self.get_logger().stderr(line.rstrip())
def prettyformat_shell_command(self, executable, args=None, kwargs=None, _cwd=None):
output = [executable]
if args:
output.extend([shell_quote(arg) for arg in args])
if kwargs:
for key, value in kwargs.items():
if value:
if len(key) == 1:
prefix = '-'
else:
prefix = '--'
if value is True:
formatted_value = ''
else:
formatted_value = '={}'.format(value)
output.append('{prefix}{key}{formatted_value}'.format(
prefix=prefix,
key=key,
formatted_value=formatted_value
))
if _cwd:
output.insert(0, '[CWD={}]'.format(_cwd))
return ' '.join(output)
[docs] def run_shell_command(self, executable, args=None, kwargs=None, _cwd=None,
_out=None, _err=None, _env=None):
"""
Run a shell command.
Parameters:
executable: The name or path of the executable.
args: List of arguments for the ``sh.Command`` object.
kwargs: Dict of keyword arguments for the ``sh.Command`` object.
Raises:
ShellCommandError: When the command fails. See :class:`.ShellCommandError`.
"""
self.get_logger().debug('Execute: {}'.format(self.prettyformat_shell_command(
executable=executable, args=args, kwargs=kwargs, _cwd=_cwd)))
command = sh.Command(executable)
args = args or []
kwargs = kwargs or {}
if _cwd:
kwargs['_cwd'] = _cwd
if _env:
kwargs['_env'] = _env
_out = _out or self.log_shell_command_stdout
_err = _err or self.log_shell_command_stderr
try:
return command(*args,
_out=_out,
_err=_err,
**kwargs)
except sh.ErrorReturnCode:
# We do not need to show any more errors here - they
# have already been printed by the _out and _err handlers.
raise ShellCommandError()
[docs] def kill_process(self, pid):
"""
Kill the system process with the given ``pid``, and all
its child processes.
.. warning::
You should normally use :meth:`.terminate_process` instead of
this method since that normally gives the process the chance to
cleanup.
Parameters:
pid: The process ID of the process you want to kill.
Returns:
A list of all the killed processes.
"""
process_ids = []
try:
process = psutil.Process(pid)
except psutil.NoSuchProcess:
pass
else:
for childprocess in process.children():
process_ids.append(childprocess.pid)
childprocess.kill()
process_ids.append(process.pid)
process.kill()
return process_ids
[docs] def terminate_process(self, pid):
"""
Terminate the system process with the given ``pid``, and all
its child processes.
Parameters:
pid: The process ID of the process you want to terminate.
Returns:
A list of all the terminated processes.
"""
process_ids = []
try:
process = psutil.Process(pid)
except psutil.NoSuchProcess:
pass
else:
for childprocess in process.children():
process_ids.append(childprocess.pid)
childprocess.terminate()
try:
process.terminate()
except psutil.NoSuchProcess:
pass
else:
process_ids.append(process.pid)
return process_ids