3 """Utilities related to user input."""
8 from typing import List
10 import readchar # type: ignore
15 logger = logging.getLogger(__file__)
18 def single_keystroke_response(
19 valid_responses: List[str],
22 default_response: str = None,
23 timeout_seconds: int = None,
25 """Get a single keystroke response to a prompt."""
27 def _handle_timeout(signum, frame) -> None:
28 raise exceptions.TimeoutError()
30 def _single_keystroke_response_internal(
31 valid_responses: List[str], timeout_seconds=None
33 os_special_keystrokes = [3, 26] # ^C, ^Z
34 if timeout_seconds is not None:
35 signal.signal(signal.SIGALRM, _handle_timeout)
36 signal.alarm(timeout_seconds)
40 response = readchar.readchar()
41 logger.debug(f'Keystroke: {ord(response)}')
42 if response in valid_responses:
44 if ord(response) in os_special_keystrokes:
48 if timeout_seconds is not None:
51 if prompt is not None:
55 response = _single_keystroke_response_internal(
56 valid_responses, timeout_seconds
58 if ord(response) == 3:
59 raise KeyboardInterrupt('User pressed ^C in input_utils.')
61 except exceptions.TimeoutError:
62 if default_response is not None:
63 response = default_response
64 if prompt is not None:
69 def yn_response(prompt: str = None, *, timeout_seconds=None) -> str:
70 """Get a Y/N response to a prompt."""
72 return single_keystroke_response(
73 ["y", "n", "Y", "N"], prompt=prompt, timeout_seconds=timeout_seconds
77 def keystroke_helper() -> None:
78 """Misc util to watch keystrokes and report what they were."""
80 print("Watching for keystrokes; ^C to quit.")
82 key = readchar.readkey()
84 print(f'That was "{key}" ({ord(key)}).')
88 print(f'That was sequence "{key}" (', end="")
90 print(f" {ord(_)} ", end="")