11import argparse
22import json
33import re
4+ from ast import literal_eval
45from pathlib import Path
56from typing import Any , Optional , get_type_hints
67
2021
2122def 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
75195def 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