subproc.pyΒΆ

 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()