diff --git a/project_manager/command.py b/project_manager/command.py index 19da07c..c79d79c 100644 --- a/project_manager/command.py +++ b/project_manager/command.py @@ -1,11 +1,14 @@ __all__ = ( "CommandError", + "CommandLogger", "CommandExecutor", ) import logging +import os import subprocess +import threading from pathlib import Path logger = logging.getLogger("command") @@ -14,7 +17,31 @@ logger = logging.getLogger("command") class CommandError(RuntimeError): @property def output(self): - return self.args[0].decode("utf-8").strip() + return self.args[0] + + +class CommandLogger(threading.Thread): + def __init__(self, level: int): + super().__init__() + self.daemon = False + self.level = level + self.fdRead, self.fdWrite = os.pipe() + self.pipeReader = os.fdopen(self.fdRead) + self.lines = [] + self.start() + + def fileno(self): + return self.fdWrite + + def run(self): + for line in iter(self.pipeReader.readline, ""): + logger.log(self.level, line.strip("\n")) + self.lines.append(line) + + self.pipeReader.close() + + def close(self): + os.close(self.fdWrite) class CommandExecutor: @@ -25,19 +52,17 @@ class CommandExecutor: def run(self, basepath: Path): self._logger.debug(self._command) - result = subprocess.run( - self._command.split(" "), - cwd=basepath, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - ) - self._logstd(result.stdout, logging.TRACE) - self._logstd(result.stderr, logging.TRACE) + out_logger = CommandLogger(logging.TRACE) + err_logger = CommandLogger(logging.TRACE) + try: + result = subprocess.run( + self._command.split(" "), + cwd=basepath, + stdout=out_logger, + stderr=err_logger, + ) + finally: + out_logger.close() + err_logger.close() if result.returncode != 0: - raise CommandError(result.stderr) - - @classmethod - def _logstd(cls, data: bytes, level: int): - if data: - output = data.decode("utf-8").strip() - cls._logger.log(level, f"\n{output}\n") + raise CommandError("".join(err_logger.lines or out_logger.lines))