diff options
author | Markus Lehtonen <markus.lehtonen@linux.intel.com> | 2015-10-09 14:22:31 +0300 |
---|---|---|
committer | Guido Günther <agx@sigxcpu.org> | 2016-09-12 22:34:05 +0200 |
commit | c6b32c81df7b5f70a339270369a12adaf0238730 (patch) | |
tree | 2b2ac92316ff71f05db4e2f37622bd9df616dde9 | |
parent | 9db7436d1f01677d76960308f7f94fe8241965de (diff) |
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 <markus.lehtonen@linux.intel.com>
-rw-r--r-- | gbp/command_wrappers.py | 65 |
1 files 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: |