1#!/usr/bin/env python
2
3"""Example: monitor and display the output of a subprocess while accepting input."""
4
5from __future__ import annotations
6
7import os
8import subprocess
9import sys
10import typing
11
12import urwid
13
14factor_me = 362923067964327863989661926737477737673859044111968554257667
15run_me = os.path.join(os.path.dirname(sys.argv[0]), "subproc2.py")
16
17output_widget = urwid.Text(f"Factors of {factor_me:d}:\n")
18edit_widget = urwid.Edit("Type anything or press enter to exit:")
19frame_widget = urwid.Frame[urwid.Filler[urwid.Text], urwid.Edit, None](
20 header=edit_widget,
21 body=urwid.Filler(output_widget, valign=urwid.BOTTOM),
22 focus_part="header",
23)
24
25
26def exit_on_enter(key: str | tuple[str, int, int, int]) -> None:
27 """Handle keys not consumed by the widgets: :kbd:`enter` to exit.
28
29 :param key: unhandled key or mouse event
30 :raises urwid.ExitMainLoop: when the user presses :kbd:`enter`
31 """
32 if key == "enter":
33 raise urwid.ExitMainLoop
34
35
36loop = urwid.MainLoop(frame_widget, unhandled_input=exit_on_enter)
37
38
39def received_output(data: bytes) -> bool:
40 """Append data received from the subprocess to the output widget.
41
42 :param data: bytes read from the subprocess's standard output
43 :returns: ``True`` to keep the pipe watch registered
44 """
45 output_widget.set_text(typing.cast("str", output_widget.text) + data.decode("utf8"))
46 return True
47
48
49write_fd = loop.watch_pipe(received_output)
50with subprocess.Popen( # noqa: S603
51 ["python", "-u", run_me, str(factor_me)], # noqa: S607 # Example can be insecure
52 stdout=write_fd,
53 close_fds=True,
54) as proc:
55 loop.run()