Source code for AutoArchive._services.external_command_executor._external_command_executor

# _external_command_executor.py
#
# Project: AutoArchive
# License: GNU GPLv3
#
# Copyright (C) 2003 - 2014 Róbert Čerňanský



""":class:`ExternalCommandExecutor` class."""



__all__ = ["ExternalCommandExecutor"]



# {{{ INCLUDES

import subprocess
import select

from AutoArchive._infrastructure.py_additions import event
from AutoArchive._infrastructure.service import IService


# }}} INCLUDES



# {{{ CLASSES

[docs]class ExternalCommandExecutor(IService): """Executes external commands.""" def __init__(self): pass @event def commandMessage(command, message, isError): """Raised when the external command produces a message on standard error. :param command: Command name or path that produced the message. :type command: ``str`` :param message: Message that the command produced. :type message: ``str`` :param isError: ``true`` if the message was sent to standard error, ``false`` otherwise. :type isError: ``bool``"""
[docs] def execute(self, command, arguments = None, environment = None): """Executes passed command. Commands standard output and standard error messages are propagated through ``commandMessage`` event. :raise OSError: If a system error occurred during command execution. :raise ChildProcessError: If the command exit code is non zero. Executes given command as a child process. Both standard output and standard error are captured and propagated via :meth:`commandMessage` event. The order of messages written to standard output vs. standard error is not guaranteed to be preserved. See also: :meth:`ExternalCommandExecutor.execute()`.""" if arguments is None: arguments = [] try: commandProcess = subprocess.Popen([command] + arguments, stdout = subprocess.PIPE, stderr = subprocess.PIPE, env = environment, universal_newlines = True) self.__processCommandOutput(command, commandProcess) except OSError as ex: raise OSError(str.format("Error while executing command '{}'.", [command] + arguments), command, ex)
def __processCommandOutput(self, command, commandProcess): # capture program's standard output and standard error and informs about captured messages; note that # the order of messages written to stdout vs. messages written to stderr might not be preserved while True: readyStreams = select.select((commandProcess.stdout, commandProcess.stderr), (), ())[0] streamActive = False for readyStream in readyStreams: line = readyStream.readline() if line: streamActive = True self.commandMessage(command, line[:-1], readyStream is not commandProcess.stdout) if commandProcess.poll() is not None and not streamActive: break if commandProcess.returncode: self.__handleCommandExitCode(command, commandProcess.returncode) @staticmethod def __handleCommandExitCode(command, exitCode): if exitCode != 0: raise ChildProcessError(str.format("Command did not finished successfully: '{}', exit code: {}.", command, exitCode), command)
# }}} CLASSES