Skip to content

Commit d14d7a6

Browse files
author
Vladimir Kotal
committed
add parameter to Command to disable stderr redirection
fixes #2176
1 parent 29ac2f8 commit d14d7a6

File tree

3 files changed

+43
-9
lines changed

3 files changed

+43
-9
lines changed

tools/sync/command.py

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,16 @@ class Command:
4949
TIMEDOUT = "timed out"
5050

5151
def __init__(self, cmd, args_subst=None, args_append=None, logger=None,
52-
excl_subst=False, work_dir=None, env_vars=None, timeout=None):
52+
excl_subst=False, work_dir=None, env_vars=None, timeout=None,
53+
redirect_stderr=True):
5354
self.cmd = cmd
5455
self.state = "notrun"
5556
self.excl_subst = excl_subst
5657
self.work_dir = work_dir
5758
self.env_vars = env_vars
5859
self.timeout = timeout
5960
self.pid = None
61+
self.redirect_stderr = redirect_stderr
6062

6163
self.logger = logger or logging.getLogger(__name__)
6264
logging.basicConfig()
@@ -167,8 +169,18 @@ def close(self):
167169
return
168170

169171
timeout_thread = None
170-
event = threading.Event()
171-
output_thread = OutputThread(event, self.logger)
172+
output_event = threading.Event()
173+
output_thread = OutputThread(output_event, self.logger)
174+
175+
# If stderr redirection is off, setup a thread that will capture
176+
# stderr data.
177+
if self.redirect_stderr:
178+
stderr_dest = subprocess.STDOUT
179+
else:
180+
stderr_event = threading.Event()
181+
stderr_thread = OutputThread(stderr_event, self.logger)
182+
stderr_dest = stderr_thread
183+
172184
try:
173185
start_time = time.time()
174186
try:
@@ -179,10 +191,10 @@ def close(self):
179191
if self.env_vars:
180192
my_env = os.environ.copy()
181193
my_env.update(self.env_vars)
182-
p = subprocess.Popen(self.cmd, stderr=subprocess.STDOUT,
194+
p = subprocess.Popen(self.cmd, stderr=stderr_dest,
183195
stdout=output_thread, env=my_env)
184196
else:
185-
p = subprocess.Popen(self.cmd, stderr=subprocess.STDOUT,
197+
p = subprocess.Popen(self.cmd, stderr=stderr_dest,
186198
stdout=output_thread)
187199

188200
self.pid = p.pid
@@ -226,9 +238,15 @@ def close(self):
226238
# exit the read loop we have to close it here ourselves.
227239
output_thread.close()
228240
self.logger.debug("Waiting on output thread to finish reading")
229-
event.wait()
230-
241+
output_event.wait()
231242
self.out = output_thread.getoutput()
243+
244+
if not self.redirect_stderr:
245+
stderr_thread.close()
246+
self.logger.debug("Waiting on stderr thread to finish reading")
247+
stderr_event.wait()
248+
self.err = stderr_thread.getoutput()
249+
232250
elapsed_time = time.time() - start_time
233251
self.logger.debug("Command {} took {} seconds".
234252
format(self.cmd, int(elapsed_time)))
@@ -286,6 +304,9 @@ def getoutput(self):
286304
else:
287305
return None
288306

307+
def geterroutput(self):
308+
return self.err
309+
289310
def getstate(self):
290311
return self.state
291312

tools/sync/projadm.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,15 @@ def exec_command(doit, logger, cmd, msg):
5555
Execute given command and return its output.
5656
Exit the program on failure.
5757
"""
58-
cmd = Command(cmd, logger=logger)
58+
cmd = Command(cmd, logger=logger, redirect_stderr=False)
5959
if not doit:
6060
logger.info(cmd)
6161
return
6262
cmd.execute()
6363
if cmd.getstate() is not Command.FINISHED or cmd.getretcode() != 0:
6464
logger.error(msg)
65-
logger.error(cmd.getoutput())
65+
logger.error("Standard output: {}".format(cmd.getoutput()))
66+
logger.error("Error output: {}".format(cmd.geterroutput()))
6667
sys.exit(1)
6768

6869
return cmd.getoutput()

tools/sync/test/test_command.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,18 @@ def test_notimeout(self):
121121
self.assertEqual(Command.FINISHED, cmd.getstate())
122122
self.assertEqual(0, cmd.getretcode())
123123

124+
@unittest.skipUnless(os.name.startswith("posix"), "requires Unix")
125+
def test_stderr(self):
126+
cmd = Command(["/bin/cat", "/foo/bar", "/etc/passwd"],
127+
redirect_stderr=False)
128+
cmd.execute()
129+
self.assertEqual(Command.FINISHED, cmd.getstate())
130+
self.assertNotEqual(0, cmd.getretcode())
131+
# The error could contain localized output strings so check just
132+
# for the path itself.
133+
self.assertTrue("/foo/bar" in "\n".join(cmd.geterroutput()))
134+
self.assertFalse("/foo/bar" in "\n".join(cmd.getoutput()))
135+
self.assertTrue("root" in "\n".join(cmd.getoutput()))
124136

125137
if __name__ == '__main__':
126138
unittest.main()

0 commit comments

Comments
 (0)