Skip to content

Commit 6dda539

Browse files
committed
add stdout_only to call_subprocess
1 parent 33d0312 commit 6dda539

File tree

1 file changed

+47
-22
lines changed

1 file changed

+47
-22
lines changed

src/pip/_internal/utils/subprocess.py

Lines changed: 47 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,8 @@ def call_subprocess(
118118
extra_environ=None, # type: Optional[Mapping[str, Any]]
119119
unset_environ=None, # type: Optional[Iterable[str]]
120120
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]
122123
):
123124
# type: (...) -> Text
124125
"""
@@ -130,6 +131,9 @@ def call_subprocess(
130131
unset_environ: an iterable of environment variable names to unset
131132
prior to calling subprocess.Popen().
132133
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.
133137
"""
134138
if extra_ok_returncodes is None:
135139
extra_ok_returncodes = []
@@ -180,8 +184,11 @@ def call_subprocess(
180184
proc = subprocess.Popen(
181185
# Convert HiddenText objects to the underlying str.
182186
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,
185192
)
186193
assert proc.stdin
187194
assert proc.stdout
@@ -193,25 +200,43 @@ def call_subprocess(
193200
)
194201
raise
195202
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
203239

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()
215240
proc_had_error = (
216241
proc.returncode and proc.returncode not in extra_ok_returncodes
217242
)
@@ -246,7 +271,7 @@ def call_subprocess(
246271
else:
247272
raise ValueError('Invalid value: on_returncode={!r}'.format(
248273
on_returncode))
249-
return ''.join(all_output)
274+
return output
250275

251276

252277
def runner_with_spinner_message(message):

0 commit comments

Comments
 (0)