blob: 36a63db7805fbb92ba2262cb31cd520797a4ebe5 [file] [log] [blame]
Michael Walsh7423c012016-10-04 10:27:21 -05001#!/usr/bin/env python
2
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
233 if len_valid_values > 0:
234 # Processing the valid_values list.
235 if var_value in valid_values:
Michael Walsh018e25f2019-08-01 11:27:12 -0500236 return process_error_message(error_message)
Michael Walsh89eff542019-09-13 16:24:51 -0500237 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500238 error_message += "Invalid variable value:\n"
239 error_message += gp.sprint_varx(var_name, var_value,
240 gp.blank() | gp.verbose()
241 | gp.show_type())
242 error_message += "\n"
243 error_message += "It must be one of the following values:\n"
244 error_message += "\n"
245 error_message += gp.sprint_var(valid_values,
246 gp.blank() | gp.show_type())
247 return process_error_message(error_message)
Michael Walsh7423c012016-10-04 10:27:21 -0500248
249 if len_invalid_values == 0:
Michael Walshbec416d2016-11-10 08:54:52 -0600250 # Assign default value.
Michael Walsh018e25f2019-08-01 11:27:12 -0500251 invalid_values = ["", None]
Michael Walsh7423c012016-10-04 10:27:21 -0500252
253 # Assertion: We have an invalid_values list. Processing it now.
254 if var_value not in invalid_values:
Michael Walsh018e25f2019-08-01 11:27:12 -0500255 return process_error_message(error_message)
Michael Walsh7423c012016-10-04 10:27:21 -0500256
Michael Walsh89eff542019-09-13 16:24:51 -0500257 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500258 error_message += "Invalid variable value:\n"
259 error_message += gp.sprint_varx(var_name, var_value,
260 gp.blank() | gp.verbose()
261 | gp.show_type())
262 error_message += "\n"
263 error_message += "It must NOT be one of the following values:\n"
264 error_message += "\n"
265 error_message += gp.sprint_var(invalid_values,
266 gp.blank() | gp.show_type())
Michael Walshe23b5ad2018-06-01 14:54:35 -0500267 return process_error_message(error_message)
Michael Walshbec416d2016-11-10 08:54:52 -0600268
Michael Walshbec416d2016-11-10 08:54:52 -0600269
Michael Walsh89eff542019-09-13 16:24:51 -0500270def valid_range(var_value, lower=None, upper=None, var_name=None):
Michael Walshbec416d2016-11-10 08:54:52 -0600271 r"""
Michael Walsh018e25f2019-08-01 11:27:12 -0500272 The variable value is valid if it is within the specified range.
Michael Walshbec416d2016-11-10 08:54:52 -0600273
Michael Walshda20f842019-10-16 11:36:40 -0500274 This function can be used with any type of operands where they can have a greater than/less than
275 relationship to each other (e.g. int, float, str).
Michael Walsh018e25f2019-08-01 11:27:12 -0500276
277 Description of argument(s):
Michael Walshbec416d2016-11-10 08:54:52 -0600278 var_value The value being validated.
Michael Walshda20f842019-10-16 11:36:40 -0500279 lower The lower end of the range. If not None, the var_value must be greater
280 than or equal to lower.
281 upper The upper end of the range. If not None, the var_value must be less than
282 or equal to upper.
Michael Walshbec416d2016-11-10 08:54:52 -0600283 """
284
Michael Walshbec416d2016-11-10 08:54:52 -0600285 error_message = ""
Michael Walshb9d8dfd2019-09-11 11:11:12 -0500286 if lower is None and upper is None:
Michael Walsh018e25f2019-08-01 11:27:12 -0500287 return process_error_message(error_message)
Michael Walshb9d8dfd2019-09-11 11:11:12 -0500288 if lower is None and var_value <= upper:
Michael Walsh018e25f2019-08-01 11:27:12 -0500289 return process_error_message(error_message)
Michael Walshb9d8dfd2019-09-11 11:11:12 -0500290 if upper is None and var_value >= lower:
Michael Walsh018e25f2019-08-01 11:27:12 -0500291 return process_error_message(error_message)
292 if lower and upper:
293 if lower > upper:
Michael Walsh89eff542019-09-13 16:24:51 -0500294 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500295 error_message += "Programmer error - the lower value is greater"
296 error_message += " than the upper value:\n"
297 error_message += gp.sprint_vars(lower, upper, fmt=gp.show_type())
298 return process_error_message(error_message)
299 if lower <= var_value <= upper:
300 return process_error_message(error_message)
Michael Walshbec416d2016-11-10 08:54:52 -0600301
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 += "The following variable is not within the expected"
304 error_message += " range:\n"
305 error_message += gp.sprint_varx(var_name, var_value, gp.show_type())
306 error_message += "\n"
307 error_message += "range:\n"
308 error_message += gp.sprint_vars(lower, upper, fmt=gp.show_type(), indent=2)
Michael Walshe23b5ad2018-06-01 14:54:35 -0500309 return process_error_message(error_message)
Michael Walsh7423c012016-10-04 10:27:21 -0500310
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600311
Michael Walsh89eff542019-09-13 16:24:51 -0500312def valid_integer(var_value, lower=None, upper=None, var_name=None):
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600313 r"""
Michael Walshda20f842019-10-16 11:36:40 -0500314 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 -0600315
Michael Walshda20f842019-10-16 11:36:40 -0500316 This function also calls valid_range to make sure the integer value is within the specified range (if
317 any).
Michael Walsh018e25f2019-08-01 11:27:12 -0500318
319 Description of argument(s):
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600320 var_value The value being validated.
Michael Walshda20f842019-10-16 11:36:40 -0500321 lower The lower end of the range. If not None, the var_value must be greater
322 than or equal to lower.
323 upper The upper end of the range. If not None, the var_value must be less than
324 or equal to upper.
Michael Walsh018e25f2019-08-01 11:27:12 -0500325 """
326
327 error_message = ""
Michael Walsh89eff542019-09-13 16:24:51 -0500328 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500329 try:
330 var_value = int(str(var_value), 0)
331 except ValueError:
332 error_message += "Invalid integer value:\n"
333 error_message += gp.sprint_varx(var_name, var_value,
334 gp.blank() | gp.show_type())
335 return process_error_message(error_message)
336
337 # Check the range (if any).
338 if lower:
339 lower = int(str(lower), 0)
340 if upper:
341 upper = int(str(upper), 0)
342 error_message = valid_range(var_value, lower, upper, var_name=var_name)
343
344 return process_error_message(error_message)
345
346
Michael Walsh8333a182019-10-22 16:19:00 -0500347def valid_float(var_value, lower=None, upper=None, var_name=None):
348 r"""
349 The variable value is valid if it is a floating point value or can be interpreted as a floating point
350 value (e.g. 7.5, "7.5", etc.).
351
352 This function also calls valid_range to make sure the float value is within the specified range (if any).
353
354 Description of argument(s):
355 var_value The value being validated.
356 lower The lower end of the range. If not None, the var_value must be greater
357 than or equal to lower.
358 upper The upper end of the range. If not None, the var_value must be less than
359 or equal to upper.
360 """
361
362 error_message = ""
363 var_name = get_var_name(var_name)
364 try:
365 var_value = float(str(var_value))
366 except ValueError:
367 error_message += "Invalid float value:\n"
368 error_message += gp.sprint_varx(var_name, var_value,
369 gp.blank() | gp.show_type())
370 return process_error_message(error_message)
371
372 # Check the range (if any).
373 if lower:
374 lower = float(str(lower))
375 if upper:
376 upper = float(str(upper))
377 error_message = valid_range(var_value, lower, upper, var_name=var_name)
378
379 return process_error_message(error_message)
380
381
382def valid_date_time(var_value, var_name=None):
383 r"""
384 The variable value is valid if it can be interpreted as a date/time (e.g. "14:49:49.981", "tomorrow",
385 etc.) by the linux date command.
386
387 Description of argument(s):
388 var_value The value being validated.
389 """
390
391 error_message = ""
392 rc, out_buf = gc.shell_cmd("date -d '" + str(var_value) + "'", quiet=1, show_err=0, ignore_err=1)
393 if rc:
394 var_name = get_var_name(var_name)
395 error_message += "Invalid date/time value:\n"
396 error_message += gp.sprint_varx(var_name, var_value,
397 gp.blank() | gp.show_type())
398 return process_error_message(error_message)
399
400 return process_error_message(error_message)
401
402
Michael Walsh89eff542019-09-13 16:24:51 -0500403def valid_dir_path(var_value, var_name=None):
Michael Walsh018e25f2019-08-01 11:27:12 -0500404 r"""
Michael Walshda20f842019-10-16 11:36:40 -0500405 The variable value is valid if it contains the path of an existing directory.
Michael Walsh018e25f2019-08-01 11:27:12 -0500406
407 Description of argument(s):
408 var_value The value being validated.
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600409 """
410
411 error_message = ""
412 if not os.path.isdir(str(var_value)):
Michael Walsh89eff542019-09-13 16:24:51 -0500413 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500414 error_message += "The following directory does not exist:\n"
Michael Walsh00244342019-08-29 10:35:50 -0500415 error_message += gp.sprint_varx(var_name, var_value, gp.blank())
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600416
Michael Walshe23b5ad2018-06-01 14:54:35 -0500417 return process_error_message(error_message)
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600418
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600419
Michael Walsh89eff542019-09-13 16:24:51 -0500420def valid_file_path(var_value, var_name=None):
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600421 r"""
Michael Walsh018e25f2019-08-01 11:27:12 -0500422 The variable value is valid if it contains the path of an existing file.
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600423
Michael Walsh018e25f2019-08-01 11:27:12 -0500424 Description of argument(s):
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600425 var_value The value being validated.
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600426 """
427
428 error_message = ""
429 if not os.path.isfile(str(var_value)):
Michael Walsh89eff542019-09-13 16:24:51 -0500430 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500431 error_message += "The following file does not exist:\n"
Michael Walsh00244342019-08-29 10:35:50 -0500432 error_message += gp.sprint_varx(var_name, var_value, gp.blank())
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600433
Michael Walshe23b5ad2018-06-01 14:54:35 -0500434 return process_error_message(error_message)
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600435
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600436
Michael Walsh89eff542019-09-13 16:24:51 -0500437def valid_path(var_value, var_name=None):
Michael Walshe23b5ad2018-06-01 14:54:35 -0500438 r"""
Michael Walshda20f842019-10-16 11:36:40 -0500439 The variable value is valid if it contains the path of an existing file or directory.
Michael Walshe23b5ad2018-06-01 14:54:35 -0500440
Michael Walsh018e25f2019-08-01 11:27:12 -0500441 Description of argument(s):
Michael Walshe23b5ad2018-06-01 14:54:35 -0500442 var_value The value being validated.
Michael Walshe23b5ad2018-06-01 14:54:35 -0500443 """
444
445 error_message = ""
446 if not (os.path.isfile(str(var_value)) or os.path.isdir(str(var_value))):
Michael Walsh89eff542019-09-13 16:24:51 -0500447 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500448 error_message += "Invalid path (file or directory does not exist):\n"
Michael Walsh00244342019-08-29 10:35:50 -0500449 error_message += gp.sprint_varx(var_name, var_value, gp.blank())
Michael Walshe23b5ad2018-06-01 14:54:35 -0500450
Michael Walshe23b5ad2018-06-01 14:54:35 -0500451 return process_error_message(error_message)
Michael Walsh2c687e92018-05-09 11:47:56 -0500452
453
Michael Walsh35026be2019-08-14 17:13:39 -0500454def valid_list(var_value, valid_values=[], invalid_values=[],
Michael Walsh89eff542019-09-13 16:24:51 -0500455 required_values=[], fail_on_empty=False, var_name=None):
Michael Walsh2c687e92018-05-09 11:47:56 -0500456 r"""
Michael Walshda20f842019-10-16 11:36:40 -0500457 The variable value is valid if it is a list where each entry can be found in the valid_values list or if
458 none of its values can be found in the invalid_values list or if all of the values in the required_values
459 list can be found in var_value.
Michael Walsh35026be2019-08-14 17:13:39 -0500460
Michael Walshda20f842019-10-16 11:36:40 -0500461 The caller may only specify one of these 3 arguments: valid_values, invalid_values, required_values.
Michael Walsh2c687e92018-05-09 11:47:56 -0500462
Michael Walsh018e25f2019-08-01 11:27:12 -0500463 Description of argument(s):
464 var_value The value being validated.
Michael Walshda20f842019-10-16 11:36:40 -0500465 valid_values A list of valid values. Each element in the var_value list must be equal
466 to one of these values to be considered valid.
467 invalid_values A list of invalid values. If any element in var_value is equal to any of
468 the values in this argument, var_value is considered invalid.
469 required_values Every value in required_values must be found in var_value. Otherwise,
470 var_value is considered invalid.
471 fail_on_empty Indicates that an empty list for the variable value should be considered
472 an error.
Michael Walshca193992018-08-02 17:20:00 -0500473 """
474
475 error_message = ""
Michael Walsh018e25f2019-08-01 11:27:12 -0500476
Michael Walsh35026be2019-08-14 17:13:39 -0500477 # Validate this function's arguments.
Michael Walshda20f842019-10-16 11:36:40 -0500478 if not (bool(len(valid_values)) ^ bool(len(invalid_values)) ^ bool(len(required_values))):
Michael Walsh35026be2019-08-14 17:13:39 -0500479 error_message += "Programmer error - You must provide only one of the"
480 error_message += " following: valid_values, invalid_values,"
481 error_message += " required_values.\n"
Michael Walshda20f842019-10-16 11:36:40 -0500482 error_message += gp.sprint_var(invalid_values, gp.show_type())
483 error_message += gp.sprint_var(valid_values, gp.show_type())
484 error_message += gp.sprint_var(required_values, gp.show_type())
Michael Walsh35026be2019-08-14 17:13:39 -0500485 return process_error_message(error_message)
486
Michael Walsh018e25f2019-08-01 11:27:12 -0500487 if type(var_value) is not list:
Michael Walsh89eff542019-09-13 16:24:51 -0500488 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500489 error_message = valid_type(var_value, list, var_name=var_name)
490 if error_message:
491 return process_error_message(error_message)
492
493 if fail_on_empty and len(var_value) == 0:
Michael Walsh89eff542019-09-13 16:24:51 -0500494 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500495 error_message += "Invalid empty list:\n"
496 error_message += gp.sprint_varx(var_name, var_value, gp.show_type())
497 return process_error_message(error_message)
Michael Walshca193992018-08-02 17:20:00 -0500498
Michael Walsh35026be2019-08-14 17:13:39 -0500499 if len(required_values):
500 found_error = 0
501 display_required_values = list(required_values)
502 for ix in range(0, len(required_values)):
503 if required_values[ix] not in var_value:
504 found_error = 1
505 display_required_values[ix] = \
506 str(display_required_values[ix]) + "*"
507 if found_error:
Michael Walsh89eff542019-09-13 16:24:51 -0500508 var_name = get_var_name(var_name)
Michael Walsh35026be2019-08-14 17:13:39 -0500509 error_message += "The following list is invalid:\n"
510 error_message += gp.sprint_varx(var_name, var_value,
511 gp.blank() | gp.show_type())
512 error_message += "\n"
513 error_message += "Because some of the values in the "
514 error_message += "required_values list are not present (see"
515 error_message += " entries marked with \"*\"):\n"
516 error_message += "\n"
517 error_message += gp.sprint_varx('required_values',
518 display_required_values,
519 gp.blank() | gp.show_type())
520 error_message += "\n"
521
522 return process_error_message(error_message)
523
524 if len(invalid_values):
525 found_error = 0
526 display_var_value = list(var_value)
527 for ix in range(0, len(var_value)):
528 if var_value[ix] in invalid_values:
529 found_error = 1
530 display_var_value[ix] = str(var_value[ix]) + "*"
531
532 if found_error:
Michael Walsh89eff542019-09-13 16:24:51 -0500533 var_name = get_var_name(var_name)
Michael Walsh35026be2019-08-14 17:13:39 -0500534 error_message += "The following list is invalid (see entries"
535 error_message += " marked with \"*\"):\n"
536 error_message += gp.sprint_varx(var_name, display_var_value,
537 gp.blank() | gp.show_type())
538 error_message += "\n"
539 error_message += gp.sprint_var(invalid_values, gp.show_type())
540 return process_error_message(error_message)
541
Michael Walshca193992018-08-02 17:20:00 -0500542 found_error = 0
543 display_var_value = list(var_value)
544 for ix in range(0, len(var_value)):
545 if var_value[ix] not in valid_values:
546 found_error = 1
Michael Walsh35026be2019-08-14 17:13:39 -0500547 display_var_value[ix] = str(var_value[ix]) + "*"
Michael Walshca193992018-08-02 17:20:00 -0500548
549 if found_error:
Michael Walsh89eff542019-09-13 16:24:51 -0500550 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500551 error_message += "The following list is invalid (see entries marked"
552 error_message += " with \"*\"):\n"
553 error_message += gp.sprint_varx(var_name, display_var_value,
554 gp.blank() | gp.show_type())
555 error_message += "\n"
Michael Walsh35026be2019-08-14 17:13:39 -0500556 error_message += gp.sprint_var(valid_values, gp.show_type())
Michael Walsh018e25f2019-08-01 11:27:12 -0500557 return process_error_message(error_message)
Michael Walshca193992018-08-02 17:20:00 -0500558
Michael Walshca193992018-08-02 17:20:00 -0500559 return process_error_message(error_message)
Michael Walsh7ac5fd82018-10-11 16:58:49 -0500560
561
Michael Walsh89eff542019-09-13 16:24:51 -0500562def valid_dict(var_value, required_keys=[], var_name=None):
Michael Walsh7ac5fd82018-10-11 16:58:49 -0500563 r"""
Michael Walshda20f842019-10-16 11:36:40 -0500564 The variable value is valid if it is a dictionary containing all of the required keys.
Michael Walsh7ac5fd82018-10-11 16:58:49 -0500565
Michael Walsh018e25f2019-08-01 11:27:12 -0500566 Description of argument(s):
567 var_value The value being validated.
Michael Walshda20f842019-10-16 11:36:40 -0500568 required_keys A list of keys which must be found in the dictionary for it to be
569 considered valid.
Michael Walsh7ac5fd82018-10-11 16:58:49 -0500570 """
571
572 error_message = ""
Michael Walsh018e25f2019-08-01 11:27:12 -0500573 missing_keys = list(set(required_keys) - set(var_value.keys()))
574 if len(missing_keys) > 0:
Michael Walsh89eff542019-09-13 16:24:51 -0500575 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500576 error_message += "The following dictionary is invalid because it is"
577 error_message += " missing required keys:\n"
578 error_message += gp.sprint_varx(var_name, var_value,
579 gp.blank() | gp.show_type())
580 error_message += "\n"
Michael Walsh35026be2019-08-14 17:13:39 -0500581 error_message += gp.sprint_var(missing_keys, gp.show_type())
Michael Walsh7ac5fd82018-10-11 16:58:49 -0500582 return process_error_message(error_message)
Michael Walsh018e25f2019-08-01 11:27:12 -0500583
584
Michael Walsh89eff542019-09-13 16:24:51 -0500585def valid_program(var_value, var_name=None):
Michael Walshbe3a8152019-08-20 16:38:19 -0500586 r"""
Michael Walshda20f842019-10-16 11:36:40 -0500587 The variable value is valid if it contains the name of a program which can be located using the "which"
588 command.
Michael Walshbe3a8152019-08-20 16:38:19 -0500589
590 Description of argument(s):
591 var_value The value being validated.
592 """
593
594 error_message = ""
595 rc, out_buf = gc.shell_cmd("which " + var_value, quiet=1, show_err=0,
596 ignore_err=1)
597 if rc:
Michael Walsh89eff542019-09-13 16:24:51 -0500598 var_name = get_var_name(var_name)
Michael Walshbe3a8152019-08-20 16:38:19 -0500599 error_message += "The following required program could not be found"
600 error_message += " using the $PATH environment variable:\n"
Michael Walsh00244342019-08-29 10:35:50 -0500601 error_message += gp.sprint_varx(var_name, var_value, gp.blank())
Michael Walshbe3a8152019-08-20 16:38:19 -0500602 PATH = os.environ.get("PATH", "").split(":")
603 error_message += "\n"
604 error_message += gp.sprint_var(PATH)
605 return process_error_message(error_message)
606
607
Michael Walsh89eff542019-09-13 16:24:51 -0500608def valid_length(var_value, min_length=None, max_length=None, var_name=None):
Michael Walshb9d8dfd2019-09-11 11:11:12 -0500609 r"""
Michael Walshda20f842019-10-16 11:36:40 -0500610 The variable value is valid if it is an object (e.g. list, dictionary) whose length is within the
611 specified range.
Michael Walshb9d8dfd2019-09-11 11:11:12 -0500612
613 Description of argument(s):
614 var_value The value being validated.
Michael Walshda20f842019-10-16 11:36:40 -0500615 min_length The minimum length of the object. If not None, the length of var_value
616 must be greater than or equal to min_length.
617 max_length The maximum length of the object. If not None, the length of var_value
618 must be less than or equal to min_length.
Michael Walshb9d8dfd2019-09-11 11:11:12 -0500619 """
620
621 error_message = ""
622 length = len(var_value)
623 error_message = valid_range(length, min_length, max_length)
624 if error_message:
Michael Walsh89eff542019-09-13 16:24:51 -0500625 var_name = get_var_name(var_name)
Michael Walshb9d8dfd2019-09-11 11:11:12 -0500626 error_message = "The length of the following object is not within the"
627 error_message += " expected range:\n"
Michael Walshda20f842019-10-16 11:36:40 -0500628 error_message += gp.sprint_vars(min_length, max_length)
Michael Walshb9d8dfd2019-09-11 11:11:12 -0500629 error_message += gp.sprint_var(length)
630 error_message += gp.sprint_varx(var_name, var_value, gp.blank())
631 error_message += "\n"
Michael Walshb9d8dfd2019-09-11 11:11:12 -0500632 return process_error_message(error_message)
633
634 return process_error_message(error_message)
635
636
Michael Walsh018e25f2019-08-01 11:27:12 -0500637# Modify selected function docstrings by adding headers/footers.
638
639func_names = [
640 "valid_type", "valid_value", "valid_range", "valid_integer",
641 "valid_dir_path", "valid_file_path", "valid_path", "valid_list",
Michael Walsh8333a182019-10-22 16:19:00 -0500642 "valid_dict", "valid_program", "valid_length", "valid_float",
643 "valid_date_time"
Michael Walsh018e25f2019-08-01 11:27:12 -0500644]
645
646raw_doc_strings = {}
647
648for func_name in func_names:
649 cmd_buf = "raw_doc_strings['" + func_name + "'] = " + func_name
650 cmd_buf += ".__doc__"
651 exec(cmd_buf)
652 cmd_buf = func_name + ".__doc__ = docstring_header + " + func_name
653 cmd_buf += ".__doc__.rstrip(\" \\n\") + additional_args_docstring_footer"
654 exec(cmd_buf)