blob: a422e0f57f1ba900f9285ae98ae525398927f8ce [file] [log] [blame]
George Keishinge7e91712021-09-03 11:28:44 -05001#!/usr/bin/env python3
Michael Walsh7423c012016-10-04 10:27:21 -05002
3r"""
Michael Walshda20f842019-10-16 11:36:40 -05004This module provides validation functions like valid_value(), valid_integer(), etc.
Michael Walsh7423c012016-10-04 10:27:21 -05005"""
6
George Keishinge635ddc2022-12-08 07:38:02 -06007import datetime
Patrick Williams20f38712022-12-08 06:18:26 -06008import os
9
10import func_args as fa
11import gen_cmd as gc
12import gen_print as gp
Michael Walsh7423c012016-10-04 10:27:21 -050013
Michael Walshe23b5ad2018-06-01 14:54:35 -050014exit_on_error = False
15
16
17def set_exit_on_error(value):
18 r"""
19 Set the exit_on_error value to either True or False.
20
Michael Walshda20f842019-10-16 11:36:40 -050021 If exit_on_error is set, validation functions like valid_value() will exit the program on error instead
22 of returning False.
Michael Walshe23b5ad2018-06-01 14:54:35 -050023
24 Description of argument(s):
25 value Value to set global exit_on_error to.
26 """
27
28 global exit_on_error
29 exit_on_error = value
30
31
Michael Walsh89eff542019-09-13 16:24:51 -050032def get_var_name(var_name):
Michael Walshe23b5ad2018-06-01 14:54:35 -050033 r"""
Michael Walshda20f842019-10-16 11:36:40 -050034 If var_name is not None, simply return its value. Otherwise, get the variable name of the first argument
35 used to call the validation function (e.g. valid, valid_integer, etc.) and return it.
Michael Walshe23b5ad2018-06-01 14:54:35 -050036
37 This function is designed solely for use by other functions in this file.
38
39 Example:
40
41 A programmer codes this:
42
43 valid_value(last_name)
44
45 Which results in the following call stack:
46
47 valid_value(last_name)
Michael Walsh018e25f2019-08-01 11:27:12 -050048 -> get_var_name(var_name)
Michael Walshe23b5ad2018-06-01 14:54:35 -050049
50 In this example, this function will return "last_name".
51
52 Example:
53
Michael Walsh018e25f2019-08-01 11:27:12 -050054 err_msg = valid_value(last_name, var_name="some_other_name")
Michael Walshe23b5ad2018-06-01 14:54:35 -050055
56 Which results in the following call stack:
57
Michael Walsh018e25f2019-08-01 11:27:12 -050058 valid_value(var_value, var_name="some_other_name")
Michael Walshe23b5ad2018-06-01 14:54:35 -050059 -> get_var_name(var_name)
60
61 In this example, this function will return "some_other_name".
62
63 Description of argument(s):
64 var_name The name of the variable.
65 """
66
Michael Walsh89eff542019-09-13 16:24:51 -050067 return var_name or gp.get_arg_name(0, 1, stack_frame_ix=3)
Michael Walshe23b5ad2018-06-01 14:54:35 -050068
69
70def process_error_message(error_message):
71 r"""
Michael Walsh018e25f2019-08-01 11:27:12 -050072 Process the error_message in the manner described below.
Michael Walshe23b5ad2018-06-01 14:54:35 -050073
Michael Walsh018e25f2019-08-01 11:27:12 -050074 This function is designed solely for use by other functions in this file.
75
76 NOTE: A blank error_message means that there is no error.
77
Michael Walshda20f842019-10-16 11:36:40 -050078 For the following explanations, assume the caller of this function is a function with the following
79 definition:
Michael Walsh89eff542019-09-13 16:24:51 -050080 valid_value(var_value, valid_values=[], invalid_values=[], var_name=None):
Michael Walsh018e25f2019-08-01 11:27:12 -050081
Michael Walshda20f842019-10-16 11:36:40 -050082 If the user of valid_value() is assigning the valid_value() return value to a variable,
83 process_error_message() will simply return the error_message. This mode of usage is illustrated by the
84 following example:
Michael Walsh018e25f2019-08-01 11:27:12 -050085
86 error_message = valid_value(var1)
87
Michael Walshda20f842019-10-16 11:36:40 -050088 This mode is useful for callers who wish to validate a variable and then decide for themselves what to do
89 with the error_message (e.g. raise(error_message), BuiltIn().fail(error_message), etc.).
Michael Walsh018e25f2019-08-01 11:27:12 -050090
Michael Walshda20f842019-10-16 11:36:40 -050091 If the user of valid_value() is NOT assigning the valid_value() return value to a variable,
92 process_error_message() will behave as follows.
Michael Walsh018e25f2019-08-01 11:27:12 -050093
Michael Walshda20f842019-10-16 11:36:40 -050094 First, if error_message is non-blank, it will be printed to stderr via a call to
95 gp.print_error_report(error_message).
Michael Walsh018e25f2019-08-01 11:27:12 -050096
97 If exit_on_error is set:
98 - If the error_message is blank, simply return.
Michael Walshda20f842019-10-16 11:36:40 -050099 - If the error_message is non-blank, exit the program with a return code of 1.
Michael Walsh018e25f2019-08-01 11:27:12 -0500100
101 If exit_on_error is NOT set:
102 - If the error_message is blank, return True.
103 - If the error_message is non-blank, return False.
Michael Walshe23b5ad2018-06-01 14:54:35 -0500104
105 Description of argument(s):
106 error_message An error message.
107 """
108
Michael Walshda20f842019-10-16 11:36:40 -0500109 # Determine whether the caller's caller is assigning the result to a variable.
Michael Walsh018e25f2019-08-01 11:27:12 -0500110 l_value = gp.get_arg_name(None, -1, stack_frame_ix=3)
111 if l_value:
112 return error_message
113
Michael Walshe23b5ad2018-06-01 14:54:35 -0500114 if error_message == "":
Michael Walsh018e25f2019-08-01 11:27:12 -0500115 if exit_on_error:
116 return
Michael Walshe23b5ad2018-06-01 14:54:35 -0500117 return True
118
Michael Walsh018e25f2019-08-01 11:27:12 -0500119 gp.print_error_report(error_message, stack_frame_ix=4)
Michael Walshe23b5ad2018-06-01 14:54:35 -0500120 if exit_on_error:
Michael Walsh10a4d982018-10-30 13:07:46 -0500121 exit(1)
Michael Walshe23b5ad2018-06-01 14:54:35 -0500122 return False
123
Michael Walsh7423c012016-10-04 10:27:21 -0500124
Michael Walshda20f842019-10-16 11:36:40 -0500125# Note to programmers: All of the validation functions in this module should follow the same basic template:
Michael Walsh89eff542019-09-13 16:24:51 -0500126# def valid_value(var_value, var1, var2, varn, var_name=None):
Michael Walsh018e25f2019-08-01 11:27:12 -0500127#
128# error_message = ""
129# if not valid:
Michael Walsh89eff542019-09-13 16:24:51 -0500130# var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500131# error_message += "The following variable is invalid because...:\n"
132# error_message += gp.sprint_varx(var_name, var_value, gp.blank())
133#
134# return process_error_message(error_message)
Michael Walsh7423c012016-10-04 10:27:21 -0500135
Michael Walsh018e25f2019-08-01 11:27:12 -0500136
Michael Walshda20f842019-10-16 11:36:40 -0500137# The docstring header and footer will be added to each validation function's existing docstring.
Patrick Williams20f38712022-12-08 06:18:26 -0600138docstring_header = r"""
Michael Walsh018e25f2019-08-01 11:27:12 -0500139 Determine whether var_value is valid, construct an error_message and call
140 process_error_message(error_message).
141
Michael Walshda20f842019-10-16 11:36:40 -0500142 See the process_error_message() function defined in this module for a description of how error messages
143 are processed.
Michael Walsh7423c012016-10-04 10:27:21 -0500144 """
145
Patrick Williams20f38712022-12-08 06:18:26 -0600146additional_args_docstring_footer = r"""
Michael Walshda20f842019-10-16 11:36:40 -0500147 var_name The name of the variable whose value is passed in var_value. For the
148 general case, this argument is unnecessary as this function can figure
149 out the var_name. This is provided for Robot callers in which case, this
150 function lacks the ability to determine the variable name.
Michael Walsh018e25f2019-08-01 11:27:12 -0500151 """
152
153
Michael Walsh89eff542019-09-13 16:24:51 -0500154def valid_type(var_value, required_type, var_name=None):
Michael Walsh018e25f2019-08-01 11:27:12 -0500155 r"""
156 The variable value is valid if it is of the required type.
157
158 Examples:
159
160 valid_type(var1, int)
161
162 valid_type(var1, (list, dict))
163
164 Description of argument(s):
165 var_value The value being validated.
Michael Walshda20f842019-10-16 11:36:40 -0500166 required_type A type or a tuple of types (e.g. str, int, etc.).
Michael Walsh018e25f2019-08-01 11:27:12 -0500167 """
168
169 error_message = ""
170 if type(required_type) is tuple:
171 if type(var_value) in required_type:
172 return process_error_message(error_message)
173 else:
174 if type(var_value) is required_type:
175 return process_error_message(error_message)
176
177 # If we get to this point, the validation has failed.
Michael Walsh89eff542019-09-13 16:24:51 -0500178 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500179 error_message += "Invalid variable type:\n"
Patrick Williams20f38712022-12-08 06:18:26 -0600180 error_message += gp.sprint_varx(
181 var_name, var_value, gp.blank() | gp.show_type()
182 )
Michael Walsh018e25f2019-08-01 11:27:12 -0500183 error_message += "\n"
184 error_message += gp.sprint_var(required_type)
185
186 return process_error_message(error_message)
187
188
Michael Walsh89eff542019-09-13 16:24:51 -0500189def valid_value(var_value, valid_values=[], invalid_values=[], var_name=None):
Michael Walsh018e25f2019-08-01 11:27:12 -0500190 r"""
Michael Walshda20f842019-10-16 11:36:40 -0500191 The variable value is valid if it is either contained in the valid_values list or if it is NOT contained
192 in the invalid_values list. If the caller specifies nothing for either of these 2 arguments,
193 invalid_values will be initialized to ['', None]. This is a good way to fail on variables which contain
194 blank values.
Michael Walsh018e25f2019-08-01 11:27:12 -0500195
196 It is illegal to specify both valid_values and invalid values.
197
198 Example:
199
200 var1 = ''
201 valid_value(var1)
202
Michael Walshda20f842019-10-16 11:36:40 -0500203 This code would fail because var1 is blank and the default value for invalid_values is ['', None].
Michael Walsh018e25f2019-08-01 11:27:12 -0500204
205 Example:
206 var1 = 'yes'
207 valid_value(var1, valid_values=['yes', 'true'])
208
209 This code would pass.
210
211 Description of argument(s):
212 var_value The value being validated.
Michael Walshda20f842019-10-16 11:36:40 -0500213 valid_values A list of valid values. The variable value must be equal to one of these
214 values to be considered valid.
215 invalid_values A list of invalid values. If the variable value is equal to any of
216 these, it is considered invalid.
Michael Walsh018e25f2019-08-01 11:27:12 -0500217 """
218
Michael Walshbec416d2016-11-10 08:54:52 -0600219 error_message = ""
Michael Walshbec416d2016-11-10 08:54:52 -0600220
Michael Walshe23b5ad2018-06-01 14:54:35 -0500221 # Validate this function's arguments.
Michael Walsh7423c012016-10-04 10:27:21 -0500222 len_valid_values = len(valid_values)
223 len_invalid_values = len(invalid_values)
224 if len_valid_values > 0 and len_invalid_values > 0:
Michael Walsh018e25f2019-08-01 11:27:12 -0500225 error_message += "Programmer error - You must provide either an"
226 error_message += " invalid_values list or a valid_values"
227 error_message += " list but NOT both:\n"
228 error_message += gp.sprint_var(invalid_values)
229 error_message += gp.sprint_var(valid_values)
230 return process_error_message(error_message)
Michael Walsh7423c012016-10-04 10:27:21 -0500231
Patrick Williams20f38712022-12-08 06:18:26 -0600232 error_message = valid_type(valid_values, list, var_name="valid_values")
Michael Walsh0f5fa532020-02-13 10:51:40 -0600233 if error_message:
234 return process_error_message(error_message)
235
Patrick Williams20f38712022-12-08 06:18:26 -0600236 error_message = valid_type(invalid_values, list, var_name="invalid_values")
Michael Walsh0f5fa532020-02-13 10:51:40 -0600237 if error_message:
238 return process_error_message(error_message)
239
Michael Walsh7423c012016-10-04 10:27:21 -0500240 if len_valid_values > 0:
241 # Processing the valid_values list.
242 if var_value in valid_values:
Michael Walsh018e25f2019-08-01 11:27:12 -0500243 return process_error_message(error_message)
Michael Walsh89eff542019-09-13 16:24:51 -0500244 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500245 error_message += "Invalid variable value:\n"
Patrick Williams20f38712022-12-08 06:18:26 -0600246 error_message += gp.sprint_varx(
247 var_name, var_value, gp.blank() | gp.verbose() | gp.show_type()
248 )
Michael Walsh018e25f2019-08-01 11:27:12 -0500249 error_message += "\n"
250 error_message += "It must be one of the following values:\n"
251 error_message += "\n"
Patrick Williams20f38712022-12-08 06:18:26 -0600252 error_message += gp.sprint_var(
253 valid_values, gp.blank() | gp.show_type()
254 )
Michael Walsh018e25f2019-08-01 11:27:12 -0500255 return process_error_message(error_message)
Michael Walsh7423c012016-10-04 10:27:21 -0500256
257 if len_invalid_values == 0:
Michael Walshbec416d2016-11-10 08:54:52 -0600258 # Assign default value.
Michael Walsh018e25f2019-08-01 11:27:12 -0500259 invalid_values = ["", None]
Michael Walsh7423c012016-10-04 10:27:21 -0500260
261 # Assertion: We have an invalid_values list. Processing it now.
262 if var_value not in invalid_values:
Michael Walsh018e25f2019-08-01 11:27:12 -0500263 return process_error_message(error_message)
Michael Walsh7423c012016-10-04 10:27:21 -0500264
Michael Walsh89eff542019-09-13 16:24:51 -0500265 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500266 error_message += "Invalid variable value:\n"
Patrick Williams20f38712022-12-08 06:18:26 -0600267 error_message += gp.sprint_varx(
268 var_name, var_value, gp.blank() | gp.verbose() | gp.show_type()
269 )
Michael Walsh018e25f2019-08-01 11:27:12 -0500270 error_message += "\n"
Michael Walsh53902dd2020-02-20 14:41:56 -0600271 error_message += "It must NOT be any of the following values:\n"
Michael Walsh018e25f2019-08-01 11:27:12 -0500272 error_message += "\n"
Patrick Williams20f38712022-12-08 06:18:26 -0600273 error_message += gp.sprint_var(invalid_values, gp.blank() | gp.show_type())
Michael Walshe23b5ad2018-06-01 14:54:35 -0500274 return process_error_message(error_message)
Michael Walshbec416d2016-11-10 08:54:52 -0600275
Michael Walshbec416d2016-11-10 08:54:52 -0600276
Michael Walsh89eff542019-09-13 16:24:51 -0500277def valid_range(var_value, lower=None, upper=None, var_name=None):
Michael Walshbec416d2016-11-10 08:54:52 -0600278 r"""
Michael Walsh018e25f2019-08-01 11:27:12 -0500279 The variable value is valid if it is within the specified range.
Michael Walshbec416d2016-11-10 08:54:52 -0600280
Michael Walshda20f842019-10-16 11:36:40 -0500281 This function can be used with any type of operands where they can have a greater than/less than
282 relationship to each other (e.g. int, float, str).
Michael Walsh018e25f2019-08-01 11:27:12 -0500283
284 Description of argument(s):
Michael Walshbec416d2016-11-10 08:54:52 -0600285 var_value The value being validated.
Michael Walshda20f842019-10-16 11:36:40 -0500286 lower The lower end of the range. If not None, the var_value must be greater
287 than or equal to lower.
288 upper The upper end of the range. If not None, the var_value must be less than
289 or equal to upper.
Michael Walshbec416d2016-11-10 08:54:52 -0600290 """
291
Michael Walshbec416d2016-11-10 08:54:52 -0600292 error_message = ""
Michael Walshb9d8dfd2019-09-11 11:11:12 -0500293 if lower is None and upper is None:
Michael Walsh018e25f2019-08-01 11:27:12 -0500294 return process_error_message(error_message)
Michael Walshb9d8dfd2019-09-11 11:11:12 -0500295 if lower is None and var_value <= upper:
Michael Walsh018e25f2019-08-01 11:27:12 -0500296 return process_error_message(error_message)
Michael Walshb9d8dfd2019-09-11 11:11:12 -0500297 if upper is None and var_value >= lower:
Michael Walsh018e25f2019-08-01 11:27:12 -0500298 return process_error_message(error_message)
Tony Lee2f88a812020-04-06 15:00:01 +0800299 if lower is not None and upper is not None:
Michael Walsh018e25f2019-08-01 11:27:12 -0500300 if lower > upper:
Michael Walsh89eff542019-09-13 16:24:51 -0500301 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500302 error_message += "Programmer error - the lower value is greater"
303 error_message += " than the upper value:\n"
304 error_message += gp.sprint_vars(lower, upper, fmt=gp.show_type())
305 return process_error_message(error_message)
306 if lower <= var_value <= upper:
307 return process_error_message(error_message)
Michael Walshbec416d2016-11-10 08:54:52 -0600308
Michael Walsh89eff542019-09-13 16:24:51 -0500309 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500310 error_message += "The following variable is not within the expected"
311 error_message += " range:\n"
312 error_message += gp.sprint_varx(var_name, var_value, gp.show_type())
313 error_message += "\n"
314 error_message += "range:\n"
315 error_message += gp.sprint_vars(lower, upper, fmt=gp.show_type(), indent=2)
Michael Walshe23b5ad2018-06-01 14:54:35 -0500316 return process_error_message(error_message)
Michael Walsh7423c012016-10-04 10:27:21 -0500317
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600318
Michael Walsh89eff542019-09-13 16:24:51 -0500319def valid_integer(var_value, lower=None, upper=None, var_name=None):
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600320 r"""
Michael Walshda20f842019-10-16 11:36:40 -0500321 The variable value is valid if it is an integer or can be interpreted as an integer (e.g. 7, "7", etc.).
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600322
Michael Walshda20f842019-10-16 11:36:40 -0500323 This function also calls valid_range to make sure the integer value is within the specified range (if
324 any).
Michael Walsh018e25f2019-08-01 11:27:12 -0500325
326 Description of argument(s):
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600327 var_value The value being validated.
Michael Walshda20f842019-10-16 11:36:40 -0500328 lower The lower end of the range. If not None, the var_value must be greater
329 than or equal to lower.
330 upper The upper end of the range. If not None, the var_value must be less than
331 or equal to upper.
Michael Walsh018e25f2019-08-01 11:27:12 -0500332 """
333
334 error_message = ""
Michael Walsh89eff542019-09-13 16:24:51 -0500335 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500336 try:
337 var_value = int(str(var_value), 0)
338 except ValueError:
339 error_message += "Invalid integer value:\n"
Patrick Williams20f38712022-12-08 06:18:26 -0600340 error_message += gp.sprint_varx(
341 var_name, var_value, gp.blank() | gp.show_type()
342 )
Michael Walsh018e25f2019-08-01 11:27:12 -0500343 return process_error_message(error_message)
344
345 # Check the range (if any).
346 if lower:
347 lower = int(str(lower), 0)
348 if upper:
349 upper = int(str(upper), 0)
350 error_message = valid_range(var_value, lower, upper, var_name=var_name)
351
352 return process_error_message(error_message)
353
354
Michael Walsh8333a182019-10-22 16:19:00 -0500355def valid_float(var_value, lower=None, upper=None, var_name=None):
356 r"""
357 The variable value is valid if it is a floating point value or can be interpreted as a floating point
358 value (e.g. 7.5, "7.5", etc.).
359
360 This function also calls valid_range to make sure the float value is within the specified range (if any).
361
362 Description of argument(s):
363 var_value The value being validated.
364 lower The lower end of the range. If not None, the var_value must be greater
365 than or equal to lower.
366 upper The upper end of the range. If not None, the var_value must be less than
367 or equal to upper.
368 """
369
370 error_message = ""
371 var_name = get_var_name(var_name)
372 try:
373 var_value = float(str(var_value))
374 except ValueError:
375 error_message += "Invalid float value:\n"
Patrick Williams20f38712022-12-08 06:18:26 -0600376 error_message += gp.sprint_varx(
377 var_name, var_value, gp.blank() | gp.show_type()
378 )
Michael Walsh8333a182019-10-22 16:19:00 -0500379 return process_error_message(error_message)
380
381 # Check the range (if any).
382 if lower:
383 lower = float(str(lower))
384 if upper:
385 upper = float(str(upper))
386 error_message = valid_range(var_value, lower, upper, var_name=var_name)
387
388 return process_error_message(error_message)
389
390
391def valid_date_time(var_value, var_name=None):
392 r"""
393 The variable value is valid if it can be interpreted as a date/time (e.g. "14:49:49.981", "tomorrow",
394 etc.) by the linux date command.
395
396 Description of argument(s):
397 var_value The value being validated.
398 """
399
400 error_message = ""
Patrick Williams20f38712022-12-08 06:18:26 -0600401 rc, out_buf = gc.shell_cmd(
402 "date -d '" + str(var_value) + "'", quiet=1, show_err=0, ignore_err=1
403 )
Michael Walsh8333a182019-10-22 16:19:00 -0500404 if rc:
405 var_name = get_var_name(var_name)
406 error_message += "Invalid date/time value:\n"
Patrick Williams20f38712022-12-08 06:18:26 -0600407 error_message += gp.sprint_varx(
408 var_name, var_value, gp.blank() | gp.show_type()
409 )
Michael Walsh8333a182019-10-22 16:19:00 -0500410 return process_error_message(error_message)
411
412 return process_error_message(error_message)
413
414
Michael Walsh89eff542019-09-13 16:24:51 -0500415def valid_dir_path(var_value, var_name=None):
Michael Walsh018e25f2019-08-01 11:27:12 -0500416 r"""
Michael Walshda20f842019-10-16 11:36:40 -0500417 The variable value is valid if it contains the path of an existing directory.
Michael Walsh018e25f2019-08-01 11:27:12 -0500418
419 Description of argument(s):
420 var_value The value being validated.
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600421 """
422
423 error_message = ""
424 if not os.path.isdir(str(var_value)):
Michael Walsh89eff542019-09-13 16:24:51 -0500425 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500426 error_message += "The following directory does not exist:\n"
Michael Walsh00244342019-08-29 10:35:50 -0500427 error_message += gp.sprint_varx(var_name, var_value, gp.blank())
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600428
Michael Walshe23b5ad2018-06-01 14:54:35 -0500429 return process_error_message(error_message)
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600430
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600431
Michael Walsh89eff542019-09-13 16:24:51 -0500432def valid_file_path(var_value, var_name=None):
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600433 r"""
Michael Walsh018e25f2019-08-01 11:27:12 -0500434 The variable value is valid if it contains the path of an existing file.
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600435
Michael Walsh018e25f2019-08-01 11:27:12 -0500436 Description of argument(s):
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600437 var_value The value being validated.
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600438 """
439
440 error_message = ""
441 if not os.path.isfile(str(var_value)):
Michael Walsh89eff542019-09-13 16:24:51 -0500442 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500443 error_message += "The following file does not exist:\n"
Michael Walsh00244342019-08-29 10:35:50 -0500444 error_message += gp.sprint_varx(var_name, var_value, gp.blank())
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600445
Michael Walshe23b5ad2018-06-01 14:54:35 -0500446 return process_error_message(error_message)
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600447
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600448
Michael Walsh89eff542019-09-13 16:24:51 -0500449def valid_path(var_value, var_name=None):
Michael Walshe23b5ad2018-06-01 14:54:35 -0500450 r"""
Michael Walshda20f842019-10-16 11:36:40 -0500451 The variable value is valid if it contains the path of an existing file or directory.
Michael Walshe23b5ad2018-06-01 14:54:35 -0500452
Michael Walsh018e25f2019-08-01 11:27:12 -0500453 Description of argument(s):
Michael Walshe23b5ad2018-06-01 14:54:35 -0500454 var_value The value being validated.
Michael Walshe23b5ad2018-06-01 14:54:35 -0500455 """
456
457 error_message = ""
458 if not (os.path.isfile(str(var_value)) or os.path.isdir(str(var_value))):
Michael Walsh89eff542019-09-13 16:24:51 -0500459 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500460 error_message += "Invalid path (file or directory does not exist):\n"
Michael Walsh00244342019-08-29 10:35:50 -0500461 error_message += gp.sprint_varx(var_name, var_value, gp.blank())
Michael Walshe23b5ad2018-06-01 14:54:35 -0500462
Michael Walshe23b5ad2018-06-01 14:54:35 -0500463 return process_error_message(error_message)
Michael Walsh2c687e92018-05-09 11:47:56 -0500464
465
Patrick Williams20f38712022-12-08 06:18:26 -0600466def valid_list(
467 var_value,
468 valid_values=[],
469 invalid_values=[],
470 required_values=[],
471 fail_on_empty=False,
472 var_name=None,
473):
Michael Walsh2c687e92018-05-09 11:47:56 -0500474 r"""
Michael Walshda20f842019-10-16 11:36:40 -0500475 The variable value is valid if it is a list where each entry can be found in the valid_values list or if
476 none of its values can be found in the invalid_values list or if all of the values in the required_values
477 list can be found in var_value.
Michael Walsh35026be2019-08-14 17:13:39 -0500478
Michael Walshda20f842019-10-16 11:36:40 -0500479 The caller may only specify one of these 3 arguments: valid_values, invalid_values, required_values.
Michael Walsh2c687e92018-05-09 11:47:56 -0500480
Michael Walsh018e25f2019-08-01 11:27:12 -0500481 Description of argument(s):
482 var_value The value being validated.
Michael Walshda20f842019-10-16 11:36:40 -0500483 valid_values A list of valid values. Each element in the var_value list must be equal
484 to one of these values to be considered valid.
485 invalid_values A list of invalid values. If any element in var_value is equal to any of
486 the values in this argument, var_value is considered invalid.
487 required_values Every value in required_values must be found in var_value. Otherwise,
488 var_value is considered invalid.
489 fail_on_empty Indicates that an empty list for the variable value should be considered
490 an error.
Michael Walshca193992018-08-02 17:20:00 -0500491 """
492
493 error_message = ""
Michael Walsh018e25f2019-08-01 11:27:12 -0500494
Michael Walsh35026be2019-08-14 17:13:39 -0500495 # Validate this function's arguments.
Patrick Williams20f38712022-12-08 06:18:26 -0600496 if not (
497 bool(len(valid_values))
498 ^ bool(len(invalid_values))
499 ^ bool(len(required_values))
500 ):
Michael Walsh35026be2019-08-14 17:13:39 -0500501 error_message += "Programmer error - You must provide only one of the"
502 error_message += " following: valid_values, invalid_values,"
503 error_message += " required_values.\n"
Michael Walshda20f842019-10-16 11:36:40 -0500504 error_message += gp.sprint_var(invalid_values, gp.show_type())
505 error_message += gp.sprint_var(valid_values, gp.show_type())
506 error_message += gp.sprint_var(required_values, gp.show_type())
Michael Walsh35026be2019-08-14 17:13:39 -0500507 return process_error_message(error_message)
508
Michael Walsh018e25f2019-08-01 11:27:12 -0500509 if type(var_value) is not list:
Michael Walsh89eff542019-09-13 16:24:51 -0500510 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500511 error_message = valid_type(var_value, list, var_name=var_name)
512 if error_message:
513 return process_error_message(error_message)
514
515 if fail_on_empty and len(var_value) == 0:
Michael Walsh89eff542019-09-13 16:24:51 -0500516 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500517 error_message += "Invalid empty list:\n"
518 error_message += gp.sprint_varx(var_name, var_value, gp.show_type())
519 return process_error_message(error_message)
Michael Walshca193992018-08-02 17:20:00 -0500520
Michael Walsh35026be2019-08-14 17:13:39 -0500521 if len(required_values):
522 found_error = 0
523 display_required_values = list(required_values)
524 for ix in range(0, len(required_values)):
525 if required_values[ix] not in var_value:
526 found_error = 1
Patrick Williams20f38712022-12-08 06:18:26 -0600527 display_required_values[ix] = (
Michael Walsh35026be2019-08-14 17:13:39 -0500528 str(display_required_values[ix]) + "*"
Patrick Williams20f38712022-12-08 06:18:26 -0600529 )
Michael Walsh35026be2019-08-14 17:13:39 -0500530 if found_error:
Michael Walsh89eff542019-09-13 16:24:51 -0500531 var_name = get_var_name(var_name)
Michael Walsh35026be2019-08-14 17:13:39 -0500532 error_message += "The following list is invalid:\n"
Patrick Williams20f38712022-12-08 06:18:26 -0600533 error_message += gp.sprint_varx(
534 var_name, var_value, gp.blank() | gp.show_type()
535 )
Michael Walsh35026be2019-08-14 17:13:39 -0500536 error_message += "\n"
537 error_message += "Because some of the values in the "
538 error_message += "required_values list are not present (see"
Patrick Williams20f38712022-12-08 06:18:26 -0600539 error_message += ' entries marked with "*"):\n'
Michael Walsh35026be2019-08-14 17:13:39 -0500540 error_message += "\n"
Patrick Williams20f38712022-12-08 06:18:26 -0600541 error_message += gp.sprint_varx(
542 "required_values",
543 display_required_values,
544 gp.blank() | gp.show_type(),
545 )
Michael Walsh35026be2019-08-14 17:13:39 -0500546 error_message += "\n"
547
548 return process_error_message(error_message)
549
550 if len(invalid_values):
551 found_error = 0
552 display_var_value = list(var_value)
553 for ix in range(0, len(var_value)):
554 if var_value[ix] in invalid_values:
555 found_error = 1
556 display_var_value[ix] = str(var_value[ix]) + "*"
557
558 if found_error:
Michael Walsh89eff542019-09-13 16:24:51 -0500559 var_name = get_var_name(var_name)
Michael Walsh35026be2019-08-14 17:13:39 -0500560 error_message += "The following list is invalid (see entries"
Patrick Williams20f38712022-12-08 06:18:26 -0600561 error_message += ' marked with "*"):\n'
562 error_message += gp.sprint_varx(
563 var_name, display_var_value, gp.blank() | gp.show_type()
564 )
Michael Walsh35026be2019-08-14 17:13:39 -0500565 error_message += "\n"
566 error_message += gp.sprint_var(invalid_values, gp.show_type())
567 return process_error_message(error_message)
568
Michael Walshca193992018-08-02 17:20:00 -0500569 found_error = 0
570 display_var_value = list(var_value)
571 for ix in range(0, len(var_value)):
572 if var_value[ix] not in valid_values:
573 found_error = 1
Michael Walsh35026be2019-08-14 17:13:39 -0500574 display_var_value[ix] = str(var_value[ix]) + "*"
Michael Walshca193992018-08-02 17:20:00 -0500575
576 if found_error:
Michael Walsh89eff542019-09-13 16:24:51 -0500577 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500578 error_message += "The following list is invalid (see entries marked"
Patrick Williams20f38712022-12-08 06:18:26 -0600579 error_message += ' with "*"):\n'
580 error_message += gp.sprint_varx(
581 var_name, display_var_value, gp.blank() | gp.show_type()
582 )
Michael Walsh018e25f2019-08-01 11:27:12 -0500583 error_message += "\n"
Michael Walsh35026be2019-08-14 17:13:39 -0500584 error_message += gp.sprint_var(valid_values, gp.show_type())
Michael Walsh018e25f2019-08-01 11:27:12 -0500585 return process_error_message(error_message)
Michael Walshca193992018-08-02 17:20:00 -0500586
Michael Walshca193992018-08-02 17:20:00 -0500587 return process_error_message(error_message)
Michael Walsh7ac5fd82018-10-11 16:58:49 -0500588
589
Patrick Williams20f38712022-12-08 06:18:26 -0600590def valid_dict(
591 var_value,
592 required_keys=[],
593 valid_values={},
594 invalid_values={},
595 var_name=None,
596):
Michael Walsh7ac5fd82018-10-11 16:58:49 -0500597 r"""
Michael Walsh53902dd2020-02-20 14:41:56 -0600598 The dictionary variable value is valid if it contains all required keys and each entry passes the
599 valid_value() call.
600
601 Examples:
602 person_record = {'last_name': 'Jones', 'first_name': 'John'}
603 valid_values = {'last_name': ['Doe', 'Jones', 'Johnson'], 'first_name': ['John', 'Mary']}
604 invalid_values = {'last_name': ['Manson', 'Hitler', 'Presley'], 'first_name': ['Mickey', 'Goofy']}
605
606 valid_dict(person_record, valid_values=valid_values)
607 valid_dict(person_record, invalid_values=invalid_values)
Michael Walsh7ac5fd82018-10-11 16:58:49 -0500608
Michael Walsh018e25f2019-08-01 11:27:12 -0500609 Description of argument(s):
610 var_value The value being validated.
Michael Walshda20f842019-10-16 11:36:40 -0500611 required_keys A list of keys which must be found in the dictionary for it to be
612 considered valid.
Michael Walsh53902dd2020-02-20 14:41:56 -0600613 valid_values A dictionary whose entries correspond to the entries in var_value. Each
George Keishing82068512020-02-27 08:46:34 -0600614 value in valid_values is itself a valid_values list for the corresponding
Michael Walsh53902dd2020-02-20 14:41:56 -0600615 value in var_value. For any var_value[key] to be considered valid, its
616 value must be found in valid_values[key].
617
618 invalid_values A dictionary whose entries correspond to the entries in var_value. Each
619 value in invalid_values is itself an invalid_values list for the
Michael Walsh81384422020-03-20 15:11:15 -0500620 corresponding value in var_value. For any var_value[key] to be
621 considered valid, its value must NOT be found in invalid_values[key].
Michael Walsh7ac5fd82018-10-11 16:58:49 -0500622 """
623
624 error_message = ""
Michael Walsh018e25f2019-08-01 11:27:12 -0500625 missing_keys = list(set(required_keys) - set(var_value.keys()))
626 if len(missing_keys) > 0:
Michael Walsh89eff542019-09-13 16:24:51 -0500627 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500628 error_message += "The following dictionary is invalid because it is"
629 error_message += " missing required keys:\n"
Patrick Williams20f38712022-12-08 06:18:26 -0600630 error_message += gp.sprint_varx(
631 var_name, var_value, gp.blank() | gp.show_type()
632 )
Michael Walsh018e25f2019-08-01 11:27:12 -0500633 error_message += "\n"
Michael Walsh35026be2019-08-14 17:13:39 -0500634 error_message += gp.sprint_var(missing_keys, gp.show_type())
Michael Walsh53902dd2020-02-20 14:41:56 -0600635 return process_error_message(error_message)
636
637 var_name = get_var_name(var_name)
638 if len(valid_values):
639 keys = valid_values.keys()
Patrick Williams20f38712022-12-08 06:18:26 -0600640 error_message = valid_dict(
641 var_value, required_keys=keys, var_name=var_name
642 )
Michael Walsh53902dd2020-02-20 14:41:56 -0600643 if error_message:
644 return process_error_message(error_message)
645 for key, value in valid_values.items():
646 key_name = " [" + key + "]"
Patrick Williams20f38712022-12-08 06:18:26 -0600647 sub_error_message = valid_value(
648 var_value[key], valid_values=value, var_name=key_name
649 )
Michael Walsh53902dd2020-02-20 14:41:56 -0600650 if sub_error_message:
Patrick Williams20f38712022-12-08 06:18:26 -0600651 error_message += (
652 "The following dictionary is invalid because one of its"
653 " entries is invalid:\n"
654 )
655 error_message += gp.sprint_varx(
656 var_name, var_value, gp.blank() | gp.show_type()
657 )
Michael Walsh53902dd2020-02-20 14:41:56 -0600658 error_message += "\n"
659 error_message += sub_error_message
660 return process_error_message(error_message)
661
662 for key, value in invalid_values.items():
663 if key not in var_value:
664 continue
665 key_name = " [" + key + "]"
Patrick Williams20f38712022-12-08 06:18:26 -0600666 sub_error_message = valid_value(
667 var_value[key], invalid_values=value, var_name=key_name
668 )
Michael Walsh53902dd2020-02-20 14:41:56 -0600669 if sub_error_message:
Patrick Williams20f38712022-12-08 06:18:26 -0600670 error_message += (
671 "The following dictionary is invalid because one of its"
672 " entries is invalid:\n"
673 )
674 error_message += gp.sprint_varx(
675 var_name, var_value, gp.blank() | gp.show_type()
676 )
Michael Walsh53902dd2020-02-20 14:41:56 -0600677 error_message += "\n"
678 error_message += sub_error_message
679 return process_error_message(error_message)
680
Michael Walsh7ac5fd82018-10-11 16:58:49 -0500681 return process_error_message(error_message)
Michael Walsh018e25f2019-08-01 11:27:12 -0500682
683
Michael Walsh89eff542019-09-13 16:24:51 -0500684def valid_program(var_value, var_name=None):
Michael Walshbe3a8152019-08-20 16:38:19 -0500685 r"""
Michael Walshda20f842019-10-16 11:36:40 -0500686 The variable value is valid if it contains the name of a program which can be located using the "which"
687 command.
Michael Walshbe3a8152019-08-20 16:38:19 -0500688
689 Description of argument(s):
690 var_value The value being validated.
691 """
692
693 error_message = ""
Patrick Williams20f38712022-12-08 06:18:26 -0600694 rc, out_buf = gc.shell_cmd(
695 "which " + var_value, quiet=1, show_err=0, ignore_err=1
696 )
Michael Walshbe3a8152019-08-20 16:38:19 -0500697 if rc:
Michael Walsh89eff542019-09-13 16:24:51 -0500698 var_name = get_var_name(var_name)
Michael Walshbe3a8152019-08-20 16:38:19 -0500699 error_message += "The following required program could not be found"
700 error_message += " using the $PATH environment variable:\n"
Michael Walsh00244342019-08-29 10:35:50 -0500701 error_message += gp.sprint_varx(var_name, var_value, gp.blank())
Michael Walshbe3a8152019-08-20 16:38:19 -0500702 PATH = os.environ.get("PATH", "").split(":")
703 error_message += "\n"
704 error_message += gp.sprint_var(PATH)
705 return process_error_message(error_message)
706
707
Michael Walsh89eff542019-09-13 16:24:51 -0500708def valid_length(var_value, min_length=None, max_length=None, var_name=None):
Michael Walshb9d8dfd2019-09-11 11:11:12 -0500709 r"""
Michael Walshda20f842019-10-16 11:36:40 -0500710 The variable value is valid if it is an object (e.g. list, dictionary) whose length is within the
711 specified range.
Michael Walshb9d8dfd2019-09-11 11:11:12 -0500712
713 Description of argument(s):
714 var_value The value being validated.
Michael Walshda20f842019-10-16 11:36:40 -0500715 min_length The minimum length of the object. If not None, the length of var_value
716 must be greater than or equal to min_length.
717 max_length The maximum length of the object. If not None, the length of var_value
718 must be less than or equal to min_length.
Michael Walshb9d8dfd2019-09-11 11:11:12 -0500719 """
720
721 error_message = ""
722 length = len(var_value)
723 error_message = valid_range(length, min_length, max_length)
724 if error_message:
Michael Walsh89eff542019-09-13 16:24:51 -0500725 var_name = get_var_name(var_name)
Michael Walshb9d8dfd2019-09-11 11:11:12 -0500726 error_message = "The length of the following object is not within the"
727 error_message += " expected range:\n"
Michael Walshda20f842019-10-16 11:36:40 -0500728 error_message += gp.sprint_vars(min_length, max_length)
Michael Walshb9d8dfd2019-09-11 11:11:12 -0500729 error_message += gp.sprint_var(length)
730 error_message += gp.sprint_varx(var_name, var_value, gp.blank())
731 error_message += "\n"
Michael Walshb9d8dfd2019-09-11 11:11:12 -0500732 return process_error_message(error_message)
733
734 return process_error_message(error_message)
735
736
Michael Walsh018e25f2019-08-01 11:27:12 -0500737# Modify selected function docstrings by adding headers/footers.
738
739func_names = [
Patrick Williams20f38712022-12-08 06:18:26 -0600740 "valid_type",
741 "valid_value",
742 "valid_range",
743 "valid_integer",
744 "valid_dir_path",
745 "valid_file_path",
746 "valid_path",
747 "valid_list",
748 "valid_dict",
749 "valid_program",
750 "valid_length",
751 "valid_float",
752 "valid_date_time",
Michael Walsh018e25f2019-08-01 11:27:12 -0500753]
754
755raw_doc_strings = {}
756
757for func_name in func_names:
758 cmd_buf = "raw_doc_strings['" + func_name + "'] = " + func_name
759 cmd_buf += ".__doc__"
760 exec(cmd_buf)
761 cmd_buf = func_name + ".__doc__ = docstring_header + " + func_name
Patrick Williams20f38712022-12-08 06:18:26 -0600762 cmd_buf += '.__doc__.rstrip(" \\n") + additional_args_docstring_footer'
Michael Walsh018e25f2019-08-01 11:27:12 -0500763 exec(cmd_buf)