Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
1462c0f
Integrate reserved toolbar ownership across Stage 4 handoffs
tleonhardt Sep 12, 2026
c322cde
Keep the toolbar visible while the pager prepares its first frame
tleonhardt Sep 12, 2026
5b4acab
Guard POSIX suspend signal lookup on Windows
tleonhardt Sep 12, 2026
3354add
Add include_py to getting_started.py for ease of testing
tleonhardt Sep 12, 2026
1795cd9
Added raise_exception.py example pyscript for just testing a script t…
tleonhardt Sep 12, 2026
9649d4d
Keep terminal pipelines in the foreground job during suspend and resume
tleonhardt Sep 12, 2026
37645b5
Repaint reserved toolbar after minimum-height reacquisition
tleonhardt Sep 12, 2026
2b78110
Accept bash 5.1+ bracketed-paste output in the job-control test
tleonhardt Sep 12, 2026
c052401
Report from the job-control test's pager with os.write, not print
tleonhardt Sep 12, 2026
b1f80a6
Cover the reserved toolbar's remaining job-control and nested-prompt …
tleonhardt Sep 12, 2026
a785b96
Wait for the whole job to stop before typing at the shell in the job-…
tleonhardt Sep 12, 2026
c16e943
Fix forwarding of process-directed SIGINT to pipelines
tleonhardt Sep 12, 2026
ece4307
Avoid duplicate SIGINT delivery to terminal pipelines
tleonhardt Sep 12, 2026
4a79cb4
Fix POSIX pipeline signal races and speed up terminal tests
tleonhardt Sep 12, 2026
66365f5
Collect coverage from terminal test subprocesses
tleonhardt Sep 12, 2026
004f835
Cover terminal pipeline cleanup and resume paths
tleonhardt Sep 12, 2026
e327df6
Fix terminal input and wrapper job control in pipelines
tleonhardt Sep 14, 2026
b75e18e
Preserve ignored Ctrl-Z for session-led pipelines
tleonhardt Sep 14, 2026
a7a733f
Run shell producers inside terminal pipelines and isolate worker-thre…
tleonhardt Sep 14, 2026
d4566a0
Cover the shell command's fallback paths in terminal pipelines
tleonhardt Sep 14, 2026
0102c44
Keep the terminal lent across an interrupted pipeline write
tleonhardt Sep 14, 2026
7643b9a
Relay pipeline stops to the main thread with a thread-directed signal
tleonhardt Sep 14, 2026
d3b2630
Let the main thread relay a pipeline stop from a blocking write or wait
tleonhardt Sep 14, 2026
589b468
Keep the pipeline descriptor blocking for shell producers
tleonhardt Sep 14, 2026
f34262c
Added stage4_manual.py example for ease of testing
tleonhardt Sep 14, 2026
9f50afb
Stabilize orphaned-session pipeline terminal test
tleonhardt Sep 14, 2026
66513f6
Hold the display thread until a Ctrl-Z stop has taken it
tleonhardt Sep 15, 2026
b163a1e
Assert pipeline isolation without a platform branch in the toolbar test
tleonhardt Sep 15, 2026
416c6da
Cover both outcomes of the wait after sending SIGTSTP
tleonhardt Sep 15, 2026
72ae6e5
Lend the terminal to a pipeline while it starts up
tleonhardt Sep 20, 2026
7f574f5
Let a shell producer keep the terminal after its consumer exits
tleonhardt Sep 20, 2026
dc1a9df
Relay a shell producer's stop once its consumer is gone
tleonhardt Sep 20, 2026
11c3688
Send pager test keys from the application's event loop thread
tleonhardt Sep 20, 2026
4ce7c82
Merge branch 'main' into stage4-handoffs-integration
tleonhardt Sep 22, 2026
b20de83
Merge branch 'reserved_row_toolbar' into stage4-handoffs-integration
tleonhardt Sep 22, 2026
add0368
Start the startup-mode test's pager without site-packages
tleonhardt Sep 24, 2026
ea47295
Fall back from auto mode when prompt-toolkit's version is unknown
tleonhardt Sep 24, 2026
06608b0
Keep the command loop running when the toolbar display will not stop
tleonhardt Sep 24, 2026
a614316
Take the terminal back before retrying a shell command in cmd2's group
tleonhardt Sep 24, 2026
9be7a4d
Block SIGTTOU only while lending the terminal to a pipeline
tleonhardt Sep 24, 2026
2bee1d3
Hold cmd2's startup check open in the pager startup-mode test
tleonhardt Sep 24, 2026
de23939
Leave the application's Ctrl-Z handler as found when the reservation …
tleonhardt Sep 24, 2026
041785f
Take the terminal back only when the last overlapping lend ends
tleonhardt Sep 24, 2026
80ce6a2
Let Ctrl-C end the wait for a toolbar display that will not stop
tleonhardt Sep 24, 2026
d4b1fd3
Reset pipe state even when restoring redirected output fails
tleonhardt Sep 24, 2026
7f6b8dc
Catch Ctrl-C that lands while the stuck-toolbar notice is printing
tleonhardt Sep 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Take the terminal back before retrying a shell command in cmd2's group
When a terminal pipeline exits before a shell command can join its
group, do_shell() retries in cmd2's own group. The terminal was still
lent to the dead pipeline, so the command stopped with SIGTTIN on its
first terminal read and the wait for it never returned. End the lend
before retrying.
  • Loading branch information
