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
31*89eff544SMichael Walshdef get_var_name(var_name):
32e23b5ad3SMichael Walsh    r"""
33*89eff544SMichael Walsh    If var_name is not None, simply return its value.  Otherwise, get the
34*89eff544SMichael Walsh    variable name of the first argument used to call the validation function
35*89eff544SMichael Walsh    (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
67*89eff544SMichael Walsh    return var_name or gp.get_arg_name(0, 1, stack_frame_ix=3)
68e23b5ad3SMichael Walsh
69e23b5ad3SMichael Walsh
70e23b5ad3SMichael Walshdef process_error_message(error_message):
71e23b5ad3SMichael Walsh    r"""
72018e25fbSMichael Walsh    Process the error_message in the manner described below.
73e23b5ad3SMichael Walsh
74018e25fbSMichael Walsh    This function is designed solely for use by other functions in this file.
75018e25fbSMichael Walsh
76018e25fbSMichael Walsh    NOTE: A blank error_message means that there is no error.
77018e25fbSMichael Walsh
78018e25fbSMichael Walsh    For the following explanations, assume the caller of this function is a
79018e25fbSMichael Walsh    function with the following definition:
80*89eff544SMichael Walsh    valid_value(var_value, valid_values=[], invalid_values=[], var_name=None):
81018e25fbSMichael Walsh
82018e25fbSMichael Walsh    If the user of valid_value() is assigning the valid_value() return value
83018e25fbSMichael Walsh    to a variable, process_error_message() will simply return the
84018e25fbSMichael Walsh    error_message.  This mode of usage is illustrated by the following example:
85018e25fbSMichael Walsh
86018e25fbSMichael Walsh    error_message = valid_value(var1)
87018e25fbSMichael Walsh
88018e25fbSMichael Walsh    This mode is useful for callers who wish to validate a variable and then
89018e25fbSMichael Walsh    decide for themselves what to do with the error_message (e.g.
90018e25fbSMichael Walsh    raise(error_message), BuiltIn().fail(error_message), etc.).
91018e25fbSMichael Walsh
92018e25fbSMichael Walsh    If the user of valid_value() is NOT assigning the valid_value() return
93018e25fbSMichael Walsh    value to a variable, process_error_message() will behave as follows.
94018e25fbSMichael Walsh
95018e25fbSMichael Walsh    First, if error_message is non-blank, it will be printed to stderr via a
96018e25fbSMichael Walsh    call to gp.print_error_report(error_message).
97018e25fbSMichael Walsh
98018e25fbSMichael Walsh    If exit_on_error is set:
99018e25fbSMichael Walsh    - If the error_message is blank, simply return.
100018e25fbSMichael Walsh    - If the error_message is non-blank, exit the program with a return code
101018e25fbSMichael Walsh      of 1.
102018e25fbSMichael Walsh
103018e25fbSMichael Walsh    If exit_on_error is NOT set:
104018e25fbSMichael Walsh    - If the error_message is blank, return True.
105018e25fbSMichael Walsh    - If the error_message is non-blank, return False.
106e23b5ad3SMichael Walsh
107e23b5ad3SMichael Walsh    Description of argument(s):
108e23b5ad3SMichael Walsh    error_message                   An error message.
109e23b5ad3SMichael Walsh    """
110e23b5ad3SMichael Walsh
111018e25fbSMichael Walsh    # Determine whether the caller's caller is assigning the result to a
112018e25fbSMichael Walsh    # variable.
113018e25fbSMichael Walsh    l_value = gp.get_arg_name(None, -1, stack_frame_ix=3)
114018e25fbSMichael Walsh    if l_value:
115018e25fbSMichael Walsh        return error_message
116018e25fbSMichael Walsh
117e23b5ad3SMichael Walsh    if error_message == "":
118018e25fbSMichael Walsh        if exit_on_error:
119018e25fbSMichael Walsh            return
120e23b5ad3SMichael Walsh        return True
121e23b5ad3SMichael Walsh
122018e25fbSMichael Walsh    gp.print_error_report(error_message, stack_frame_ix=4)
123e23b5ad3SMichael Walsh    if exit_on_error:
12410a4d98bSMichael Walsh        exit(1)
125e23b5ad3SMichael Walsh    return False
126e23b5ad3SMichael Walsh
1277423c01aSMichael Walsh
128018e25fbSMichael Walsh# Note to programmers:  All of the validation functions in this module should
129018e25fbSMichael Walsh# follow the same basic template:
130*89eff544SMichael Walsh# def valid_value(var_value, var1, var2, varn, var_name=None):
131018e25fbSMichael Walsh#
132018e25fbSMichael Walsh#     error_message = ""
133018e25fbSMichael Walsh#     if not valid:
134*89eff544SMichael Walsh#         var_name = get_var_name(var_name)
135018e25fbSMichael Walsh#         error_message += "The following variable is invalid because...:\n"
136018e25fbSMichael Walsh#         error_message += gp.sprint_varx(var_name, var_value, gp.blank())
137018e25fbSMichael Walsh#
138018e25fbSMichael Walsh#     return process_error_message(error_message)
1397423c01aSMichael Walsh
140018e25fbSMichael Walsh
141018e25fbSMichael Walsh# The docstring header and footer will be added to each validation function's
142018e25fbSMichael Walsh# existing docstring.
143018e25fbSMichael Walshdocstring_header = \
144018e25fbSMichael Walsh    r"""
145018e25fbSMichael Walsh    Determine whether var_value is valid, construct an error_message and call
146018e25fbSMichael Walsh    process_error_message(error_message).
147018e25fbSMichael Walsh
148018e25fbSMichael Walsh    See the process_error_message() function defined in this module for a
149018e25fbSMichael Walsh    description of how error messages are processed.
1507423c01aSMichael Walsh    """
1517423c01aSMichael Walsh
152018e25fbSMichael Walshadditional_args_docstring_footer = \
153018e25fbSMichael Walsh    r"""
154018e25fbSMichael Walsh    var_name                        The name of the variable whose value is
155018e25fbSMichael Walsh                                    passed in var_value.  For the general
156018e25fbSMichael Walsh                                    case, this argument is unnecessary as this
157018e25fbSMichael Walsh                                    function can figure out the var_name.
158018e25fbSMichael Walsh                                    This is provided for Robot callers in
159018e25fbSMichael Walsh                                    which case, this function lacks the
160018e25fbSMichael Walsh                                    ability to determine the variable name.
161018e25fbSMichael Walsh    """
162018e25fbSMichael Walsh
163018e25fbSMichael Walsh
164*89eff544SMichael Walshdef valid_type(var_value, required_type, var_name=None):
165018e25fbSMichael Walsh    r"""
166018e25fbSMichael Walsh    The variable value is valid if it is of the required type.
167018e25fbSMichael Walsh
168018e25fbSMichael Walsh    Examples:
169018e25fbSMichael Walsh
170018e25fbSMichael Walsh    valid_type(var1, int)
171018e25fbSMichael Walsh
172018e25fbSMichael Walsh    valid_type(var1, (list, dict))
173018e25fbSMichael Walsh
174018e25fbSMichael Walsh    Description of argument(s):
175018e25fbSMichael Walsh    var_value                       The value being validated.
176018e25fbSMichael Walsh    required_type                   A type or a tuple of types (e.g. str, int,
177018e25fbSMichael Walsh                                    etc.).
178018e25fbSMichael Walsh    """
179018e25fbSMichael Walsh
180018e25fbSMichael Walsh    error_message = ""
181018e25fbSMichael Walsh    if type(required_type) is tuple:
182018e25fbSMichael Walsh        if type(var_value) in required_type:
183018e25fbSMichael Walsh            return process_error_message(error_message)
184018e25fbSMichael Walsh    else:
185018e25fbSMichael Walsh        if type(var_value) is required_type:
186018e25fbSMichael Walsh            return process_error_message(error_message)
187018e25fbSMichael Walsh
188018e25fbSMichael Walsh    # If we get to this point, the validation has failed.
189*89eff544SMichael Walsh    var_name = get_var_name(var_name)
190018e25fbSMichael Walsh    error_message += "Invalid variable type:\n"
191018e25fbSMichael Walsh    error_message += gp.sprint_varx(var_name, var_value,
192018e25fbSMichael Walsh                                    gp.blank() | gp.show_type())
193018e25fbSMichael Walsh    error_message += "\n"
194018e25fbSMichael Walsh    error_message += gp.sprint_var(required_type)
195018e25fbSMichael Walsh
196018e25fbSMichael Walsh    return process_error_message(error_message)
197018e25fbSMichael Walsh
198018e25fbSMichael Walsh
199*89eff544SMichael Walshdef valid_value(var_value, valid_values=[], invalid_values=[], var_name=None):
200018e25fbSMichael Walsh
201018e25fbSMichael Walsh    r"""
202018e25fbSMichael Walsh    The variable value is valid if it is either contained in the valid_values
203018e25fbSMichael Walsh    list or if it is NOT contained in the invalid_values list.  If the caller
204018e25fbSMichael Walsh    specifies nothing for either of these 2 arguments, invalid_values will be
205018e25fbSMichael Walsh    initialized to ['', None].  This is a good way to fail on variables which
206018e25fbSMichael Walsh    contain blank values.
207018e25fbSMichael Walsh
208018e25fbSMichael Walsh    It is illegal to specify both valid_values and invalid values.
209018e25fbSMichael Walsh
210018e25fbSMichael Walsh    Example:
211018e25fbSMichael Walsh
212018e25fbSMichael Walsh    var1 = ''
213018e25fbSMichael Walsh    valid_value(var1)
214018e25fbSMichael Walsh
215018e25fbSMichael Walsh    This code would fail because var1 is blank and the default value for
216018e25fbSMichael Walsh    invalid_values is ['', None].
217018e25fbSMichael Walsh
218018e25fbSMichael Walsh    Example:
219018e25fbSMichael Walsh    var1 = 'yes'
220018e25fbSMichael Walsh    valid_value(var1, valid_values=['yes', 'true'])
221018e25fbSMichael Walsh
222018e25fbSMichael Walsh    This code would pass.
223018e25fbSMichael Walsh
224018e25fbSMichael Walsh    Description of argument(s):
225018e25fbSMichael Walsh    var_value                       The value being validated.
226018e25fbSMichael Walsh    valid_values                    A list of valid values.  The variable
227018e25fbSMichael Walsh                                    value must be equal to one of these values
228018e25fbSMichael Walsh                                    to be considered valid.
229018e25fbSMichael Walsh    invalid_values                  A list of invalid values.  If the variable
230018e25fbSMichael Walsh                                    value is equal to any of these, it is
231018e25fbSMichael Walsh                                    considered invalid.
232018e25fbSMichael Walsh    """
233018e25fbSMichael Walsh
234bec416ddSMichael Walsh    error_message = ""
235bec416ddSMichael Walsh
236e23b5ad3SMichael Walsh    # Validate this function's arguments.
2377423c01aSMichael Walsh    len_valid_values = len(valid_values)
2387423c01aSMichael Walsh    len_invalid_values = len(invalid_values)
2397423c01aSMichael Walsh    if len_valid_values > 0 and len_invalid_values > 0:
240018e25fbSMichael Walsh        error_message += "Programmer error - You must provide either an"
241018e25fbSMichael Walsh        error_message += " invalid_values list or a valid_values"
242018e25fbSMichael Walsh        error_message += " list but NOT both:\n"
243018e25fbSMichael Walsh        error_message += gp.sprint_var(invalid_values)
244018e25fbSMichael Walsh        error_message += gp.sprint_var(valid_values)
245018e25fbSMichael Walsh        return process_error_message(error_message)
2467423c01aSMichael Walsh
2477423c01aSMichael Walsh    if len_valid_values > 0:
2487423c01aSMichael Walsh        # Processing the valid_values list.
2497423c01aSMichael Walsh        if var_value in valid_values:
250018e25fbSMichael Walsh            return process_error_message(error_message)
251*89eff544SMichael Walsh        var_name = get_var_name(var_name)
252018e25fbSMichael Walsh        error_message += "Invalid variable value:\n"
253018e25fbSMichael Walsh        error_message += gp.sprint_varx(var_name, var_value,
254018e25fbSMichael Walsh                                        gp.blank() | gp.verbose()
255018e25fbSMichael Walsh                                        | gp.show_type())
256018e25fbSMichael Walsh        error_message += "\n"
257018e25fbSMichael Walsh        error_message += "It must be one of the following values:\n"
258018e25fbSMichael Walsh        error_message += "\n"
259018e25fbSMichael Walsh        error_message += gp.sprint_var(valid_values,
260018e25fbSMichael Walsh                                       gp.blank() | gp.show_type())
261018e25fbSMichael Walsh        return process_error_message(error_message)
2627423c01aSMichael Walsh
2637423c01aSMichael Walsh    if len_invalid_values == 0:
264bec416ddSMichael Walsh        # Assign default value.
265018e25fbSMichael Walsh        invalid_values = ["", None]
2667423c01aSMichael Walsh
2677423c01aSMichael Walsh    # Assertion: We have an invalid_values list.  Processing it now.
2687423c01aSMichael Walsh    if var_value not in invalid_values:
269018e25fbSMichael Walsh        return process_error_message(error_message)
270bec416ddSMichael Walsh
271*89eff544SMichael Walsh    var_name = get_var_name(var_name)
272018e25fbSMichael Walsh    error_message += "Invalid variable value:\n"
273018e25fbSMichael Walsh    error_message += gp.sprint_varx(var_name, var_value,
274018e25fbSMichael Walsh                                    gp.blank() | gp.verbose()
275018e25fbSMichael Walsh                                    | gp.show_type())
276018e25fbSMichael Walsh    error_message += "\n"
277018e25fbSMichael Walsh    error_message += "It must NOT be one of the following values:\n"
278018e25fbSMichael Walsh    error_message += "\n"
279018e25fbSMichael Walsh    error_message += gp.sprint_var(invalid_values,
280018e25fbSMichael Walsh                                   gp.blank() | gp.show_type())
281e23b5ad3SMichael Walsh    return process_error_message(error_message)
2827423c01aSMichael Walsh
283bec416ddSMichael Walsh
284*89eff544SMichael Walshdef valid_range(var_value, lower=None, upper=None, var_name=None):
285bec416ddSMichael Walsh    r"""
286018e25fbSMichael Walsh    The variable value is valid if it is within the specified range.
287bec416ddSMichael Walsh
288018e25fbSMichael Walsh    This function can be used with any type of operands where they can have a
289018e25fbSMichael Walsh    greater than/less than relationship to each other (e.g. int, float, str).
290018e25fbSMichael Walsh
291018e25fbSMichael Walsh    Description of argument(s):
292bec416ddSMichael Walsh    var_value                       The value being validated.
293018e25fbSMichael Walsh    lower                           The lower end of the range.  If not None,
294018e25fbSMichael Walsh                                    the var_value must be greater than or
295018e25fbSMichael Walsh                                    equal to lower.
296018e25fbSMichael Walsh    upper                           The upper end of the range.  If not None,
297018e25fbSMichael Walsh                                    the var_value must be less than or equal
298018e25fbSMichael Walsh                                    to upper.
299bec416ddSMichael Walsh    """
300bec416ddSMichael Walsh
301bec416ddSMichael Walsh    error_message = ""
302b9d8dfd2SMichael Walsh    if lower is None and upper is None:
303018e25fbSMichael Walsh        return process_error_message(error_message)
304b9d8dfd2SMichael Walsh    if lower is None and var_value <= upper:
305018e25fbSMichael Walsh        return process_error_message(error_message)
306b9d8dfd2SMichael Walsh    if upper is None and var_value >= lower:
307018e25fbSMichael Walsh        return process_error_message(error_message)
308018e25fbSMichael Walsh    if lower and upper:
309018e25fbSMichael Walsh        if lower > upper:
310*89eff544SMichael Walsh            var_name = get_var_name(var_name)
311018e25fbSMichael Walsh            error_message += "Programmer error - the lower value is greater"
312018e25fbSMichael Walsh            error_message += " than the upper value:\n"
313018e25fbSMichael Walsh            error_message += gp.sprint_vars(lower, upper, fmt=gp.show_type())
314018e25fbSMichael Walsh            return process_error_message(error_message)
315018e25fbSMichael Walsh        if lower <= var_value <= upper:
316018e25fbSMichael Walsh            return process_error_message(error_message)
317bec416ddSMichael Walsh
318*89eff544SMichael Walsh    var_name = get_var_name(var_name)
319018e25fbSMichael Walsh    error_message += "The following variable is not within the expected"
320018e25fbSMichael Walsh    error_message += " range:\n"
321018e25fbSMichael Walsh    error_message += gp.sprint_varx(var_name, var_value, gp.show_type())
322018e25fbSMichael Walsh    error_message += "\n"
323018e25fbSMichael Walsh    error_message += "range:\n"
324018e25fbSMichael Walsh    error_message += gp.sprint_vars(lower, upper, fmt=gp.show_type(), indent=2)
325e23b5ad3SMichael Walsh    return process_error_message(error_message)
3267423c01aSMichael Walsh
32778bdfdd6SMichael Walsh
328*89eff544SMichael Walshdef valid_integer(var_value, lower=None, upper=None, var_name=None):
32978bdfdd6SMichael Walsh    r"""
330018e25fbSMichael Walsh    The variable value is valid if it is an integer or can be interpreted as
331018e25fbSMichael Walsh    an integer (e.g. 7, "7", etc.).
33278bdfdd6SMichael Walsh
333018e25fbSMichael Walsh    This function also calls valid_range to make sure the integer value is
334018e25fbSMichael Walsh    within the specified range (if any).
335018e25fbSMichael Walsh
336018e25fbSMichael Walsh    Description of argument(s):
33778bdfdd6SMichael Walsh    var_value                       The value being validated.
338018e25fbSMichael Walsh    lower                           The lower end of the range.  If not None,
339018e25fbSMichael Walsh                                    the var_value must be greater than or
340018e25fbSMichael Walsh                                    equal to lower.
341018e25fbSMichael Walsh    upper                           The upper end of the range.  If not None,
342018e25fbSMichael Walsh                                    the var_value must be less than or equal
343018e25fbSMichael Walsh                                    to upper.
344018e25fbSMichael Walsh    """
345018e25fbSMichael Walsh
346018e25fbSMichael Walsh    error_message = ""
347*89eff544SMichael Walsh    var_name = get_var_name(var_name)
348018e25fbSMichael Walsh    try:
349018e25fbSMichael Walsh        var_value = int(str(var_value), 0)
350018e25fbSMichael Walsh    except ValueError:
351018e25fbSMichael Walsh        error_message += "Invalid integer value:\n"
352018e25fbSMichael Walsh        error_message += gp.sprint_varx(var_name, var_value,
353018e25fbSMichael Walsh                                        gp.blank() | gp.show_type())
354018e25fbSMichael Walsh        return process_error_message(error_message)
355018e25fbSMichael Walsh
356018e25fbSMichael Walsh    # Check the range (if any).
357018e25fbSMichael Walsh    if lower:
358018e25fbSMichael Walsh        lower = int(str(lower), 0)
359018e25fbSMichael Walsh    if upper:
360018e25fbSMichael Walsh        upper = int(str(upper), 0)
361018e25fbSMichael Walsh    error_message = valid_range(var_value, lower, upper, var_name=var_name)
362018e25fbSMichael Walsh
363018e25fbSMichael Walsh    return process_error_message(error_message)
364018e25fbSMichael Walsh
365018e25fbSMichael Walsh
366*89eff544SMichael Walshdef valid_dir_path(var_value, var_name=None):
367018e25fbSMichael Walsh    r"""
368018e25fbSMichael Walsh    The variable value is valid if it contains the path of an existing
369018e25fbSMichael Walsh    directory.
370018e25fbSMichael Walsh
371018e25fbSMichael Walsh    Description of argument(s):
372018e25fbSMichael Walsh    var_value                       The value being validated.
37378bdfdd6SMichael Walsh    """
37478bdfdd6SMichael Walsh
37578bdfdd6SMichael Walsh    error_message = ""
37678bdfdd6SMichael Walsh    if not os.path.isdir(str(var_value)):
377*89eff544SMichael Walsh        var_name = get_var_name(var_name)
378018e25fbSMichael Walsh        error_message += "The following directory does not exist:\n"
37900244345SMichael Walsh        error_message += gp.sprint_varx(var_name, var_value, gp.blank())
38078bdfdd6SMichael Walsh
381e23b5ad3SMichael Walsh    return process_error_message(error_message)
38278bdfdd6SMichael Walsh
38378bdfdd6SMichael Walsh
384*89eff544SMichael Walshdef valid_file_path(var_value, var_name=None):
38578bdfdd6SMichael Walsh    r"""
386018e25fbSMichael Walsh    The variable value is valid if it contains the path of an existing file.
38778bdfdd6SMichael Walsh
388018e25fbSMichael Walsh    Description of argument(s):
38978bdfdd6SMichael Walsh    var_value                       The value being validated.
39078bdfdd6SMichael Walsh    """
39178bdfdd6SMichael Walsh
39278bdfdd6SMichael Walsh    error_message = ""
39378bdfdd6SMichael Walsh    if not os.path.isfile(str(var_value)):
394*89eff544SMichael Walsh        var_name = get_var_name(var_name)
395018e25fbSMichael Walsh        error_message += "The following file does not exist:\n"
39600244345SMichael Walsh        error_message += gp.sprint_varx(var_name, var_value, gp.blank())
39778bdfdd6SMichael Walsh
398e23b5ad3SMichael Walsh    return process_error_message(error_message)
39978bdfdd6SMichael Walsh
40078bdfdd6SMichael Walsh
401*89eff544SMichael Walshdef valid_path(var_value, var_name=None):
402e23b5ad3SMichael Walsh    r"""
403018e25fbSMichael Walsh    The variable value is valid if it contains the path of an existing file or
404018e25fbSMichael Walsh    directory.
405e23b5ad3SMichael Walsh
406018e25fbSMichael Walsh    Description of argument(s):
407e23b5ad3SMichael Walsh    var_value                       The value being validated.
408e23b5ad3SMichael Walsh    """
409e23b5ad3SMichael Walsh
410e23b5ad3SMichael Walsh    error_message = ""
411e23b5ad3SMichael Walsh    if not (os.path.isfile(str(var_value)) or os.path.isdir(str(var_value))):
412*89eff544SMichael Walsh        var_name = get_var_name(var_name)
413018e25fbSMichael Walsh        error_message += "Invalid path (file or directory does not exist):\n"
41400244345SMichael Walsh        error_message += gp.sprint_varx(var_name, var_value, gp.blank())
415e23b5ad3SMichael Walsh
416e23b5ad3SMichael Walsh    return process_error_message(error_message)
4172c687e98SMichael Walsh
4182c687e98SMichael Walsh
41935026be5SMichael Walshdef valid_list(var_value, valid_values=[], invalid_values=[],
420*89eff544SMichael Walsh               required_values=[], fail_on_empty=False, var_name=None):
4212c687e98SMichael Walsh    r"""
422018e25fbSMichael Walsh    The variable value is valid if it is a list where each entry can be found
42335026be5SMichael Walsh    in the valid_values list or if none of its values can be found in the
42435026be5SMichael Walsh    invalid_values list or if all of the values in the required_values list
42535026be5SMichael Walsh    can be found in var_value.
42635026be5SMichael Walsh
42735026be5SMichael Walsh    The caller may only specify one of these 3 arguments: valid_values,
42835026be5SMichael Walsh    invalid_values, required_values.
4292c687e98SMichael Walsh
430018e25fbSMichael Walsh    Description of argument(s):
431018e25fbSMichael Walsh    var_value                       The value being validated.
432ca193993SMichael Walsh    valid_values                    A list of valid values.  Each element in
433ca193993SMichael Walsh                                    the var_value list must be equal to one of
434ca193993SMichael Walsh                                    these values to be considered valid.
43535026be5SMichael Walsh    invalid_values                  A list of invalid values.  If any element
43635026be5SMichael Walsh                                    in var_value is equal to any of the values
43735026be5SMichael Walsh                                    in this argument, var_value is considered
43835026be5SMichael Walsh                                    invalid.
43935026be5SMichael Walsh    required_values                 Every value in required_values must be
44035026be5SMichael Walsh                                    found in var_value.  Otherwise, var_value
44135026be5SMichael Walsh                                    is considered invalid.
442018e25fbSMichael Walsh    fail_on_empty                   Indicates that an empty list for the
443018e25fbSMichael Walsh                                    variable value should be considered an
444018e25fbSMichael Walsh                                    error.
445ca193993SMichael Walsh    """
446ca193993SMichael Walsh
447ca193993SMichael Walsh    error_message = ""
448018e25fbSMichael Walsh
44935026be5SMichael Walsh    # Validate this function's arguments.
45035026be5SMichael Walsh    len_valid_values = len(valid_values)
45135026be5SMichael Walsh    len_invalid_values = len(invalid_values)
45235026be5SMichael Walsh    len_required_values = len(required_values)
45335026be5SMichael Walsh    if (len_valid_values + len_invalid_values + len_required_values) > 1:
45435026be5SMichael Walsh        error_message += "Programmer error - You must provide only one of the"
45535026be5SMichael Walsh        error_message += " following: valid_values, invalid_values,"
45635026be5SMichael Walsh        error_message += " required_values.\n"
45735026be5SMichael Walsh        error_message += gp.sprint_var(invalid_values)
45835026be5SMichael Walsh        error_message += gp.sprint_var(valid_values)
45935026be5SMichael Walsh        error_message += gp.sprint_var(required_values)
46035026be5SMichael Walsh        return process_error_message(error_message)
46135026be5SMichael Walsh
462018e25fbSMichael Walsh    if type(var_value) is not list:
463*89eff544SMichael Walsh        var_name = get_var_name(var_name)
464018e25fbSMichael Walsh        error_message = valid_type(var_value, list, var_name=var_name)
465018e25fbSMichael Walsh        if error_message:
466018e25fbSMichael Walsh            return process_error_message(error_message)
467018e25fbSMichael Walsh
468018e25fbSMichael Walsh    if fail_on_empty and len(var_value) == 0:
469*89eff544SMichael Walsh        var_name = get_var_name(var_name)
470018e25fbSMichael Walsh        error_message += "Invalid empty list:\n"
471018e25fbSMichael Walsh        error_message += gp.sprint_varx(var_name, var_value, gp.show_type())
472018e25fbSMichael Walsh        return process_error_message(error_message)
473ca193993SMichael Walsh
47435026be5SMichael Walsh    if len(required_values):
47535026be5SMichael Walsh        found_error = 0
47635026be5SMichael Walsh        display_required_values = list(required_values)
47735026be5SMichael Walsh        for ix in range(0, len(required_values)):
47835026be5SMichael Walsh            if required_values[ix] not in var_value:
47935026be5SMichael Walsh                found_error = 1
48035026be5SMichael Walsh                display_required_values[ix] = \
48135026be5SMichael Walsh                    str(display_required_values[ix]) + "*"
48235026be5SMichael Walsh        if found_error:
483*89eff544SMichael Walsh            var_name = get_var_name(var_name)
48435026be5SMichael Walsh            error_message += "The following list is invalid:\n"
48535026be5SMichael Walsh            error_message += gp.sprint_varx(var_name, var_value,
48635026be5SMichael Walsh                                            gp.blank() | gp.show_type())
48735026be5SMichael Walsh            error_message += "\n"
48835026be5SMichael Walsh            error_message += "Because some of the values in the "
48935026be5SMichael Walsh            error_message += "required_values list are not present (see"
49035026be5SMichael Walsh            error_message += " entries marked with \"*\"):\n"
49135026be5SMichael Walsh            error_message += "\n"
49235026be5SMichael Walsh            error_message += gp.sprint_varx('required_values',
49335026be5SMichael Walsh                                            display_required_values,
49435026be5SMichael Walsh                                            gp.blank() | gp.show_type())
49535026be5SMichael Walsh            error_message += "\n"
49635026be5SMichael Walsh
49735026be5SMichael Walsh        return process_error_message(error_message)
49835026be5SMichael Walsh
49935026be5SMichael Walsh    if len(invalid_values):
50035026be5SMichael Walsh        found_error = 0
50135026be5SMichael Walsh        display_var_value = list(var_value)
50235026be5SMichael Walsh        for ix in range(0, len(var_value)):
50335026be5SMichael Walsh            if var_value[ix] in invalid_values:
50435026be5SMichael Walsh                found_error = 1
50535026be5SMichael Walsh                display_var_value[ix] = str(var_value[ix]) + "*"
50635026be5SMichael Walsh
50735026be5SMichael Walsh        if found_error:
508*89eff544SMichael Walsh            var_name = get_var_name(var_name)
50935026be5SMichael Walsh            error_message += "The following list is invalid (see entries"
51035026be5SMichael Walsh            error_message += " marked with \"*\"):\n"
51135026be5SMichael Walsh            error_message += gp.sprint_varx(var_name, display_var_value,
51235026be5SMichael Walsh                                            gp.blank() | gp.show_type())
51335026be5SMichael Walsh            error_message += "\n"
51435026be5SMichael Walsh            error_message += gp.sprint_var(invalid_values, gp.show_type())
51535026be5SMichael Walsh        return process_error_message(error_message)
51635026be5SMichael Walsh
517ca193993SMichael Walsh    found_error = 0
518ca193993SMichael Walsh    display_var_value = list(var_value)
519ca193993SMichael Walsh    for ix in range(0, len(var_value)):
520ca193993SMichael Walsh        if var_value[ix] not in valid_values:
521ca193993SMichael Walsh            found_error = 1
52235026be5SMichael Walsh            display_var_value[ix] = str(var_value[ix]) + "*"
523ca193993SMichael Walsh
524ca193993SMichael Walsh    if found_error:
525*89eff544SMichael Walsh        var_name = get_var_name(var_name)
526018e25fbSMichael Walsh        error_message += "The following list is invalid (see entries marked"
527018e25fbSMichael Walsh        error_message += " with \"*\"):\n"
528018e25fbSMichael Walsh        error_message += gp.sprint_varx(var_name, display_var_value,
529018e25fbSMichael Walsh                                        gp.blank() | gp.show_type())
530018e25fbSMichael Walsh        error_message += "\n"
53135026be5SMichael Walsh        error_message += gp.sprint_var(valid_values, gp.show_type())
532018e25fbSMichael Walsh        return process_error_message(error_message)
533ca193993SMichael Walsh
534ca193993SMichael Walsh    return process_error_message(error_message)
5357ac5fd81SMichael Walsh
5367ac5fd81SMichael Walsh
537*89eff544SMichael Walshdef valid_dict(var_value, required_keys=[], var_name=None):
5387ac5fd81SMichael Walsh    r"""
539018e25fbSMichael Walsh    The variable value is valid if it is a dictionary containing all of the
540018e25fbSMichael Walsh    required keys.
5417ac5fd81SMichael Walsh
542018e25fbSMichael Walsh    Description of argument(s):
543018e25fbSMichael Walsh    var_value                       The value being validated.
5447ac5fd81SMichael Walsh    required_keys                   A list of keys which must be found in the
5457ac5fd81SMichael Walsh                                    dictionary for it to be considered valid.
5467ac5fd81SMichael Walsh    """
5477ac5fd81SMichael Walsh
5487ac5fd81SMichael Walsh    error_message = ""
549018e25fbSMichael Walsh    missing_keys = list(set(required_keys) - set(var_value.keys()))
550018e25fbSMichael Walsh    if len(missing_keys) > 0:
551*89eff544SMichael Walsh        var_name = get_var_name(var_name)
552018e25fbSMichael Walsh        error_message += "The following dictionary is invalid because it is"
553018e25fbSMichael Walsh        error_message += " missing required keys:\n"
554018e25fbSMichael Walsh        error_message += gp.sprint_varx(var_name, var_value,
555018e25fbSMichael Walsh                                        gp.blank() | gp.show_type())
556018e25fbSMichael Walsh        error_message += "\n"
55735026be5SMichael Walsh        error_message += gp.sprint_var(missing_keys, gp.show_type())
5587ac5fd81SMichael Walsh    return process_error_message(error_message)
559018e25fbSMichael Walsh
560018e25fbSMichael Walsh
561*89eff544SMichael Walshdef valid_program(var_value, var_name=None):
562be3a8150SMichael Walsh    r"""
563be3a8150SMichael Walsh    The variable value is valid if it contains the name of a program which can
564be3a8150SMichael Walsh    be located using the "which" command.
565be3a8150SMichael Walsh
566be3a8150SMichael Walsh    Description of argument(s):
567be3a8150SMichael Walsh    var_value                       The value being validated.
568be3a8150SMichael Walsh    """
569be3a8150SMichael Walsh
570be3a8150SMichael Walsh    error_message = ""
571be3a8150SMichael Walsh    rc, out_buf = gc.shell_cmd("which " + var_value, quiet=1, show_err=0,
572be3a8150SMichael Walsh                               ignore_err=1)
573be3a8150SMichael Walsh    if rc:
574*89eff544SMichael Walsh        var_name = get_var_name(var_name)
575be3a8150SMichael Walsh        error_message += "The following required program could not be found"
576be3a8150SMichael Walsh        error_message += " using the $PATH environment variable:\n"
57700244345SMichael Walsh        error_message += gp.sprint_varx(var_name, var_value, gp.blank())
578be3a8150SMichael Walsh        PATH = os.environ.get("PATH", "").split(":")
579be3a8150SMichael Walsh        error_message += "\n"
580be3a8150SMichael Walsh        error_message += gp.sprint_var(PATH)
581be3a8150SMichael Walsh    return process_error_message(error_message)
582be3a8150SMichael Walsh
583be3a8150SMichael Walsh
584*89eff544SMichael Walshdef valid_length(var_value, min_length=None, max_length=None, var_name=None):
585b9d8dfd2SMichael Walsh    r"""
586b9d8dfd2SMichael Walsh    The variable value is valid if it is an object (e.g. list, dictionary)
587b9d8dfd2SMichael Walsh    whose length is within the specified range.
588b9d8dfd2SMichael Walsh
589b9d8dfd2SMichael Walsh    Description of argument(s):
590b9d8dfd2SMichael Walsh    var_value                       The value being validated.
591b9d8dfd2SMichael Walsh    min_length                      The minimum length of the object.  If not
592b9d8dfd2SMichael Walsh                                    None, the length of var_value must be
593b9d8dfd2SMichael Walsh                                    greater than or equal to min_length.
594b9d8dfd2SMichael Walsh    max_length                      The maximum length of the object.  If not
595b9d8dfd2SMichael Walsh                                    None, the length of var_value must be less
596b9d8dfd2SMichael Walsh                                    than or equal to min_length.
597b9d8dfd2SMichael Walsh    """
598b9d8dfd2SMichael Walsh
599b9d8dfd2SMichael Walsh    error_message = ""
600b9d8dfd2SMichael Walsh    length = len(var_value)
601b9d8dfd2SMichael Walsh    error_message = valid_range(length, min_length, max_length)
602b9d8dfd2SMichael Walsh    if error_message:
603*89eff544SMichael Walsh        var_name = get_var_name(var_name)
604b9d8dfd2SMichael Walsh        error_message = "The length of the following object is not within the"
605b9d8dfd2SMichael Walsh        error_message += " expected range:\n"
606b9d8dfd2SMichael Walsh        error_message += gp.sprint_var(length)
607b9d8dfd2SMichael Walsh        error_message += gp.sprint_varx(var_name, var_value, gp.blank())
608b9d8dfd2SMichael Walsh        error_message += "\n"
609b9d8dfd2SMichael Walsh        error_message += gp.sprint_vars(min_length, max_length)
610b9d8dfd2SMichael Walsh        return process_error_message(error_message)
611b9d8dfd2SMichael Walsh
612b9d8dfd2SMichael Walsh    return process_error_message(error_message)
613b9d8dfd2SMichael Walsh
614b9d8dfd2SMichael Walsh
615018e25fbSMichael Walsh# Modify selected function docstrings by adding headers/footers.
616018e25fbSMichael Walsh
617018e25fbSMichael Walshfunc_names = [
618018e25fbSMichael Walsh    "valid_type", "valid_value", "valid_range", "valid_integer",
619018e25fbSMichael Walsh    "valid_dir_path", "valid_file_path", "valid_path", "valid_list",
620*89eff544SMichael Walsh    "valid_dict", "valid_program", "valid_length"
621018e25fbSMichael Walsh]
622018e25fbSMichael Walsh
623018e25fbSMichael Walshraw_doc_strings = {}
624018e25fbSMichael Walsh
625018e25fbSMichael Walshfor func_name in func_names:
626018e25fbSMichael Walsh    cmd_buf = "raw_doc_strings['" + func_name + "'] = " + func_name
627018e25fbSMichael Walsh    cmd_buf += ".__doc__"
628018e25fbSMichael Walsh    exec(cmd_buf)
629018e25fbSMichael Walsh    cmd_buf = func_name + ".__doc__ = docstring_header + " + func_name
630018e25fbSMichael Walsh    cmd_buf += ".__doc__.rstrip(\" \\n\") + additional_args_docstring_footer"
631018e25fbSMichael Walsh    exec(cmd_buf)
632