3 """Utilities related to user input."""
8 from typing import List
10 import readchar # type: ignore
14 logger = logging.getLogger(__file__)
17 def single_keystroke_response(
18 valid_responses: List[str],
21 default_response: str = None,
22 timeout_seconds: int = None,
24 """Get a single keystroke response to a prompt."""
26 def _handle_timeout(signum, frame) -> None:
27 raise exceptions.TimeoutError()
29 def _single_keystroke_response_internal(
30 valid_responses: List[str], timeout_seconds=None
32 os_special_keystrokes = [3, 26] # ^C, ^Z
33 if timeout_seconds is not None:
34 signal.signal(signal.SIGALRM, _handle_timeout)
35 signal.alarm(timeout_seconds)
39 response = readchar.readchar()
40 logger.debug(f'Keystroke: {ord(response)}')
41 if response in valid_responses:
43 if ord(response) in os_special_keystrokes:
47 if timeout_seconds is not None:
50 if prompt is not None:
54 response = _single_keystroke_response_internal(valid_responses, timeout_seconds)
55 if ord(response) == 3:
56 raise KeyboardInterrupt('User pressed ^C in input_utils.')
58 except exceptions.TimeoutError:
59 if default_response is not None:
60 response = default_response
61 if prompt is not None:
66 def yn_response(prompt: str = None, *, timeout_seconds=None) -> str:
67 """Get a Y/N response to a prompt."""
69 return single_keystroke_response(
70 ["y", "n", "Y", "N"], prompt=prompt, timeout_seconds=timeout_seconds
74 def keystroke_helper() -> None:
75 """Misc util to watch keystrokes and report what they were."""
77 print("Watching for keystrokes; ^C to quit.")
79 key = readchar.readkey()
81 print(f'That was "{key}" ({ord(key)}).')
85 print(f'That was sequence "{key}" (', end="")
87 print(f" {ord(_)} ", end="")