@@ -48,6 +48,8 @@ class _ProcFuture:
4848
4949 def __init__ (self , check = False , * args , ** kwargs ):
5050 self ._proc = None
51+ self ._stdout = None
52+ self ._stderr = None
5153 self ._exitcode = None
5254 self ._signal = None
5355 self ._check = check
@@ -77,6 +79,17 @@ def start(self):
7779
7880 return self
7981
82+ def _shutdown (self ):
83+ '''Shut down cleanly the underlying `Popen` object.'''
84+ if self ._stdout is None and self ._proc .stdout :
85+ self ._stdout = self ._proc .stdout .read ()
86+
87+ if self ._stderr is None and self ._proc .stderr :
88+ self ._stderr = self ._proc .stderr .read ()
89+
90+ self ._proc .wait ()
91+ self ._proc .communicate ()
92+
8093 @property
8194 def pid (self ):
8295 '''The PID of the spawned process.'''
@@ -179,6 +192,7 @@ def _wait(self, *, nohang=False):
179192 except OSError as e :
180193 if e .errno == errno .ECHILD :
181194 self ._completed = True
195+ self ._shutdown ()
182196 return self ._completed
183197 else :
184198 raise e
@@ -191,16 +205,19 @@ def _wait(self, *, nohang=False):
191205 elif os .WIFSIGNALED (status ):
192206 self ._signal = os .WTERMSIG (status )
193207
208+ # Retrieve the stdout/stderr and clean up the underlying Popen object
194209 self ._completed = True
210+ self ._shutdown ()
195211
196212 # Call any done callbacks
197213 for func in self ._done_callbacks :
198214 func (self )
199215
200216 # Start the next futures in the chain
201- for fut , cond in self ._next :
202- if cond (self ):
203- fut .start ()
217+ if not self ._cancelled :
218+ for fut , cond in self ._next :
219+ if cond (self ):
220+ fut .start ()
204221
205222 return self ._completed
206223
@@ -230,29 +247,28 @@ def exception(self):
230247 if not self ._check :
231248 return
232249
233- if self ._proc . returncode == 0 :
250+ if self ._exitcode == 0 :
234251 return
235252
236253 return SpawnedProcessError (self ._proc .args ,
237- self ._proc .stdout .read (),
238- self ._proc .stderr .read (),
239- self ._proc .returncode )
254+ self ._stdout , self ._stderr ,
255+ self ._exitcode )
240256
241257 def stdout (self ):
242258 '''Retrieve the standard output of the spawned process.
243259
244260 This is a blocking call and will wait until the future finishes.
245261 '''
246262 self ._wait ()
247- return self ._proc . stdout
263+ return self ._stdout
248264
249265 def stderr (self ):
250266 '''Retrieve the standard error of the spawned process.
251267
252268 This is a blocking call and will wait until the future finishes.
253269 '''
254270 self ._wait ()
255- return self ._proc . stderr
271+ return self ._stderr
256272
257273
258274def run_command (cmd , check = False , timeout = None , ** kwargs ):
@@ -281,9 +297,12 @@ def run_command(cmd, check=False, timeout=None, **kwargs):
281297 proc_stdout , proc_stderr = proc .communicate (timeout = timeout )
282298 except subprocess .TimeoutExpired as e :
283299 os .killpg (proc .pid , signal .SIGKILL )
284- raise SpawnedProcessTimeout (e .cmd ,
285- proc .stdout .read (),
286- proc .stderr .read (), timeout ) from None
300+ os .waitpid (proc .pid , 0 )
301+ proc_stdout = proc .stdout .read ()
302+ proc_stderr = proc .stderr .read ()
303+ proc .communicate ()
304+ raise SpawnedProcessTimeout (e .cmd , proc_stdout ,
305+ proc_stderr , timeout ) from None
287306
288307 completed = subprocess .CompletedProcess (cmd ,
289308 returncode = proc .returncode ,
0 commit comments