aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorskullydazed <skullydazed@users.noreply.github.com>2020-02-03 09:04:58 -0800
committerGitHub <noreply@github.com>2020-02-03 09:04:58 -0800
commit5780c94423615ba51343cd88ae55cf5fdaffb6df (patch)
tree9804d68c5a3283cf1826e32c380594d2647cfac4
parentacef512730479d5489a1ddd3b598c899efd8784e (diff)
downloadqmk_firmware-5780c94423615ba51343cd88ae55cf5fdaffb6df.tar.gz
qmk_firmware-5780c94423615ba51343cd88ae55cf5fdaffb6df.zip
Improve the functionality of qmk.questions (#8020)
* wip * tested and working * remove unused import * Update lib/python/qmk/questions.py Co-Authored-By: Erovia <Erovia@users.noreply.github.com> * fix flake8 errors Co-authored-by: Erovia <Erovia@users.noreply.github.com>
-rw-r--r--lib/python/qmk/questions.py97
1 files changed, 89 insertions, 8 deletions
diff --git a/lib/python/qmk/questions.py b/lib/python/qmk/questions.py
index 34b0b43bc..27f43ac1e 100644
--- a/lib/python/qmk/questions.py
+++ b/lib/python/qmk/questions.py
@@ -16,7 +16,7 @@ def yesno(prompt, *args, default=None, **kwargs):
16 16
17 Arguments: 17 Arguments:
18 prompt 18 prompt
19 The prompt to present to the user. Can include ANSI and format strings like milc's `cli.print()`. 19 The prompt to present to the user. Can include ANSI and format strings like milc's `cli.echo()`.
20 20
21 default 21 default
22 Whether to default to a Yes or No when the user presses enter. 22 Whether to default to a Yes or No when the user presses enter.
@@ -43,9 +43,9 @@ def yesno(prompt, *args, default=None, **kwargs):
43 prompt = prompt + ' [y/N] ' 43 prompt = prompt + ' [y/N] '
44 44
45 while True: 45 while True:
46 print() 46 cli.echo('')
47 answer = input(format_ansi(prompt % args)) 47 answer = input(format_ansi(prompt % args))
48 print() 48 cli.echo('')
49 49
50 if not answer and prompt is not None: 50 if not answer and prompt is not None:
51 return default 51 return default
@@ -57,17 +57,26 @@ def yesno(prompt, *args, default=None, **kwargs):
57 return False 57 return False
58 58
59 59
60def question(prompt, *args, default=None, confirm=False, answer_type=str, **kwargs): 60def question(prompt, *args, default=None, confirm=False, answer_type=str, validate=None, **kwargs):
61 """Prompt the user to answer a question with a free-form input. 61 """Prompt the user to answer a question with a free-form input.
62 62
63 Arguments:
63 prompt 64 prompt
64 The prompt to present to the user. Can include ANSI and format strings like milc's `cli.print()`. 65 The prompt to present to the user. Can include ANSI and format strings like milc's `cli.echo()`.
65 66
66 default 67 default
67 The value to return when the user doesn't enter any value. Use None to prompt until they enter a value. 68 The value to return when the user doesn't enter any value. Use None to prompt until they enter a value.
68 69
70 confirm
71 Present the user with a confirmation dialog before accepting their answer.
72
69 answer_type 73 answer_type
70 Specify a type function for the answer. Will re-prompt the user if the function raises any errors. Common choices here include int, float, and decimal.Decimal. 74 Specify a type function for the answer. Will re-prompt the user if the function raises any errors. Common choices here include int, float, and decimal.Decimal.
75
76 validate
77 This is an optional function that can be used to validate the answer. It should return True or False and have the following signature:
78
79 def function_name(answer, *args, **kwargs):
71 """ 80 """
72 if not args and kwargs: 81 if not args and kwargs:
73 args = kwargs 82 args = kwargs
@@ -76,17 +85,21 @@ def question(prompt, *args, default=None, confirm=False, answer_type=str, **kwar
76 prompt = '%s [%s] ' % (prompt, default) 85 prompt = '%s [%s] ' % (prompt, default)
77 86
78 while True: 87 while True:
79 print() 88 cli.echo('')
80 answer = input(format_ansi(prompt % args)) 89 answer = input(format_ansi(prompt % args))
81 print() 90 cli.echo('')
82 91
83 if answer: 92 if answer:
84 if confirm: 93 if validate is not None and not validate(answer, *args, **kwargs):
94 continue
95
96 elif confirm:
85 if yesno('Is the answer "%s" correct?', answer, default=True): 97 if yesno('Is the answer "%s" correct?', answer, default=True):
86 try: 98 try:
87 return answer_type(answer) 99 return answer_type(answer)
88 except Exception as e: 100 except Exception as e:
89 cli.log.error('Could not convert answer (%s) to type %s: %s', answer, answer_type.__name__, str(e)) 101 cli.log.error('Could not convert answer (%s) to type %s: %s', answer, answer_type.__name__, str(e))
102
90 else: 103 else:
91 try: 104 try:
92 return answer_type(answer) 105 return answer_type(answer)
@@ -95,3 +108,71 @@ def question(prompt, *args, default=None, confirm=False, answer_type=str, **kwar
95 108
96 elif default is not None: 109 elif default is not None:
97 return default 110 return default
111
112
113def choice(heading, options, *args, default=None, confirm=False, prompt='Please enter your choice: ', **kwargs):
114 """Present the user with a list of options and let them pick one.
115
116 Users can enter either the number or the text of their choice.
117
118 This will return the value of the item they choose, not the numerical index.
119
120 Arguments:
121 heading
122 The text to place above the list of options.
123
124 options
125 A sequence of items to choose from.
126
127 default
128 The index of the item to return when the user doesn't enter any value. Use None to prompt until they enter a value.
129
130 confirm
131 Present the user with a confirmation dialog before accepting their answer.
132
133 prompt
134 The prompt to present to the user. Can include ANSI and format strings like milc's `cli.echo()`.
135 """
136 if not args and kwargs:
137 args = kwargs
138
139 if prompt and default:
140 prompt = prompt + ' [%s] ' % (default + 1,)
141
142 while True:
143 # Prompt for an answer.
144 cli.echo('')
145 cli.echo(heading % args)
146 cli.echo('')
147 for i, option in enumerate(options, 1):
148 cli.echo('\t{fg_cyan}%d.{fg_reset} %s', i, option)
149
150 cli.echo('')
151 answer = input(format_ansi(prompt))
152 cli.echo('')
153
154 # If the user types in one of the options exactly use that
155 if answer in options:
156 return answer
157
158 # Massage the answer into a valid integer
159 if answer == '' and default:
160 answer = default
161 else:
162 try:
163 answer = int(answer) - 1
164 except Exception:
165 # Normally we would log the exception here, but in the interest of clean UI we do not.
166 cli.log.error('Invalid choice: %s', answer + 1)
167 continue
168
169 # Validate the answer
170 if answer >= len(options) or answer < 0:
171 cli.log.error('Invalid choice: %s', answer + 1)
172 continue
173
174 if confirm and not yesno('Is the answer "%s" correct?', answer + 1, default=True):
175 continue
176
177 # Return the answer they chose.
178 return options[answer]