Skip to content

Commit 03d60f5

Browse files
committed
Added more helper functions to construct generics lists and dicts, and to check for value duplicates; updated grammar for some of the auto-generated prompts
1 parent 97e7117 commit 03d60f5

File tree

1 file changed

+139
-19
lines changed

1 file changed

+139
-19
lines changed

src/murfey/cli/generate_config.py

Lines changed: 139 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import argparse
22
import json
33
import re
4+
from ast import literal_eval
45
from pathlib import Path
56
from typing import Any, Optional, get_type_hints
67

@@ -20,8 +21,8 @@
2021

2122
def prompt(message: str, style: str = "") -> str:
2223
"""
23-
Helper function to pretty print the prompt message and add the actual prompt on a
24-
newline.
24+
Helper function to pretty print a message and have the user input their response
25+
on a new line.
2526
"""
2627
console.print(message, style=style)
2728
return input("> ")
@@ -41,35 +42,154 @@ def print_field_info(field: ModelField):
4142
console.print(f"Default: {field.field_info.default!r}", style="bright_cyan")
4243

4344

44-
def ask_for_input(category: str, again: bool = False):
45+
def ask_for_permission(message: str) -> bool:
4546
"""
46-
Perform a Boolean check to see if another value is to be appended to the current
47-
parameter being set up.
47+
Helper function to generate a Boolean based on user input
4848
"""
49-
message = (
50-
"Would you like to add " + ("another" if again else "a") + f" {category}? (y/n)"
51-
)
5249
while True:
5350
answer = prompt(message, style="yellow").lower().strip()
5451
if answer in ("y", "yes"):
5552
return True
5653
if answer in ("n", "no"):
5754
return False
5855
console.print("Invalid input. Please try again.", style="red")
56+
continue
5957

6058

61-
def confirm_overwrite(key: str):
59+
def ask_for_input(parameter: str, again: bool = False):
6260
"""
63-
Check whether a key should be overwritten if a duplicate is detected.
61+
Asks the user if another value should be entered into the current data structure.
6462
"""
65-
message = f"{key!r} already exists; do you wish to overwrite it? (y/n)"
66-
while True:
67-
answer = prompt(message, style="yellow").lower().strip()
68-
if answer in ("y", "yes"):
69-
return True
70-
if answer in ("n", "no"):
71-
return False
72-
console.print("Invalid input. Please try again.", style="red")
63+
message = (
64+
"Would you like to add "
65+
+ (
66+
"another"
67+
if again
68+
else (
69+
"an" if parameter.lower().startswith(("a", "e", "i", "o", "u")) else "a"
70+
)
71+
)
72+
+ f" {parameter}? (y/n)"
73+
)
74+
return ask_for_permission(message)
75+
76+
77+
def confirm_overwrite(value: str):
78+
"""
79+
Asks the user if a value that already exists should be overwritten.
80+
"""
81+
message = f"{value!r} already exists; do you wish to overwrite it? (y/n)"
82+
return ask_for_permission(message)
83+
84+
85+
def confirm_duplicate(value: str):
86+
"""
87+
Asks the user if a duplicate value should be allowed.
88+
"""
89+
message = f"{value!r} already exists; do you want to add a duplicate? (y/n)"
90+
return ask_for_permission(message)
91+
92+
93+
def construct_list(
94+
list_name: str,
95+
prompt_message: str,
96+
allow_empty: bool = False,
97+
allow_eval: bool = True,
98+
many_types: bool = True,
99+
debug: bool = False,
100+
) -> list[Any]:
101+
"""
102+
Helper function to facilitate interactive construction of a list to be stored
103+
under the current parameter.
104+
"""
105+
lst: list = []
106+
add_entry = ask_for_input(list_name, False)
107+
message = prompt_message
108+
while add_entry is True:
109+
value = prompt(message, style="yellow").strip()
110+
# Reject empty inputs if set
111+
if not value and not allow_empty:
112+
console.print("No value provided.", style="red")
113+
add_entry = ask_for_input(list_name, True)
114+
continue
115+
# Convert numericals if set
116+
try:
117+
eval_value = (
118+
literal_eval(value)
119+
if allow_eval and isinstance(literal_eval(value), (int, float, complex))
120+
else value
121+
)
122+
except Exception:
123+
eval_value = value
124+
# Confirm if duplicate entry should be added
125+
if eval_value in lst and confirm_duplicate(str(eval_value)) is False:
126+
add_entry = ask_for_input(list_name, True)
127+
continue
128+
lst.append(eval_value)
129+
# Reject list with multiple types if set
130+
if not many_types and len({type(item) for item in lst}) > 1:
131+
console.print(
132+
"The provided value is of a different type to the other members. \n"
133+
"It won't be added to the list.",
134+
style="red",
135+
)
136+
lst = lst[:-1]
137+
add_entry = ask_for_input(list_name, True)
138+
continue
139+
return lst
140+
141+
142+
def construct_dict(
143+
dict_name: str,
144+
key_name: str,
145+
value_name: str,
146+
allow_empty_key: bool = True,
147+
allow_empty_value: bool = True,
148+
allow_eval: bool = True,
149+
sort_keys: bool = True,
150+
debug: bool = False,
151+
) -> dict[str, Any]:
152+
"""
153+
Helper function to facilitate interative construction of a dictionary.
154+
"""
155+
dct: dict = {}
156+
add_entry = ask_for_input(dict_name, False)
157+
key_message = f"Please enter a {key_name}"
158+
value_message = f"Please enter a {value_name}"
159+
while add_entry is True:
160+
key = prompt(key_message, style="yellow").strip().lower()
161+
# Reject empty keys if set
162+
if not allow_empty_key and not key:
163+
console.print(f"No {key_name} provided.")
164+
add_entry = ask_for_input(dict_name, True)
165+
continue
166+
# Confirm overwrite key on duplicate
167+
if key in dct.keys():
168+
if confirm_overwrite(key) is False:
169+
add_entry = ask_for_input(dict_name, True)
170+
continue
171+
value = prompt(value_message, style="yellow").strip()
172+
# Reject empty values if set
173+
if not allow_empty_value and not value:
174+
console.print("No value provided", style="red")
175+
add_entry = ask_for_input(dict_name, True)
176+
continue
177+
# Convert values to numericals if set
178+
try:
179+
eval_value = (
180+
literal_eval(value)
181+
if allow_eval and isinstance(literal_eval(value), (int, float, complex))
182+
else value
183+
)
184+
except Exception:
185+
eval_value = value
186+
dct[key] = eval_value
187+
add_entry = ask_for_input(dict_name, True)
188+
continue
189+
190+
# Sort keys if set
191+
dct = {key: dct[key] for key in sorted(dct.keys())} if sort_keys else dct
192+
return dct
73193

74194

75195
def validate_value(value: Any, key: str, field: ModelField, debug: bool = False) -> Any:
@@ -187,7 +307,7 @@ def get_calibration():
187307
console.print(f"{calibration_values}", style="bright_green")
188308

189309
# Check if any more calibrations need to be added
190-
add_calibration = ask_for_input(category="calibration setting", again=True)
310+
add_calibration = ask_for_input("calibration setting", again=True)
191311

192312
# Validate the nested dictionary structure
193313
try:

0 commit comments

Comments
 (0)