@@ -26,6 +26,7 @@ def __init__(self, loop, protocol, args, shell,
2626 self ._pending_calls = collections .deque ()
2727 self ._pipes = {}
2828 self ._finished = False
29+ self ._pipes_connected = False
2930
3031 if stdin == subprocess .PIPE :
3132 self ._pipes [0 ] = None
@@ -213,6 +214,7 @@ async def _connect_pipes(self, waiter):
213214 else :
214215 if waiter is not None and not waiter .cancelled ():
215216 waiter .set_result (None )
217+ self ._pipes_connected = True
216218
217219 def _call (self , cb , * data ):
218220 if self ._pending_calls is not None :
@@ -256,6 +258,16 @@ def _try_finish(self):
256258 assert not self ._finished
257259 if self ._returncode is None :
258260 return
261+ if not self ._pipes_connected :
262+ # self._pipes_connected can be False if not all pipes were connected
263+ # because either the process failed to start or the self._connect_pipes task
264+ # got cancelled. In this broken state we consider all pipes disconnected and
265+ # to avoid hanging forever in self._wait as otherwise _exit_waiters
266+ # would never be woken up, we wake them up here.
267+ self ._finished = True
268+ for waiter in self ._exit_waiters :
269+ if not waiter .cancelled ():
270+ waiter .set_result (self ._returncode )
259271 if all (p is not None and p .disconnected
260272 for p in self ._pipes .values ()):
261273 self ._finished = True
0 commit comments