aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMarkus Lehtonen <markus.lehtonen@linux.intel.com>2015-10-09 14:22:31 +0300
committerGuido Günther <agx@sigxcpu.org>2016-09-12 22:34:05 +0200
commitc6b32c81df7b5f70a339270369a12adaf0238730 (patch)
tree2b2ac92316ff71f05db4e2f37622bd9df616dde9
parent9db7436d1f01677d76960308f7f94fe8241965de (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.py65
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: