From c6b32c81df7b5f70a339270369a12adaf0238730 Mon Sep 17 00:00:00 2001 From: Markus Lehtonen Date: Fri, 9 Oct 2015 14:22:31 +0300 Subject: Command: redirect stdout/stderr to sys.stdout/stderr Redirect stdout and stderr of the (child) command to sys.stdout and sys.stderr of the caller, respectively. This change is mainly for the unit tests. It makes Python nose to correctly capture the output of the child command, too, which in turn suppresses a lot of spurious output when running nosetests. Closes: #829690 Signed-off-by: Markus Lehtonen --- gbp/command_wrappers.py | 65 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 16 deletions(-) diff --git a/gbp/command_wrappers.py b/gbp/command_wrappers.py index f99b1e85..e16c1080 100644 --- a/gbp/command_wrappers.py +++ b/gbp/command_wrappers.py @@ -22,6 +22,10 @@ git-buildpackage and friends import subprocess import os import signal +import sys +from contextlib import contextmanager +from tempfile import TemporaryFile + import gbp.log as log @@ -30,6 +34,35 @@ class CommandExecFailed(Exception): pass +@contextmanager +def proxy_stdf(): + """ + Circulate stdout/stderr via a proper file object. Designed to work around a + problem where Python nose replaces sys.stdout/stderr with a custom 'Tee' + object that is not a file object (compatible) and thus causes a crash with + Popen. + """ + stdout = None + if not hasattr(sys.stdout, 'fileno'): + stdout = sys.stdout + sys.stdout = TemporaryFile() + stderr = None + if not hasattr(sys.stderr, 'fileno'): + stderr = sys.stderr + sys.stderr = TemporaryFile() + try: + yield + finally: + if stdout: + sys.stdout.seek(0) + stdout.write(sys.stdout.read()) + sys.stdout = stdout + if stderr: + sys.stderr.seek(0) + stderr.write(sys.stderr.read()) + sys.stderr = stderr + + class Command(object): """ Wraps a shell command, so we don't have to store any kind of command @@ -67,26 +100,26 @@ class Command(object): log.debug("%s %s %s" % (self.cmd, self.args, args)) self._reset_state() - stdout_arg = subprocess.PIPE if self.capture_stdout else None - stderr_arg = subprocess.PIPE if self.capture_stderr else None cmd = [self.cmd] + self.args + args if self.shell: # subprocess.call only cares about the first argument if shell=True cmd = " ".join(cmd) - - try: - popen = subprocess.Popen(cmd, - cwd=self.cwd, - shell=self.shell, - env=self.env, - preexec_fn=default_sigpipe, - stdout=stdout_arg, - stderr=stderr_arg) - (self.stdout, self.stderr) = popen.communicate() - except OSError as err: - self.err_reason = "execution failed: %s" % str(err) - self.retcode = 1 - raise + with proxy_stdf(): + stdout_arg = subprocess.PIPE if self.capture_stdout else sys.stdout + stderr_arg = subprocess.PIPE if self.capture_stderr else sys.stderr + try: + popen = subprocess.Popen(cmd, + cwd=self.cwd, + shell=self.shell, + env=self.env, + preexec_fn=default_sigpipe, + stdout=stdout_arg, + stderr=stderr_arg) + (self.stdout, self.stderr) = popen.communicate() + except OSError as err: + self.err_reason = "execution failed: %s" % str(err) + self.retcode = 1 + raise self.retcode = popen.returncode if self.retcode < 0: -- cgit v1.2.3