1#!/usr/bin/env python
2
3"""Example: hand the terminal to an external editor and show what was written in it.
4
5Press :kbd:`e` to edit the text in ``$EDITOR`` (``vi`` when it is not set) and :kbd:`q` to exit.
6"""
7
8from __future__ import annotations
9
10import os
11import pathlib
12import shlex
13import subprocess
14import tempfile
15
16import urwid
17
18text_widget = urwid.Text("Press e to edit this text in $EDITOR, q to exit.\n")
19
20
21def edit_text(loop: urwid.MainLoop) -> None:
22 """Run the editor on a temporary file holding the text and show the edited content.
23
24 :param loop: main loop whose screen is paused while the editor runs
25 """
26 # $EDITOR is a shell command line such as "vim -f" or "code --wait", not an executable name
27 editor = shlex.split(os.environ.get("EDITOR", "")) or ["vi"]
28 fd, name = tempfile.mkstemp(suffix=".txt")
29 path = pathlib.Path(name)
30 try:
31 with os.fdopen(fd, "w", encoding="utf-8") as file:
32 file.write(str(text_widget.text))
33 with loop.paused():
34 subprocess.run([*editor, str(path)], check=False) # noqa: S603 # Example can be insecure
35 text_widget.set_text(path.read_text(encoding="utf-8"))
36 finally:
37 path.unlink(missing_ok=True)
38
39
40def main() -> None:
41 """Run the example."""
42
43 def handle_key(key: str | tuple[str, int, int, int]) -> None:
44 """Handle keys not consumed by the widgets: :kbd:`e` to edit, :kbd:`q` to exit.
45
46 :param key: unhandled key or mouse event
47 :raises urwid.ExitMainLoop: when the user presses :kbd:`q`
48 """
49 if key == "e":
50 edit_text(loop)
51 elif key in {"q", "Q"}:
52 raise urwid.ExitMainLoop
53
54 loop = urwid.MainLoop(urwid.Filler(text_widget, valign=urwid.TOP), unhandled_input=handle_key)
55 loop.run()
56
57
58if __name__ == "__main__":
59 main()