17423c01aSMichael Walsh#!/usr/bin/env python
27423c01aSMichael Walsh
37423c01aSMichael Walshr"""
4018e25fbSMichael WalshThis module provides validation functions like valid_value(), valid_integer(),
5018e25fbSMichael Walshetc.
67423c01aSMichael Walsh"""
77423c01aSMichael Walsh
878bdfdd6SMichael Walshimport os
97423c01aSMichael Walshimport gen_print as gp
10be3a8150SMichael Walshimport gen_cmd as gc
11018e25fbSMichael Walshimport func_args as fa
127423c01aSMichael Walsh
13e23b5ad3SMichael Walshexit_on_error = False
14e23b5ad3SMichael Walsh
15e23b5ad3SMichael Walsh
16e23b5ad3SMichael Walshdef set_exit_on_error(value):
17e23b5ad3SMichael Walsh    r"""
18e23b5ad3SMichael Walsh    Set the exit_on_error value to either True or False.
19e23b5ad3SMichael Walsh
20018e25fbSMichael Walsh    If exit_on_error is set, validation functions like valid_value() will exit
21e23b5ad3SMichael Walsh    the program on error instead of returning False.
22e23b5ad3SMichael Walsh
23e23b5ad3SMichael Walsh    Description of argument(s):
24e23b5ad3SMichael Walsh    value                           Value to set global exit_on_error to.
25e23b5ad3SMichael Walsh    """
26e23b5ad3SMichael Walsh
27e23b5ad3SMichael Walsh    global exit_on_error
28e23b5ad3SMichael Walsh    exit_on_error = value
29e23b5ad3SMichael Walsh
30e23b5ad3SMichael Walsh
31018e25fbSMichael Walshdef get_var_name(*args, **kwargs):
32e23b5ad3SMichael Walsh    r"""
33018e25fbSMichael Walsh    If args/kwargs contain a var_name, simply return its value.  Otherwise,
34018e25fbSMichael Walsh    get the variable name of the first argument used to call the validation
35018e25fbSMichael Walsh    function (e.g. valid, valid_integer, etc.) and return it.
36e23b5ad3SMichael Walsh
37e23b5ad3SMichael Walsh    This function is designed solely for use by other functions in this file.
38e23b5ad3SMichael Walsh
39e23b5ad3SMichael Walsh    Example:
40e23b5ad3SMichael Walsh
41e23b5ad3SMichael Walsh    A programmer codes this:
42e23b5ad3SMichael Walsh
43e23b5ad3SMichael Walsh    valid_value(last_name)
44e23b5ad3SMichael Walsh
45e23b5ad3SMichael Walsh    Which results in the following call stack:
46e23b5ad3SMichael Walsh
47e23b5ad3SMichael Walsh    valid_value(last_name)
48e23b5ad3SMichael Walsh      -> get_var_name(var_name)
49e23b5ad3SMichael Walsh
50e23b5ad3SMichael Walsh    In this example, this function will return "last_name".
51e23b5ad3SMichael Walsh
52e23b5ad3SMichael Walsh    Example:
53e23b5ad3SMichael Walsh
54018e25fbSMichael Walsh    err_msg = valid_value(last_name, var_name="some_other_name")
55e23b5ad3SMichael Walsh
56e23b5ad3SMichael Walsh    Which results in the following call stack:
57e23b5ad3SMichael Walsh
58018e25fbSMichael Walsh    valid_value(var_value, var_name="some_other_name")
59e23b5ad3SMichael Walsh      -> get_var_name(var_name)
60e23b5ad3SMichael Walsh
61e23b5ad3SMichael Walsh    In this example, this function will return "some_other_name".
62e23b5ad3SMichael Walsh
63e23b5ad3SMichael Walsh    Description of argument(s):
64e23b5ad3SMichael Walsh    var_name                        The name of the variable.
65e23b5ad3SMichael Walsh    """
66e23b5ad3SMichael Walsh
67018e25fbSMichael Walsh    var_name, args, kwargs = fa.pop_arg(*args, **kwargs)
68018e25fbSMichael Walsh    if var_name:
69e23b5ad3SMichael Walsh        return var_name
70018e25fbSMichael Walsh    return gp.get_arg_name(0, 1, stack_frame_ix=3)
71e23b5ad3SMichael Walsh
72e23b5ad3SMichael Walsh
73e23b5ad3SMichael Walshdef process_error_message(error_message):
74e23b5ad3SMichael Walsh    r"""
75018e25fbSMichael Walsh    Process the error_message in the manner described below.
76e23b5ad3SMichael Walsh
77018e25fbSMichael Walsh    This function is designed solely for use by other functions in this file.
78018e25fbSMichael Walsh
79018e25fbSMichael Walsh    NOTE: A blank error_message means that there is no error.
80018e25fbSMichael Walsh
81018e25fbSMichael Walsh    For the following explanations, assume the caller of this function is a
82018e25fbSMichael Walsh    function with the following definition:
83018e25fbSMichael Walsh    valid_value(var_value, valid_values=[], invalid_values=[], *args,
84018e25fbSMichael Walsh    **kwargs):
85018e25fbSMichael Walsh
86018e25fbSMichael Walsh    If the user of valid_value() is assigning the valid_value() return value
87018e25fbSMichael Walsh    to a variable, process_error_message() will simply return the
88018e25fbSMichael Walsh    error_message.  This mode of usage is illustrated by the following example:
89018e25fbSMichael Walsh
90018e25fbSMichael Walsh    error_message = valid_value(var1)
91018e25fbSMichael Walsh
92018e25fbSMichael Walsh    This mode is useful for callers who wish to validate a variable and then
93018e25fbSMichael Walsh    decide for themselves what to do with the error_message (e.g.
94018e25fbSMichael Walsh    raise(error_message), BuiltIn().fail(error_message), etc.).
95018e25fbSMichael Walsh
96018e25fbSMichael Walsh    If the user of valid_value() is NOT assigning the valid_value() return
97018e25fbSMichael Walsh    value to a variable, process_error_message() will behave as follows.
98018e25fbSMichael Walsh
99018e25fbSMichael Walsh    First, if error_message is non-blank, it will be printed to stderr via a
100018e25fbSMichael Walsh    call to gp.print_error_report(error_message).
101018e25fbSMichael Walsh
102018e25fbSMichael Walsh    If exit_on_error is set:
103018e25fbSMichael Walsh    - If the error_message is blank, simply return.
104018e25fbSMichael Walsh    - If the error_message is non-blank, exit the program with a return code
105018e25fbSMichael Walsh      of 1.
106018e25fbSMichael Walsh
107018e25fbSMichael Walsh    If exit_on_error is NOT set:
108018e25fbSMichael Walsh    - If the error_message is blank, return True.
109018e25fbSMichael Walsh    - If the error_message is non-blank, return False.
110e23b5ad3SMichael Walsh
111e23b5ad3SMichael Walsh    Description of argument(s):
112e23b5ad3SMichael Walsh    error_message                   An error message.
113e23b5ad3SMichael Walsh    """
114e23b5ad3SMichael Walsh
115018e25fbSMichael Walsh    # Determine whether the caller's caller is assigning the result to a
116018e25fbSMichael Walsh    # variable.
117018e25fbSMichael Walsh    l_value = gp.get_arg_name(None, -1, stack_frame_ix=3)
118018e25fbSMichael Walsh    if l_value:
119018e25fbSMichael Walsh        return error_message
120018e25fbSMichael Walsh
121e23b5ad3SMichael Walsh    if error_message == "":
122018e25fbSMichael Walsh        if exit_on_error:
123018e25fbSMichael Walsh            return
124e23b5ad3SMichael Walsh        return True
125e23b5ad3SMichael Walsh
126018e25fbSMichael Walsh    gp.print_error_report(error_message, stack_frame_ix=4)
127e23b5ad3SMichael Walsh    if exit_on_error:
12810a4d98bSMichael Walsh        exit(1)
129e23b5ad3SMichael Walsh    return False
130e23b5ad3SMichael Walsh
1317423c01aSMichael Walsh
132018e25fbSMichael Walsh# Note to programmers:  All of the validation functions in this module should
133018e25fbSMichael Walsh# follow the same basic template:
134018e25fbSMichael Walsh# def valid_value(var_value, var1, var2, varn, *args, **kwargs):
135018e25fbSMichael Walsh#
136018e25fbSMichael Walsh#     error_message = ""
137018e25fbSMichael Walsh#     if not valid:
138018e25fbSMichael Walsh#         var_name = get_var_name(*args, **kwargs)
139018e25fbSMichael Walsh#         error_message += "The following variable is invalid because...:\n"
140018e25fbSMichael Walsh#         error_message += gp.sprint_varx(var_name, var_value, gp.blank())
141018e25fbSMichael Walsh#
142018e25fbSMichael Walsh#     return process_error_message(error_message)
1437423c01aSMichael Walsh
144018e25fbSMichael Walsh
145018e25fbSMichael Walsh# The docstring header and footer will be added to each validation function's
146018e25fbSMichael Walsh# existing docstring.
147018e25fbSMichael Walshdocstring_header = \
148018e25fbSMichael Walsh    r"""
149018e25fbSMichael Walsh    Determine whether var_value is valid, construct an error_message and call
150018e25fbSMichael Walsh    process_error_message(error_message).
151018e25fbSMichael Walsh
152018e25fbSMichael Walsh    See the process_error_message() function defined in this module for a
153018e25fbSMichael Walsh    description of how error messages are processed.
1547423c01aSMichael Walsh    """
1557423c01aSMichael Walsh
156018e25fbSMichael Walshadditional_args_docstring_footer = \
157018e25fbSMichael Walsh    r"""
158018e25fbSMichael Walsh    args                            Additional positional arguments (described
159018e25fbSMichael Walsh                                    below).
160018e25fbSMichael Walsh    kwargs                          Additional keyword arguments (described
161018e25fbSMichael Walsh                                    below).
162018e25fbSMichael Walsh
163018e25fbSMichael Walsh    Additional argument(s):
164018e25fbSMichael Walsh    var_name                        The name of the variable whose value is
165018e25fbSMichael Walsh                                    passed in var_value.  For the general
166018e25fbSMichael Walsh                                    case, this argument is unnecessary as this
167018e25fbSMichael Walsh                                    function can figure out the var_name.
168018e25fbSMichael Walsh                                    This is provided for Robot callers in
169018e25fbSMichael Walsh                                    which case, this function lacks the
170018e25fbSMichael Walsh                                    ability to determine the variable name.
171018e25fbSMichael Walsh    """
172018e25fbSMichael Walsh
173018e25fbSMichael Walsh
174018e25fbSMichael Walshdef valid_type(var_value, required_type, *args, **kwargs):
175018e25fbSMichael Walsh    r"""
176018e25fbSMichael Walsh    The variable value is valid if it is of the required type.
177018e25fbSMichael Walsh
178018e25fbSMichael Walsh    Examples:
179018e25fbSMichael Walsh
180018e25fbSMichael Walsh    valid_type(var1, int)
181018e25fbSMichael Walsh
182018e25fbSMichael Walsh    valid_type(var1, (list, dict))
183018e25fbSMichael Walsh
184018e25fbSMichael Walsh    Description of argument(s):
185018e25fbSMichael Walsh    var_value                       The value being validated.
186018e25fbSMichael Walsh    required_type                   A type or a tuple of types (e.g. str, int,
187018e25fbSMichael Walsh                                    etc.).
188018e25fbSMichael Walsh    """
189018e25fbSMichael Walsh
190018e25fbSMichael Walsh    error_message = ""
191018e25fbSMichael Walsh    if type(required_type) is tuple:
192018e25fbSMichael Walsh        if type(var_value) in required_type:
193018e25fbSMichael Walsh            return process_error_message(error_message)
194018e25fbSMichael Walsh    else:
195018e25fbSMichael Walsh        if type(var_value) is required_type:
196018e25fbSMichael Walsh            return process_error_message(error_message)
197018e25fbSMichael Walsh
198018e25fbSMichael Walsh    # If we get to this point, the validation has failed.
199018e25fbSMichael Walsh    var_name = get_var_name(*args, **kwargs)
200018e25fbSMichael Walsh    error_message += "Invalid variable type:\n"
201018e25fbSMichael Walsh    error_message += gp.sprint_varx(var_name, var_value,
202018e25fbSMichael Walsh                                    gp.blank() | gp.show_type())
203018e25fbSMichael Walsh    error_message += "\n"
204018e25fbSMichael Walsh    error_message += gp.sprint_var(required_type)
205018e25fbSMichael Walsh
206018e25fbSMichael Walsh    return process_error_message(error_message)
207018e25fbSMichael Walsh
208018e25fbSMichael Walsh
209018e25fbSMichael Walshdef valid_value(var_value, valid_values=[], invalid_values=[], *args,
210018e25fbSMichael Walsh                **kwargs):
211018e25fbSMichael Walsh
212018e25fbSMichael Walsh    r"""
213018e25fbSMichael Walsh    The variable value is valid if it is either contained in the valid_values
214018e25fbSMichael Walsh    list or if it is NOT contained in the invalid_values list.  If the caller
215018e25fbSMichael Walsh    specifies nothing for either of these 2 arguments, invalid_values will be
216018e25fbSMichael Walsh    initialized to ['', None].  This is a good way to fail on variables which
217018e25fbSMichael Walsh    contain blank values.
218018e25fbSMichael Walsh
219018e25fbSMichael Walsh    It is illegal to specify both valid_values and invalid values.
220018e25fbSMichael Walsh
221018e25fbSMichael Walsh    Example:
222018e25fbSMichael Walsh
223018e25fbSMichael Walsh    var1 = ''
224018e25fbSMichael Walsh    valid_value(var1)
225018e25fbSMichael Walsh
226018e25fbSMichael Walsh    This code would fail because var1 is blank and the default value for
227018e25fbSMichael Walsh    invalid_values is ['', None].
228018e25fbSMichael Walsh
229018e25fbSMichael Walsh    Example:
230018e25fbSMichael Walsh    var1 = 'yes'
231018e25fbSMichael Walsh    valid_value(var1, valid_values=['yes', 'true'])
232018e25fbSMichael Walsh
233018e25fbSMichael Walsh    This code would pass.
234018e25fbSMichael Walsh
235018e25fbSMichael Walsh    Description of argument(s):
236018e25fbSMichael Walsh    var_value                       The value being validated.
237018e25fbSMichael Walsh    valid_values                    A list of valid values.  The variable
238018e25fbSMichael Walsh                                    value must be equal to one of these values
239018e25fbSMichael Walsh                                    to be considered valid.
240018e25fbSMichael Walsh    invalid_values                  A list of invalid values.  If the variable
241018e25fbSMichael Walsh                                    value is equal to any of these, it is
242018e25fbSMichael Walsh                                    considered invalid.
243018e25fbSMichael Walsh    """
244018e25fbSMichael Walsh
245bec416ddSMichael Walsh    error_message = ""
246bec416ddSMichael Walsh
247e23b5ad3SMichael Walsh    # Validate this function's arguments.
2487423c01aSMichael Walsh    len_valid_values = len(valid_values)
2497423c01aSMichael Walsh    len_invalid_values = len(invalid_values)
2507423c01aSMichael Walsh    if len_valid_values > 0 and len_invalid_values > 0:
251018e25fbSMichael Walsh        error_message += "Programmer error - You must provide either an"
252018e25fbSMichael Walsh        error_message += " invalid_values list or a valid_values"
253018e25fbSMichael Walsh        error_message += " list but NOT both:\n"
254018e25fbSMichael Walsh        error_message += gp.sprint_var(invalid_values)
255018e25fbSMichael Walsh        error_message += gp.sprint_var(valid_values)
256018e25fbSMichael Walsh        return process_error_message(error_message)
2577423c01aSMichael Walsh
2587423c01aSMichael Walsh    if len_valid_values > 0:
2597423c01aSMichael Walsh        # Processing the valid_values list.
2607423c01aSMichael Walsh        if var_value in valid_values:
261018e25fbSMichael Walsh            return process_error_message(error_message)
262018e25fbSMichael Walsh        var_name = get_var_name(*args, **kwargs)
263018e25fbSMichael Walsh        error_message += "Invalid variable value:\n"
264018e25fbSMichael Walsh        error_message += gp.sprint_varx(var_name, var_value,
265018e25fbSMichael Walsh                                        gp.blank() | gp.verbose()
266018e25fbSMichael Walsh                                        | gp.show_type())
267018e25fbSMichael Walsh        error_message += "\n"
268018e25fbSMichael Walsh        error_message += "It must be one of the following values:\n"
269018e25fbSMichael Walsh        error_message += "\n"
270018e25fbSMichael Walsh        error_message += gp.sprint_var(valid_values,
271018e25fbSMichael Walsh                                       gp.blank() | gp.show_type())
272018e25fbSMichael Walsh        return process_error_message(error_message)
2737423c01aSMichael Walsh
2747423c01aSMichael Walsh    if len_invalid_values == 0:
275bec416ddSMichael Walsh        # Assign default value.
276018e25fbSMichael Walsh        invalid_values = ["", None]
2777423c01aSMichael Walsh
2787423c01aSMichael Walsh    # Assertion: We have an invalid_values list.  Processing it now.
2797423c01aSMichael Walsh    if var_value not in invalid_values:
280018e25fbSMichael Walsh        return process_error_message(error_message)
281bec416ddSMichael Walsh
282018e25fbSMichael Walsh    var_name = get_var_name(*args, **kwargs)
283018e25fbSMichael Walsh    error_message += "Invalid variable value:\n"
284018e25fbSMichael Walsh    error_message += gp.sprint_varx(var_name, var_value,
285018e25fbSMichael Walsh                                    gp.blank() | gp.verbose()
286018e25fbSMichael Walsh                                    | gp.show_type())
287018e25fbSMichael Walsh    error_message += "\n"
288018e25fbSMichael Walsh    error_message += "It must NOT be one of the following values:\n"
289018e25fbSMichael Walsh    error_message += "\n"
290018e25fbSMichael Walsh    error_message += gp.sprint_var(invalid_values,
291018e25fbSMichael Walsh                                   gp.blank() | gp.show_type())
292e23b5ad3SMichael Walsh    return process_error_message(error_message)
2937423c01aSMichael Walsh
294bec416ddSMichael Walsh
295018e25fbSMichael Walshdef valid_range(var_value, lower=None, upper=None, *args, **kwargs):
296bec416ddSMichael Walsh    r"""
297018e25fbSMichael Walsh    The variable value is valid if it is within the specified range.
298bec416ddSMichael Walsh
299018e25fbSMichael Walsh    This function can be used with any type of operands where they can have a
300018e25fbSMichael Walsh    greater than/less than relationship to each other (e.g. int, float, str).
301018e25fbSMichael Walsh
302018e25fbSMichael Walsh    Description of argument(s):
303bec416ddSMichael Walsh    var_value                       The value being validated.
304018e25fbSMichael Walsh    lower                           The lower end of the range.  If not None,
305018e25fbSMichael Walsh                                    the var_value must be greater than or
306018e25fbSMichael Walsh                                    equal to lower.
307018e25fbSMichael Walsh    upper                           The upper end of the range.  If not None,
308018e25fbSMichael Walsh                                    the var_value must be less than or equal
309018e25fbSMichael Walsh                                    to upper.
310bec416ddSMichael Walsh    """
311bec416ddSMichael Walsh
312bec416ddSMichael Walsh    error_message = ""
313*b9d8dfd2SMichael Walsh    if lower is None and upper is None:
314018e25fbSMichael Walsh        return process_error_message(error_message)
315*b9d8dfd2SMichael Walsh    if lower is None and var_value <= upper:
316018e25fbSMichael Walsh        return process_error_message(error_message)
317*b9d8dfd2SMichael Walsh    if upper is None and var_value >= lower:
318018e25fbSMichael Walsh        return process_error_message(error_message)
319018e25fbSMichael Walsh    if lower and upper:
320018e25fbSMichael Walsh        if lower > upper:
321018e25fbSMichael Walsh            var_name = get_var_name(*args, **kwargs)
322018e25fbSMichael Walsh            error_message += "Programmer error - the lower value is greater"
323018e25fbSMichael Walsh            error_message += " than the upper value:\n"
324018e25fbSMichael Walsh            error_message += gp.sprint_vars(lower, upper, fmt=gp.show_type())
325018e25fbSMichael Walsh            return process_error_message(error_message)
326018e25fbSMichael Walsh        if lower <= var_value <= upper:
327018e25fbSMichael Walsh            return process_error_message(error_message)
328bec416ddSMichael Walsh
329018e25fbSMichael Walsh    var_name = get_var_name(*args, **kwargs)
330018e25fbSMichael Walsh    error_message += "The following variable is not within the expected"
331018e25fbSMichael Walsh    error_message += " range:\n"
332018e25fbSMichael Walsh    error_message += gp.sprint_varx(var_name, var_value, gp.show_type())
333018e25fbSMichael Walsh    error_message += "\n"
334018e25fbSMichael Walsh    error_message += "range:\n"
335018e25fbSMichael Walsh    error_message += gp.sprint_vars(lower, upper, fmt=gp.show_type(), indent=2)
336e23b5ad3SMichael Walsh    return process_error_message(error_message)
3377423c01aSMichael Walsh
33878bdfdd6SMichael Walsh
339018e25fbSMichael Walshdef valid_integer(var_value, lower=None, upper=None, *args, **kwargs):
34078bdfdd6SMichael Walsh    r"""
341018e25fbSMichael Walsh    The variable value is valid if it is an integer or can be interpreted as
342018e25fbSMichael Walsh    an integer (e.g. 7, "7", etc.).
34378bdfdd6SMichael Walsh
344018e25fbSMichael Walsh    This function also calls valid_range to make sure the integer value is
345018e25fbSMichael Walsh    within the specified range (if any).
346018e25fbSMichael Walsh
347018e25fbSMichael Walsh    Description of argument(s):
34878bdfdd6SMichael Walsh    var_value                       The value being validated.
349018e25fbSMichael Walsh    lower                           The lower end of the range.  If not None,
350018e25fbSMichael Walsh                                    the var_value must be greater than or
351018e25fbSMichael Walsh                                    equal to lower.
352018e25fbSMichael Walsh    upper                           The upper end of the range.  If not None,
353018e25fbSMichael Walsh                                    the var_value must be less than or equal
354018e25fbSMichael Walsh                                    to upper.
355018e25fbSMichael Walsh    """
356018e25fbSMichael Walsh
357018e25fbSMichael Walsh    error_message = ""
358018e25fbSMichael Walsh    var_name = get_var_name(*args, **kwargs)
359018e25fbSMichael Walsh    try:
360018e25fbSMichael Walsh        var_value = int(str(var_value), 0)
361018e25fbSMichael Walsh    except ValueError:
362018e25fbSMichael Walsh        error_message += "Invalid integer value:\n"
363018e25fbSMichael Walsh        error_message += gp.sprint_varx(var_name, var_value,
364018e25fbSMichael Walsh                                        gp.blank() | gp.show_type())
365018e25fbSMichael Walsh        return process_error_message(error_message)
366018e25fbSMichael Walsh
367018e25fbSMichael Walsh    # Check the range (if any).
368018e25fbSMichael Walsh    if lower:
369018e25fbSMichael Walsh        lower = int(str(lower), 0)
370018e25fbSMichael Walsh    if upper:
371018e25fbSMichael Walsh        upper = int(str(upper), 0)
372018e25fbSMichael Walsh    error_message = valid_range(var_value, lower, upper, var_name=var_name)
373018e25fbSMichael Walsh
374018e25fbSMichael Walsh    return process_error_message(error_message)
375018e25fbSMichael Walsh
376018e25fbSMichael Walsh
377018e25fbSMichael Walshdef valid_dir_path(var_value, *args, **kwargs):
378018e25fbSMichael Walsh    r"""
379018e25fbSMichael Walsh    The variable value is valid if it contains the path of an existing
380018e25fbSMichael Walsh    directory.
381018e25fbSMichael Walsh
382018e25fbSMichael Walsh    Description of argument(s):
383018e25fbSMichael Walsh    var_value                       The value being validated.
38478bdfdd6SMichael Walsh    """
38578bdfdd6SMichael Walsh
38678bdfdd6SMichael Walsh    error_message = ""
38778bdfdd6SMichael Walsh    if not os.path.isdir(str(var_value)):
388018e25fbSMichael Walsh        var_name = get_var_name(*args, **kwargs)
389018e25fbSMichael Walsh        error_message += "The following directory does not exist:\n"
39000244345SMichael Walsh        error_message += gp.sprint_varx(var_name, var_value, gp.blank())
39178bdfdd6SMichael Walsh
392e23b5ad3SMichael Walsh    return process_error_message(error_message)
39378bdfdd6SMichael Walsh
39478bdfdd6SMichael Walsh
395018e25fbSMichael Walshdef valid_file_path(var_value, *args, **kwargs):
39678bdfdd6SMichael Walsh    r"""
397018e25fbSMichael Walsh    The variable value is valid if it contains the path of an existing file.
39878bdfdd6SMichael Walsh
399018e25fbSMichael Walsh    Description of argument(s):
40078bdfdd6SMichael Walsh    var_value                       The value being validated.
40178bdfdd6SMichael Walsh    """
40278bdfdd6SMichael Walsh
40378bdfdd6SMichael Walsh    error_message = ""
40478bdfdd6SMichael Walsh    if not os.path.isfile(str(var_value)):
405018e25fbSMichael Walsh        var_name = get_var_name(*args, **kwargs)
406018e25fbSMichael Walsh        error_message += "The following file does not exist:\n"
40700244345SMichael Walsh        error_message += gp.sprint_varx(var_name, var_value, gp.blank())
40878bdfdd6SMichael Walsh
409e23b5ad3SMichael Walsh    return process_error_message(error_message)
41078bdfdd6SMichael Walsh
41178bdfdd6SMichael Walsh
412018e25fbSMichael Walshdef valid_path(var_value, *args, **kwargs):
413e23b5ad3SMichael Walsh    r"""
414018e25fbSMichael Walsh    The variable value is valid if it contains the path of an existing file or
415018e25fbSMichael Walsh    directory.
416e23b5ad3SMichael Walsh
417018e25fbSMichael Walsh    Description of argument(s):
418e23b5ad3SMichael Walsh    var_value                       The value being validated.
419e23b5ad3SMichael Walsh    """
420e23b5ad3SMichael Walsh
421e23b5ad3SMichael Walsh    error_message = ""
422e23b5ad3SMichael Walsh    if not (os.path.isfile(str(var_value)) or os.path.isdir(str(var_value))):
423018e25fbSMichael Walsh        var_name = get_var_name(*args, **kwargs)
424018e25fbSMichael Walsh        error_message += "Invalid path (file or directory does not exist):\n"
42500244345SMichael Walsh        error_message += gp.sprint_varx(var_name, var_value, gp.blank())
426e23b5ad3SMichael Walsh
427e23b5ad3SMichael Walsh    return process_error_message(error_message)
4282c687e98SMichael Walsh
4292c687e98SMichael Walsh
43035026be5SMichael Walshdef valid_list(var_value, valid_values=[], invalid_values=[],
43135026be5SMichael Walsh               required_values=[], fail_on_empty=False, *args, **kwargs):
4322c687e98SMichael Walsh    r"""
433018e25fbSMichael Walsh    The variable value is valid if it is a list where each entry can be found
43435026be5SMichael Walsh    in the valid_values list or if none of its values can be found in the
43535026be5SMichael Walsh    invalid_values list or if all of the values in the required_values list
43635026be5SMichael Walsh    can be found in var_value.
43735026be5SMichael Walsh
43835026be5SMichael Walsh    The caller may only specify one of these 3 arguments: valid_values,
43935026be5SMichael Walsh    invalid_values, required_values.
4402c687e98SMichael Walsh
441018e25fbSMichael Walsh    Description of argument(s):
442018e25fbSMichael Walsh    var_value                       The value being validated.
443ca193993SMichael Walsh    valid_values                    A list of valid values.  Each element in
444ca193993SMichael Walsh                                    the var_value list must be equal to one of
445ca193993SMichael Walsh                                    these values to be considered valid.
44635026be5SMichael Walsh    invalid_values                  A list of invalid values.  If any element
44735026be5SMichael Walsh                                    in var_value is equal to any of the values
44835026be5SMichael Walsh                                    in this argument, var_value is considered
44935026be5SMichael Walsh                                    invalid.
45035026be5SMichael Walsh    required_values                 Every value in required_values must be
45135026be5SMichael Walsh                                    found in var_value.  Otherwise, var_value
45235026be5SMichael Walsh                                    is considered invalid.
453018e25fbSMichael Walsh    fail_on_empty                   Indicates that an empty list for the
454018e25fbSMichael Walsh                                    variable value should be considered an
455018e25fbSMichael Walsh                                    error.
456ca193993SMichael Walsh    """
457ca193993SMichael Walsh
458ca193993SMichael Walsh    error_message = ""
459018e25fbSMichael Walsh
46035026be5SMichael Walsh    # Validate this function's arguments.
46135026be5SMichael Walsh    len_valid_values = len(valid_values)
46235026be5SMichael Walsh    len_invalid_values = len(invalid_values)
46335026be5SMichael Walsh    len_required_values = len(required_values)
46435026be5SMichael Walsh    if (len_valid_values + len_invalid_values + len_required_values) > 1:
46535026be5SMichael Walsh        error_message += "Programmer error - You must provide only one of the"
46635026be5SMichael Walsh        error_message += " following: valid_values, invalid_values,"
46735026be5SMichael Walsh        error_message += " required_values.\n"
46835026be5SMichael Walsh        error_message += gp.sprint_var(invalid_values)
46935026be5SMichael Walsh        error_message += gp.sprint_var(valid_values)
47035026be5SMichael Walsh        error_message += gp.sprint_var(required_values)
47135026be5SMichael Walsh        return process_error_message(error_message)
47235026be5SMichael Walsh
473018e25fbSMichael Walsh    if type(var_value) is not list:
474018e25fbSMichael Walsh        var_name = get_var_name(*args, **kwargs)
475018e25fbSMichael Walsh        error_message = valid_type(var_value, list, var_name=var_name)
476018e25fbSMichael Walsh        if error_message:
477018e25fbSMichael Walsh            return process_error_message(error_message)
478018e25fbSMichael Walsh
479018e25fbSMichael Walsh    if fail_on_empty and len(var_value) == 0:
480018e25fbSMichael Walsh        var_name = get_var_name(*args, **kwargs)
481018e25fbSMichael Walsh        error_message += "Invalid empty list:\n"
482018e25fbSMichael Walsh        error_message += gp.sprint_varx(var_name, var_value, gp.show_type())
483018e25fbSMichael Walsh        return process_error_message(error_message)
484ca193993SMichael Walsh
48535026be5SMichael Walsh    if len(required_values):
48635026be5SMichael Walsh        found_error = 0
48735026be5SMichael Walsh        display_required_values = list(required_values)
48835026be5SMichael Walsh        for ix in range(0, len(required_values)):
48935026be5SMichael Walsh            if required_values[ix] not in var_value:
49035026be5SMichael Walsh                found_error = 1
49135026be5SMichael Walsh                display_required_values[ix] = \
49235026be5SMichael Walsh                    str(display_required_values[ix]) + "*"
49335026be5SMichael Walsh        if found_error:
49435026be5SMichael Walsh            var_name = get_var_name(*args, **kwargs)
49535026be5SMichael Walsh            error_message += "The following list is invalid:\n"
49635026be5SMichael Walsh            error_message += gp.sprint_varx(var_name, var_value,
49735026be5SMichael Walsh                                            gp.blank() | gp.show_type())
49835026be5SMichael Walsh            error_message += "\n"
49935026be5SMichael Walsh            error_message += "Because some of the values in the "
50035026be5SMichael Walsh            error_message += "required_values list are not present (see"
50135026be5SMichael Walsh            error_message += " entries marked with \"*\"):\n"
50235026be5SMichael Walsh            error_message += "\n"
50335026be5SMichael Walsh            error_message += gp.sprint_varx('required_values',
50435026be5SMichael Walsh                                            display_required_values,
50535026be5SMichael Walsh                                            gp.blank() | gp.show_type())
50635026be5SMichael Walsh            error_message += "\n"
50735026be5SMichael Walsh
50835026be5SMichael Walsh        return process_error_message(error_message)
50935026be5SMichael Walsh
51035026be5SMichael Walsh    if len(invalid_values):
51135026be5SMichael Walsh        found_error = 0
51235026be5SMichael Walsh        display_var_value = list(var_value)
51335026be5SMichael Walsh        for ix in range(0, len(var_value)):
51435026be5SMichael Walsh            if var_value[ix] in invalid_values:
51535026be5SMichael Walsh                found_error = 1
51635026be5SMichael Walsh                display_var_value[ix] = str(var_value[ix]) + "*"
51735026be5SMichael Walsh
51835026be5SMichael Walsh        if found_error:
51935026be5SMichael Walsh            var_name = get_var_name(*args, **kwargs)
52035026be5SMichael Walsh            error_message += "The following list is invalid (see entries"
52135026be5SMichael Walsh            error_message += " marked with \"*\"):\n"
52235026be5SMichael Walsh            error_message += gp.sprint_varx(var_name, display_var_value,
52335026be5SMichael Walsh                                            gp.blank() | gp.show_type())
52435026be5SMichael Walsh            error_message += "\n"
52535026be5SMichael Walsh            error_message += gp.sprint_var(invalid_values, gp.show_type())
52635026be5SMichael Walsh        return process_error_message(error_message)
52735026be5SMichael Walsh
528ca193993SMichael Walsh    found_error = 0
529ca193993SMichael Walsh    display_var_value = list(var_value)
530ca193993SMichael Walsh    for ix in range(0, len(var_value)):
531ca193993SMichael Walsh        if var_value[ix] not in valid_values:
532ca193993SMichael Walsh            found_error = 1
53335026be5SMichael Walsh            display_var_value[ix] = str(var_value[ix]) + "*"
534ca193993SMichael Walsh
535ca193993SMichael Walsh    if found_error:
536018e25fbSMichael Walsh        var_name = get_var_name(*args, **kwargs)
537018e25fbSMichael Walsh        error_message += "The following list is invalid (see entries marked"
538018e25fbSMichael Walsh        error_message += " with \"*\"):\n"
539018e25fbSMichael Walsh        error_message += gp.sprint_varx(var_name, display_var_value,
540018e25fbSMichael Walsh                                        gp.blank() | gp.show_type())
541018e25fbSMichael Walsh        error_message += "\n"
54235026be5SMichael Walsh        error_message += gp.sprint_var(valid_values, gp.show_type())
543018e25fbSMichael Walsh        return process_error_message(error_message)
544ca193993SMichael Walsh
545ca193993SMichael Walsh    return process_error_message(error_message)
5467ac5fd81SMichael Walsh
5477ac5fd81SMichael Walsh
548018e25fbSMichael Walshdef valid_dict(var_value, required_keys=[], *args, **kwargs):
5497ac5fd81SMichael Walsh    r"""
550018e25fbSMichael Walsh    The variable value is valid if it is a dictionary containing all of the
551018e25fbSMichael Walsh    required keys.
5527ac5fd81SMichael Walsh
553018e25fbSMichael Walsh    Description of argument(s):
554018e25fbSMichael Walsh    var_value                       The value being validated.
5557ac5fd81SMichael Walsh    required_keys                   A list of keys which must be found in the
5567ac5fd81SMichael Walsh                                    dictionary for it to be considered valid.
5577ac5fd81SMichael Walsh    """
5587ac5fd81SMichael Walsh
5597ac5fd81SMichael Walsh    error_message = ""
560018e25fbSMichael Walsh    missing_keys = list(set(required_keys) - set(var_value.keys()))
561018e25fbSMichael Walsh    if len(missing_keys) > 0:
562018e25fbSMichael Walsh        var_name = get_var_name(*args, **kwargs)
563018e25fbSMichael Walsh        error_message += "The following dictionary is invalid because it is"
564018e25fbSMichael Walsh        error_message += " missing required keys:\n"
565018e25fbSMichael Walsh        error_message += gp.sprint_varx(var_name, var_value,
566018e25fbSMichael Walsh                                        gp.blank() | gp.show_type())
567018e25fbSMichael Walsh        error_message += "\n"
56835026be5SMichael Walsh        error_message += gp.sprint_var(missing_keys, gp.show_type())
5697ac5fd81SMichael Walsh    return process_error_message(error_message)
570018e25fbSMichael Walsh
571018e25fbSMichael Walsh
572be3a8150SMichael Walshdef valid_program(var_value, *args, **kwargs):
573be3a8150SMichael Walsh    r"""
574be3a8150SMichael Walsh    The variable value is valid if it contains the name of a program which can
575be3a8150SMichael Walsh    be located using the "which" command.
576be3a8150SMichael Walsh
577be3a8150SMichael Walsh    Description of argument(s):
578be3a8150SMichael Walsh    var_value                       The value being validated.
579be3a8150SMichael Walsh    """
580be3a8150SMichael Walsh
581be3a8150SMichael Walsh    error_message = ""
582be3a8150SMichael Walsh    rc, out_buf = gc.shell_cmd("which " + var_value, quiet=1, show_err=0,
583be3a8150SMichael Walsh                               ignore_err=1)
584be3a8150SMichael Walsh    if rc:
585be3a8150SMichael Walsh        var_name = get_var_name(*args, **kwargs)
586be3a8150SMichael Walsh        error_message += "The following required program could not be found"
587be3a8150SMichael Walsh        error_message += " using the $PATH environment variable:\n"
58800244345SMichael Walsh        error_message += gp.sprint_varx(var_name, var_value, gp.blank())
589be3a8150SMichael Walsh        PATH = os.environ.get("PATH", "").split(":")
590be3a8150SMichael Walsh        error_message += "\n"
591be3a8150SMichael Walsh        error_message += gp.sprint_var(PATH)
592be3a8150SMichael Walsh    return process_error_message(error_message)
593be3a8150SMichael Walsh
594be3a8150SMichael Walsh
595*b9d8dfd2SMichael Walshdef valid_length(var_value, min_length=None, max_length=None, *args, **kwargs):
596*b9d8dfd2SMichael Walsh    r"""
597*b9d8dfd2SMichael Walsh    The variable value is valid if it is an object (e.g. list, dictionary)
598*b9d8dfd2SMichael Walsh    whose length is within the specified range.
599*b9d8dfd2SMichael Walsh
600*b9d8dfd2SMichael Walsh    Description of argument(s):
601*b9d8dfd2SMichael Walsh    var_value                       The value being validated.
602*b9d8dfd2SMichael Walsh    min_length                      The minimum length of the object.  If not
603*b9d8dfd2SMichael Walsh                                    None, the length of var_value must be
604*b9d8dfd2SMichael Walsh                                    greater than or equal to min_length.
605*b9d8dfd2SMichael Walsh    max_length                      The maximum length of the object.  If not
606*b9d8dfd2SMichael Walsh                                    None, the length of var_value must be less
607*b9d8dfd2SMichael Walsh                                    than or equal to min_length.
608*b9d8dfd2SMichael Walsh    """
609*b9d8dfd2SMichael Walsh
610*b9d8dfd2SMichael Walsh    error_message = ""
611*b9d8dfd2SMichael Walsh    length = len(var_value)
612*b9d8dfd2SMichael Walsh    error_message = valid_range(length, min_length, max_length)
613*b9d8dfd2SMichael Walsh    if error_message:
614*b9d8dfd2SMichael Walsh        var_name = get_var_name(*args, **kwargs)
615*b9d8dfd2SMichael Walsh        error_message = "The length of the following object is not within the"
616*b9d8dfd2SMichael Walsh        error_message += " expected range:\n"
617*b9d8dfd2SMichael Walsh        error_message += gp.sprint_var(length)
618*b9d8dfd2SMichael Walsh        error_message += gp.sprint_varx(var_name, var_value, gp.blank())
619*b9d8dfd2SMichael Walsh        error_message += "\n"
620*b9d8dfd2SMichael Walsh        error_message += gp.sprint_vars(min_length, max_length)
621*b9d8dfd2SMichael Walsh        return process_error_message(error_message)
622*b9d8dfd2SMichael Walsh
623*b9d8dfd2SMichael Walsh    return process_error_message(error_message)
624*b9d8dfd2SMichael Walsh
625*b9d8dfd2SMichael Walsh
626018e25fbSMichael Walsh# Modify selected function docstrings by adding headers/footers.
627018e25fbSMichael Walsh
628018e25fbSMichael Walshfunc_names = [
629018e25fbSMichael Walsh    "valid_type", "valid_value", "valid_range", "valid_integer",
630018e25fbSMichael Walsh    "valid_dir_path", "valid_file_path", "valid_path", "valid_list",
631be3a8150SMichael Walsh    "valid_dict", "valid_program"
632018e25fbSMichael Walsh]
633018e25fbSMichael Walsh
634018e25fbSMichael Walshraw_doc_strings = {}
635018e25fbSMichael Walsh
636018e25fbSMichael Walshfor func_name in func_names:
637018e25fbSMichael Walsh    cmd_buf = "raw_doc_strings['" + func_name + "'] = " + func_name
638018e25fbSMichael Walsh    cmd_buf += ".__doc__"
639018e25fbSMichael Walsh    exec(cmd_buf)
640018e25fbSMichael Walsh    cmd_buf = func_name + ".__doc__ = docstring_header + " + func_name
641018e25fbSMichael Walsh    cmd_buf += ".__doc__.rstrip(\" \\n\") + additional_args_docstring_footer"
642018e25fbSMichael Walsh    exec(cmd_buf)
643