blob: 34c406bee28dccee79e0d5fff0a74cdf9a5a8b3e [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 Walsh7423c012016-10-04 10:27:21 -050011
Michael Walshe23b5ad2018-06-01 14:54:35 -050012exit_on_error = False
13
14
15def set_exit_on_error(value):
16 r"""
17 Set the exit_on_error value to either True or False.
18
Michael Walshda20f842019-10-16 11:36:40 -050019 If exit_on_error is set, validation functions like valid_value() will exit the program on error instead
20 of returning False.
Michael Walshe23b5ad2018-06-01 14:54:35 -050021
22 Description of argument(s):
23 value Value to set global exit_on_error to.
24 """
25
26 global exit_on_error
27 exit_on_error = value
28
29
Michael Walsh89eff542019-09-13 16:24:51 -050030def get_var_name(var_name):
Michael Walshe23b5ad2018-06-01 14:54:35 -050031 r"""
Michael Walshda20f842019-10-16 11:36:40 -050032 If var_name is not None, simply return its value. Otherwise, get the variable name of the first argument
33 used to call the validation function (e.g. valid, valid_integer, etc.) and return it.
Michael Walshe23b5ad2018-06-01 14:54:35 -050034
35 This function is designed solely for use by other functions in this file.
36
37 Example:
38
39 A programmer codes this:
40
41 valid_value(last_name)
42
43 Which results in the following call stack:
44
45 valid_value(last_name)
Michael Walsh018e25f2019-08-01 11:27:12 -050046 -> get_var_name(var_name)
Michael Walshe23b5ad2018-06-01 14:54:35 -050047
48 In this example, this function will return "last_name".
49
50 Example:
51
Michael Walsh018e25f2019-08-01 11:27:12 -050052 err_msg = valid_value(last_name, var_name="some_other_name")
Michael Walshe23b5ad2018-06-01 14:54:35 -050053
54 Which results in the following call stack:
55
Michael Walsh018e25f2019-08-01 11:27:12 -050056 valid_value(var_value, var_name="some_other_name")
Michael Walshe23b5ad2018-06-01 14:54:35 -050057 -> get_var_name(var_name)
58
59 In this example, this function will return "some_other_name".
60
61 Description of argument(s):
62 var_name The name of the variable.
63 """
64
Michael Walsh89eff542019-09-13 16:24:51 -050065 return var_name or gp.get_arg_name(0, 1, stack_frame_ix=3)
Michael Walshe23b5ad2018-06-01 14:54:35 -050066
67
68def process_error_message(error_message):
69 r"""
Michael Walsh018e25f2019-08-01 11:27:12 -050070 Process the error_message in the manner described below.
Michael Walshe23b5ad2018-06-01 14:54:35 -050071
Michael Walsh018e25f2019-08-01 11:27:12 -050072 This function is designed solely for use by other functions in this file.
73
74 NOTE: A blank error_message means that there is no error.
75
Michael Walshda20f842019-10-16 11:36:40 -050076 For the following explanations, assume the caller of this function is a function with the following
77 definition:
Michael Walsh89eff542019-09-13 16:24:51 -050078 valid_value(var_value, valid_values=[], invalid_values=[], var_name=None):
Michael Walsh018e25f2019-08-01 11:27:12 -050079
Michael Walshda20f842019-10-16 11:36:40 -050080 If the user of valid_value() is assigning the valid_value() return value to a variable,
81 process_error_message() will simply return the error_message. This mode of usage is illustrated by the
82 following example:
Michael Walsh018e25f2019-08-01 11:27:12 -050083
84 error_message = valid_value(var1)
85
Michael Walshda20f842019-10-16 11:36:40 -050086 This mode is useful for callers who wish to validate a variable and then decide for themselves what to do
87 with the error_message (e.g. raise(error_message), BuiltIn().fail(error_message), etc.).
Michael Walsh018e25f2019-08-01 11:27:12 -050088
Michael Walshda20f842019-10-16 11:36:40 -050089 If the user of valid_value() is NOT assigning the valid_value() return value to a variable,
90 process_error_message() will behave as follows.
Michael Walsh018e25f2019-08-01 11:27:12 -050091
Michael Walshda20f842019-10-16 11:36:40 -050092 First, if error_message is non-blank, it will be printed to stderr via a call to
93 gp.print_error_report(error_message).
Michael Walsh018e25f2019-08-01 11:27:12 -050094
95 If exit_on_error is set:
96 - If the error_message is blank, simply return.
Michael Walshda20f842019-10-16 11:36:40 -050097 - If the error_message is non-blank, exit the program with a return code of 1.
Michael Walsh018e25f2019-08-01 11:27:12 -050098
99 If exit_on_error is NOT set:
100 - If the error_message is blank, return True.
101 - If the error_message is non-blank, return False.
Michael Walshe23b5ad2018-06-01 14:54:35 -0500102
103 Description of argument(s):
104 error_message An error message.
105 """
106
Michael Walshda20f842019-10-16 11:36:40 -0500107 # Determine whether the caller's caller is assigning the result to a variable.
Michael Walsh018e25f2019-08-01 11:27:12 -0500108 l_value = gp.get_arg_name(None, -1, stack_frame_ix=3)
109 if l_value:
110 return error_message
111
Michael Walshe23b5ad2018-06-01 14:54:35 -0500112 if error_message == "":
Michael Walsh018e25f2019-08-01 11:27:12 -0500113 if exit_on_error:
114 return
Michael Walshe23b5ad2018-06-01 14:54:35 -0500115 return True
116
Michael Walsh018e25f2019-08-01 11:27:12 -0500117 gp.print_error_report(error_message, stack_frame_ix=4)
Michael Walshe23b5ad2018-06-01 14:54:35 -0500118 if exit_on_error:
Michael Walsh10a4d982018-10-30 13:07:46 -0500119 exit(1)
Michael Walshe23b5ad2018-06-01 14:54:35 -0500120 return False
121
Michael Walsh7423c012016-10-04 10:27:21 -0500122
Michael Walshda20f842019-10-16 11:36:40 -0500123# Note to programmers: All of the validation functions in this module should follow the same basic template:
Michael Walsh89eff542019-09-13 16:24:51 -0500124# def valid_value(var_value, var1, var2, varn, var_name=None):
Michael Walsh018e25f2019-08-01 11:27:12 -0500125#
126# error_message = ""
127# if not valid:
Michael Walsh89eff542019-09-13 16:24:51 -0500128# var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500129# error_message += "The following variable is invalid because...:\n"
130# error_message += gp.sprint_varx(var_name, var_value, gp.blank())
131#
132# return process_error_message(error_message)
Michael Walsh7423c012016-10-04 10:27:21 -0500133
Michael Walsh018e25f2019-08-01 11:27:12 -0500134
Michael Walshda20f842019-10-16 11:36:40 -0500135# The docstring header and footer will be added to each validation function's existing docstring.
Michael Walsh018e25f2019-08-01 11:27:12 -0500136docstring_header = \
137 r"""
138 Determine whether var_value is valid, construct an error_message and call
139 process_error_message(error_message).
140
Michael Walshda20f842019-10-16 11:36:40 -0500141 See the process_error_message() function defined in this module for a description of how error messages
142 are processed.
Michael Walsh7423c012016-10-04 10:27:21 -0500143 """
144
Michael Walsh018e25f2019-08-01 11:27:12 -0500145additional_args_docstring_footer = \
146 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"
180 error_message += gp.sprint_varx(var_name, var_value,
181 gp.blank() | gp.show_type())
182 error_message += "\n"
183 error_message += gp.sprint_var(required_type)
184
185 return process_error_message(error_message)
186
187
Michael Walsh89eff542019-09-13 16:24:51 -0500188def valid_value(var_value, valid_values=[], invalid_values=[], var_name=None):
Michael Walsh018e25f2019-08-01 11:27:12 -0500189
190 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
232 if len_valid_values > 0:
233 # Processing the valid_values list.
234 if var_value in valid_values:
Michael Walsh018e25f2019-08-01 11:27:12 -0500235 return process_error_message(error_message)
Michael Walsh89eff542019-09-13 16:24:51 -0500236 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500237 error_message += "Invalid variable value:\n"
238 error_message += gp.sprint_varx(var_name, var_value,
239 gp.blank() | gp.verbose()
240 | gp.show_type())
241 error_message += "\n"
242 error_message += "It must be one of the following values:\n"
243 error_message += "\n"
244 error_message += gp.sprint_var(valid_values,
245 gp.blank() | gp.show_type())
246 return process_error_message(error_message)
Michael Walsh7423c012016-10-04 10:27:21 -0500247
248 if len_invalid_values == 0:
Michael Walshbec416d2016-11-10 08:54:52 -0600249 # Assign default value.
Michael Walsh018e25f2019-08-01 11:27:12 -0500250 invalid_values = ["", None]
Michael Walsh7423c012016-10-04 10:27:21 -0500251
252 # Assertion: We have an invalid_values list. Processing it now.
253 if var_value not in invalid_values:
Michael Walsh018e25f2019-08-01 11:27:12 -0500254 return process_error_message(error_message)
Michael Walsh7423c012016-10-04 10:27:21 -0500255
Michael Walsh89eff542019-09-13 16:24:51 -0500256 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500257 error_message += "Invalid variable value:\n"
258 error_message += gp.sprint_varx(var_name, var_value,
259 gp.blank() | gp.verbose()
260 | gp.show_type())
261 error_message += "\n"
262 error_message += "It must NOT be one of the following values:\n"
263 error_message += "\n"
264 error_message += gp.sprint_var(invalid_values,
265 gp.blank() | gp.show_type())
Michael Walshe23b5ad2018-06-01 14:54:35 -0500266 return process_error_message(error_message)
Michael Walshbec416d2016-11-10 08:54:52 -0600267
Michael Walshbec416d2016-11-10 08:54:52 -0600268
Michael Walsh89eff542019-09-13 16:24:51 -0500269def valid_range(var_value, lower=None, upper=None, var_name=None):
Michael Walshbec416d2016-11-10 08:54:52 -0600270 r"""
Michael Walsh018e25f2019-08-01 11:27:12 -0500271 The variable value is valid if it is within the specified range.
Michael Walshbec416d2016-11-10 08:54:52 -0600272
Michael Walshda20f842019-10-16 11:36:40 -0500273 This function can be used with any type of operands where they can have a greater than/less than
274 relationship to each other (e.g. int, float, str).
Michael Walsh018e25f2019-08-01 11:27:12 -0500275
276 Description of argument(s):
Michael Walshbec416d2016-11-10 08:54:52 -0600277 var_value The value being validated.
Michael Walshda20f842019-10-16 11:36:40 -0500278 lower The lower end of the range. If not None, the var_value must be greater
279 than or equal to lower.
280 upper The upper end of the range. If not None, the var_value must be less than
281 or equal to upper.
Michael Walshbec416d2016-11-10 08:54:52 -0600282 """
283
Michael Walshbec416d2016-11-10 08:54:52 -0600284 error_message = ""
Michael Walshb9d8dfd2019-09-11 11:11:12 -0500285 if lower is None and upper is None:
Michael Walsh018e25f2019-08-01 11:27:12 -0500286 return process_error_message(error_message)
Michael Walshb9d8dfd2019-09-11 11:11:12 -0500287 if lower is None and var_value <= upper:
Michael Walsh018e25f2019-08-01 11:27:12 -0500288 return process_error_message(error_message)
Michael Walshb9d8dfd2019-09-11 11:11:12 -0500289 if upper is None and var_value >= lower:
Michael Walsh018e25f2019-08-01 11:27:12 -0500290 return process_error_message(error_message)
291 if lower and upper:
292 if lower > upper:
Michael Walsh89eff542019-09-13 16:24:51 -0500293 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500294 error_message += "Programmer error - the lower value is greater"
295 error_message += " than the upper value:\n"
296 error_message += gp.sprint_vars(lower, upper, fmt=gp.show_type())
297 return process_error_message(error_message)
298 if lower <= var_value <= upper:
299 return process_error_message(error_message)
Michael Walshbec416d2016-11-10 08:54:52 -0600300
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 += "The following variable is not within the expected"
303 error_message += " range:\n"
304 error_message += gp.sprint_varx(var_name, var_value, gp.show_type())
305 error_message += "\n"
306 error_message += "range:\n"
307 error_message += gp.sprint_vars(lower, upper, fmt=gp.show_type(), indent=2)
Michael Walshe23b5ad2018-06-01 14:54:35 -0500308 return process_error_message(error_message)
Michael Walsh7423c012016-10-04 10:27:21 -0500309
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600310
Michael Walsh89eff542019-09-13 16:24:51 -0500311def valid_integer(var_value, lower=None, upper=None, var_name=None):
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600312 r"""
Michael Walshda20f842019-10-16 11:36:40 -0500313 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 -0600314
Michael Walshda20f842019-10-16 11:36:40 -0500315 This function also calls valid_range to make sure the integer value is within the specified range (if
316 any).
Michael Walsh018e25f2019-08-01 11:27:12 -0500317
318 Description of argument(s):
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600319 var_value The value being validated.
Michael Walshda20f842019-10-16 11:36:40 -0500320 lower The lower end of the range. If not None, the var_value must be greater
321 than or equal to lower.
322 upper The upper end of the range. If not None, the var_value must be less than
323 or equal to upper.
Michael Walsh018e25f2019-08-01 11:27:12 -0500324 """
325
326 error_message = ""
Michael Walsh89eff542019-09-13 16:24:51 -0500327 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500328 try:
329 var_value = int(str(var_value), 0)
330 except ValueError:
331 error_message += "Invalid integer value:\n"
332 error_message += gp.sprint_varx(var_name, var_value,
333 gp.blank() | gp.show_type())
334 return process_error_message(error_message)
335
336 # Check the range (if any).
337 if lower:
338 lower = int(str(lower), 0)
339 if upper:
340 upper = int(str(upper), 0)
341 error_message = valid_range(var_value, lower, upper, var_name=var_name)
342
343 return process_error_message(error_message)
344
345
Michael Walsh89eff542019-09-13 16:24:51 -0500346def valid_dir_path(var_value, var_name=None):
Michael Walsh018e25f2019-08-01 11:27:12 -0500347 r"""
Michael Walshda20f842019-10-16 11:36:40 -0500348 The variable value is valid if it contains the path of an existing directory.
Michael Walsh018e25f2019-08-01 11:27:12 -0500349
350 Description of argument(s):
351 var_value The value being validated.
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600352 """
353
354 error_message = ""
355 if not os.path.isdir(str(var_value)):
Michael Walsh89eff542019-09-13 16:24:51 -0500356 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500357 error_message += "The following directory does not exist:\n"
Michael Walsh00244342019-08-29 10:35:50 -0500358 error_message += gp.sprint_varx(var_name, var_value, gp.blank())
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600359
Michael Walshe23b5ad2018-06-01 14:54:35 -0500360 return process_error_message(error_message)
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600361
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600362
Michael Walsh89eff542019-09-13 16:24:51 -0500363def valid_file_path(var_value, var_name=None):
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600364 r"""
Michael Walsh018e25f2019-08-01 11:27:12 -0500365 The variable value is valid if it contains the path of an existing file.
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600366
Michael Walsh018e25f2019-08-01 11:27:12 -0500367 Description of argument(s):
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600368 var_value The value being validated.
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600369 """
370
371 error_message = ""
372 if not os.path.isfile(str(var_value)):
Michael Walsh89eff542019-09-13 16:24:51 -0500373 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500374 error_message += "The following file does not exist:\n"
Michael Walsh00244342019-08-29 10:35:50 -0500375 error_message += gp.sprint_varx(var_name, var_value, gp.blank())
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600376
Michael Walshe23b5ad2018-06-01 14:54:35 -0500377 return process_error_message(error_message)
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600378
Michael Walsh78bdfdd2017-01-10 11:27:30 -0600379
Michael Walsh89eff542019-09-13 16:24:51 -0500380def valid_path(var_value, var_name=None):
Michael Walshe23b5ad2018-06-01 14:54:35 -0500381 r"""
Michael Walshda20f842019-10-16 11:36:40 -0500382 The variable value is valid if it contains the path of an existing file or directory.
Michael Walshe23b5ad2018-06-01 14:54:35 -0500383
Michael Walsh018e25f2019-08-01 11:27:12 -0500384 Description of argument(s):
Michael Walshe23b5ad2018-06-01 14:54:35 -0500385 var_value The value being validated.
Michael Walshe23b5ad2018-06-01 14:54:35 -0500386 """
387
388 error_message = ""
389 if not (os.path.isfile(str(var_value)) or os.path.isdir(str(var_value))):
Michael Walsh89eff542019-09-13 16:24:51 -0500390 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500391 error_message += "Invalid path (file or directory does not exist):\n"
Michael Walsh00244342019-08-29 10:35:50 -0500392 error_message += gp.sprint_varx(var_name, var_value, gp.blank())
Michael Walshe23b5ad2018-06-01 14:54:35 -0500393
Michael Walshe23b5ad2018-06-01 14:54:35 -0500394 return process_error_message(error_message)
Michael Walsh2c687e92018-05-09 11:47:56 -0500395
396
Michael Walsh35026be2019-08-14 17:13:39 -0500397def valid_list(var_value, valid_values=[], invalid_values=[],
Michael Walsh89eff542019-09-13 16:24:51 -0500398 required_values=[], fail_on_empty=False, var_name=None):
Michael Walsh2c687e92018-05-09 11:47:56 -0500399 r"""
Michael Walshda20f842019-10-16 11:36:40 -0500400 The variable value is valid if it is a list where each entry can be found in the valid_values list or if
401 none of its values can be found in the invalid_values list or if all of the values in the required_values
402 list can be found in var_value.
Michael Walsh35026be2019-08-14 17:13:39 -0500403
Michael Walshda20f842019-10-16 11:36:40 -0500404 The caller may only specify one of these 3 arguments: valid_values, invalid_values, required_values.
Michael Walsh2c687e92018-05-09 11:47:56 -0500405
Michael Walsh018e25f2019-08-01 11:27:12 -0500406 Description of argument(s):
407 var_value The value being validated.
Michael Walshda20f842019-10-16 11:36:40 -0500408 valid_values A list of valid values. Each element in the var_value list must be equal
409 to one of these values to be considered valid.
410 invalid_values A list of invalid values. If any element in var_value is equal to any of
411 the values in this argument, var_value is considered invalid.
412 required_values Every value in required_values must be found in var_value. Otherwise,
413 var_value is considered invalid.
414 fail_on_empty Indicates that an empty list for the variable value should be considered
415 an error.
Michael Walshca193992018-08-02 17:20:00 -0500416 """
417
418 error_message = ""
Michael Walsh018e25f2019-08-01 11:27:12 -0500419
Michael Walsh35026be2019-08-14 17:13:39 -0500420 # Validate this function's arguments.
Michael Walshda20f842019-10-16 11:36:40 -0500421 if not (bool(len(valid_values)) ^ bool(len(invalid_values)) ^ bool(len(required_values))):
Michael Walsh35026be2019-08-14 17:13:39 -0500422 error_message += "Programmer error - You must provide only one of the"
423 error_message += " following: valid_values, invalid_values,"
424 error_message += " required_values.\n"
Michael Walshda20f842019-10-16 11:36:40 -0500425 error_message += gp.sprint_var(invalid_values, gp.show_type())
426 error_message += gp.sprint_var(valid_values, gp.show_type())
427 error_message += gp.sprint_var(required_values, gp.show_type())
Michael Walsh35026be2019-08-14 17:13:39 -0500428 return process_error_message(error_message)
429
Michael Walsh018e25f2019-08-01 11:27:12 -0500430 if type(var_value) is not list:
Michael Walsh89eff542019-09-13 16:24:51 -0500431 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500432 error_message = valid_type(var_value, list, var_name=var_name)
433 if error_message:
434 return process_error_message(error_message)
435
436 if fail_on_empty and len(var_value) == 0:
Michael Walsh89eff542019-09-13 16:24:51 -0500437 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500438 error_message += "Invalid empty list:\n"
439 error_message += gp.sprint_varx(var_name, var_value, gp.show_type())
440 return process_error_message(error_message)
Michael Walshca193992018-08-02 17:20:00 -0500441
Michael Walsh35026be2019-08-14 17:13:39 -0500442 if len(required_values):
443 found_error = 0
444 display_required_values = list(required_values)
445 for ix in range(0, len(required_values)):
446 if required_values[ix] not in var_value:
447 found_error = 1
448 display_required_values[ix] = \
449 str(display_required_values[ix]) + "*"
450 if found_error:
Michael Walsh89eff542019-09-13 16:24:51 -0500451 var_name = get_var_name(var_name)
Michael Walsh35026be2019-08-14 17:13:39 -0500452 error_message += "The following list is invalid:\n"
453 error_message += gp.sprint_varx(var_name, var_value,
454 gp.blank() | gp.show_type())
455 error_message += "\n"
456 error_message += "Because some of the values in the "
457 error_message += "required_values list are not present (see"
458 error_message += " entries marked with \"*\"):\n"
459 error_message += "\n"
460 error_message += gp.sprint_varx('required_values',
461 display_required_values,
462 gp.blank() | gp.show_type())
463 error_message += "\n"
464
465 return process_error_message(error_message)
466
467 if len(invalid_values):
468 found_error = 0
469 display_var_value = list(var_value)
470 for ix in range(0, len(var_value)):
471 if var_value[ix] in invalid_values:
472 found_error = 1
473 display_var_value[ix] = str(var_value[ix]) + "*"
474
475 if found_error:
Michael Walsh89eff542019-09-13 16:24:51 -0500476 var_name = get_var_name(var_name)
Michael Walsh35026be2019-08-14 17:13:39 -0500477 error_message += "The following list is invalid (see entries"
478 error_message += " marked with \"*\"):\n"
479 error_message += gp.sprint_varx(var_name, display_var_value,
480 gp.blank() | gp.show_type())
481 error_message += "\n"
482 error_message += gp.sprint_var(invalid_values, gp.show_type())
483 return process_error_message(error_message)
484
Michael Walshca193992018-08-02 17:20:00 -0500485 found_error = 0
486 display_var_value = list(var_value)
487 for ix in range(0, len(var_value)):
488 if var_value[ix] not in valid_values:
489 found_error = 1
Michael Walsh35026be2019-08-14 17:13:39 -0500490 display_var_value[ix] = str(var_value[ix]) + "*"
Michael Walshca193992018-08-02 17:20:00 -0500491
492 if found_error:
Michael Walsh89eff542019-09-13 16:24:51 -0500493 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500494 error_message += "The following list is invalid (see entries marked"
495 error_message += " with \"*\"):\n"
496 error_message += gp.sprint_varx(var_name, display_var_value,
497 gp.blank() | gp.show_type())
498 error_message += "\n"
Michael Walsh35026be2019-08-14 17:13:39 -0500499 error_message += gp.sprint_var(valid_values, gp.show_type())
Michael Walsh018e25f2019-08-01 11:27:12 -0500500 return process_error_message(error_message)
Michael Walshca193992018-08-02 17:20:00 -0500501
Michael Walshca193992018-08-02 17:20:00 -0500502 return process_error_message(error_message)
Michael Walsh7ac5fd82018-10-11 16:58:49 -0500503
504
Michael Walsh89eff542019-09-13 16:24:51 -0500505def valid_dict(var_value, required_keys=[], var_name=None):
Michael Walsh7ac5fd82018-10-11 16:58:49 -0500506 r"""
Michael Walshda20f842019-10-16 11:36:40 -0500507 The variable value is valid if it is a dictionary containing all of the required keys.
Michael Walsh7ac5fd82018-10-11 16:58:49 -0500508
Michael Walsh018e25f2019-08-01 11:27:12 -0500509 Description of argument(s):
510 var_value The value being validated.
Michael Walshda20f842019-10-16 11:36:40 -0500511 required_keys A list of keys which must be found in the dictionary for it to be
512 considered valid.
Michael Walsh7ac5fd82018-10-11 16:58:49 -0500513 """
514
515 error_message = ""
Michael Walsh018e25f2019-08-01 11:27:12 -0500516 missing_keys = list(set(required_keys) - set(var_value.keys()))
517 if len(missing_keys) > 0:
Michael Walsh89eff542019-09-13 16:24:51 -0500518 var_name = get_var_name(var_name)
Michael Walsh018e25f2019-08-01 11:27:12 -0500519 error_message += "The following dictionary is invalid because it is"
520 error_message += " missing required keys:\n"
521 error_message += gp.sprint_varx(var_name, var_value,
522 gp.blank() | gp.show_type())
523 error_message += "\n"
Michael Walsh35026be2019-08-14 17:13:39 -0500524 error_message += gp.sprint_var(missing_keys, gp.show_type())
Michael Walsh7ac5fd82018-10-11 16:58:49 -0500525 return process_error_message(error_message)
Michael Walsh018e25f2019-08-01 11:27:12 -0500526
527
Michael Walsh89eff542019-09-13 16:24:51 -0500528def valid_program(var_value, var_name=None):
Michael Walshbe3a8152019-08-20 16:38:19 -0500529 r"""
Michael Walshda20f842019-10-16 11:36:40 -0500530 The variable value is valid if it contains the name of a program which can be located using the "which"
531 command.
Michael Walshbe3a8152019-08-20 16:38:19 -0500532
533 Description of argument(s):
534 var_value The value being validated.
535 """
536
537 error_message = ""
538 rc, out_buf = gc.shell_cmd("which " + var_value, quiet=1, show_err=0,
539 ignore_err=1)
540 if rc:
Michael Walsh89eff542019-09-13 16:24:51 -0500541 var_name = get_var_name(var_name)
Michael Walshbe3a8152019-08-20 16:38:19 -0500542 error_message += "The following required program could not be found"
543 error_message += " using the $PATH environment variable:\n"
Michael Walsh00244342019-08-29 10:35:50 -0500544 error_message += gp.sprint_varx(var_name, var_value, gp.blank())
Michael Walshbe3a8152019-08-20 16:38:19 -0500545 PATH = os.environ.get("PATH", "").split(":")
546 error_message += "\n"
547 error_message += gp.sprint_var(PATH)
548 return process_error_message(error_message)
549
550
Michael Walsh89eff542019-09-13 16:24:51 -0500551def valid_length(var_value, min_length=None, max_length=None, var_name=None):
Michael Walshb9d8dfd2019-09-11 11:11:12 -0500552 r"""
Michael Walshda20f842019-10-16 11:36:40 -0500553 The variable value is valid if it is an object (e.g. list, dictionary) whose length is within the
554 specified range.
Michael Walshb9d8dfd2019-09-11 11:11:12 -0500555
556 Description of argument(s):
557 var_value The value being validated.
Michael Walshda20f842019-10-16 11:36:40 -0500558 min_length The minimum length of the object. If not None, the length of var_value
559 must be greater than or equal to min_length.
560 max_length The maximum length of the object. If not None, the length of var_value
561 must be less than or equal to min_length.
Michael Walshb9d8dfd2019-09-11 11:11:12 -0500562 """
563
564 error_message = ""
565 length = len(var_value)
566 error_message = valid_range(length, min_length, max_length)
567 if error_message:
Michael Walsh89eff542019-09-13 16:24:51 -0500568 var_name = get_var_name(var_name)
Michael Walshb9d8dfd2019-09-11 11:11:12 -0500569 error_message = "The length of the following object is not within the"
570 error_message += " expected range:\n"
Michael Walshda20f842019-10-16 11:36:40 -0500571 error_message += gp.sprint_vars(min_length, max_length)
Michael Walshb9d8dfd2019-09-11 11:11:12 -0500572 error_message += gp.sprint_var(length)
573 error_message += gp.sprint_varx(var_name, var_value, gp.blank())
574 error_message += "\n"
Michael Walshb9d8dfd2019-09-11 11:11:12 -0500575 return process_error_message(error_message)
576
577 return process_error_message(error_message)
578
579
Michael Walsh018e25f2019-08-01 11:27:12 -0500580# Modify selected function docstrings by adding headers/footers.
581
582func_names = [
583 "valid_type", "valid_value", "valid_range", "valid_integer",
584 "valid_dir_path", "valid_file_path", "valid_path", "valid_list",
Michael Walsh89eff542019-09-13 16:24:51 -0500585 "valid_dict", "valid_program", "valid_length"
Michael Walsh018e25f2019-08-01 11:27:12 -0500586]
587
588raw_doc_strings = {}
589
590for func_name in func_names:
591 cmd_buf = "raw_doc_strings['" + func_name + "'] = " + func_name
592 cmd_buf += ".__doc__"
593 exec(cmd_buf)
594 cmd_buf = func_name + ".__doc__ = docstring_header + " + func_name
595 cmd_buf += ".__doc__.rstrip(\" \\n\") + additional_args_docstring_footer"
596 exec(cmd_buf)