1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
"""Shared entry-point plumbing for the backend setup wizards.
Every backend module exposes the same surface — ``_wizard`` (TUI screens),
``_execute_steps``/``_execute`` (the work, as task-view steps),
``setup_screen`` (hub-embedded flow), ``run_tui`` (standalone curses
flow), ``main`` (CLI) — and this module holds the parts of that surface
that are identical everywhere. The backend keeps the thin named wrappers
so its public names (and test seams) stay on the backend module.
"""
import sys
from ui import taskview, tui
def interactive() -> bool:
"""True when the TUI wizard can run (curses importable + tty)."""
try:
import curses # noqa: F401
except ImportError:
return False
try:
return sys.stdin.isatty() and sys.stdout.isatty()
except (AttributeError, ValueError):
return False
def screen_flow(stdscr, *, wizard, steps_of, title,
parser_factory) -> int:
"""Run the setup wizard on an existing curses screen (the hub's).
The hub drives this as one screen of its own ``tui.Wizard`` stack, so
Esc on the wizard's first screen simply returns here and the hub pops
back to the menu that launched it. The setup tail runs inside the TUI
task view on this same screen, so the hub's curses session stays intact
and the user sees per-step status instead of being dropped to the
console. Returns 0 on completion, 1 when the user aborted.
WIZARD is ``(stdscr, args) -> settings | None``; STEPS_OF turns the
settings into task-view steps; PARSER_FACTORY builds the argparse
parser whose empty namespace seeds the wizard.
"""
args = parser_factory().parse_args([])
settings = wizard(stdscr, args)
if settings is None:
return 1
return taskview.run_steps(stdscr, title, steps_of(settings))
def tui_flow(wizard, execute, *, args, execute_takes_args=False,
aborted_message="[INFO] Aborted") -> int:
"""Run the setup wizard end-to-end in its own curses session.
WIZARD is ``(args) -> settings | None`` via ``curses.wrapper``;
EXECUTE performs the settings (called with ARGS too when
EXECUTE_TAKES_ARGS). Restores the text cursor afterwards and reports
cancellation/abort consistently. Returns the process exit code.
"""
import curses
try:
settings = curses.wrapper(wizard, args)
except tui.WizardCancelled:
print("\n[INFO] Cancelled; nothing was written")
return 1
try:
curses.curs_set(1)
except curses.error:
pass
if settings is None:
print(aborted_message)
return 1
return execute(settings) if not execute_takes_args \
else execute(settings, args)
|