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
9*be3a8150SMichael Walshimport re
107423c01aSMichael Walshimport gen_print as gp
11*be3a8150SMichael Walshimport gen_cmd as gc
12018e25fbSMichael Walshimport func_args as fa
137423c01aSMichael Walsh
14e23b5ad3SMichael Walshexit_on_error = False
15e23b5ad3SMichael Walsh
16e23b5ad3SMichael Walsh
17e23b5ad3SMichael Walshdef set_exit_on_error(value):
18e23b5ad3SMichael Walsh    r"""
19e23b5ad3SMichael Walsh    Set the exit_on_error value to either True or False.
20e23b5ad3SMichael Walsh
21018e25fbSMichael Walsh    If exit_on_error is set, validation functions like valid_value() will exit
22e23b5ad3SMichael Walsh    the program on error instead of returning False.
23e23b5ad3SMichael Walsh
24e23b5ad3SMichael Walsh    Description of argument(s):
25e23b5ad3SMichael Walsh    value                           Value to set global exit_on_error to.
26e23b5ad3SMichael Walsh    """
27e23b5ad3SMichael Walsh
28e23b5ad3SMichael Walsh    global exit_on_error
29e23b5ad3SMichael Walsh    exit_on_error = value
30e23b5ad3SMichael Walsh
31e23b5ad3SMichael Walsh
32018e25fbSMichael Walshdef get_var_name(*args, **kwargs):
33e23b5ad3SMichael Walsh    r"""
34018e25fbSMichael Walsh    If args/kwargs contain a var_name, simply return its value.  Otherwise,
35018e25fbSMichael Walsh    get the variable name of the first argument used to call the validation
36018e25fbSMichael Walsh    function (e.g. valid, valid_integer, etc.) and return it.
37e23b5ad3SMichael Walsh
38e23b5ad3SMichael Walsh    This function is designed solely for use by other functions in this file.
39e23b5ad3SMichael Walsh
40e23b5ad3SMichael Walsh    Example:
41e23b5ad3SMichael Walsh
42e23b5ad3SMichael Walsh    A programmer codes this:
43e23b5ad3SMichael Walsh
44e23b5ad3SMichael Walsh    valid_value(last_name)
45e23b5ad3SMichael Walsh
46e23b5ad3SMichael Walsh    Which results in the following call stack:
47e23b5ad3SMichael Walsh
48e23b5ad3SMichael Walsh    valid_value(last_name)
49e23b5ad3SMichael Walsh      -> get_var_name(var_name)
50e23b5ad3SMichael Walsh
51e23b5ad3SMichael Walsh    In this example, this function will return "last_name".
52e23b5ad3SMichael Walsh
53e23b5ad3SMichael Walsh    Example:
54e23b5ad3SMichael Walsh
55018e25fbSMichael Walsh    err_msg = valid_value(last_name, var_name="some_other_name")
56e23b5ad3SMichael Walsh
57e23b5ad3SMichael Walsh    Which results in the following call stack:
58e23b5ad3SMichael Walsh
59018e25fbSMichael Walsh    valid_value(var_value, var_name="some_other_name")
60e23b5ad3SMichael Walsh      -> get_var_name(var_name)
61e23b5ad3SMichael Walsh
62e23b5ad3SMichael Walsh    In this example, this function will return "some_other_name".
63e23b5ad3SMichael Walsh
64e23b5ad3SMichael Walsh    Description of argument(s):
65e23b5ad3SMichael Walsh    var_name                        The name of the variable.
66e23b5ad3SMichael Walsh    """
67e23b5ad3SMichael Walsh
68018e25fbSMichael Walsh    var_name, args, kwargs = fa.pop_arg(*args, **kwargs)
69018e25fbSMichael Walsh    if var_name:
70e23b5ad3SMichael Walsh        return var_name
71018e25fbSMichael Walsh    return gp.get_arg_name(0, 1, stack_frame_ix=3)
72e23b5ad3SMichael Walsh
73e23b5ad3SMichael Walsh
74e23b5ad3SMichael Walshdef process_error_message(error_message):
75e23b5ad3SMichael Walsh    r"""
76018e25fbSMichael Walsh    Process the error_message in the manner described below.
77e23b5ad3SMichael Walsh
78018e25fbSMichael Walsh    This function is designed solely for use by other functions in this file.
79018e25fbSMichael Walsh
80018e25fbSMichael Walsh    NOTE: A blank error_message means that there is no error.
81018e25fbSMichael Walsh
82018e25fbSMichael Walsh    For the following explanations, assume the caller of this function is a
83018e25fbSMichael Walsh    function with the following definition:
84018e25fbSMichael Walsh    valid_value(var_value, valid_values=[], invalid_values=[], *args,
85018e25fbSMichael Walsh    **kwargs):
86018e25fbSMichael Walsh
87018e25fbSMichael Walsh    If the user of valid_value() is assigning the valid_value() return value
88018e25fbSMichael Walsh    to a variable, process_error_message() will simply return the
89018e25fbSMichael Walsh    error_message.  This mode of usage is illustrated by the following example:
90018e25fbSMichael Walsh
91018e25fbSMichael Walsh    error_message = valid_value(var1)
92018e25fbSMichael Walsh
93018e25fbSMichael Walsh    This mode is useful for callers who wish to validate a variable and then
94018e25fbSMichael Walsh    decide for themselves what to do with the error_message (e.g.
95018e25fbSMichael Walsh    raise(error_message), BuiltIn().fail(error_message), etc.).
96018e25fbSMichael Walsh
97018e25fbSMichael Walsh    If the user of valid_value() is NOT assigning the valid_value() return
98018e25fbSMichael Walsh    value to a variable, process_error_message() will behave as follows.
99018e25fbSMichael Walsh
100018e25fbSMichael Walsh    First, if error_message is non-blank, it will be printed to stderr via a
101018e25fbSMichael Walsh    call to gp.print_error_report(error_message).
102018e25fbSMichael Walsh
103018e25fbSMichael Walsh    If exit_on_error is set:
104018e25fbSMichael Walsh    - If the error_message is blank, simply return.
105018e25fbSMichael Walsh    - If the error_message is non-blank, exit the program with a return code
106018e25fbSMichael Walsh      of 1.
107018e25fbSMichael Walsh
108018e25fbSMichael Walsh    If exit_on_error is NOT set:
109018e25fbSMichael Walsh    - If the error_message is blank, return True.
110018e25fbSMichael Walsh    - If the error_message is non-blank, return False.
111e23b5ad3SMichael Walsh
112e23b5ad3SMichael Walsh    Description of argument(s):
113e23b5ad3SMichael Walsh    error_message                   An error message.
114e23b5ad3SMichael Walsh    """
115e23b5ad3SMichael Walsh
116018e25fbSMichael Walsh    # Determine whether the caller's caller is assigning the result to a
117018e25fbSMichael Walsh    # variable.
118018e25fbSMichael Walsh    l_value = gp.get_arg_name(None, -1, stack_frame_ix=3)
119018e25fbSMichael Walsh    if l_value:
120018e25fbSMichael Walsh        return error_message
121018e25fbSMichael Walsh
122e23b5ad3SMichael Walsh    if error_message == "":
123018e25fbSMichael Walsh        if exit_on_error:
124018e25fbSMichael Walsh            return
125e23b5ad3SMichael Walsh        return True
126e23b5ad3SMichael Walsh
127018e25fbSMichael Walsh    gp.print_error_report(error_message, stack_frame_ix=4)
128e23b5ad3SMichael Walsh    if exit_on_error:
12910a4d98bSMichael Walsh        exit(1)
130e23b5ad3SMichael Walsh    return False
131e23b5ad3SMichael Walsh
1327423c01aSMichael Walsh
133018e25fbSMichael Walsh# Note to programmers:  All of the validation functions in this module should
134018e25fbSMichael Walsh# follow the same basic template:
135018e25fbSMichael Walsh# def valid_value(var_value, var1, var2, varn, *args, **kwargs):
136018e25fbSMichael Walsh#
137018e25fbSMichael Walsh#     error_message = ""
138018e25fbSMichael Walsh#     if not valid:
139018e25fbSMichael Walsh#         var_name = get_var_name(*args, **kwargs)
140018e25fbSMichael Walsh#         error_message += "The following variable is invalid because...:\n"
141018e25fbSMichael Walsh#         error_message += gp.sprint_varx(var_name, var_value, gp.blank())
142018e25fbSMichael Walsh#
143018e25fbSMichael Walsh#     return process_error_message(error_message)
1447423c01aSMichael Walsh
145018e25fbSMichael Walsh
146018e25fbSMichael Walsh# The docstring header and footer will be added to each validation function's
147018e25fbSMichael Walsh# existing docstring.
148018e25fbSMichael Walshdocstring_header = \
149018e25fbSMichael Walsh    r"""
150018e25fbSMichael Walsh    Determine whether var_value is valid, construct an error_message and call
151018e25fbSMichael Walsh    process_error_message(error_message).
152018e25fbSMichael Walsh
153018e25fbSMichael Walsh    See the process_error_message() function defined in this module for a
154018e25fbSMichael Walsh    description of how error messages are processed.
1557423c01aSMichael Walsh    """
1567423c01aSMichael Walsh
157018e25fbSMichael Walshadditional_args_docstring_footer = \
158018e25fbSMichael Walsh    r"""
159018e25fbSMichael Walsh    args                            Additional positional arguments (described
160018e25fbSMichael Walsh                                    below).
161018e25fbSMichael Walsh    kwargs                          Additional keyword arguments (described
162018e25fbSMichael Walsh                                    below).
163018e25fbSMichael Walsh
164018e25fbSMichael Walsh    Additional argument(s):
165018e25fbSMichael Walsh    var_name                        The name of the variable whose value is
166018e25fbSMichael Walsh                                    passed in var_value.  For the general
167018e25fbSMichael Walsh                                    case, this argument is unnecessary as this
168018e25fbSMichael Walsh                                    function can figure out the var_name.
169018e25fbSMichael Walsh                                    This is provided for Robot callers in
170018e25fbSMichael Walsh                                    which case, this function lacks the
171018e25fbSMichael Walsh                                    ability to determine the variable name.
172018e25fbSMichael Walsh    """
173018e25fbSMichael Walsh
174018e25fbSMichael Walsh
175018e25fbSMichael Walshdef valid_type(var_value, required_type, *args, **kwargs):
176018e25fbSMichael Walsh    r"""
177018e25fbSMichael Walsh    The variable value is valid if it is of the required type.
178018e25fbSMichael Walsh
179018e25fbSMichael Walsh    Examples:
180018e25fbSMichael Walsh
181018e25fbSMichael Walsh    valid_type(var1, int)
182018e25fbSMichael Walsh
183018e25fbSMichael Walsh    valid_type(var1, (list, dict))
184018e25fbSMichael Walsh
185018e25fbSMichael Walsh    Description of argument(s):
186018e25fbSMichael Walsh    var_value                       The value being validated.
187018e25fbSMichael Walsh    required_type                   A type or a tuple of types (e.g. str, int,
188018e25fbSMichael Walsh                                    etc.).
189018e25fbSMichael Walsh    """
190018e25fbSMichael Walsh
191018e25fbSMichael Walsh    error_message = ""
192018e25fbSMichael Walsh    if type(required_type) is tuple:
193018e25fbSMichael Walsh        if type(var_value) in required_type:
194018e25fbSMichael Walsh            return process_error_message(error_message)
195018e25fbSMichael Walsh    else:
196018e25fbSMichael Walsh        if type(var_value) is required_type:
197018e25fbSMichael Walsh            return process_error_message(error_message)
198018e25fbSMichael Walsh
199018e25fbSMichael Walsh    # If we get to this point, the validation has failed.
200018e25fbSMichael Walsh    var_name = get_var_name(*args, **kwargs)
201018e25fbSMichael Walsh    error_message += "Invalid variable type:\n"
202018e25fbSMichael Walsh    error_message += gp.sprint_varx(var_name, var_value,
203018e25fbSMichael Walsh                                    gp.blank() | gp.show_type())
204018e25fbSMichael Walsh    error_message += "\n"
205018e25fbSMichael Walsh    error_message += gp.sprint_var(required_type)
206018e25fbSMichael Walsh
207018e25fbSMichael Walsh    return process_error_message(error_message)
208018e25fbSMichael Walsh
209018e25fbSMichael Walsh
210018e25fbSMichael Walshdef valid_value(var_value, valid_values=[], invalid_values=[], *args,
211018e25fbSMichael Walsh                **kwargs):
212018e25fbSMichael Walsh
213018e25fbSMichael Walsh    r"""
214018e25fbSMichael Walsh    The variable value is valid if it is either contained in the valid_values
215018e25fbSMichael Walsh    list or if it is NOT contained in the invalid_values list.  If the caller
216018e25fbSMichael Walsh    specifies nothing for either of these 2 arguments, invalid_values will be
217018e25fbSMichael Walsh    initialized to ['', None].  This is a good way to fail on variables which
218018e25fbSMichael Walsh    contain blank values.
219018e25fbSMichael Walsh
220018e25fbSMichael Walsh    It is illegal to specify both valid_values and invalid values.
221018e25fbSMichael Walsh
222018e25fbSMichael Walsh    Example:
223018e25fbSMichael Walsh
224018e25fbSMichael Walsh    var1 = ''
225018e25fbSMichael Walsh    valid_value(var1)
226018e25fbSMichael Walsh
227018e25fbSMichael Walsh    This code would fail because var1 is blank and the default value for
228018e25fbSMichael Walsh    invalid_values is ['', None].
229018e25fbSMichael Walsh
230018e25fbSMichael Walsh    Example:
231018e25fbSMichael Walsh    var1 = 'yes'
232018e25fbSMichael Walsh    valid_value(var1, valid_values=['yes', 'true'])
233018e25fbSMichael Walsh
234018e25fbSMichael Walsh    This code would pass.
235018e25fbSMichael Walsh
236018e25fbSMichael Walsh    Description of argument(s):
237018e25fbSMichael Walsh    var_value                       The value being validated.
238018e25fbSMichael Walsh    valid_values                    A list of valid values.  The variable
239018e25fbSMichael Walsh                                    value must be equal to one of these values
240018e25fbSMichael Walsh                                    to be considered valid.
241018e25fbSMichael Walsh    invalid_values                  A list of invalid values.  If the variable
242018e25fbSMichael Walsh                                    value is equal to any of these, it is
243018e25fbSMichael Walsh                                    considered invalid.
244018e25fbSMichael Walsh    """
245018e25fbSMichael Walsh
246bec416ddSMichael Walsh    error_message = ""
247bec416ddSMichael Walsh
248e23b5ad3SMichael Walsh    # Validate this function's arguments.
2497423c01aSMichael Walsh    len_valid_values = len(valid_values)
2507423c01aSMichael Walsh    len_invalid_values = len(invalid_values)
2517423c01aSMichael Walsh    if len_valid_values > 0 and len_invalid_values > 0:
252018e25fbSMichael Walsh        error_message += "Programmer error - You must provide either an"
253018e25fbSMichael Walsh        error_message += " invalid_values list or a valid_values"
254018e25fbSMichael Walsh        error_message += " list but NOT both:\n"
255018e25fbSMichael Walsh        error_message += gp.sprint_var(invalid_values)
256018e25fbSMichael Walsh        error_message += gp.sprint_var(valid_values)
257018e25fbSMichael Walsh        return process_error_message(error_message)
2587423c01aSMichael Walsh
2597423c01aSMichael Walsh    if len_valid_values > 0:
2607423c01aSMichael Walsh        # Processing the valid_values list.
2617423c01aSMichael Walsh        if var_value in valid_values:
262018e25fbSMichael Walsh            return process_error_message(error_message)
263018e25fbSMichael Walsh        var_name = get_var_name(*args, **kwargs)
264018e25fbSMichael Walsh        error_message += "Invalid variable value:\n"
265018e25fbSMichael Walsh        error_message += gp.sprint_varx(var_name, var_value,
266018e25fbSMichael Walsh                                        gp.blank() | gp.verbose()
267018e25fbSMichael Walsh                                        | gp.show_type())
268018e25fbSMichael Walsh        error_message += "\n"
269018e25fbSMichael Walsh        error_message += "It must be one of the following values:\n"
270018e25fbSMichael Walsh        error_message += "\n"
271018e25fbSMichael Walsh        error_message += gp.sprint_var(valid_values,
272018e25fbSMichael Walsh                                       gp.blank() | gp.show_type())
273018e25fbSMichael Walsh        return process_error_message(error_message)
2747423c01aSMichael Walsh
2757423c01aSMichael Walsh    if len_invalid_values == 0:
276bec416ddSMichael Walsh        # Assign default value.
277018e25fbSMichael Walsh        invalid_values = ["", None]
2787423c01aSMichael Walsh
2797423c01aSMichael Walsh    # Assertion: We have an invalid_values list.  Processing it now.
2807423c01aSMichael Walsh    if var_value not in invalid_values:
281018e25fbSMichael Walsh        return process_error_message(error_message)
282bec416ddSMichael Walsh
283018e25fbSMichael Walsh    var_name = get_var_name(*args, **kwargs)
284018e25fbSMichael Walsh    error_message += "Invalid variable value:\n"
285018e25fbSMichael Walsh    error_message += gp.sprint_varx(var_name, var_value,
286018e25fbSMichael Walsh                                    gp.blank() | gp.verbose()
287018e25fbSMichael Walsh                                    | gp.show_type())
288018e25fbSMichael Walsh    error_message += "\n"
289018e25fbSMichael Walsh    error_message += "It must NOT be one of the following values:\n"
290018e25fbSMichael Walsh    error_message += "\n"
291018e25fbSMichael Walsh    error_message += gp.sprint_var(invalid_values,
292018e25fbSMichael Walsh                                   gp.blank() | gp.show_type())
293e23b5ad3SMichael Walsh    return process_error_message(error_message)
2947423c01aSMichael Walsh
295bec416ddSMichael Walsh
296018e25fbSMichael Walshdef valid_range(var_value, lower=None, upper=None, *args, **kwargs):
297bec416ddSMichael Walsh    r"""
298018e25fbSMichael Walsh    The variable value is valid if it is within the specified range.
299bec416ddSMichael Walsh
300018e25fbSMichael Walsh    This function can be used with any type of operands where they can have a
301018e25fbSMichael Walsh    greater than/less than relationship to each other (e.g. int, float, str).
302018e25fbSMichael Walsh
303018e25fbSMichael Walsh    Description of argument(s):
304bec416ddSMichael Walsh    var_value                       The value being validated.
305018e25fbSMichael Walsh    lower                           The lower end of the range.  If not None,
306018e25fbSMichael Walsh                                    the var_value must be greater than or
307018e25fbSMichael Walsh                                    equal to lower.
308018e25fbSMichael Walsh    upper                           The upper end of the range.  If not None,
309018e25fbSMichael Walsh                                    the var_value must be less than or equal
310018e25fbSMichael Walsh                                    to upper.
311bec416ddSMichael Walsh    """
312bec416ddSMichael Walsh
313bec416ddSMichael Walsh    error_message = ""
314018e25fbSMichael Walsh    if not lower and not upper:
315018e25fbSMichael Walsh        return process_error_message(error_message)
316018e25fbSMichael Walsh    if not lower and var_value <= upper:
317018e25fbSMichael Walsh        return process_error_message(error_message)
318018e25fbSMichael Walsh    if not upper and var_value >= lower:
319018e25fbSMichael Walsh        return process_error_message(error_message)
320018e25fbSMichael Walsh    if lower and upper:
321018e25fbSMichael Walsh        if lower > upper:
322018e25fbSMichael Walsh            var_name = get_var_name(*args, **kwargs)
323018e25fbSMichael Walsh            error_message += "Programmer error - the lower value is greater"
324018e25fbSMichael Walsh            error_message += " than the upper value:\n"
325018e25fbSMichael Walsh            error_message += gp.sprint_vars(lower, upper, fmt=gp.show_type())
326018e25fbSMichael Walsh            return process_error_message(error_message)
327018e25fbSMichael Walsh        if lower <= var_value <= upper:
328018e25fbSMichael Walsh            return process_error_message(error_message)
329bec416ddSMichael Walsh
330018e25fbSMichael Walsh    var_name = get_var_name(*args, **kwargs)
331018e25fbSMichael Walsh    error_message += "The following variable is not within the expected"
332018e25fbSMichael Walsh    error_message += " range:\n"
333018e25fbSMichael Walsh    error_message += gp.sprint_varx(var_name, var_value, gp.show_type())
334018e25fbSMichael Walsh    error_message += "\n"
335018e25fbSMichael Walsh    error_message += "range:\n"
336018e25fbSMichael Walsh    error_message += gp.sprint_vars(lower, upper, fmt=gp.show_type(), indent=2)
337e23b5ad3SMichael Walsh    return process_error_message(error_message)
3387423c01aSMichael Walsh
33978bdfdd6SMichael Walsh
340018e25fbSMichael Walshdef valid_integer(var_value, lower=None, upper=None, *args, **kwargs):
34178bdfdd6SMichael Walsh    r"""
342018e25fbSMichael Walsh    The variable value is valid if it is an integer or can be interpreted as
343018e25fbSMichael Walsh    an integer (e.g. 7, "7", etc.).
34478bdfdd6SMichael Walsh
345018e25fbSMichael Walsh    This function also calls valid_range to make sure the integer value is
346018e25fbSMichael Walsh    within the specified range (if any).
347018e25fbSMichael Walsh
348018e25fbSMichael Walsh    Description of argument(s):
34978bdfdd6SMichael Walsh    var_value                       The value being validated.
350018e25fbSMichael Walsh    lower                           The lower end of the range.  If not None,
351018e25fbSMichael Walsh                                    the var_value must be greater than or
352018e25fbSMichael Walsh                                    equal to lower.
353018e25fbSMichael Walsh    upper                           The upper end of the range.  If not None,
354018e25fbSMichael Walsh                                    the var_value must be less than or equal
355018e25fbSMichael Walsh                                    to upper.
356018e25fbSMichael Walsh    """
357018e25fbSMichael Walsh
358018e25fbSMichael Walsh    error_message = ""
359018e25fbSMichael Walsh    var_name = get_var_name(*args, **kwargs)
360018e25fbSMichael Walsh    try:
361018e25fbSMichael Walsh        var_value = int(str(var_value), 0)
362018e25fbSMichael Walsh    except ValueError:
363018e25fbSMichael Walsh        error_message += "Invalid integer value:\n"
364018e25fbSMichael Walsh        error_message += gp.sprint_varx(var_name, var_value,
365018e25fbSMichael Walsh                                        gp.blank() | gp.show_type())
366018e25fbSMichael Walsh        return process_error_message(error_message)
367018e25fbSMichael Walsh
368018e25fbSMichael Walsh    # Check the range (if any).
369018e25fbSMichael Walsh    if lower:
370018e25fbSMichael Walsh        lower = int(str(lower), 0)
371018e25fbSMichael Walsh    if upper:
372018e25fbSMichael Walsh        upper = int(str(upper), 0)
373018e25fbSMichael Walsh    error_message = valid_range(var_value, lower, upper, var_name=var_name)
374018e25fbSMichael Walsh
375018e25fbSMichael Walsh    return process_error_message(error_message)
376018e25fbSMichael Walsh
377018e25fbSMichael Walsh
378018e25fbSMichael Walshdef valid_dir_path(var_value, *args, **kwargs):
379018e25fbSMichael Walsh    r"""
380018e25fbSMichael Walsh    The variable value is valid if it contains the path of an existing
381018e25fbSMichael Walsh    directory.
382018e25fbSMichael Walsh
383018e25fbSMichael Walsh    Description of argument(s):
384018e25fbSMichael Walsh    var_value                       The value being validated.
38578bdfdd6SMichael Walsh    """
38678bdfdd6SMichael Walsh
38778bdfdd6SMichael Walsh    error_message = ""
38878bdfdd6SMichael Walsh    if not os.path.isdir(str(var_value)):
389018e25fbSMichael Walsh        var_name = get_var_name(*args, **kwargs)
390018e25fbSMichael Walsh        error_message += "The following directory does not exist:\n"
391018e25fbSMichael Walsh        error_message += gp.sprint_varx(var_name, var_value)
39278bdfdd6SMichael Walsh
393e23b5ad3SMichael Walsh    return process_error_message(error_message)
39478bdfdd6SMichael Walsh
39578bdfdd6SMichael Walsh
396018e25fbSMichael Walshdef valid_file_path(var_value, *args, **kwargs):
39778bdfdd6SMichael Walsh    r"""
398018e25fbSMichael Walsh    The variable value is valid if it contains the path of an existing file.
39978bdfdd6SMichael Walsh
400018e25fbSMichael Walsh    Description of argument(s):
40178bdfdd6SMichael Walsh    var_value                       The value being validated.
40278bdfdd6SMichael Walsh    """
40378bdfdd6SMichael Walsh
40478bdfdd6SMichael Walsh    error_message = ""
40578bdfdd6SMichael Walsh    if not os.path.isfile(str(var_value)):
406018e25fbSMichael Walsh        var_name = get_var_name(*args, **kwargs)
407018e25fbSMichael Walsh        error_message += "The following file does not exist:\n"
408018e25fbSMichael Walsh        error_message += gp.sprint_varx(var_name, var_value)
40978bdfdd6SMichael Walsh
410e23b5ad3SMichael Walsh    return process_error_message(error_message)
41178bdfdd6SMichael Walsh
41278bdfdd6SMichael Walsh
413018e25fbSMichael Walshdef valid_path(var_value, *args, **kwargs):
414e23b5ad3SMichael Walsh    r"""
415018e25fbSMichael Walsh    The variable value is valid if it contains the path of an existing file or
416018e25fbSMichael Walsh    directory.
417e23b5ad3SMichael Walsh
418018e25fbSMichael Walsh    Description of argument(s):
419e23b5ad3SMichael Walsh    var_value                       The value being validated.
420e23b5ad3SMichael Walsh    """
421e23b5ad3SMichael Walsh
422e23b5ad3SMichael Walsh    error_message = ""
423e23b5ad3SMichael Walsh    if not (os.path.isfile(str(var_value)) or os.path.isdir(str(var_value))):
424018e25fbSMichael Walsh        var_name = get_var_name(*args, **kwargs)
425018e25fbSMichael Walsh        error_message += "Invalid path (file or directory does not exist):\n"
426018e25fbSMichael Walsh        error_message += gp.sprint_varx(var_name, var_value)
427e23b5ad3SMichael Walsh
428e23b5ad3SMichael Walsh    return process_error_message(error_message)
4292c687e98SMichael Walsh
4302c687e98SMichael Walsh
43135026be5SMichael Walshdef valid_list(var_value, valid_values=[], invalid_values=[],
43235026be5SMichael Walsh               required_values=[], fail_on_empty=False, *args, **kwargs):
4332c687e98SMichael Walsh    r"""
434018e25fbSMichael Walsh    The variable value is valid if it is a list where each entry can be found
43535026be5SMichael Walsh    in the valid_values list or if none of its values can be found in the
43635026be5SMichael Walsh    invalid_values list or if all of the values in the required_values list
43735026be5SMichael Walsh    can be found in var_value.
43835026be5SMichael Walsh
43935026be5SMichael Walsh    The caller may only specify one of these 3 arguments: valid_values,
44035026be5SMichael Walsh    invalid_values, required_values.
4412c687e98SMichael Walsh
442018e25fbSMichael Walsh    Description of argument(s):
443018e25fbSMichael Walsh    var_value                       The value being validated.
444ca193993SMichael Walsh    valid_values                    A list of valid values.  Each element in
445ca193993SMichael Walsh                                    the var_value list must be equal to one of
446ca193993SMichael Walsh                                    these values to be considered valid.
44735026be5SMichael Walsh    invalid_values                  A list of invalid values.  If any element
44835026be5SMichael Walsh                                    in var_value is equal to any of the values
44935026be5SMichael Walsh                                    in this argument, var_value is considered
45035026be5SMichael Walsh                                    invalid.
45135026be5SMichael Walsh    required_values                 Every value in required_values must be
45235026be5SMichael Walsh                                    found in var_value.  Otherwise, var_value
45335026be5SMichael Walsh                                    is considered invalid.
454018e25fbSMichael Walsh    fail_on_empty                   Indicates that an empty list for the
455018e25fbSMichael Walsh                                    variable value should be considered an
456018e25fbSMichael Walsh                                    error.
457ca193993SMichael Walsh    """
458ca193993SMichael Walsh
459ca193993SMichael Walsh    error_message = ""
460018e25fbSMichael Walsh
46135026be5SMichael Walsh    # Validate this function's arguments.
46235026be5SMichael Walsh    len_valid_values = len(valid_values)
46335026be5SMichael Walsh    len_invalid_values = len(invalid_values)
46435026be5SMichael Walsh    len_required_values = len(required_values)
46535026be5SMichael Walsh    if (len_valid_values + len_invalid_values + len_required_values) > 1:
46635026be5SMichael Walsh        error_message += "Programmer error - You must provide only one of the"
46735026be5SMichael Walsh        error_message += " following: valid_values, invalid_values,"
46835026be5SMichael Walsh        error_message += " required_values.\n"
46935026be5SMichael Walsh        error_message += gp.sprint_var(invalid_values)
47035026be5SMichael Walsh        error_message += gp.sprint_var(valid_values)
47135026be5SMichael Walsh        error_message += gp.sprint_var(required_values)
47235026be5SMichael Walsh        return process_error_message(error_message)
47335026be5SMichael Walsh
474018e25fbSMichael Walsh    if type(var_value) is not list:
475018e25fbSMichael Walsh        var_name = get_var_name(*args, **kwargs)
476018e25fbSMichael Walsh        error_message = valid_type(var_value, list, var_name=var_name)
477018e25fbSMichael Walsh        if error_message:
478018e25fbSMichael Walsh            return process_error_message(error_message)
479018e25fbSMichael Walsh
480018e25fbSMichael Walsh    if fail_on_empty and len(var_value) == 0:
481018e25fbSMichael Walsh        var_name = get_var_name(*args, **kwargs)
482018e25fbSMichael Walsh        error_message += "Invalid empty list:\n"
483018e25fbSMichael Walsh        error_message += gp.sprint_varx(var_name, var_value, gp.show_type())
484018e25fbSMichael Walsh        return process_error_message(error_message)
485ca193993SMichael Walsh
48635026be5SMichael Walsh    if len(required_values):
48735026be5SMichael Walsh        found_error = 0
48835026be5SMichael Walsh        display_required_values = list(required_values)
48935026be5SMichael Walsh        for ix in range(0, len(required_values)):
49035026be5SMichael Walsh            if required_values[ix] not in var_value:
49135026be5SMichael Walsh                found_error = 1
49235026be5SMichael Walsh                display_required_values[ix] = \
49335026be5SMichael Walsh                    str(display_required_values[ix]) + "*"
49435026be5SMichael Walsh        if found_error:
49535026be5SMichael Walsh            var_name = get_var_name(*args, **kwargs)
49635026be5SMichael Walsh            error_message += "The following list is invalid:\n"
49735026be5SMichael Walsh            error_message += gp.sprint_varx(var_name, var_value,
49835026be5SMichael Walsh                                            gp.blank() | gp.show_type())
49935026be5SMichael Walsh            error_message += "\n"
50035026be5SMichael Walsh            error_message += "Because some of the values in the "
50135026be5SMichael Walsh            error_message += "required_values list are not present (see"
50235026be5SMichael Walsh            error_message += " entries marked with \"*\"):\n"
50335026be5SMichael Walsh            error_message += "\n"
50435026be5SMichael Walsh            error_message += gp.sprint_varx('required_values',
50535026be5SMichael Walsh                                            display_required_values,
50635026be5SMichael Walsh                                            gp.blank() | gp.show_type())
50735026be5SMichael Walsh            error_message += "\n"
50835026be5SMichael Walsh
50935026be5SMichael Walsh        return process_error_message(error_message)
51035026be5SMichael Walsh
51135026be5SMichael Walsh    if len(invalid_values):
51235026be5SMichael Walsh        found_error = 0
51335026be5SMichael Walsh        display_var_value = list(var_value)
51435026be5SMichael Walsh        for ix in range(0, len(var_value)):
51535026be5SMichael Walsh            if var_value[ix] in invalid_values:
51635026be5SMichael Walsh                found_error = 1
51735026be5SMichael Walsh                display_var_value[ix] = str(var_value[ix]) + "*"
51835026be5SMichael Walsh
51935026be5SMichael Walsh        if found_error:
52035026be5SMichael Walsh            var_name = get_var_name(*args, **kwargs)
52135026be5SMichael Walsh            error_message += "The following list is invalid (see entries"
52235026be5SMichael Walsh            error_message += " marked with \"*\"):\n"
52335026be5SMichael Walsh            error_message += gp.sprint_varx(var_name, display_var_value,
52435026be5SMichael Walsh                                            gp.blank() | gp.show_type())
52535026be5SMichael Walsh            error_message += "\n"
52635026be5SMichael Walsh            error_message += gp.sprint_var(invalid_values, gp.show_type())
52735026be5SMichael Walsh        return process_error_message(error_message)
52835026be5SMichael Walsh
529ca193993SMichael Walsh    found_error = 0
530ca193993SMichael Walsh    display_var_value = list(var_value)
531ca193993SMichael Walsh    for ix in range(0, len(var_value)):
532ca193993SMichael Walsh        if var_value[ix] not in valid_values:
533ca193993SMichael Walsh            found_error = 1
53435026be5SMichael Walsh            display_var_value[ix] = str(var_value[ix]) + "*"
535ca193993SMichael Walsh
536ca193993SMichael Walsh    if found_error:
537018e25fbSMichael Walsh        var_name = get_var_name(*args, **kwargs)
538018e25fbSMichael Walsh        error_message += "The following list is invalid (see entries marked"
539018e25fbSMichael Walsh        error_message += " with \"*\"):\n"
540018e25fbSMichael Walsh        error_message += gp.sprint_varx(var_name, display_var_value,
541018e25fbSMichael Walsh                                        gp.blank() | gp.show_type())
542018e25fbSMichael Walsh        error_message += "\n"
54335026be5SMichael Walsh        error_message += gp.sprint_var(valid_values, gp.show_type())
544018e25fbSMichael Walsh        return process_error_message(error_message)
545ca193993SMichael Walsh
546ca193993SMichael Walsh    return process_error_message(error_message)
5477ac5fd81SMichael Walsh
5487ac5fd81SMichael Walsh
549018e25fbSMichael Walshdef valid_dict(var_value, required_keys=[], *args, **kwargs):
5507ac5fd81SMichael Walsh    r"""
551018e25fbSMichael Walsh    The variable value is valid if it is a dictionary containing all of the
552018e25fbSMichael Walsh    required keys.
5537ac5fd81SMichael Walsh
554018e25fbSMichael Walsh    Description of argument(s):
555018e25fbSMichael Walsh    var_value                       The value being validated.
5567ac5fd81SMichael Walsh    required_keys                   A list of keys which must be found in the
5577ac5fd81SMichael Walsh                                    dictionary for it to be considered valid.
5587ac5fd81SMichael Walsh    """
5597ac5fd81SMichael Walsh
5607ac5fd81SMichael Walsh    error_message = ""
561018e25fbSMichael Walsh    missing_keys = list(set(required_keys) - set(var_value.keys()))
562018e25fbSMichael Walsh    if len(missing_keys) > 0:
563018e25fbSMichael Walsh        var_name = get_var_name(*args, **kwargs)
564018e25fbSMichael Walsh        error_message += "The following dictionary is invalid because it is"
565018e25fbSMichael Walsh        error_message += " missing required keys:\n"
566018e25fbSMichael Walsh        error_message += gp.sprint_varx(var_name, var_value,
567018e25fbSMichael Walsh                                        gp.blank() | gp.show_type())
568018e25fbSMichael Walsh        error_message += "\n"
56935026be5SMichael Walsh        error_message += gp.sprint_var(missing_keys, gp.show_type())
5707ac5fd81SMichael Walsh    return process_error_message(error_message)
571018e25fbSMichael Walsh
572018e25fbSMichael Walsh
573*be3a8150SMichael Walshdef valid_program(var_value, *args, **kwargs):
574*be3a8150SMichael Walsh    r"""
575*be3a8150SMichael Walsh    The variable value is valid if it contains the name of a program which can
576*be3a8150SMichael Walsh    be located using the "which" command.
577*be3a8150SMichael Walsh
578*be3a8150SMichael Walsh    Description of argument(s):
579*be3a8150SMichael Walsh    var_value                       The value being validated.
580*be3a8150SMichael Walsh    """
581*be3a8150SMichael Walsh
582*be3a8150SMichael Walsh    error_message = ""
583*be3a8150SMichael Walsh    rc, out_buf = gc.shell_cmd("which " + var_value, quiet=1, show_err=0,
584*be3a8150SMichael Walsh                               ignore_err=1)
585*be3a8150SMichael Walsh    if rc:
586*be3a8150SMichael Walsh        var_name = get_var_name(*args, **kwargs)
587*be3a8150SMichael Walsh        error_message += "The following required program could not be found"
588*be3a8150SMichael Walsh        error_message += " using the $PATH environment variable:\n"
589*be3a8150SMichael Walsh        error_message += gp.sprint_varx(var_name, var_value)
590*be3a8150SMichael Walsh        PATH = os.environ.get("PATH", "").split(":")
591*be3a8150SMichael Walsh        error_message += "\n"
592*be3a8150SMichael Walsh        error_message += gp.sprint_var(PATH)
593*be3a8150SMichael Walsh    return process_error_message(error_message)
594*be3a8150SMichael Walsh
595*be3a8150SMichael Walsh
596018e25fbSMichael Walsh# Modify selected function docstrings by adding headers/footers.
597018e25fbSMichael Walsh
598018e25fbSMichael Walshfunc_names = [
599018e25fbSMichael Walsh    "valid_type", "valid_value", "valid_range", "valid_integer",
600018e25fbSMichael Walsh    "valid_dir_path", "valid_file_path", "valid_path", "valid_list",
601*be3a8150SMichael Walsh    "valid_dict", "valid_program"
602018e25fbSMichael Walsh]
603018e25fbSMichael Walsh
604018e25fbSMichael Walshraw_doc_strings = {}
605018e25fbSMichael Walsh
606018e25fbSMichael Walshfor func_name in func_names:
607018e25fbSMichael Walsh    cmd_buf = "raw_doc_strings['" + func_name + "'] = " + func_name
608018e25fbSMichael Walsh    cmd_buf += ".__doc__"
609018e25fbSMichael Walsh    exec(cmd_buf)
610018e25fbSMichael Walsh    cmd_buf = func_name + ".__doc__ = docstring_header + " + func_name
611018e25fbSMichael Walsh    cmd_buf += ".__doc__.rstrip(\" \\n\") + additional_args_docstring_footer"
612018e25fbSMichael Walsh    exec(cmd_buf)
613