1#!/usr/bin/env python
2
3r"""
4This module provides valuable argument processing functions like
5gen_get_options and sprint_args.
6"""
7
8import os
9import gen_print as gp
10
11exit_on_error = False
12
13
14def set_exit_on_error(value):
15    r"""
16    Set the exit_on_error value to either True or False.
17
18    If exit_on_error is set, validation functions like valid_value will exit
19    the program on error instead of returning False.
20
21    Description of argument(s):
22    value                           Value to set global exit_on_error to.
23    """
24
25    global exit_on_error
26    exit_on_error = value
27
28
29def get_var_name(var_name):
30    r"""
31    If var_name has a value, simply return it.  Otherwise, get the variable
32    name of the first argument used to call the validation function (e.g.
33    valid_value, valid_integer, etc.) and return it.
34
35    This function is designed solely for use by other functions in this file.
36
37    Example:
38
39    A programmer codes this:
40
41    valid_value(last_name)
42
43    Which results in the following call stack:
44
45    valid_value(last_name)
46      -> svalid_value(var_value...)
47        -> get_var_name(var_name)
48
49    In this example, this function will return "last_name".
50
51    Example:
52
53    err_msg = svalid_value(last_name, var_name="some_other_name")
54
55    Which results in the following call stack:
56
57    svalid_value(var_value, var_name="some_other_name")
58      -> get_var_name(var_name)
59
60    In this example, this function will return "some_other_name".
61
62    Description of argument(s):
63    var_name                        The name of the variable.
64    """
65
66    if var_name != "":
67        return var_name
68    # Calculate stack_frame_ix.  The validation functions in this file come
69    # in pairs.  There is an "s" version of each validation function (e.g.
70    # svalid_value) whose job is to return an error message string.  Then
71    # there is a wrapper function (e.g. valid_value) that will call the "s"
72    # version and print the result if there is an error.  See examples 1 and 2
73    # above for illustration.  This function must be cognizant of both
74    # scenarios to accurately determine the name of the variable being
75    # validated.  Where the "s" function is being called directly, the
76    # stack_frame_ix should be set to 3.  Where the wrapper function is being
77    # called, the stack_frame_ix should be incremented to 4.
78    stack_frame_ix = 3
79    parent_func_name = gp.sprint_func_name(2)
80    grandparent_func_name = gp.sprint_func_name(3)
81    if parent_func_name == "s" + grandparent_func_name:
82        stack_frame_ix += 1
83    var_name = gp.get_arg_name(0, 1, stack_frame_ix)
84    return var_name
85
86
87def process_error_message(error_message):
88    r"""
89    Process the error_message as follows:
90    - If the error_message is blank, return True.
91    - If the error_message contains a value:
92        - Print the error_message as part of a full error report.
93        - If global exit_on_error is set, then exit the program with a return
94          code of 1.
95        - If exit_on_error is not set, return False.
96
97    This function is designed solely for use by wrapper functions in this file
98    (e.g. "valid_value").
99
100    Description of argument(s):
101    error_message                   An error message.
102    """
103
104    if error_message == "":
105        return True
106
107    gp.print_error_report(error_message)
108    if exit_on_error:
109        exit(1)
110    return False
111
112
113def svalid_value(var_value,
114                 invalid_values=[],
115                 valid_values=[],
116                 var_name=""):
117    r"""
118    Return an empty string if var_value is a valid value.  Otherwise, return
119    an error string.
120
121    Description of arguments:
122    var_value                       The value being validated.
123    invalid_values                  A list of invalid values.  If var_value is
124                                    equal to any of these, it is invalid.
125                                    Note that if you specify anything for
126                                    invalid_values (below), the valid_values
127                                    list is not even processed.  If you
128                                    specify nothing for both invalid_values
129                                    and valid_values, invalid_values will be
130                                    set to a default value of [""].
131    valid_values                    A list of valid values.  var_value must be
132                                    equal to one of these values to be
133                                    considered valid.
134    var_name                        The name of the variable whose value is
135                                    passed in var_value.  This parameter is
136                                    normally unnecessary as this function can
137                                    figure out the var_name.  This is provided
138                                    for Robot callers.  In this scenario, we
139                                    are unable to get the variable name
140                                    ourselves.
141    """
142
143    success_message = ""
144    error_message = ""
145
146    # Validate this function's arguments.
147    len_valid_values = len(valid_values)
148    len_invalid_values = len(invalid_values)
149    if len_valid_values > 0 and len_invalid_values > 0:
150        error_message += "Programmer error - You must provide either an" +\
151                         " invalid_values list or a valid_values" +\
152                         " list but NOT both.\n" +\
153                         gp.sprint_var(invalid_values) +\
154                         gp.sprint_var(valid_values)
155        return error_message
156
157    if len_valid_values > 0:
158        # Processing the valid_values list.
159        if var_value in valid_values:
160            return success_message
161        error_message += "The following variable has an invalid" +\
162                         " value:\n" +\
163                         gp.sprint_varx(get_var_name(var_name), var_value,
164                                        gp.blank() | gp.verbose()) +\
165                         "\nIt must be one of the following values:\n" +\
166                         gp.sprint_var(valid_values, gp.blank())
167        return error_message
168
169    if len_invalid_values == 0:
170        # Assign default value.
171        invalid_values = [""]
172
173    # Assertion: We have an invalid_values list.  Processing it now.
174    if var_value not in invalid_values:
175        return success_message
176
177    error_message += "The following variable has an invalid value:\n" +\
178                     gp.sprint_varx(get_var_name(var_name), var_value,
179                                    gp.blank() | gp.verbose()) +\
180                     "\nIt must NOT be one of the following values:\n" +\
181                     gp.sprint_var(invalid_values, gp.blank())
182    return error_message
183
184
185def valid_value(var_value,
186                invalid_values=[],
187                valid_values=[],
188                var_name=""):
189    r"""
190    Return True if var_value is valid.  Otherwise, print an error message and
191    either return False or exit(1) depending on the value of exit_on_error.
192
193    Description of arguments:
194    (See description of arguments for svalid_value (above)).
195    """
196
197    error_message = svalid_value(var_value, invalid_values, valid_values,
198                                 var_name)
199    return process_error_message(error_message)
200
201
202def svalid_integer(var_value,
203                   var_name=""):
204    r"""
205    Return an empty string if var_value is a valid integer.  Otherwise, return
206    an error string.
207
208    Description of arguments:
209    var_value                       The value being validated.
210    var_name                        The name of the variable whose value is
211                                    passed in var_value.  This parameter is
212                                    normally unnecessary as this function can
213                                    figure out the var_name.  This is provided
214                                    for Robot callers.  In this scenario, we
215                                    are unable to get the variable name
216                                    ourselves.
217    """
218
219    success_message = ""
220    error_message = ""
221    try:
222        if isinstance(int(str(var_value), 0), int):
223            return success_message
224    except ValueError:
225        pass
226
227    # If we get to this point, the validation has failed.
228    error_message +=\
229        "Invalid integer value:\n" +\
230        gp.sprint_varx(get_var_name(var_name), var_value, gp.blank())
231
232    return error_message
233
234
235def valid_integer(var_value,
236                  var_name=""):
237    r"""
238    Return True if var_value is a valid integer.  Otherwise, print an error
239    message and either return False or exit(1) depending on the value of
240    exit_on_error.
241
242    Description of arguments:
243    (See description of arguments for svalid_integer (above)).
244    """
245
246    error_message = svalid_integer(var_value, var_name)
247    return process_error_message(error_message)
248
249
250def svalid_dir_path(var_value,
251                    var_name=""):
252    r"""
253    Return an empty string if var_value is a valid directory path.  Otherwise,
254    return an error string.
255
256    Description of arguments:
257    var_value                       The value being validated.
258    var_name                        The name of the variable whose value is
259                                    passed in var_value.  This parameter is
260                                    normally unnecessary as this function can
261                                    figure out the var_name.  This is provided
262                                    for Robot callers.  In this scenario, we
263                                    are unable to get the variable name
264                                    ourselves.
265    """
266
267    error_message = ""
268    if not os.path.isdir(str(var_value)):
269        error_message += "The following directory does not exist:\n" +\
270            gp.sprint_varx(get_var_name(var_name), var_value)
271
272    return error_message
273
274
275def valid_dir_path(var_value,
276                   var_name=""):
277    r"""
278    Return True if var_value is a valid directory path.  Otherwise, print an
279    error message and either return False or exit(1) depending on the value of
280    exit_on_error.
281
282    Valid means that the directory path exists.
283
284    Description of arguments:
285    (See description of arguments for svalid_dir_path (above)).
286    """
287
288    error_message = svalid_dir_path(var_value, var_name)
289    return process_error_message(error_message)
290
291
292def svalid_file_path(var_value,
293                     var_name=""):
294    r"""
295    Return an empty string if var_value is a valid file path.  Otherwise,
296    return an error string.
297
298    Description of arguments:
299    var_value                       The value being validated.
300    var_name                        The name of the variable whose value is
301                                    passed in var_value.  This parameter is
302                                    normally unnecessary as this function can
303                                    figure out the var_name.  This is provided
304                                    for Robot callers.  In this scenario, we
305                                    are unable to get the variable name
306                                    ourselves.
307    """
308
309    error_message = ""
310    if not os.path.isfile(str(var_value)):
311        error_message += "Invalid file (does not exist):\n" +\
312            gp.sprint_varx(get_var_name(var_name), var_value)
313
314    return error_message
315
316
317def valid_file_path(var_value,
318                    var_name=""):
319    r"""
320    Return True if var_value is a valid file path.  Otherwise, print an error
321    message and either return False or exit(1) depending on the value of
322    exit_on_error.
323
324    Valid means that the file exists.
325
326    Description of arguments:
327    (See description of arguments for svalid_file_path (above)).
328    """
329
330    error_message = svalid_file_path(var_value, var_name)
331    return process_error_message(error_message)
332
333
334def svalid_path(var_value,
335                var_name=""):
336    r"""
337    Return an empty string if var_value is either a valid file path or
338    directory path.  Otherwise, return an error string.
339
340    Description of arguments:
341    var_value                       The value being validated.
342    var_name                        The name of the variable whose value is
343                                    passed in var_value.  This parameter is
344                                    normally unnecessary as this function can
345                                    figure out the var_name.  This is provided
346                                    for Robot callers.  In this scenario, we
347                                    are unable to get the variable name
348                                    ourselves.
349    """
350
351    error_message = ""
352    if not (os.path.isfile(str(var_value)) or os.path.isdir(str(var_value))):
353        error_message = "Invalid path (file or directory does not exist):\n" +\
354            gp.sprint_varx(get_var_name(var_name), var_value)
355
356    return error_message
357
358
359def valid_path(var_value,
360               var_name=""):
361    r"""
362    Return True if var_value is a valid file path.  Otherwise, print an error
363    message and either return False or exit(1) depending on the value of
364    exit_on_error.
365
366    Valid means that the file exists.
367
368    Description of arguments:
369    (See description of arguments for svalid_path (above)).
370    """
371
372    error_message = svalid_path(var_value, var_name)
373    return process_error_message(error_message)
374
375
376def svalid_range(var_value,
377                 valid_range=[],
378                 var_name=""):
379    r"""
380    Return an empty string if var_value is within the range.  Otherwise,
381    return an error string.
382
383    Description of arguments:
384    var_value                       The value being validated.  This value
385                                    must be an integer.
386    valid_range                     A list comprised of one or two elements
387                                    which are the lower and upper ends of a
388                                    range.  These values must be integers
389                                    except where noted.  Valid specifications
390                                    may be of the following forms: [lower,
391                                    upper], [lower] or [None, upper].
392    var_name                        The name of the variable whose value is
393                                    passed in var_value.  This parameter is
394                                    normally unnecessary as this function can
395                                    figure out the var_name.  This is provided
396                                    for Robot callers.  In this scenario, we
397                                    are unable to get the variable name
398                                    ourselves.
399    """
400
401    error_message = ""
402
403    # Validate this function's parms:
404    # First, ensure that the value is an integer.
405    error_message = svalid_integer(var_value, var_name)
406    if not error_message == "":
407        return error_message
408    var_value = int(var_value)
409
410    len_valid_range = len(valid_range)
411    if len_valid_range == 0 or len_valid_range > 2:
412        error_message += "Programmer error - For the valid_range parameter," +\
413                         " you must provide a list consisting of one or two" +\
414                         " elements.\n" +\
415                         gp.sprint_var(valid_range)
416        return error_message
417
418    if len_valid_range == 1 or valid_range[0] is not None:
419        # Make sure lower valid_range value is an integer.
420        error_message = svalid_integer(valid_range[0], "valid_range[0]")
421        if not error_message == "":
422            error_message = "Programmer error:\n" + error_message
423            return error_message
424    if valid_range[0] is not None:
425        valid_range[0] = int(valid_range[0])
426    if len_valid_range == 2:
427        # Make sure upper valid_range value is an integer.
428        error_message = svalid_integer(valid_range[1], "valid_range[1]")
429        if not error_message == "":
430            error_message = "Programmer error:\n" + error_message
431            return error_message
432        valid_range[1] = int(valid_range[1])
433        if valid_range[0] is not None and valid_range[0] > valid_range[1]:
434            error_message = "Programmer error - In the following range, the" +\
435                            " lower limit is greater than the upper" +\
436                            " limit:\n" + gp.sprint_var(valid_range)
437            return error_message
438
439    if len_valid_range == 1:
440        if var_value < valid_range[0]:
441            error_message += "The following variable is not within the" +\
442                             " expected range:\n" +\
443                             gp.sprint_varx(get_var_name(var_name),
444                                            var_value) +\
445                             gp.sprint_varx("valid_range",
446                                            str(valid_range[0]) + "..")
447            return error_message
448        return error_message
449
450    if valid_range[0] is None:
451        if var_value > valid_range[1]:
452            error_message += "The following variable is not within the" +\
453                             " expected range:\n" +\
454                             gp.sprint_varx(get_var_name(var_name),
455                                            var_value) +\
456                             gp.sprint_varx("valid_range",
457                                            ".." + str(valid_range[1]))
458            return error_message
459
460    if var_value < valid_range[0] or var_value > valid_range[1]:
461        error_message += "The following variable is not within the expected" +\
462                         " range:\n" +\
463                         gp.sprint_varx(get_var_name(var_name), var_value) +\
464                         gp.sprint_varx("valid_range",
465                                        str(valid_range[0]) + ".."
466                                        + str(valid_range[1]))
467        return error_message
468
469    return error_message
470
471
472def valid_range(var_value,
473                valid_range=[],
474                var_name=""):
475    r"""
476    Return True if var_value is within range.  Otherwise, print an error
477    message and either return False or exit(1) depending on the value of
478    exit_on_error.
479
480    Description of arguments:
481    (See description of arguments for svalid_range (above)).
482    """
483
484    error_message = svalid_range(var_value, valid_range, var_name)
485    return process_error_message(error_message)
486
487
488def svalid_list(var_value,
489                valid_values=[],
490                var_name=""):
491    r"""
492    Return an empty string if var_value is a valid list.  Otherwise, return an
493    error string.
494
495    Description of arguments:
496    var_value                       The value (i.e. list) being validated.
497    valid_values                    A list of valid values.  Each element in
498                                    the var_value list must be equal to one of
499                                    these values to be considered valid.
500    var_name                        The name of the variable whose value is
501                                    passed in var_value.  This parameter is
502                                    normally unnecessary as this function can
503                                    figure out the var_name.  This is provided
504                                    for Robot callers.  In this scenario, we
505                                    are unable to get the variable name
506                                    ourselves.
507    """
508
509    error_message = ""
510    if len(var_value) == 0:
511        error_message += "The \"" + get_var_name(var_name)
512        error_message += "\" list is empty and is therefore invalid:\n"
513        return error_message
514
515    found_error = 0
516    display_var_value = list(var_value)
517    for ix in range(0, len(var_value)):
518        if var_value[ix] not in valid_values:
519            found_error = 1
520            display_var_value[ix] = var_value[ix] + "*"
521
522    if found_error:
523        error_message += "The list entries marked with \"*\" are not valid:\n"
524        error_message += gp.sprint_varx(get_var_name(var_name),
525                                        display_var_value, gp.blank())
526        error_message += gp.sprint_var(valid_values)
527        return error_message
528
529    return ""
530
531
532def valid_list(var_value,
533               valid_values=[],
534               var_name=""):
535    r"""
536    Return True if var_value is a valid list.  Otherwise, print an error
537    message and either return False or exit(1) depending on the value of
538    exit_on_error.
539
540    Description of arguments:
541    (See description of arguments for svalid_list (above)).
542    """
543
544    error_message = svalid_list(var_value, valid_values, var_name)
545    return process_error_message(error_message)
546
547
548def svalid_dict(var_value,
549                required_keys=[],
550                var_name=""):
551    r"""
552    Return an empty string if var_value is a valid dictionary.  Otherwise,
553    return an error string.
554
555    Description of arguments:
556    var_value                       The value (i.e. dictionary) being
557                                    validated.
558    required_keys                   A list of keys which must be found in the
559                                    dictionary for it to be considered valid.
560    var_name                        The name of the variable whose value is
561                                    passed in var_value.  This parameter is
562                                    normally unnecessary as this function can
563                                    figure out the var_name.  This is provided
564                                    for Robot callers.  In this scenario, we
565                                    are unable to get the variable name
566                                    ourselves.
567    """
568
569    error_message = ""
570
571    keys_missing = list(set(required_keys) - set(var_value.keys()))
572    if len(keys_missing) > 0:
573        var_name = get_var_name(var_name)
574        error_message = "The following key fields are missing from "
575        error_message += var_name + ":\n"
576        error_message += gp.sprint_var(keys_missing)
577        error_message += gp.sprint_varx(var_name, var_value, gp.blank())
578        return error_message
579
580    return ""
581
582
583def valid_dict(var_value,
584               required_keys=[],
585               var_name=""):
586    r"""
587    Return True if var_value is a valid dictionary.  Otherwise, print an error
588    message and either return False or exit(1) depending on the value of
589    exit_on_error.
590
591    Description of arguments:
592    (See description of arguments for svalid_list (above)).
593    """
594
595    error_message = svalid_dict(var_value, required_keys, var_name)
596    return process_error_message(error_message)
597