From 6f24a2b6c5366ce34ff6cc432b06d64b3dc0c9e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Fri, 3 Jul 2015 13:56:31 +0200 Subject: [PATCH] New helper CommandlineCaller Wraps sarge and allows asynchronous execution of commands incl. logging to provided logging methods for stdout, stderr and command calls. Refactored PipCaller to utilized that. --- src/octoprint/util/commandline.py | 87 +++++++++++++++++++++++++++++++ src/octoprint/util/pip.py | 58 +++------------------ 2 files changed, 93 insertions(+), 52 deletions(-) create mode 100644 src/octoprint/util/commandline.py diff --git a/src/octoprint/util/commandline.py b/src/octoprint/util/commandline.py new file mode 100644 index 00000000..1b38de89 --- /dev/null +++ b/src/octoprint/util/commandline.py @@ -0,0 +1,87 @@ +# coding=utf-8 +from __future__ import absolute_import + +__author__ = "Gina Häußge " +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' +__copyright__ = "Copyright (C) 2015 The OctoPrint Project - Released under terms of the AGPLv3 License" + + +import sarge +import logging + + +class CommandlineError(Exception): + def __init__(self, returncode, stdout, stderr): + self.returncode = returncode + self.stdout = stdout + self.stderr = stderr + + +class CommandlineCaller(object): + + def __init__(self): + self._logger = logging.getLogger(__name__) + + self.on_log_call = lambda *args, **kwargs: None + self.on_log_stdout = lambda *args, **kwargs: None + self.on_log_stderr = lambda *args, **kwargs: None + + def checked_call(self, command, **kwargs): + returncode, stdout, stderr = self.call(command, **kwargs) + + if returncode != 0: + raise CommandlineError(returncode, stdout, stderr) + + return returncode, stdout, stderr + + def call(self, command, **kwargs): + if isinstance(command, (list, tuple)): + joined_command = " ".join(command) + else: + joined_command = command + self._logger.debug(u"Calling: {}".format(joined_command)) + self.on_log_call(joined_command) + + kwargs.update(dict(async=True, stdout=sarge.Capture(), stderr=sarge.Capture())) + + p = sarge.run(command, **kwargs) + p.wait_events() + + all_stdout = [] + all_stderr = [] + try: + while p.returncode is None: + line = p.stderr.readline(timeout=0.5) + if line: + self._log_stderr(line) + all_stderr.append(line) + + line = p.stdout.readline(timeout=0.5) + if line: + self._log_stdout(line) + all_stdout.append(line) + + p.commands[0].poll() + + finally: + p.close() + + stderr = p.stderr.text + if stderr: + split_lines = stderr.split("\n") + self._log_stderr(*split_lines) + all_stderr += split_lines + + stdout = p.stdout.text + if stdout: + split_lines = stdout.split("\n") + self._log_stdout(*split_lines) + all_stdout += split_lines + + return p.returncode, all_stdout, all_stderr + + def _log_stdout(self, *lines): + self.on_log_stdout(*lines) + + def _log_stderr(self, *lines): + self.on_log_stderr(*lines) diff --git a/src/octoprint/util/pip.py b/src/octoprint/util/pip.py index 7d3d1da1..4117546d 100644 --- a/src/octoprint/util/pip.py +++ b/src/octoprint/util/pip.py @@ -3,19 +3,22 @@ from __future__ import absolute_import __author__ = "Gina Häußge " __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' -__copyright__ = "Copyright (C) 2014 The OctoPrint Project - Released under terms of the AGPLv3 License" +__copyright__ = "Copyright (C) 2015 The OctoPrint Project - Released under terms of the AGPLv3 License" import sarge import sys import logging +from .commandline import CommandlineCaller + class UnknownPip(Exception): pass -class PipCaller(object): +class PipCaller(CommandlineCaller): def __init__(self, configured=None): + CommandlineCaller.__init__(self) self._logger = logging.getLogger(__name__) self._configured = configured @@ -26,9 +29,6 @@ class PipCaller(object): self._command, self._version = self._find_pip() self.refresh = False - self.on_log_call = lambda *args, **kwargs: None - self.on_log_stdout = lambda *args, **kwargs: None - self.on_log_stderr = lambda *args, **kwargs: None def __le__(self, other): return self.version is not None and self.version <= other @@ -63,47 +63,7 @@ class PipCaller(object): raise UnknownPip() command = [self._command] + list(args) - - joined_command = " ".join(command) - self._logger.debug(u"Calling: {}".format(joined_command)) - self.on_log_call(joined_command) - - p = sarge.run(command, async=True, stdout=sarge.Capture(), stderr=sarge.Capture()) - p.wait_events() - - all_stdout = [] - all_stderr = [] - try: - while p.returncode is None: - line = p.stderr.readline(timeout=0.5) - if line: - self._log_stderr(line) - all_stderr.append(line) - - line = p.stdout.readline(timeout=0.5) - if line: - self._log_stdout(line) - all_stdout.append(line) - - p.commands[0].poll() - - finally: - p.close() - - stderr = p.stderr.text - if stderr: - split_lines = stderr.split("\n") - self._log_stderr(*split_lines) - all_stderr += split_lines - - stdout = p.stdout.text - if stdout: - split_lines = stdout.split("\n") - self._log_stdout(*split_lines) - all_stdout += split_lines - - return p.returncode, all_stdout, all_stderr - + return self.call(command) def _find_pip(self): pip_command = self._configured @@ -170,9 +130,3 @@ class PipCaller(object): self._logger.info("Found pip at {}, version is {}".format(pip_command, version_segment)) return pip_command, pip_version - - def _log_stdout(self, *lines): - self.on_log_stdout(*lines) - - def _log_stderr(self, *lines): - self.on_log_stderr(*lines)