Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""LICENSE
2Copyright 2019 Hermann Krumrey <hermann@krumreyh.com>
4This file is part of puffotter.
6puffotter is free software: you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 3 of the License, or
9(at your option) any later version.
11puffotter is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
16You should have received a copy of the GNU General Public License
17along with puffotter. If not, see <http://www.gnu.org/licenses/>.
18LICENSE"""
20from typing import List, Any, Callable, Optional, Set
21from colorama import Fore, Style
24def yn_prompt(
25 message: str,
26 make_sure: bool = True,
27 case_sensitive: bool = False
28) -> bool:
29 """
30 Creates a yes/no prompt
31 :param message: The message to display
32 :param make_sure: Continuously prompts if the response is neither
33 'y' or 'n' until it is.
34 If false, every input besides 'y' will result in the
35 return value being False
36 :param case_sensitive: Whether or not the prompt should be case-sensitive
37 :return: True if the user specified 'y', else False
38 """
39 resp = input("{} {}(y|n){}: ".format(
40 message, Fore.LIGHTYELLOW_EX, Style.RESET_ALL
41 )).strip()
42 if not case_sensitive:
43 resp = resp.lower()
45 if resp == "y":
46 return True
47 elif resp == "n" or not make_sure:
48 return False
49 else:
50 return yn_prompt(message, make_sure, case_sensitive)
53def selection_prompt(objects: List[object]) -> List[object]:
54 """
55 Prompts the user for a selection from a list of objects
56 :param objects: The objects to show
57 :return: The selection of objects
58 """
59 fill = len(str(len(objects)))
60 for i, obj in enumerate(objects):
61 print("[{}] {}".format(str(i + 1).zfill(fill), str(obj)))
63 while True:
65 selection = input("Selection: ").split(",")
66 valid = True
67 selected_objects = []
69 for item in selection:
71 try:
72 start_index = int(item)
73 end_index = start_index
74 except ValueError:
75 try:
76 start, end = item.split("-", 1)
77 start_index = int(start)
78 end_index = int(end)
79 except (IndexError, ValueError):
80 valid = False
81 break
83 for i in range(start_index, end_index + 1):
84 try:
85 selected_objects.append(objects[i - 1])
86 except IndexError:
87 valid = False
88 break
90 if not valid:
91 print("Invalid selection")
92 else:
93 return selected_objects
96def prompt_comma_list(
97 message: str,
98 primitive_type: Callable[[str], Any] = str,
99 min_count: int = 0,
100 no_empty: bool = True,
101 default: Optional[List[Any]] = None
102) -> List[Any]:
103 """
104 Prompts the user for a comma-separated list
105 :param message: The message to display
106 :param primitive_type: The primitive type of the elements in the list
107 :param min_count: The minimum amount of elements to be provided by the user
108 :param no_empty: Removes any empty strings
109 :param default: A default value
110 :return: The result of the prompt
111 """
112 while True:
113 try:
114 prompt_message = message
115 if default is not None:
116 prompt_message = "{} {}{}{}".format(
117 prompt_message,
118 Fore.LIGHTGREEN_EX,
119 default,
120 Style.RESET_ALL
121 )
122 prompt_message += ": "
124 response = input(prompt_message).strip()
126 if default is not None and response == "":
127 return default
129 if default is not None and response == "[]":
130 result = [] # type: List[str]
131 else:
132 result = list(map(lambda x: x.strip(), response.split(",")))
134 if "" in result and no_empty:
135 result.remove("")
136 result = list(map(lambda x: primitive_type(x), result))
138 if len(result) < min_count:
139 print("Not enough values")
140 raise ValueError()
141 else:
142 return result
144 except (ValueError, TypeError):
145 print("Invalid input")
148def prompt(
149 prompt_text: str = "",
150 default: Optional[Any] = None,
151 _type: Callable[[str], Any] = str,
152 required: bool = True,
153 choices: Optional[Set[str]] = None
154) -> Optional[Any]:
155 """
156 Generic prompt with configuration options
157 :param prompt_text: The text to display before the prompt
158 :param default: A default value to use if the user responds with ''
159 :param _type: The type of the object prompted. Must take a single string
160 as a parameter
161 :param required: Whether or not a response is required
162 :param choices: Valid choices for the prompt
163 :return: The prompt result. May be None if required is False
164 """
165 prompt_message = prompt_text
167 if choices is not None:
168 prompt_message = "{} {}{}{}".format(
169 prompt_message,
170 Fore.LIGHTYELLOW_EX,
171 choices,
172 Style.RESET_ALL
173 )
175 if default is not None:
176 prompt_message = "{} {}{}{}".format(
177 prompt_message,
178 Fore.LIGHTGREEN_EX,
179 default,
180 Style.RESET_ALL
181 )
183 prompt_message += ": "
185 response = input(prompt_message).strip()
187 while \
188 response == "" and default is None \
189 or (
190 choices is not None
191 and response not in choices
192 and default is None
193 ):
194 response = input(prompt_message).strip()
196 if response == "" and default is not None:
197 return default
198 elif response == "":
199 if required:
200 return prompt(prompt_text, default, _type, required)
201 else:
202 return None
203 else:
204 try:
205 return _type(response)
206 except (TypeError, ValueError):
207 return prompt(prompt_text, default, _type, required)