Skip to content

Commit 75c4d5e

Browse files
Chris Plockbitprophet
Chris Plock
authored andcommitted
Unblock subprocesses waiting for stdin close
Partial fix for #552 BREAKING CHANGE FOR RUNNER IMPLEMENTATIONS: New method added close_proc_stdin to allow the parent Runner to ask subclass to close the stream after it finds the end of the stream. Fix ExceptionHandlingThread.is_dead which before would report dead when thread was not alive and there was no exception info This does not fix when pty=True, more significant changes are required for that case Two test cases were adjusted that were wrongly asserting that the thread should be "is_dead" after thread.join()
1 parent 96771fd commit 75c4d5e

File tree

4 files changed

+28
-3
lines changed

4 files changed

+28
-3
lines changed

invoke/runners.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,8 @@ def handle_stdin(self, input_, output, echo):
656656
elif data is not None:
657657
# When reading from file-like objects that aren't "real"
658658
# terminal streams, an empty byte signals EOF.
659+
if not self.using_pty:
660+
self._close_proc_stdin()
659661
break
660662
# Dual all-done signals: program being executed is done
661663
# running, *and* we don't seem to be reading anything out of
@@ -863,6 +865,18 @@ def _write_proc_stdin(self, data):
863865
"""
864866
raise NotImplementedError
865867

868+
def _close_proc_stdin(self):
869+
"""
870+
Close running process' stdin.
871+
872+
This should never be called directly; it's for subclasses to implement.
873+
874+
:returns: ``None``.
875+
876+
.. versionadded:: 1.1
877+
"""
878+
raise NotImplementedError
879+
866880
def default_encoding(self):
867881
"""
868882
Return a string naming the expected encoding of subprocess streams.
@@ -1006,6 +1020,13 @@ def _write_proc_stdin(self, data):
10061020
if "Broken pipe" not in str(e):
10071021
raise
10081022

1023+
def _close_proc_stdin(self):
1024+
if self.using_pty:
1025+
# there is no working scenario to tell the process that stdin
1026+
# closed when using pty
1027+
raise Exception("Cannot close stdin when pty=True")
1028+
self.process.stdin.close()
1029+
10091030
def start(self, command, shell, env):
10101031
if self.using_pty:
10111032
if pty is None: # Encountered ImportError

invoke/util.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,8 @@ def is_dead(self):
271271
# NOTE: it seems highly unlikely that a thread could still be
272272
# is_alive() but also have encountered an exception. But hey. Why not
273273
# be thorough?
274-
return (not self.is_alive()) and self.exc_info is not None
274+
alive = self.is_alive() and (self.exc_info is None)
275+
return not alive
275276

276277
def __repr__(self):
277278
# TODO: beef this up more

tests/_util.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,9 @@ def read_proc_stderr(self, num_bytes):
277277
def _write_proc_stdin(self, data):
278278
pass
279279

280+
def _close_proc_stdin(self):
281+
pass
282+
280283
@property
281284
def process_is_finished(self):
282285
return True

tests/concurrency.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def exhibits_is_dead_flag(self):
3939
t = EHThread(target=self.worker, args=[Queue()])
4040
t.start()
4141
t.join()
42-
assert not t.is_dead
42+
assert t.is_dead
4343

4444
class via_subclassing:
4545
def setup(self):
@@ -80,4 +80,4 @@ def exhibits_is_dead_flag(self):
8080
t = self.klass(queue=Queue())
8181
t.start()
8282
t.join()
83-
assert not t.is_dead
83+
assert t.is_dead

0 commit comments

Comments
 (0)