blob: 57c1a1d4f2e159ea267b98d4591bcbfd24c01507 [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
Michael Walsh78bdfdd2017-01-10 11:27:30 -06007import os
Michael Walsh7423c012016-10-04 10:27:21 -05008import gen_print as gp
Michael Walshbe3a8152019-08-20 16:38:19 -05009import gen_cmd as gc
Michael Walsh018e25f2019-08-01 11:27:12 -050010import func_args as fa
Michael Walsh8333a182019-10-22 16:19:00 -050011import datetime
Michael Walsh7423c012016-10-04 10:27:21 -050012
Michael Walshe23b5ad2018-06-01 14:54:35 -050013exit_on_error = False
14
15
16def set_exit_on_error(value):
17 r"""
18 Set the exit_on_error value to either True or False.
19
Michael Walshda20f842019-10-16 11:36:40 -050020 If exit_on_error is set, validation functions like valid_value() will exit the program on error instead
21 of returning False.
Michael Walshe23b5ad2018-06-01 14:54:35 -050022
23 Description of argument(s):
24 value Value to set global exit_on_error to.
25 """
26
27 global exit_on_error
28 exit_on_error = value
29
30
Michael Walsh89eff542019-09-13 16:24:51 -050031def get_var_name(var_name):
Michael Walshe23b5ad2018-06-01 14:54:35 -050032 r"""
Michael Walshda20f842019-10-16 11:36:40 -050033 If var_name is not None, simply return its value. Otherwise, get the variable name of the first argument
34 used to call the validation function (e.g. valid, valid_integer, etc.) and return it.
Michael Walshe23b5ad2018-06-01 14:54:35 -050035
36 This function is designed solely for use by other functions in this file.
37
38 Example:
39
40 A programmer codes this:
41
42 valid_value(last_name)
43
44 Which results in the following call stack:
45
46 valid_value(last_name)
Michael Walsh018e25f2019-08-01 11:27:12 -050047 -> get_var_name(var_name)
Michael Walshe23b5ad2018-06-01 14:54:35 -050048
49 In this example, this function will return "last_name".
50
51 Example:
52
Michael Walsh018e25f2019-08-01 11:27:12 -050053 err_msg = valid_value(last_name, var_name="some_other_name")
Michael Walshe23b5ad2018-06-01 14:54:35 -050054
55 Which results in the following call stack:
56
Michael Walsh018e25f2019-08-01 11:27:12 -050057 valid_value(var_value, var_name="some_other_name")
Michael Walshe23b5ad2018-06-01 14:54:35 -050058 -> get_var_name(var_name)
59
60 In this example, this function will return "some_other_name".
61
62 Description of argument(s):
63 var_name The name of the variable.
64 """
65
Michael Walsh89eff542019-09-13 16:24:51 -050066 return var_name or gp.get_arg_name(0, 1, stack_frame_ix=3)
Michael Walshe23b5ad2018-06-01 14:54:35 -050067
68
69def process_error_message(error_message):
70 r"""
Michael Walsh018e25f2019-08-01 11:27:12 -050071 Process the error_message in the manner described below.
Michael Walshe23b5ad2018-06-01 14:54:35 -050072
Michael Walsh018e25f2019-08-01 11:27:12 -050073 This function is designed solely for use by other functions in this file.
74
75 NOTE: A blank error_message means that there is no error.
76
Michael Walshda20f842019-10-16 11:36:40 -050077 For the following explanations, assume the caller of this function is a function with the following
78 definition:
Michael Walsh89eff542019-09-13 16:24:51 -050079 valid_value(var_value, valid_values=[], invalid_values=[], var_name=None):
Michael Walsh018e25f2019-08-01 11:27:12 -050080
Michael Walshda20f842019-10-16 11:36:40 -050081 If the user of valid_value() is assigning the valid_value() return value to a variable,
82 process_error_message() will simply return the error_message. This mode of usage is illustrated by the
83 following example:
Michael Walsh018e25f2019-08-01 11:27:12 -050084
85 error_message = valid_value(var1)
86
Michael Walshda20f842019-10-16 11:36:40 -050087 This mode is useful for callers who wish to validate a variable and then decide for themselves what to do
88 with the error_message (e.g. raise(error_message), BuiltIn().fail(error_message), etc.).
Michael Walsh018e25f2019-08-01 11:27:12 -050089
Michael Walshda20f842019-10-16 11:36:40 -050090 If the user of valid_value() is NOT assigning the valid_value() return value to a variable,
91 process_error_message() will behave as follows.
Michael Walsh018e25f2019-08-01 11:27:12 -050092
Michael Walshda20f842019-10-16 11:36:40 -050093 First, if error_message is non-blank, it will be printed to stderr via a call to
94 gp.print_error_report(error_message).
Michael Walsh018e25f2019-08-01 11:27:12 -050095
96 If exit_on_error is set:
97 - If the error_message is blank, simply return.
Michael Walshda20f842019-10-16 11:36:40 -050098 - If the error_message is non-blank, exit the program with a return code of 1.
Michael Walsh018e25f2019-08-01 11:27:12 -050099
100 If exit_on_error is NOT set:
101 - If the error_message is blank, return True.
102 - If the error_message is non-blank, return False.
Michael Walshe23b5ad2018-06-01 14:54:35 -0500103
104 Description of argument(s):
105 error_message An error message.
106 """
107
Michael Walshda20f842019-10-16 11:36:40 -0500108 # Determine whether the caller's caller is assigning the result to a variable.
Michael Walsh018e25f2019-08-01 11:27:12 -0500109 l_value = gp.get_arg_name(None, -1, stack_frame_ix=3)
110 if l_value:
111 return error_message
112
Michael Walshe23b5ad2018-06-01 14:54:35 -0500113 if error_message == "":
Michael Walsh018e25f2019-08-01 11:27:12 -0500114 if exit_on_error:
115 return
Michael Walshe23b5ad2018-06-01 14:54:35 -0500116 return True
117
Michael Walsh018e25f2019-08-01 11:27:12 -0500118 gp.print_error_report(error_message, stack_frame_ix=4)
Michael Walshe23b5ad2018-06-01 14:54:35 -0500119 if exit_on_error:
Michael Walsh10a4d982018-10-30 13:07:46 -0500120 exit(1)
Michael Walshe23b5ad2018-06-01 14:54:35 -0500121 return False
122
Michael Walsh7423c012016-10-04 10:27:21 -0500123
Michael Walshda20f842019-10-16 11:36:40 -0500124# Note to programmers: All of the validation functions in this module should follow the same basic template:
Michael Walsh89eff542019-09-13 16:24:51 -0500125# def valid_value(var_value, var1, var2, varn, var_name=None):
Michael Walsh018e25f2019-08-01 11:27:12 -0500126#
127# error_message = ""
128# if not valid:
Michael Walsh89eff542019-09-13 16:24:51 -0500129# var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500130# error_message += "The following variable is invalid because...:\n"
131# error_message += gp.sprint_varx(var_name, var_value, gp.blank())
132#
133# return process_error_message(error_message)
Michael Walsh7423c012016-10-04 10:27:21 -0500134
Michael Walsh018e25f2019-08-01 11:27:12 -0500135
Michael Walshda20f842019-10-16 11:36:40 -0500136# The docstring header and footer will be added to each validation function's existing docstring.
Michael Walsh018e25f2019-08-01 11:27:12 -0500137docstring_header = \
138 r"""
139 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
Michael Walsh018e25f2019-08-01 11:27:12 -0500146additional_args_docstring_footer = \
147 r"""
Michael Walshda20f842019-10-16 11:36:40 -0500148 var_name The name of the variable whose value is passed in var_value. For the
149 general case, this argument is unnecessary as this function can figure
150 out the var_name. This is provided for Robot callers in which case, this
151 function lacks the ability to determine the variable name.
Michael Walsh018e25f2019-08-01 11:27:12 -0500152 """
153
154
Michael Walsh89eff542019-09-13 16:24:51 -0500155def valid_type(var_value, required_type, var_name=None):
Michael Walsh018e25f2019-08-01 11:27:12 -0500156 r"""
157 The variable value is valid if it is of the required type.
158
159 Examples:
160
161 valid_type(var1, int)
162
163 valid_type(var1, (list, dict))
164
165 Description of argument(s):
166 var_value The value being validated.
Michael Walshda20f842019-10-16 11:36:40 -0500167 required_type A type or a tuple of types (e.g. str, int, etc.).
Michael Walsh018e25f2019-08-01 11:27:12 -0500168 """
169
170 error_message = ""
171 if type(required_type) is tuple:
172 if type(var_value) in required_type:
173 return process_error_message(error_message)
174 else:
175 if type(var_value) is required_type:
176 return process_error_message(error_message)
177
178 # If we get to this point, the validation has failed.
Michael Walsh89eff542019-09-13 16:24:51 -0500179 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500180 error_message += "Invalid variable type:\n"
181 error_message += gp.sprint_varx(var_name, var_value,
182 gp.blank() | gp.show_type())
183 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
191 r"""
Michael Walshda20f842019-10-16 11:36:40 -0500192 The variable value is valid if it is either contained in the valid_values list or if it is NOT contained
193 in the invalid_values list. If the caller specifies nothing for either of these 2 arguments,
194 invalid_values will be initialized to ['', None]. This is a good way to fail on variables which contain
195 blank values.
Michael Walsh018e25f2019-08-01 11:27:12 -0500196
197 It is illegal to specify both valid_values and invalid values.
198
199 Example:
200
201 var1 = ''
202 valid_value(var1)
203
Michael Walshda20f842019-10-16 11:36:40 -0500204 This code would fail because var1 is blank and the default value for invalid_values is ['', None].
Michael Walsh018e25f2019-08-01 11:27:12 -0500205
206 Example:
207 var1 = 'yes'
208 valid_value(var1, valid_values=['yes', 'true'])
209
210 This code would pass.
211
212 Description of argument(s):
213 var_value The value being validated.
Michael Walshda20f842019-10-16 11:36:40 -0500214 valid_values A list of valid values. The variable value must be equal to one of these
215 values to be considered valid.
216 invalid_values A list of invalid values. If the variable value is equal to any of
217 these, it is considered invalid.
Michael Walsh018e25f2019-08-01 11:27:12 -0500218 """
219
Michael Walshbec416d2016-11-10 08:54:52 -0600220 error_message = ""
Michael Walshbec416d2016-11-10 08:54:52 -0600221
Michael Walshe23b5ad2018-06-01 14:54:35 -0500222 # Validate this function's arguments.
Michael Walsh7423c012016-10-04 10:27:21 -0500223 len_valid_values = len(valid_values)
224 len_invalid_values = len(invalid_values)
225 if len_valid_values > 0 and len_invalid_values > 0:
Michael Walsh018e25f2019-08-01 11:27:12 -0500226 error_message += "Programmer error - You must provide either an"
227 error_message += " invalid_values list or a valid_values"
228 error_message += " list but NOT both:\n"
229 error_message += gp.sprint_var(invalid_values)
230 error_message += gp.sprint_var(valid_values)
231 return process_error_message(error_message)
Michael Walsh7423c012016-10-04 10:27:21 -0500232
Michael Walsh0f5fa532020-02-13 10:51:40 -0600233 error_message = valid_type(valid_values, list, var_name='valid_values')
234 if error_message:
235 return process_error_message(error_message)
236
237 error_message = valid_type(invalid_values, list, var_name='invalid_values')
238 if error_message:
239 return process_error_message(error_message)
240
Michael Walsh7423c012016-10-04 10:27:21 -0500241 if len_valid_values > 0:
242 # Processing the valid_values list.
243 if var_value in valid_values:
Michael Walsh018e25f2019-08-01 11:27:12 -0500244 return process_error_message(error_message)
Michael Walsh89eff542019-09-13 16:24:51 -0500245 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500246 error_message += "Invalid variable value:\n"
247 error_message += gp.sprint_varx(var_name, var_value,
248 gp.blank() | gp.verbose()
249 | gp.show_type())
250 error_message += "\n"
251 error_message += "It must be one of the following values:\n"
252 error_message += "\n"
253 error_message += gp.sprint_var(valid_values,
254 gp.blank() | gp.show_type())
255 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"
267 error_message += gp.sprint_varx(var_name, var_value,
268 gp.blank() | gp.verbose()
269 | gp.show_type())
270 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"
273 error_message += gp.sprint_var(invalid_values,
274 gp.blank() | gp.show_type())
Michael Walshe23b5ad2018-06-01 14:54:35 -0500275 return process_error_message(error_message)
Michael Walshbec416d2016-11-10 08:54:52 -0600276
Michael Walshbec416d2016-11-10 08:54:52 -0600277
Michael Walsh89eff542019-09-13 16:24:51 -0500278def valid_range(var_value, lower=None, upper=None, var_name=None):
Michael Walshbec416d2016-11-10 08:54:52 -0600279 r"""
Michael Walsh018e25f2019-08-01 11:27:12 -0500280 The variable value is valid if it is within the specified range.
Michael Walshbec416d2016-11-10 08:54:52 -0600281
Michael Walshda20f842019-10-16 11:36:40 -0500282 This function can be used with any type of operands where they can have a greater than/less than
283 relationship to each other (e.g. int, float, str).
Michael Walsh018e25f2019-08-01 11:27:12 -0500284
285 Description of argument(s):
Michael Walshbec416d2016-11-10 08:54:52 -0600286 var_value The value being validated.
Michael Walshda20f842019-10-16 11:36:40 -0500287 lower The lower end of the range. If not None, the var_value must be greater
288 than or equal to lower.
289 upper The upper end of the range. If not None, the var_value must be less than
290 or equal to upper.
Michael Walshbec416d2016-11-10 08:54:52 -0600291 """
292
Michael Walshbec416d2016-11-10 08:54:52 -0600293 error_message = ""
Michael Walshb9d8dfd2019-09-11 11:11:12 -0500294 if lower is None and upper is None:
Michael Walsh018e25f2019-08-01 11:27:12 -0500295 return process_error_message(error_message)
Michael Walshb9d8dfd2019-09-11 11:11:12 -0500296 if lower is None and var_value <= upper:
Michael Walsh018e25f2019-08-01 11:27:12 -0500297 return process_error_message(error_message)
Michael Walshb9d8dfd2019-09-11 11:11:12 -0500298 if upper is None and var_value >= lower:
Michael Walsh018e25f2019-08-01 11:27:12 -0500299 return process_error_message(error_message)
Tony Lee2f88a812020-04-06 15:00:01 +0800300 if lower is not None and upper is not None:
Michael Walsh018e25f2019-08-01 11:27:12 -0500301 if lower > upper:
Michael Walsh89eff542019-09-13 16:24:51 -0500302 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500303 error_message += "Programmer error - the lower value is greater"
304 error_message += " than the upper value:\n"
305 error_message += gp.sprint_vars(lower, upper, fmt=gp.show_type())
306 return process_error_message(error_message)
307 if lower <= var_value <= upper:
308 return process_error_message(error_message)
Michael Walshbec416d2016-11-10 08:54:52 -0600309
Michael Walsh89eff542019-09-13 16:24:51 -0500310 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500311 error_message += "The following variable is not within the expected"
312 error_message += " range:\n"
313 error_message += gp.sprint_varx(var_name, var_value, gp.show_type())
314 error_message += "\n"
315 error_message += "range:\n"
316 error_message += gp.sprint_vars(lower, upper, fmt=gp.show_type(), indent=2)
Michael Walshe23b5ad2018-06-01 14:54:35 -0500317 return process_error_message(error_message)
Michael Walsh7423c012016-10-04 10:27:21 -0500318
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600319
Michael Walsh89eff542019-09-13 16:24:51 -0500320def valid_integer(var_value, lower=None, upper=None, var_name=None):
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600321 r"""
Michael Walshda20f842019-10-16 11:36:40 -0500322 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 -0600323
Michael Walshda20f842019-10-16 11:36:40 -0500324 This function also calls valid_range to make sure the integer value is within the specified range (if
325 any).
Michael Walsh018e25f2019-08-01 11:27:12 -0500326
327 Description of argument(s):
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600328 var_value The value being validated.
Michael Walshda20f842019-10-16 11:36:40 -0500329 lower The lower end of the range. If not None, the var_value must be greater
330 than or equal to lower.
331 upper The upper end of the range. If not None, the var_value must be less than
332 or equal to upper.
Michael Walsh018e25f2019-08-01 11:27:12 -0500333 """
334
335 error_message = ""
Michael Walsh89eff542019-09-13 16:24:51 -0500336 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500337 try:
338 var_value = int(str(var_value), 0)
339 except ValueError:
340 error_message += "Invalid integer value:\n"
341 error_message += gp.sprint_varx(var_name, var_value,
342 gp.blank() | gp.show_type())
343 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"
376 error_message += gp.sprint_varx(var_name, var_value,
377 gp.blank() | gp.show_type())
378 return process_error_message(error_message)
379
380 # Check the range (if any).
381 if lower:
382 lower = float(str(lower))
383 if upper:
384 upper = float(str(upper))
385 error_message = valid_range(var_value, lower, upper, var_name=var_name)
386
387 return process_error_message(error_message)
388
389
390def valid_date_time(var_value, var_name=None):
391 r"""
392 The variable value is valid if it can be interpreted as a date/time (e.g. "14:49:49.981", "tomorrow",
393 etc.) by the linux date command.
394
395 Description of argument(s):
396 var_value The value being validated.
397 """
398
399 error_message = ""
400 rc, out_buf = gc.shell_cmd("date -d '" + str(var_value) + "'", quiet=1, show_err=0, ignore_err=1)
401 if rc:
402 var_name = get_var_name(var_name)
403 error_message += "Invalid date/time value:\n"
404 error_message += gp.sprint_varx(var_name, var_value,
405 gp.blank() | gp.show_type())
406 return process_error_message(error_message)
407
408 return process_error_message(error_message)
409
410
Michael Walsh89eff542019-09-13 16:24:51 -0500411def valid_dir_path(var_value, var_name=None):
Michael Walsh018e25f2019-08-01 11:27:12 -0500412 r"""
Michael Walshda20f842019-10-16 11:36:40 -0500413 The variable value is valid if it contains the path of an existing directory.
Michael Walsh018e25f2019-08-01 11:27:12 -0500414
415 Description of argument(s):
416 var_value The value being validated.
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600417 """
418
419 error_message = ""
420 if not os.path.isdir(str(var_value)):
Michael Walsh89eff542019-09-13 16:24:51 -0500421 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500422 error_message += "The following directory does not exist:\n"
Michael Walsh00244342019-08-29 10:35:50 -0500423 error_message += gp.sprint_varx(var_name, var_value, gp.blank())
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600424
Michael Walshe23b5ad2018-06-01 14:54:35 -0500425 return process_error_message(error_message)
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600426
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600427
Michael Walsh89eff542019-09-13 16:24:51 -0500428def valid_file_path(var_value, var_name=None):
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600429 r"""
Michael Walsh018e25f2019-08-01 11:27:12 -0500430 The variable value is valid if it contains the path of an existing file.
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600431
Michael Walsh018e25f2019-08-01 11:27:12 -0500432 Description of argument(s):
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600433 var_value The value being validated.
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600434 """
435
436 error_message = ""
437 if not os.path.isfile(str(var_value)):
Michael Walsh89eff542019-09-13 16:24:51 -0500438 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500439 error_message += "The following file does not exist:\n"
Michael Walsh00244342019-08-29 10:35:50 -0500440 error_message += gp.sprint_varx(var_name, var_value, gp.blank())
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600441
Michael Walshe23b5ad2018-06-01 14:54:35 -0500442 return process_error_message(error_message)
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600443
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600444
Michael Walsh89eff542019-09-13 16:24:51 -0500445def valid_path(var_value, var_name=None):
Michael Walshe23b5ad2018-06-01 14:54:35 -0500446 r"""
Michael Walshda20f842019-10-16 11:36:40 -0500447 The variable value is valid if it contains the path of an existing file or directory.
Michael Walshe23b5ad2018-06-01 14:54:35 -0500448
Michael Walsh018e25f2019-08-01 11:27:12 -0500449 Description of argument(s):
Michael Walshe23b5ad2018-06-01 14:54:35 -0500450 var_value The value being validated.
Michael Walshe23b5ad2018-06-01 14:54:35 -0500451 """
452
453 error_message = ""
454 if not (os.path.isfile(str(var_value)) or os.path.isdir(str(var_value))):
Michael Walsh89eff542019-09-13 16:24:51 -0500455 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500456 error_message += "Invalid path (file or directory does not exist):\n"
Michael Walsh00244342019-08-29 10:35:50 -0500457 error_message += gp.sprint_varx(var_name, var_value, gp.blank())
Michael Walshe23b5ad2018-06-01 14:54:35 -0500458
Michael Walshe23b5ad2018-06-01 14:54:35 -0500459 return process_error_message(error_message)
Michael Walsh2c687e92018-05-09 11:47:56 -0500460
461
Michael Walsh35026be2019-08-14 17:13:39 -0500462def valid_list(var_value, valid_values=[], invalid_values=[],
Michael Walsh89eff542019-09-13 16:24:51 -0500463 required_values=[], fail_on_empty=False, var_name=None):
Michael Walsh2c687e92018-05-09 11:47:56 -0500464 r"""
Michael Walshda20f842019-10-16 11:36:40 -0500465 The variable value is valid if it is a list where each entry can be found in the valid_values list or if
466 none of its values can be found in the invalid_values list or if all of the values in the required_values
467 list can be found in var_value.
Michael Walsh35026be2019-08-14 17:13:39 -0500468
Michael Walshda20f842019-10-16 11:36:40 -0500469 The caller may only specify one of these 3 arguments: valid_values, invalid_values, required_values.
Michael Walsh2c687e92018-05-09 11:47:56 -0500470
Michael Walsh018e25f2019-08-01 11:27:12 -0500471 Description of argument(s):
472 var_value The value being validated.
Michael Walshda20f842019-10-16 11:36:40 -0500473 valid_values A list of valid values. Each element in the var_value list must be equal
474 to one of these values to be considered valid.
475 invalid_values A list of invalid values. If any element in var_value is equal to any of
476 the values in this argument, var_value is considered invalid.
477 required_values Every value in required_values must be found in var_value. Otherwise,
478 var_value is considered invalid.
479 fail_on_empty Indicates that an empty list for the variable value should be considered
480 an error.
Michael Walshca193992018-08-02 17:20:00 -0500481 """
482
483 error_message = ""
Michael Walsh018e25f2019-08-01 11:27:12 -0500484
Michael Walsh35026be2019-08-14 17:13:39 -0500485 # Validate this function's arguments.
Michael Walshda20f842019-10-16 11:36:40 -0500486 if not (bool(len(valid_values)) ^ bool(len(invalid_values)) ^ bool(len(required_values))):
Michael Walsh35026be2019-08-14 17:13:39 -0500487 error_message += "Programmer error - You must provide only one of the"
488 error_message += " following: valid_values, invalid_values,"
489 error_message += " required_values.\n"
Michael Walshda20f842019-10-16 11:36:40 -0500490 error_message += gp.sprint_var(invalid_values, gp.show_type())
491 error_message += gp.sprint_var(valid_values, gp.show_type())
492 error_message += gp.sprint_var(required_values, gp.show_type())
Michael Walsh35026be2019-08-14 17:13:39 -0500493 return process_error_message(error_message)
494
Michael Walsh018e25f2019-08-01 11:27:12 -0500495 if type(var_value) is not list:
Michael Walsh89eff542019-09-13 16:24:51 -0500496 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500497 error_message = valid_type(var_value, list, var_name=var_name)
498 if error_message:
499 return process_error_message(error_message)
500
501 if fail_on_empty and len(var_value) == 0:
Michael Walsh89eff542019-09-13 16:24:51 -0500502 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500503 error_message += "Invalid empty list:\n"
504 error_message += gp.sprint_varx(var_name, var_value, gp.show_type())
505 return process_error_message(error_message)
Michael Walshca193992018-08-02 17:20:00 -0500506
Michael Walsh35026be2019-08-14 17:13:39 -0500507 if len(required_values):
508 found_error = 0
509 display_required_values = list(required_values)
510 for ix in range(0, len(required_values)):
511 if required_values[ix] not in var_value:
512 found_error = 1
513 display_required_values[ix] = \
514 str(display_required_values[ix]) + "*"
515 if found_error:
Michael Walsh89eff542019-09-13 16:24:51 -0500516 var_name = get_var_name(var_name)
Michael Walsh35026be2019-08-14 17:13:39 -0500517 error_message += "The following list is invalid:\n"
518 error_message += gp.sprint_varx(var_name, var_value,
519 gp.blank() | gp.show_type())
520 error_message += "\n"
521 error_message += "Because some of the values in the "
522 error_message += "required_values list are not present (see"
523 error_message += " entries marked with \"*\"):\n"
524 error_message += "\n"
525 error_message += gp.sprint_varx('required_values',
526 display_required_values,
527 gp.blank() | gp.show_type())
528 error_message += "\n"
529
530 return process_error_message(error_message)
531
532 if len(invalid_values):
533 found_error = 0
534 display_var_value = list(var_value)
535 for ix in range(0, len(var_value)):
536 if var_value[ix] in invalid_values:
537 found_error = 1
538 display_var_value[ix] = str(var_value[ix]) + "*"
539
540 if found_error:
Michael Walsh89eff542019-09-13 16:24:51 -0500541 var_name = get_var_name(var_name)
Michael Walsh35026be2019-08-14 17:13:39 -0500542 error_message += "The following list is invalid (see entries"
543 error_message += " marked with \"*\"):\n"
544 error_message += gp.sprint_varx(var_name, display_var_value,
545 gp.blank() | gp.show_type())
546 error_message += "\n"
547 error_message += gp.sprint_var(invalid_values, gp.show_type())
548 return process_error_message(error_message)
549
Michael Walshca193992018-08-02 17:20:00 -0500550 found_error = 0
551 display_var_value = list(var_value)
552 for ix in range(0, len(var_value)):
553 if var_value[ix] not in valid_values:
554 found_error = 1
Michael Walsh35026be2019-08-14 17:13:39 -0500555 display_var_value[ix] = str(var_value[ix]) + "*"
Michael Walshca193992018-08-02 17:20:00 -0500556
557 if found_error:
Michael Walsh89eff542019-09-13 16:24:51 -0500558 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500559 error_message += "The following list is invalid (see entries marked"
560 error_message += " with \"*\"):\n"
561 error_message += gp.sprint_varx(var_name, display_var_value,
562 gp.blank() | gp.show_type())
563 error_message += "\n"
Michael Walsh35026be2019-08-14 17:13:39 -0500564 error_message += gp.sprint_var(valid_values, gp.show_type())
Michael Walsh018e25f2019-08-01 11:27:12 -0500565 return process_error_message(error_message)
Michael Walshca193992018-08-02 17:20:00 -0500566
Michael Walshca193992018-08-02 17:20:00 -0500567 return process_error_message(error_message)
Michael Walsh7ac5fd82018-10-11 16:58:49 -0500568
569
Michael Walsh53902dd2020-02-20 14:41:56 -0600570def valid_dict(var_value, required_keys=[], valid_values={}, invalid_values={}, var_name=None):
Michael Walsh7ac5fd82018-10-11 16:58:49 -0500571 r"""
Michael Walsh53902dd2020-02-20 14:41:56 -0600572 The dictionary variable value is valid if it contains all required keys and each entry passes the
573 valid_value() call.
574
575 Examples:
576 person_record = {'last_name': 'Jones', 'first_name': 'John'}
577 valid_values = {'last_name': ['Doe', 'Jones', 'Johnson'], 'first_name': ['John', 'Mary']}
578 invalid_values = {'last_name': ['Manson', 'Hitler', 'Presley'], 'first_name': ['Mickey', 'Goofy']}
579
580 valid_dict(person_record, valid_values=valid_values)
581 valid_dict(person_record, invalid_values=invalid_values)
Michael Walsh7ac5fd82018-10-11 16:58:49 -0500582
Michael Walsh018e25f2019-08-01 11:27:12 -0500583 Description of argument(s):
584 var_value The value being validated.
Michael Walshda20f842019-10-16 11:36:40 -0500585 required_keys A list of keys which must be found in the dictionary for it to be
586 considered valid.
Michael Walsh53902dd2020-02-20 14:41:56 -0600587 valid_values A dictionary whose entries correspond to the entries in var_value. Each
George Keishing82068512020-02-27 08:46:34 -0600588 value in valid_values is itself a valid_values list for the corresponding
Michael Walsh53902dd2020-02-20 14:41:56 -0600589 value in var_value. For any var_value[key] to be considered valid, its
590 value must be found in valid_values[key].
591
592 invalid_values A dictionary whose entries correspond to the entries in var_value. Each
593 value in invalid_values is itself an invalid_values list for the
Michael Walsh81384422020-03-20 15:11:15 -0500594 corresponding value in var_value. For any var_value[key] to be
595 considered valid, its value must NOT be found in invalid_values[key].
Michael Walsh7ac5fd82018-10-11 16:58:49 -0500596 """
597
598 error_message = ""
Michael Walsh018e25f2019-08-01 11:27:12 -0500599 missing_keys = list(set(required_keys) - set(var_value.keys()))
600 if len(missing_keys) > 0:
Michael Walsh89eff542019-09-13 16:24:51 -0500601 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500602 error_message += "The following dictionary is invalid because it is"
603 error_message += " missing required keys:\n"
Michael Walsh53902dd2020-02-20 14:41:56 -0600604 error_message += gp.sprint_varx(var_name, var_value, gp.blank() | gp.show_type())
Michael Walsh018e25f2019-08-01 11:27:12 -0500605 error_message += "\n"
Michael Walsh35026be2019-08-14 17:13:39 -0500606 error_message += gp.sprint_var(missing_keys, gp.show_type())
Michael Walsh53902dd2020-02-20 14:41:56 -0600607 return process_error_message(error_message)
608
609 var_name = get_var_name(var_name)
610 if len(valid_values):
611 keys = valid_values.keys()
612 error_message = valid_dict(var_value, required_keys=keys, var_name=var_name)
613 if error_message:
614 return process_error_message(error_message)
615 for key, value in valid_values.items():
616 key_name = " [" + key + "]"
617 sub_error_message = valid_value(var_value[key], valid_values=value, var_name=key_name)
618 if sub_error_message:
619 error_message += "The following dictionary is invalid because one of its entries is invalid:\n"
620 error_message += gp.sprint_varx(var_name, var_value, gp.blank() | gp.show_type())
621 error_message += "\n"
622 error_message += sub_error_message
623 return process_error_message(error_message)
624
625 for key, value in invalid_values.items():
626 if key not in var_value:
627 continue
628 key_name = " [" + key + "]"
629 sub_error_message = valid_value(var_value[key], invalid_values=value, var_name=key_name)
630 if sub_error_message:
631 error_message += "The following dictionary is invalid because one of its entries is invalid:\n"
632 error_message += gp.sprint_varx(var_name, var_value, gp.blank() | gp.show_type())
633 error_message += "\n"
634 error_message += sub_error_message
635 return process_error_message(error_message)
636
Michael Walsh7ac5fd82018-10-11 16:58:49 -0500637 return process_error_message(error_message)
Michael Walsh018e25f2019-08-01 11:27:12 -0500638
639
Michael Walsh89eff542019-09-13 16:24:51 -0500640def valid_program(var_value, var_name=None):
Michael Walshbe3a8152019-08-20 16:38:19 -0500641 r"""
Michael Walshda20f842019-10-16 11:36:40 -0500642 The variable value is valid if it contains the name of a program which can be located using the "which"
643 command.
Michael Walshbe3a8152019-08-20 16:38:19 -0500644
645 Description of argument(s):
646 var_value The value being validated.
647 """
648
649 error_message = ""
650 rc, out_buf = gc.shell_cmd("which " + var_value, quiet=1, show_err=0,
651 ignore_err=1)
652 if rc:
Michael Walsh89eff542019-09-13 16:24:51 -0500653 var_name = get_var_name(var_name)
Michael Walshbe3a8152019-08-20 16:38:19 -0500654 error_message += "The following required program could not be found"
655 error_message += " using the $PATH environment variable:\n"
Michael Walsh00244342019-08-29 10:35:50 -0500656 error_message += gp.sprint_varx(var_name, var_value, gp.blank())
Michael Walshbe3a8152019-08-20 16:38:19 -0500657 PATH = os.environ.get("PATH", "").split(":")
658 error_message += "\n"
659 error_message += gp.sprint_var(PATH)
660 return process_error_message(error_message)
661
662
Michael Walsh89eff542019-09-13 16:24:51 -0500663def valid_length(var_value, min_length=None, max_length=None, var_name=None):
Michael Walshb9d8dfd2019-09-11 11:11:12 -0500664 r"""
Michael Walshda20f842019-10-16 11:36:40 -0500665 The variable value is valid if it is an object (e.g. list, dictionary) whose length is within the
666 specified range.
Michael Walshb9d8dfd2019-09-11 11:11:12 -0500667
668 Description of argument(s):
669 var_value The value being validated.
Michael Walshda20f842019-10-16 11:36:40 -0500670 min_length The minimum length of the object. If not None, the length of var_value
671 must be greater than or equal to min_length.
672 max_length The maximum length of the object. If not None, the length of var_value
673 must be less than or equal to min_length.
Michael Walshb9d8dfd2019-09-11 11:11:12 -0500674 """
675
676 error_message = ""
677 length = len(var_value)
678 error_message = valid_range(length, min_length, max_length)
679 if error_message:
Michael Walsh89eff542019-09-13 16:24:51 -0500680 var_name = get_var_name(var_name)
Michael Walshb9d8dfd2019-09-11 11:11:12 -0500681 error_message = "The length of the following object is not within the"
682 error_message += " expected range:\n"
Michael Walshda20f842019-10-16 11:36:40 -0500683 error_message += gp.sprint_vars(min_length, max_length)
Michael Walshb9d8dfd2019-09-11 11:11:12 -0500684 error_message += gp.sprint_var(length)
685 error_message += gp.sprint_varx(var_name, var_value, gp.blank())
686 error_message += "\n"
Michael Walshb9d8dfd2019-09-11 11:11:12 -0500687 return process_error_message(error_message)
688
689 return process_error_message(error_message)
690
691
Michael Walsh018e25f2019-08-01 11:27:12 -0500692# Modify selected function docstrings by adding headers/footers.
693
694func_names = [
695 "valid_type", "valid_value", "valid_range", "valid_integer",
696 "valid_dir_path", "valid_file_path", "valid_path", "valid_list",
Michael Walsh8333a182019-10-22 16:19:00 -0500697 "valid_dict", "valid_program", "valid_length", "valid_float",
698 "valid_date_time"
Michael Walsh018e25f2019-08-01 11:27:12 -0500699]
700
701raw_doc_strings = {}
702
703for func_name in func_names:
704 cmd_buf = "raw_doc_strings['" + func_name + "'] = " + func_name
705 cmd_buf += ".__doc__"
706 exec(cmd_buf)
707 cmd_buf = func_name + ".__doc__ = docstring_header + " + func_name
708 cmd_buf += ".__doc__.rstrip(\" \\n\") + additional_args_docstring_footer"
709 exec(cmd_buf)