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    show_blanks = 1
158    if len_valid_values > 0:
159        # Processing the valid_values list.
160        if var_value in valid_values:
161            return success_message
162        error_message += "The following variable has an invalid" +\
163                         " value:\n" +\
164                         gp.sprint_varx(get_var_name(var_name), var_value,
165                                        show_blanks) +\
166                         "\nIt must be one of the following values:\n" +\
167                         gp.sprint_varx("valid_values", valid_values,
168                                        show_blanks)
169        return error_message
170
171    if len_invalid_values == 0:
172        # Assign default value.
173        invalid_values = [""]
174
175    # Assertion: We have an invalid_values list.  Processing it now.
176    if var_value not in invalid_values:
177        return success_message
178
179    error_message += "The following variable has an invalid value:\n" +\
180                     gp.sprint_varx(get_var_name(var_name), var_value,
181                                    show_blanks) +\
182                     "\nIt must NOT be one of the following values:\n" +\
183                     gp.sprint_varx("invalid_values", invalid_values,
184                                    show_blanks)
185    return error_message
186
187
188def valid_value(var_value,
189                invalid_values=[],
190                valid_values=[],
191                var_name=""):
192    r"""
193    Return True if var_value is valid.  Otherwise, print an error message and
194    either return False or exit(1) depending on the value of exit_on_error.
195
196    Description of arguments:
197    (See description of arguments for svalid_value (above)).
198    """
199
200    error_message = svalid_value(var_value, invalid_values, valid_values,
201                                 var_name)
202    return process_error_message(error_message)
203
204
205def svalid_integer(var_value,
206                   var_name=""):
207    r"""
208    Return an empty string if var_value is a valid integer.  Otherwise, return
209    an error string.
210
211    Description of arguments:
212    var_value                       The value being validated.
213    var_name                        The name of the variable whose value is
214                                    passed in var_value.  This parameter is
215                                    normally unnecessary as this function can
216                                    figure out the var_name.  This is provided
217                                    for Robot callers.  In this scenario, we
218                                    are unable to get the variable name
219                                    ourselves.
220    """
221
222    success_message = ""
223    error_message = ""
224    try:
225        if isinstance(int(str(var_value), 0), int):
226            return success_message
227    except ValueError:
228        pass
229
230    # If we get to this point, the validation has failed.
231    show_blanks = 1
232    error_message +=\
233        "Invalid integer value:\n" +\
234        gp.sprint_varx(get_var_name(var_name), var_value, show_blanks)
235
236    return error_message
237
238
239def valid_integer(var_value,
240                  var_name=""):
241    r"""
242    Return True if var_value is a valid integer.  Otherwise, print an error
243    message and either return False or exit(1) depending on the value of
244    exit_on_error.
245
246    Description of arguments:
247    (See description of arguments for svalid_integer (above)).
248    """
249
250    error_message = svalid_integer(var_value, var_name)
251    return process_error_message(error_message)
252
253
254def svalid_dir_path(var_value,
255                    var_name=""):
256    r"""
257    Return an empty string if var_value is a valid directory path.  Otherwise,
258    return an error string.
259
260    Description of arguments:
261    var_value                       The value being validated.
262    var_name                        The name of the variable whose value is
263                                    passed in var_value.  This parameter is
264                                    normally unnecessary as this function can
265                                    figure out the var_name.  This is provided
266                                    for Robot callers.  In this scenario, we
267                                    are unable to get the variable name
268                                    ourselves.
269    """
270
271    error_message = ""
272    if not os.path.isdir(str(var_value)):
273        error_message += "The following directory does not exist:\n" +\
274            gp.sprint_varx(get_var_name(var_name), var_value)
275
276    return error_message
277
278
279def valid_dir_path(var_value,
280                   var_name=""):
281    r"""
282    Return True if var_value is a valid directory path.  Otherwise, print an
283    error message and either return False or exit(1) depending on the value of
284    exit_on_error.
285
286    Valid means that the directory path exists.
287
288    Description of arguments:
289    (See description of arguments for svalid_dir_path (above)).
290    """
291
292    error_message = svalid_dir_path(var_value, var_name)
293    return process_error_message(error_message)
294
295
296def svalid_file_path(var_value,
297                     var_name=""):
298    r"""
299    Return an empty string if var_value is a valid file path.  Otherwise,
300    return an error string.
301
302    Description of arguments:
303    var_value                       The value being validated.
304    var_name                        The name of the variable whose value is
305                                    passed in var_value.  This parameter is
306                                    normally unnecessary as this function can
307                                    figure out the var_name.  This is provided
308                                    for Robot callers.  In this scenario, we
309                                    are unable to get the variable name
310                                    ourselves.
311    """
312
313    error_message = ""
314    if not os.path.isfile(str(var_value)):
315        error_message += "Invalid file (does not exist):\n" +\
316            gp.sprint_varx(get_var_name(var_name), var_value)
317
318    return error_message
319
320
321def valid_file_path(var_value,
322                    var_name=""):
323    r"""
324    Return True if var_value is a valid file path.  Otherwise, print an error
325    message and either return False or exit(1) depending on the value of
326    exit_on_error.
327
328    Valid means that the file exists.
329
330    Description of arguments:
331    (See description of arguments for svalid_file_path (above)).
332    """
333
334    error_message = svalid_file_path(var_value, var_name)
335    return process_error_message(error_message)
336
337
338def svalid_path(var_value,
339                var_name=""):
340    r"""
341    Return an empty string if var_value is either a valid file path or
342    directory path.  Otherwise, return an error string.
343
344    Description of arguments:
345    var_value                       The value being validated.
346    var_name                        The name of the variable whose value is
347                                    passed in var_value.  This parameter is
348                                    normally unnecessary as this function can
349                                    figure out the var_name.  This is provided
350                                    for Robot callers.  In this scenario, we
351                                    are unable to get the variable name
352                                    ourselves.
353    """
354
355    error_message = ""
356    if not (os.path.isfile(str(var_value)) or os.path.isdir(str(var_value))):
357        error_message = "Invalid path (file or directory does not exist):\n" +\
358            gp.sprint_varx(get_var_name(var_name), var_value)
359
360    return error_message
361
362
363def valid_path(var_value,
364               var_name=""):
365    r"""
366    Return True if var_value is a valid file path.  Otherwise, print an error
367    message and either return False or exit(1) depending on the value of
368    exit_on_error.
369
370    Valid means that the file exists.
371
372    Description of arguments:
373    (See description of arguments for svalid_path (above)).
374    """
375
376    error_message = svalid_path(var_value, var_name)
377    return process_error_message(error_message)
378
379
380def svalid_range(var_value,
381                 valid_range=[],
382                 var_name=""):
383    r"""
384    Return an empty string if var_value is within the range.  Otherwise,
385    return an error string.
386
387    Description of arguments:
388    var_value                       The value being validated.  This value
389                                    must be an integer.
390    valid_range                     A list comprised of one or two elements
391                                    which are the lower and upper ends of a
392                                    range.  These values must be integers
393                                    except where noted.  Valid specifications
394                                    may be of the following forms: [lower,
395                                    upper], [lower] or [None, upper].
396    var_name                        The name of the variable whose value is
397                                    passed in var_value.  This parameter is
398                                    normally unnecessary as this function can
399                                    figure out the var_name.  This is provided
400                                    for Robot callers.  In this scenario, we
401                                    are unable to get the variable name
402                                    ourselves.
403    """
404
405    error_message = ""
406
407    # Validate this function's parms:
408    # First, ensure that the value is an integer.
409    error_message = svalid_integer(var_value, var_name)
410    if not error_message == "":
411        return error_message
412    var_value = int(var_value)
413
414    len_valid_range = len(valid_range)
415    if len_valid_range == 0 or len_valid_range > 2:
416        error_message += "Programmer error - For the valid_range parameter," +\
417                         " you must provide a list consisting of one or two" +\
418                         " elements.\n" +\
419                         gp.sprint_var(valid_range)
420        return error_message
421
422    if len_valid_range == 1 or valid_range[0] is not None:
423        # Make sure lower valid_range value is an integer.
424        error_message = svalid_integer(valid_range[0], "valid_range[0]")
425        if not error_message == "":
426            error_message = "Programmer error:\n" + error_message
427            return error_message
428    if valid_range[0] is not None:
429        valid_range[0] = int(valid_range[0])
430    if len_valid_range == 2:
431        # Make sure upper valid_range value is an integer.
432        error_message = svalid_integer(valid_range[1], "valid_range[1]")
433        if not error_message == "":
434            error_message = "Programmer error:\n" + error_message
435            return error_message
436        valid_range[1] = int(valid_range[1])
437        if valid_range[0] is not None and valid_range[0] > valid_range[1]:
438            error_message = "Programmer error - In the following range, the" +\
439                            " lower limit is greater than the upper" +\
440                            " limit:\n" + gp.sprint_varx("valid_range",
441                                                         valid_range)
442            return error_message
443
444    if len_valid_range == 1:
445        if var_value < valid_range[0]:
446            error_message += "The following variable is not within the" +\
447                             " expected range:\n" +\
448                             gp.sprint_varx(get_var_name(var_name),
449                                            var_value) +\
450                             gp.sprint_varx("valid_range",
451                                            str(valid_range[0]) + "..")
452            return error_message
453        return error_message
454
455    if valid_range[0] is None:
456        if var_value > valid_range[1]:
457            error_message += "The following variable is not within the" +\
458                             " expected range:\n" +\
459                             gp.sprint_varx(get_var_name(var_name),
460                                            var_value) +\
461                             gp.sprint_varx("valid_range",
462                                            ".." + str(valid_range[1]))
463            return error_message
464
465    if var_value < valid_range[0] or var_value > valid_range[1]:
466        error_message += "The following variable is not within the expected" +\
467                         " range:\n" +\
468                         gp.sprint_varx(get_var_name(var_name), var_value) +\
469                         gp.sprint_varx("valid_range",
470                                        str(valid_range[0]) + ".."
471                                        + str(valid_range[1]))
472        return error_message
473
474    return error_message
475
476
477def valid_range(var_value,
478                valid_range=[],
479                var_name=""):
480    r"""
481    Return True if var_value is within range.  Otherwise, print an error
482    message and either return False or exit(1) depending on the value of
483    exit_on_error.
484
485    Description of arguments:
486    (See description of arguments for svalid_range (above)).
487    """
488
489    error_message = svalid_range(var_value, valid_range, var_name)
490    return process_error_message(error_message)
491
492
493def svalid_list(var_value,
494                valid_values=[],
495                var_name=""):
496    r"""
497    Return an empty string if var_value is a valid list.  Otherwise, return an
498    error string.
499
500    Description of arguments:
501    var_value                       The value (i.e. list) being validated.
502    valid_values                    A list of valid values.  Each element in
503                                    the var_value list must be equal to one of
504                                    these values to be considered valid.
505    var_name                        The name of the variable whose value is
506                                    passed in var_value.  This parameter is
507                                    normally unnecessary as this function can
508                                    figure out the var_name.  This is provided
509                                    for Robot callers.  In this scenario, we
510                                    are unable to get the variable name
511                                    ourselves.
512    """
513
514    error_message = ""
515    if len(var_value) == 0:
516        show_blanks = 1
517        error_message += "The \"" + get_var_name(var_name)
518        error_message += "\" list is empty and is therefore invalid:\n"
519        return error_message
520
521    found_error = 0
522    display_var_value = list(var_value)
523    for ix in range(0, len(var_value)):
524        if var_value[ix] not in valid_values:
525            found_error = 1
526            display_var_value[ix] = var_value[ix] + "*"
527
528    if found_error:
529        show_blanks = 1
530        error_message += "The list entries marked with \"*\" are not valid:\n"
531        error_message += gp.sprint_varx(get_var_name(var_name),
532                                        display_var_value, show_blanks)
533        error_message += gp.sprint_var(valid_values)
534        return error_message
535
536    return ""
537
538
539def valid_list(var_value,
540               valid_values=[],
541               var_name=""):
542    r"""
543    Return True if var_value is a valid list.  Otherwise, print an error
544    message and either return False or exit(1) depending on the value of
545    exit_on_error.
546
547    Description of arguments:
548    (See description of arguments for svalid_list (above)).
549    """
550
551    error_message = svalid_list(var_value, valid_values, var_name)
552    return process_error_message(error_message)
553
554
555def svalid_dict(var_value,
556                required_keys=[],
557                var_name=""):
558    r"""
559    Return an empty string if var_value is a valid dictionary.  Otherwise,
560    return an error string.
561
562    Description of arguments:
563    var_value                       The value (i.e. dictionary) being
564                                    validated.
565    required_keys                   A list of keys which must be found in the
566                                    dictionary for it to be considered valid.
567    var_name                        The name of the variable whose value is
568                                    passed in var_value.  This parameter is
569                                    normally unnecessary as this function can
570                                    figure out the var_name.  This is provided
571                                    for Robot callers.  In this scenario, we
572                                    are unable to get the variable name
573                                    ourselves.
574    """
575
576    error_message = ""
577
578    keys_missing = list(set(required_keys) - set(var_value.keys()))
579    if len(keys_missing) > 0:
580        show_blanks = 1
581        var_name = get_var_name(var_name)
582        error_message = "The following key fields are missing from "
583        error_message += var_name + ":\n"
584        error_message += gp.sprint_var(keys_missing)
585        error_message += gp.sprint_varx(var_name, var_value, show_blanks)
586        return error_message
587
588    return ""
589
590
591def valid_dict(var_value,
592               required_keys=[],
593               var_name=""):
594    r"""
595    Return True if var_value is a valid dictionary.  Otherwise, print an error
596    message and either return False or exit(1) depending on the value of
597    exit_on_error.
598
599    Description of arguments:
600    (See description of arguments for svalid_list (above)).
601    """
602
603    error_message = svalid_dict(var_value, required_keys, var_name)
604    return process_error_message(error_message)
605