aboutsummaryrefslogtreecommitdiffhomepage
path: root/gbp/command_wrappers.py
diff options
context:
space:
mode:
Diffstat (limited to 'gbp/command_wrappers.py')
-rw-r--r--gbp/command_wrappers.py84
1 files changed, 59 insertions, 25 deletions
diff --git a/gbp/command_wrappers.py b/gbp/command_wrappers.py
index a4643c54..f4b00b89 100644
--- a/gbp/command_wrappers.py
+++ b/gbp/command_wrappers.py
@@ -21,15 +21,48 @@ git-buildpackage and friends
import subprocess
import os
-import os.path
import signal
+import sys
+from contextlib import contextmanager
+from tempfile import TemporaryFile
+
import gbp.log as log
+
class CommandExecFailed(Exception):
"""Exception raised by the Command class"""
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
+ 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:
@@ -107,8 +140,10 @@ class Command(object):
"""
stdout = self.stdout.rstrip() if self.stdout else self.stdout
stderr = self.stderr.rstrip() if self.stderr else self.stderr
+ stderr_or_reason = self.stderr.rstrip() if self.stderr else self.err_reason
return self.run_error.format(stdout=stdout,
stderr=stderr,
+ stderr_or_reason=stderr_or_reason,
err_reason=self.err_reason)
def __call__(self, args=[], quiet=False):
@@ -117,7 +152,7 @@ class Command(object):
If run quietly it will not print an error message via the
L{gbp.log} logging API.
- Wether the command prints anything to stdout/stderr depends on
+ Whether the command prints anything to stdout/stderr depends on
the I{capture_stderr}, I{capture_stdout} instance variables.
All errors will be reported as subclass of the
@@ -142,10 +177,9 @@ class Command(object):
ret = 1
if ret:
if not quiet:
- self._log_err()
+ self._log_err()
raise CommandExecFailed(self._format_err())
-
def call(self, args, quiet=True):
"""Like L{__call__} but let the caller handle the return status.
@@ -217,7 +251,7 @@ class UnpackTarArchive(Command):
compression = '-a'
Command.__init__(self, 'tar', exclude +
- ['-C', dir, compression, '-xf', archive ])
+ ['-C', dir, compression, '-xf', archive])
self.run_error = 'Couldn\'t unpack "%s": {err_reason}' % self.archive
@@ -250,7 +284,7 @@ class RemoveTree(Command):
"Wrap rm to remove a whole directory tree"
def __init__(self, tree):
self.tree = tree
- Command.__init__(self, 'rm', [ '-rf', tree ])
+ Command.__init__(self, 'rm', ['-rf', tree])
self.run_error = 'Couldn\'t remove "%s": {err_reason}' % self.tree
@@ -283,7 +317,7 @@ class UnpackZipArchive(Command):
self.archive = archive
self.dir = dir
- Command.__init__(self, 'unzip', [ "-q", archive, '-d', dir ])
+ Command.__init__(self, 'unzip', ["-q", archive, '-d', dir])
self.run_error = 'Couldn\'t unpack "%s": {err_reason}' % self.archive
@@ -295,7 +329,7 @@ class CatenateZipArchive(Command):
def __call__(self, target):
self.run_error = 'Couldn\'t append "%s" to "%s": {err_reason}' % \
- (target, self.archive)
+ (target, self.archive)
Command.__call__(self, [target])