tleonhardt committed Sep 24, 2026
commit a6143168b49f435a7fe83d6aaf5048a144610ba5
4 changes: 4 additions & 0 deletions cmd2/cmd2.py
Original file line number Diff line number Diff line change
Expand Up @@ -5317,6 +5317,10 @@ def do_shell(self, args: argparse.Namespace) -> None:
# The pipeline exited before the command could join its group.
if kwargs.pop("process_group", None) is None:
raise
# The retry runs in our own group, so take the terminal back from the dead
# pipeline first. Its watcher left it lent, and the command would otherwise
# stop with SIGTTIN on its first terminal read, with nothing to resume it.
terminal_stack.close()

# A command that joined the pipeline's job is waited for in short polls. Only the
# main thread runs Python signal handlers, and the job-control stop the pipeline's
Expand Down
24 changes: 22 additions & 2 deletions tests/test_cmd2.py
Original file line number Diff line number Diff line change
Expand Up @@ -438,13 +438,33 @@ def test_shell_falls_back_to_own_group_when_pipeline_exited(base_app, tmp_path)
# pipeline can exit between the check and the spawn, like `shell sleep 1 | true`.
leader = subprocess.Popen([sys.executable, "-c", "pass"], process_group=0)
leader.wait()
base_app._cur_pipe_proc_reader = mock.Mock(terminal_group=leader.pid, lend_terminal=contextlib.nullcontext)
with (tmp_path / "output").open("w+") as output:
lent = []

@contextlib.contextmanager
def lend_terminal():
lent.append(True)
try:
yield
finally:
lent.pop()

# The retry runs in our own group, so the terminal has to come back from the dead
# pipeline first. Otherwise the command stops with SIGTTIN on its first terminal read.
spawned_while_lent = []
real_popen = subprocess.Popen

def popen(*args, **kwargs):
spawned_while_lent.append(bool(lent))
return real_popen(*args, **kwargs)

base_app._cur_pipe_proc_reader = mock.Mock(terminal_group=leader.pid, lend_terminal=lend_terminal)
with (tmp_path / "output").open("w+") as output, mock.patch("subprocess.Popen", popen):
base_app.stdout = output
base_app.do_shell("echo joined")
output.seek(0)
assert output.read() == "joined\n"
assert base_app.last_result == 0
assert spawned_while_lent == [True, False]


@pytest.mark.skipif(sys.platform == "win32", reason="POSIX shell executable")
Expand Down