Skip to content

Add example embedding a cmd2 app in a Textual app with textual-tty - #1781

Merged
tleonhardt merged 13 commits into
mainfrom
textual-tty-example
Oct 24, 2026
Merged

tleonhardt merged 13 commits into
mainfrom
textual-tty-example

Conversation

@tleonhardt

@tleonhardt tleonhardt commented Oct 24, 2026 •

Copy link
Copy Markdown
Member

This continues #1773 by @DG-Elom, which adds examples/textual_tty_embed.py: a Textual application that runs a cmd2 application in a textual-tty Terminal widget, with a realtime status panel beside it. #1773 was merged into this branch so its commits and authorship are preserved. This branch adds fixes for the review comments on that PR, documentation, and a dev dependency.

Review fixes

  • Escape text from the terminal before putting it in Textual markup, so commands like [/b] no longer crash the host
  • Track commands from a postparsing hook and ignore finalization for lines that never became a statement (blank lines, comments, Ctrl-C, syntax errors), so they aren't counted or timed from a stale start time
  • Count nested commands (run_script, history -r, run_pyscript) so only the outermost one is reported
  • Ignore status titles that aren't JSON objects or whose values have the wrong type, instead of crashing
  • Import Textual only in the host code path, so --cmd2-only works without it
  • Return the cmd2 application's exit code from the host, and keep the terminal on screen when it fails so a traceback can still be read
  • Only report status through the window title with --report-status, which the host passes, so --cmd2-only doesn't overwrite a normal terminal's title
  • Validate the slow command's argument before printing anything
  • Document the Textual host as POSIX-only and refuse to start it on Windows, since bittty's Windows PTY can only start a program by name and can't pass it arguments
  • Clarify that the Ctrl-Q binding is repeated only to show it in the footer

Developer environment

textual-tty is now in the dev dependency group on non-Windows platforms, so uv run examples/textual_tty_embed.py works out of the box in a development checkout. It is not a runtime dependency of cmd2, and CI is unaffected because it installs only specific groups.

NOTE: While cmd2 and textual both fully supports Windows, textual-tty relies on POSIX pseudo-terminals (PTYs).

Documentation

  • New "Embedding in Textual" page under Examples, explaining how the example works and the pitfalls it handles
  • Linked from the Alternatives overview and the Bottom Toolbar documentation
  • Changelog entry under 4.3.1

Testing

  • make check, make test, and make docs-test pass
  • Ran the Textual host headless with Textual's Pilot, typing commands into the embedded terminal and checking the status panel: markup-like commands, blank lines, run_script, malformed status titles set with shell printf, slow 1, quit, Ctrl-Q, and a child process that crashes at startup
  • Ran --cmd2-only with and without --report-status in a real pty to confirm the window title is only set when asked
  • Not tested on Windows

Closes #1771

DG-Elom and others added 8 commits October 14, 2026 12:22
- Escape the command name and state before putting them in the status
  panel's markup, so a command like "[/b]" no longer crashes the host.
- Start tracking a command in a postparsing hook and ignore finalization
  for lines that never became a statement (blank lines, comments, Ctrl-C,
  syntax errors), so they aren't counted or timed from a stale start time.
- Count running commands so ones run by run_script, history -r, and
  run_pyscript don't flip the panel to idle or inflate the command count.
- Ignore status titles that aren't JSON objects or whose values have the
  wrong type, instead of crashing the host.
- Document the Textual host as POSIX-only and refuse to start it on
  Windows, where bittty's PTY can't pass arguments to the child process.
- Import Textual only in the host code path so --cmd2-only works without it.
- Return the cmd2 app's exit code from the host, and keep the terminal on
  screen when the cmd2 app fails so its traceback can still be read.
- Only report status through the window title with --report-status, which
  the host passes, so --cmd2-only doesn't overwrite a terminal's title.
- Reject negative, NaN, infinite, and huge values in the slow command
  before printing anything.
- Clarify that the Ctrl-Q binding is repeated only to show it in the footer.
Developers can now run the textual_tty_embed example out of the box with
"uv run examples/textual_tty_embed.py". It is only a dev dependency, which
CI doesn't install, and is limited to non-Windows platforms because the
example's Textual host is POSIX-only and bittty pins pywinpty on Windows.
Add an "Embedding in Textual" page to the examples section of the docs
which explains how the textual_tty_embed example works and the pitfalls it
handles. Link to it from the alternatives overview and the bottom toolbar
docs, and add a changelog entry for the example.
@codecov

codecov Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.75%. Comparing base (07495f8) to head (fdfac20).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1781   +/-   ##
=======================================
  Coverage   99.75%   99.75%           
=======================================
  Files          23       23           
  Lines        6474     6474           
=======================================
  Hits         6458     6458           
  Misses         16       16           
Flag Coverage Δ
unittests 99.75% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

Add a short teaser to the README's introduction which links to the Embedding in Textual docs, so the capability is easy to discover.
- Ignore status titles which make json.loads raise ValueError (integers
  with too many digits) or RecursionError (documents nested too deeply),
  not just json.JSONDecodeError.
- Report a cmd2 app killed by a signal as 128 + the signal number, like a
  shell does, instead of passing a negative exit code to sys.exit.
- Never let the command depth go negative when a postparsing hook which
  ran first stopped the status hook from running, and document that the
  hook should be registered first.
- Reject --report-status without --cmd2-only.
- Limit textual-tty to <0.5 in the dev group since it is pre-alpha.
- Make the compose() snippet in the docs match the example's code.
- Report the running command separately and only update the last command
  when it finishes, so the panel no longer shows a running command's name
  next to the previous command's duration. The state line now reads, for
  example, "running slow".
- Ignore OSError when setting the window title, which happens when the
  Textual application exits while a command is running and has already
  closed the terminal.
- Keep the child's command line in a variable so the compose() snippet in
  the docs stays within the Markdown line length.
@tleonhardt
tleonhardt merged commit 611a3a9 into main Oct 24, 2026
32 checks passed
@tleonhardt
tleonhardt deleted the textual-tty-example branch October 24, 2026 20:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Create demo example app using textual-tty to embed a cmd2 app within a Textual app

3 participants