@@ -118,7 +118,8 @@ def call_subprocess(
118
118
extra_environ = None , # type: Optional[Mapping[str, Any]]
119
119
unset_environ = None , # type: Optional[Iterable[str]]
120
120
spinner = None , # type: Optional[SpinnerInterface]
121
- log_failed_cmd = True # type: Optional[bool]
121
+ log_failed_cmd = True , # type: Optional[bool]
122
+ stdout_only = False , # type: Optional[bool]
122
123
):
123
124
# type: (...) -> Text
124
125
"""
@@ -130,6 +131,9 @@ def call_subprocess(
130
131
unset_environ: an iterable of environment variable names to unset
131
132
prior to calling subprocess.Popen().
132
133
log_failed_cmd: if false, failed commands are not logged, only raised.
134
+ stdout_only: if true, return only stdout, else return both. When true,
135
+ logging of both stdout and stderr occurs when the subprocess has
136
+ terminated, else logging occurs as subprocess output is produced.
133
137
"""
134
138
if extra_ok_returncodes is None :
135
139
extra_ok_returncodes = []
@@ -180,8 +184,11 @@ def call_subprocess(
180
184
proc = subprocess .Popen (
181
185
# Convert HiddenText objects to the underlying str.
182
186
reveal_command_args (cmd ),
183
- stderr = subprocess .STDOUT , stdin = subprocess .PIPE ,
184
- stdout = subprocess .PIPE , cwd = cwd , env = env ,
187
+ stdin = subprocess .PIPE ,
188
+ stdout = subprocess .PIPE ,
189
+ stderr = subprocess .STDOUT if not stdout_only else subprocess .PIPE ,
190
+ cwd = cwd ,
191
+ env = env ,
185
192
)
186
193
assert proc .stdin
187
194
assert proc .stdout
@@ -193,25 +200,43 @@ def call_subprocess(
193
200
)
194
201
raise
195
202
all_output = []
196
- while True :
197
- # The "line" value is a unicode string in Python 2.
198
- line = console_to_str (proc .stdout .readline ())
199
- if not line :
200
- break
201
- line = line .rstrip ()
202
- all_output .append (line + '\n ' )
203
+ if not stdout_only :
204
+ # In this mode, stdout and stderr are in the same pip.
205
+ while True :
206
+ # The "line" value is a unicode string in Python 2.
207
+ line = console_to_str (proc .stdout .readline ())
208
+ if not line :
209
+ break
210
+ line = line .rstrip ()
211
+ all_output .append (line + '\n ' )
212
+
213
+ # Show the line immediately.
214
+ log_subprocess (line )
215
+ # Update the spinner.
216
+ if use_spinner :
217
+ assert spinner
218
+ spinner .spin ()
219
+ try :
220
+ proc .wait ()
221
+ finally :
222
+ if proc .stdout :
223
+ proc .stdout .close ()
224
+ output = '' .join (all_output )
225
+ else :
226
+ # In this mode, stdout and stderr are in different pipes.
227
+ # We must use the communicate which is the only safe way to read both.
228
+ out_bytes , err_bytes = proc .communicate ()
229
+ # log line by line to preserve pip log indenting
230
+ out = console_to_str (out_bytes )
231
+ for out_line in out .splitlines ():
232
+ log_subprocess (out_line )
233
+ all_output .append (out )
234
+ err = console_to_str (err_bytes )
235
+ for err_line in err .splitlines ():
236
+ log_subprocess (err_line )
237
+ all_output .append (err )
238
+ output = out
203
239
204
- # Show the line immediately.
205
- log_subprocess (line )
206
- # Update the spinner.
207
- if use_spinner :
208
- assert spinner
209
- spinner .spin ()
210
- try :
211
- proc .wait ()
212
- finally :
213
- if proc .stdout :
214
- proc .stdout .close ()
215
240
proc_had_error = (
216
241
proc .returncode and proc .returncode not in extra_ok_returncodes
217
242
)
@@ -246,7 +271,7 @@ def call_subprocess(
246
271
else :
247
272
raise ValueError ('Invalid value: on_returncode={!r}' .format (
248
273
on_returncode ))
249
- return '' . join ( all_output )
274
+ return output
250
275
251
276
252
277
def runner_with_spinner_message (message ):
0 commit comments