3 # © Copyright 2021-2022, Scott Gasch
5 """Utilities related to user input."""
10 from typing import List, Optional
12 import readchar # type: ignore
16 logger = logging.getLogger(__file__)
19 def single_keystroke_response(
20 valid_responses: Optional[List[str]], # None = accept anything
23 default_response: str = None,
24 timeout_seconds: int = None,
25 ) -> Optional[str]: # None if timeout w/o keystroke
26 """Get a single keystroke response to a prompt."""
28 def _handle_timeout(signum, frame) -> None:
29 raise exceptions.TimeoutError()
31 def _single_keystroke_response_internal(
32 valid_responses: Optional[List[str]], timeout_seconds: int = None
34 os_special_keystrokes = [3, 26] # ^C, ^Z
35 if timeout_seconds is not None:
36 signal.signal(signal.SIGALRM, _handle_timeout)
37 signal.alarm(timeout_seconds)
41 response = readchar.readchar()
42 logger.debug('Keystroke: 0x%x', ord(response))
43 if not valid_responses or response in valid_responses:
45 if ord(response) in os_special_keystrokes:
49 if timeout_seconds is not None:
53 if prompt is not None:
57 response = _single_keystroke_response_internal(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 and response:
69 def yn_response(prompt: str = None, *, timeout_seconds=None) -> Optional[str]:
70 """Get a Y/N response to a prompt."""
72 yn = single_keystroke_response(
73 ["y", "n", "Y", "N"], prompt=prompt, timeout_seconds=timeout_seconds
81 prompt: str = "Press any key to continue...", *, timeout_seconds=None
83 """Press any key to continue..."""
85 return single_keystroke_response(None, prompt=prompt, timeout_seconds=timeout_seconds)
88 def keystroke_helper() -> None:
89 """Misc util to watch keystrokes and report what they were."""
91 print("Watching for keystrokes; ^C to quit.")
93 key = readchar.readkey()
95 print(f'That was "{key}" ({ord(key)}).')
99 print(f'That was sequence "{key}" (', end="")
101 print(f" {ord(_)} ", end="")