3 """Utilities related to user input."""
5 import readchar # type: ignore
8 from typing import List
13 def single_keystroke_response(
14 valid_responses: List[str],
17 default_response: str = None,
18 timeout_seconds: int = None,
20 """Get a single keystroke response to a prompt."""
22 def _handle_timeout(signum, frame) -> None:
23 raise exceptions.TimeoutError()
25 def _single_keystroke_response_internal(
26 valid_responses: List[str], timeout_seconds=None
28 os_special_keystrokes = [3, 26] # ^C, ^Z
29 if timeout_seconds is not None:
30 signal.signal(signal.SIGALRM, _handle_timeout)
31 signal.alarm(timeout_seconds)
35 response = readchar.readchar()
36 if response in valid_responses:
38 if ord(response) in os_special_keystrokes:
42 if timeout_seconds is not None:
45 if prompt is not None:
49 response = _single_keystroke_response_internal(
50 valid_responses, timeout_seconds
52 except exceptions.TimeoutError:
53 if default_response is not None:
54 response = default_response
55 if prompt is not None:
60 def yn_response(prompt: str = None, *, timeout_seconds=None) -> str:
61 """Get a Y/N response to a prompt."""
63 return single_keystroke_response(
64 ["y", "n", "Y", "N"], prompt=prompt, timeout_seconds=timeout_seconds
68 def keystroke_helper() -> None:
69 """Misc util to watch keystrokes and report what they were."""
71 print("Watching for keystrokes; ^C to quit.")
73 key = readchar.readkey()
75 print(f'That was "{key}" ({ord(key)}).')
79 print(f'That was sequence "{key}" (', end="")
81 print(f" {ord(_)} ", end="")