Overhaul of gen_valid.py

This includes the following features:

- Previously, the validation functions were created as pairs of
  svalid/valid functions.  For example, there was an svalid_value
  paired with a valid_value, an svalid_integer paired with a
  valid_integer, etc.  In this design, the "svalid" base function would
  do the work of detecting a particular error, formulating an error
  message and returning it.  The "valid" wrapper function would call
  upon its svalid counterpart and then process any error by printing an
  error message, exiting the program, etc.

  In the new design, the base function determines whether it ought to
  simply return an error message or continue on to the error processing
  (printing, failing, etc.).  It does this by determining whether the
  caller is assigning the output to a variable.  If so, it simply
  returns the error message.

- Docstrings have been standardized with several goals in mind:
  - Making them easier to maintain in this program.
  - Making them manipulatable by gen_robot_valid.py.
  - Making them more palatable for use by robot's libdoc function.

- Since all of the validation functions' docstrings share a lot of
  common text, the docstrings are manipulated after the function have
  been defined.  Namely, each one has a standard header pre-pended and a
  standard footer appended to it.

- The validation functions' approach has been standardized so that all
  of the functions look quite similar to each other except for the
  actual error detection code.

- The validation functions have been re-worked for improved readability.

- valid_type():  New function.

- valid_range() changes:
  valid_range previously accepted a valid_range string argument whose
  format was "<lower>..<upper>".  The valid_range argument has been
  deprecated and replaced by 2 arguments: lower and upper.

Change-Id: I1d7c124abcd7675e9892a00757e5c482bf3746f8
Signed-off-by: Michael Walsh <micwalsh@us.ibm.com>
diff --git a/lib/gen_valid.py b/lib/gen_valid.py
index 77099c0..d1601ec 100755
--- a/lib/gen_valid.py
+++ b/lib/gen_valid.py
@@ -1,12 +1,13 @@
 #!/usr/bin/env python
 
 r"""
-This module provides valuable argument processing functions like
-gen_get_options and sprint_args.
+This module provides validation functions like valid_value(), valid_integer(),
+etc.
 """
 
 import os
 import gen_print as gp
+import func_args as fa
 
 exit_on_error = False
 
@@ -15,7 +16,7 @@
     r"""
     Set the exit_on_error value to either True or False.
 
-    If exit_on_error is set, validation functions like valid_value will exit
+    If exit_on_error is set, validation functions like valid_value() will exit
     the program on error instead of returning False.
 
     Description of argument(s):
@@ -26,11 +27,11 @@
     exit_on_error = value
 
 
-def get_var_name(var_name):
+def get_var_name(*args, **kwargs):
     r"""
-    If var_name has a value, simply return it.  Otherwise, get the variable
-    name of the first argument used to call the validation function (e.g.
-    valid_value, valid_integer, etc.) and return it.
+    If args/kwargs contain a var_name, simply return its value.  Otherwise,
+    get the variable name of the first argument used to call the validation
+    function (e.g. valid, valid_integer, etc.) and return it.
 
     This function is designed solely for use by other functions in this file.
 
@@ -43,18 +44,17 @@
     Which results in the following call stack:
 
     valid_value(last_name)
-      -> svalid_value(var_value...)
-        -> get_var_name(var_name)
+      -> get_var_name(var_name)
 
     In this example, this function will return "last_name".
 
     Example:
 
-    err_msg = svalid_value(last_name, var_name="some_other_name")
+    err_msg = valid_value(last_name, var_name="some_other_name")
 
     Which results in the following call stack:
 
-    svalid_value(var_value, var_name="some_other_name")
+    valid_value(var_value, var_name="some_other_name")
       -> get_var_name(var_name)
 
     In this example, this function will return "some_other_name".
@@ -63,454 +63,398 @@
     var_name                        The name of the variable.
     """
 
-    if var_name != "":
+    var_name, args, kwargs = fa.pop_arg(*args, **kwargs)
+    if var_name:
         return var_name
-    # Calculate stack_frame_ix.  The validation functions in this file come
-    # in pairs.  There is an "s" version of each validation function (e.g.
-    # svalid_value) whose job is to return an error message string.  Then
-    # there is a wrapper function (e.g. valid_value) that will call the "s"
-    # version and print the result if there is an error.  See examples 1 and 2
-    # above for illustration.  This function must be cognizant of both
-    # scenarios to accurately determine the name of the variable being
-    # validated.  Where the "s" function is being called directly, the
-    # stack_frame_ix should be set to 3.  Where the wrapper function is being
-    # called, the stack_frame_ix should be incremented to 4.
-    stack_frame_ix = 3
-    parent_func_name = gp.sprint_func_name(2)
-    grandparent_func_name = gp.sprint_func_name(3)
-    if parent_func_name == "s" + grandparent_func_name:
-        stack_frame_ix += 1
-    var_name = gp.get_arg_name(0, 1, stack_frame_ix)
-    return var_name
+    return gp.get_arg_name(0, 1, stack_frame_ix=3)
 
 
 def process_error_message(error_message):
     r"""
-    Process the error_message as follows:
-    - If the error_message is blank, return True.
-    - If the error_message contains a value:
-        - Print the error_message as part of a full error report.
-        - If global exit_on_error is set, then exit the program with a return
-          code of 1.
-        - If exit_on_error is not set, return False.
+    Process the error_message in the manner described below.
 
-    This function is designed solely for use by wrapper functions in this file
-    (e.g. "valid_value").
+    This function is designed solely for use by other functions in this file.
+
+    NOTE: A blank error_message means that there is no error.
+
+    For the following explanations, assume the caller of this function is a
+    function with the following definition:
+    valid_value(var_value, valid_values=[], invalid_values=[], *args,
+    **kwargs):
+
+    If the user of valid_value() is assigning the valid_value() return value
+    to a variable, process_error_message() will simply return the
+    error_message.  This mode of usage is illustrated by the following example:
+
+    error_message = valid_value(var1)
+
+    This mode is useful for callers who wish to validate a variable and then
+    decide for themselves what to do with the error_message (e.g.
+    raise(error_message), BuiltIn().fail(error_message), etc.).
+
+    If the user of valid_value() is NOT assigning the valid_value() return
+    value to a variable, process_error_message() will behave as follows.
+
+    First, if error_message is non-blank, it will be printed to stderr via a
+    call to gp.print_error_report(error_message).
+
+    If exit_on_error is set:
+    - If the error_message is blank, simply return.
+    - If the error_message is non-blank, exit the program with a return code
+      of 1.
+
+    If exit_on_error is NOT set:
+    - If the error_message is blank, return True.
+    - If the error_message is non-blank, return False.
 
     Description of argument(s):
     error_message                   An error message.
     """
 
+    # Determine whether the caller's caller is assigning the result to a
+    # variable.
+    l_value = gp.get_arg_name(None, -1, stack_frame_ix=3)
+    if l_value:
+        return error_message
+
     if error_message == "":
+        if exit_on_error:
+            return
         return True
 
-    gp.print_error_report(error_message)
+    gp.print_error_report(error_message, stack_frame_ix=4)
     if exit_on_error:
         exit(1)
     return False
 
 
-def svalid_value(var_value,
-                 invalid_values=[],
-                 valid_values=[],
-                 var_name=""):
-    r"""
-    Return an empty string if var_value is a valid value.  Otherwise, return
-    an error string.
+# Note to programmers:  All of the validation functions in this module should
+# follow the same basic template:
+# def valid_value(var_value, var1, var2, varn, *args, **kwargs):
+#
+#     error_message = ""
+#     if not valid:
+#         var_name = get_var_name(*args, **kwargs)
+#         error_message += "The following variable is invalid because...:\n"
+#         error_message += gp.sprint_varx(var_name, var_value, gp.blank())
+#
+#     return process_error_message(error_message)
 
-    Description of arguments:
-    var_value                       The value being validated.
-    invalid_values                  A list of invalid values.  If var_value is
-                                    equal to any of these, it is invalid.
-                                    Note that if you specify anything for
-                                    invalid_values (below), the valid_values
-                                    list is not even processed.  If you
-                                    specify nothing for both invalid_values
-                                    and valid_values, invalid_values will be
-                                    set to a default value of [""].
-    valid_values                    A list of valid values.  var_value must be
-                                    equal to one of these values to be
-                                    considered valid.
-    var_name                        The name of the variable whose value is
-                                    passed in var_value.  This parameter is
-                                    normally unnecessary as this function can
-                                    figure out the var_name.  This is provided
-                                    for Robot callers.  In this scenario, we
-                                    are unable to get the variable name
-                                    ourselves.
+
+# The docstring header and footer will be added to each validation function's
+# existing docstring.
+docstring_header = \
+    r"""
+    Determine whether var_value is valid, construct an error_message and call
+    process_error_message(error_message).
+
+    See the process_error_message() function defined in this module for a
+    description of how error messages are processed.
     """
 
-    success_message = ""
+additional_args_docstring_footer = \
+    r"""
+    args                            Additional positional arguments (described
+                                    below).
+    kwargs                          Additional keyword arguments (described
+                                    below).
+
+    Additional argument(s):
+    var_name                        The name of the variable whose value is
+                                    passed in var_value.  For the general
+                                    case, this argument is unnecessary as this
+                                    function can figure out the var_name.
+                                    This is provided for Robot callers in
+                                    which case, this function lacks the
+                                    ability to determine the variable name.
+    """
+
+
+def valid_type(var_value, required_type, *args, **kwargs):
+    r"""
+    The variable value is valid if it is of the required type.
+
+    Examples:
+
+    valid_type(var1, int)
+
+    valid_type(var1, (list, dict))
+
+    Description of argument(s):
+    var_value                       The value being validated.
+    required_type                   A type or a tuple of types (e.g. str, int,
+                                    etc.).
+    """
+
+    error_message = ""
+    if type(required_type) is tuple:
+        if type(var_value) in required_type:
+            return process_error_message(error_message)
+    else:
+        if type(var_value) is required_type:
+            return process_error_message(error_message)
+
+    # If we get to this point, the validation has failed.
+    var_name = get_var_name(*args, **kwargs)
+    error_message += "Invalid variable type:\n"
+    error_message += gp.sprint_varx(var_name, var_value,
+                                    gp.blank() | gp.show_type())
+    error_message += "\n"
+    error_message += gp.sprint_var(required_type)
+
+    return process_error_message(error_message)
+
+
+def valid_value(var_value, valid_values=[], invalid_values=[], *args,
+                **kwargs):
+
+    r"""
+    The variable value is valid if it is either contained in the valid_values
+    list or if it is NOT contained in the invalid_values list.  If the caller
+    specifies nothing for either of these 2 arguments, invalid_values will be
+    initialized to ['', None].  This is a good way to fail on variables which
+    contain blank values.
+
+    It is illegal to specify both valid_values and invalid values.
+
+    Example:
+
+    var1 = ''
+    valid_value(var1)
+
+    This code would fail because var1 is blank and the default value for
+    invalid_values is ['', None].
+
+    Example:
+    var1 = 'yes'
+    valid_value(var1, valid_values=['yes', 'true'])
+
+    This code would pass.
+
+    Description of argument(s):
+    var_value                       The value being validated.
+    valid_values                    A list of valid values.  The variable
+                                    value must be equal to one of these values
+                                    to be considered valid.
+    invalid_values                  A list of invalid values.  If the variable
+                                    value is equal to any of these, it is
+                                    considered invalid.
+    """
+
     error_message = ""
 
     # Validate this function's arguments.
     len_valid_values = len(valid_values)
     len_invalid_values = len(invalid_values)
     if len_valid_values > 0 and len_invalid_values > 0:
-        error_message += "Programmer error - You must provide either an" +\
-                         " invalid_values list or a valid_values" +\
-                         " list but NOT both.\n" +\
-                         gp.sprint_var(invalid_values) +\
-                         gp.sprint_var(valid_values)
-        return error_message
+        error_message += "Programmer error - You must provide either an"
+        error_message += " invalid_values list or a valid_values"
+        error_message += " list but NOT both:\n"
+        error_message += gp.sprint_var(invalid_values)
+        error_message += gp.sprint_var(valid_values)
+        return process_error_message(error_message)
 
     if len_valid_values > 0:
         # Processing the valid_values list.
         if var_value in valid_values:
-            return success_message
-        error_message += "The following variable has an invalid" +\
-                         " value:\n" +\
-                         gp.sprint_varx(get_var_name(var_name), var_value,
-                                        gp.blank() | gp.verbose()) +\
-                         "\nIt must be one of the following values:\n" +\
-                         gp.sprint_var(valid_values, gp.blank())
-        return error_message
+            return process_error_message(error_message)
+        var_name = get_var_name(*args, **kwargs)
+        error_message += "Invalid variable value:\n"
+        error_message += gp.sprint_varx(var_name, var_value,
+                                        gp.blank() | gp.verbose()
+                                        | gp.show_type())
+        error_message += "\n"
+        error_message += "It must be one of the following values:\n"
+        error_message += "\n"
+        error_message += gp.sprint_var(valid_values,
+                                       gp.blank() | gp.show_type())
+        return process_error_message(error_message)
 
     if len_invalid_values == 0:
         # Assign default value.
-        invalid_values = [""]
+        invalid_values = ["", None]
 
     # Assertion: We have an invalid_values list.  Processing it now.
     if var_value not in invalid_values:
-        return success_message
+        return process_error_message(error_message)
 
-    error_message += "The following variable has an invalid value:\n" +\
-                     gp.sprint_varx(get_var_name(var_name), var_value,
-                                    gp.blank() | gp.verbose()) +\
-                     "\nIt must NOT be one of the following values:\n" +\
-                     gp.sprint_var(invalid_values, gp.blank())
-    return error_message
-
-
-def valid_value(var_value,
-                invalid_values=[],
-                valid_values=[],
-                var_name=""):
-    r"""
-    Return True if var_value is valid.  Otherwise, print an error message and
-    either return False or exit(1) depending on the value of exit_on_error.
-
-    Description of arguments:
-    (See description of arguments for svalid_value (above)).
-    """
-
-    error_message = svalid_value(var_value, invalid_values, valid_values,
-                                 var_name)
+    var_name = get_var_name(*args, **kwargs)
+    error_message += "Invalid variable value:\n"
+    error_message += gp.sprint_varx(var_name, var_value,
+                                    gp.blank() | gp.verbose()
+                                    | gp.show_type())
+    error_message += "\n"
+    error_message += "It must NOT be one of the following values:\n"
+    error_message += "\n"
+    error_message += gp.sprint_var(invalid_values,
+                                   gp.blank() | gp.show_type())
     return process_error_message(error_message)
 
 
-def svalid_integer(var_value,
-                   var_name=""):
+def valid_range(var_value, lower=None, upper=None, *args, **kwargs):
     r"""
-    Return an empty string if var_value is a valid integer.  Otherwise, return
-    an error string.
+    The variable value is valid if it is within the specified range.
 
-    Description of arguments:
+    This function can be used with any type of operands where they can have a
+    greater than/less than relationship to each other (e.g. int, float, str).
+
+    Description of argument(s):
     var_value                       The value being validated.
-    var_name                        The name of the variable whose value is
-                                    passed in var_value.  This parameter is
-                                    normally unnecessary as this function can
-                                    figure out the var_name.  This is provided
-                                    for Robot callers.  In this scenario, we
-                                    are unable to get the variable name
-                                    ourselves.
+    lower                           The lower end of the range.  If not None,
+                                    the var_value must be greater than or
+                                    equal to lower.
+    upper                           The upper end of the range.  If not None,
+                                    the var_value must be less than or equal
+                                    to upper.
     """
 
-    success_message = ""
     error_message = ""
-    try:
-        if isinstance(int(str(var_value), 0), int):
-            return success_message
-    except ValueError:
-        pass
+    if not lower and not upper:
+        return process_error_message(error_message)
+    if not lower and var_value <= upper:
+        return process_error_message(error_message)
+    if not upper and var_value >= lower:
+        return process_error_message(error_message)
+    if lower and upper:
+        if lower > upper:
+            var_name = get_var_name(*args, **kwargs)
+            error_message += "Programmer error - the lower value is greater"
+            error_message += " than the upper value:\n"
+            error_message += gp.sprint_vars(lower, upper, fmt=gp.show_type())
+            return process_error_message(error_message)
+        if lower <= var_value <= upper:
+            return process_error_message(error_message)
 
-    # If we get to this point, the validation has failed.
-    error_message +=\
-        "Invalid integer value:\n" +\
-        gp.sprint_varx(get_var_name(var_name), var_value, gp.blank())
-
-    return error_message
-
-
-def valid_integer(var_value,
-                  var_name=""):
-    r"""
-    Return True if var_value is a valid integer.  Otherwise, print an error
-    message and either return False or exit(1) depending on the value of
-    exit_on_error.
-
-    Description of arguments:
-    (See description of arguments for svalid_integer (above)).
-    """
-
-    error_message = svalid_integer(var_value, var_name)
+    var_name = get_var_name(*args, **kwargs)
+    error_message += "The following variable is not within the expected"
+    error_message += " range:\n"
+    error_message += gp.sprint_varx(var_name, var_value, gp.show_type())
+    error_message += "\n"
+    error_message += "range:\n"
+    error_message += gp.sprint_vars(lower, upper, fmt=gp.show_type(), indent=2)
     return process_error_message(error_message)
 
 
-def svalid_dir_path(var_value,
-                    var_name=""):
+def valid_integer(var_value, lower=None, upper=None, *args, **kwargs):
     r"""
-    Return an empty string if var_value is a valid directory path.  Otherwise,
-    return an error string.
+    The variable value is valid if it is an integer or can be interpreted as
+    an integer (e.g. 7, "7", etc.).
 
-    Description of arguments:
+    This function also calls valid_range to make sure the integer value is
+    within the specified range (if any).
+
+    Description of argument(s):
     var_value                       The value being validated.
-    var_name                        The name of the variable whose value is
-                                    passed in var_value.  This parameter is
-                                    normally unnecessary as this function can
-                                    figure out the var_name.  This is provided
-                                    for Robot callers.  In this scenario, we
-                                    are unable to get the variable name
-                                    ourselves.
+    lower                           The lower end of the range.  If not None,
+                                    the var_value must be greater than or
+                                    equal to lower.
+    upper                           The upper end of the range.  If not None,
+                                    the var_value must be less than or equal
+                                    to upper.
+    """
+
+    error_message = ""
+    var_name = get_var_name(*args, **kwargs)
+    try:
+        var_value = int(str(var_value), 0)
+    except ValueError:
+        error_message += "Invalid integer value:\n"
+        error_message += gp.sprint_varx(var_name, var_value,
+                                        gp.blank() | gp.show_type())
+        return process_error_message(error_message)
+
+    # Check the range (if any).
+    if lower:
+        lower = int(str(lower), 0)
+    if upper:
+        upper = int(str(upper), 0)
+    error_message = valid_range(var_value, lower, upper, var_name=var_name)
+
+    return process_error_message(error_message)
+
+
+def valid_dir_path(var_value, *args, **kwargs):
+    r"""
+    The variable value is valid if it contains the path of an existing
+    directory.
+
+    Description of argument(s):
+    var_value                       The value being validated.
     """
 
     error_message = ""
     if not os.path.isdir(str(var_value)):
-        error_message += "The following directory does not exist:\n" +\
-            gp.sprint_varx(get_var_name(var_name), var_value)
+        var_name = get_var_name(*args, **kwargs)
+        error_message += "The following directory does not exist:\n"
+        error_message += gp.sprint_varx(var_name, var_value)
 
-    return error_message
-
-
-def valid_dir_path(var_value,
-                   var_name=""):
-    r"""
-    Return True if var_value is a valid directory path.  Otherwise, print an
-    error message and either return False or exit(1) depending on the value of
-    exit_on_error.
-
-    Valid means that the directory path exists.
-
-    Description of arguments:
-    (See description of arguments for svalid_dir_path (above)).
-    """
-
-    error_message = svalid_dir_path(var_value, var_name)
     return process_error_message(error_message)
 
 
-def svalid_file_path(var_value,
-                     var_name=""):
+def valid_file_path(var_value, *args, **kwargs):
     r"""
-    Return an empty string if var_value is a valid file path.  Otherwise,
-    return an error string.
+    The variable value is valid if it contains the path of an existing file.
 
-    Description of arguments:
+    Description of argument(s):
     var_value                       The value being validated.
-    var_name                        The name of the variable whose value is
-                                    passed in var_value.  This parameter is
-                                    normally unnecessary as this function can
-                                    figure out the var_name.  This is provided
-                                    for Robot callers.  In this scenario, we
-                                    are unable to get the variable name
-                                    ourselves.
     """
 
     error_message = ""
     if not os.path.isfile(str(var_value)):
-        error_message += "Invalid file (does not exist):\n" +\
-            gp.sprint_varx(get_var_name(var_name), var_value)
+        var_name = get_var_name(*args, **kwargs)
+        error_message += "The following file does not exist:\n"
+        error_message += gp.sprint_varx(var_name, var_value)
 
-    return error_message
-
-
-def valid_file_path(var_value,
-                    var_name=""):
-    r"""
-    Return True if var_value is a valid file path.  Otherwise, print an error
-    message and either return False or exit(1) depending on the value of
-    exit_on_error.
-
-    Valid means that the file exists.
-
-    Description of arguments:
-    (See description of arguments for svalid_file_path (above)).
-    """
-
-    error_message = svalid_file_path(var_value, var_name)
     return process_error_message(error_message)
 
 
-def svalid_path(var_value,
-                var_name=""):
+def valid_path(var_value, *args, **kwargs):
     r"""
-    Return an empty string if var_value is either a valid file path or
-    directory path.  Otherwise, return an error string.
+    The variable value is valid if it contains the path of an existing file or
+    directory.
 
-    Description of arguments:
+    Description of argument(s):
     var_value                       The value being validated.
-    var_name                        The name of the variable whose value is
-                                    passed in var_value.  This parameter is
-                                    normally unnecessary as this function can
-                                    figure out the var_name.  This is provided
-                                    for Robot callers.  In this scenario, we
-                                    are unable to get the variable name
-                                    ourselves.
     """
 
     error_message = ""
     if not (os.path.isfile(str(var_value)) or os.path.isdir(str(var_value))):
-        error_message = "Invalid path (file or directory does not exist):\n" +\
-            gp.sprint_varx(get_var_name(var_name), var_value)
+        var_name = get_var_name(*args, **kwargs)
+        error_message += "Invalid path (file or directory does not exist):\n"
+        error_message += gp.sprint_varx(var_name, var_value)
 
-    return error_message
-
-
-def valid_path(var_value,
-               var_name=""):
-    r"""
-    Return True if var_value is a valid file path.  Otherwise, print an error
-    message and either return False or exit(1) depending on the value of
-    exit_on_error.
-
-    Valid means that the file exists.
-
-    Description of arguments:
-    (See description of arguments for svalid_path (above)).
-    """
-
-    error_message = svalid_path(var_value, var_name)
     return process_error_message(error_message)
 
 
-def svalid_range(var_value,
-                 valid_range=[],
-                 var_name=""):
+def valid_list(var_value, valid_values=[], fail_on_empty=False, *args,
+               **kwargs):
     r"""
-    Return an empty string if var_value is within the range.  Otherwise,
-    return an error string.
+    The variable value is valid if it is a list where each entry can be found
+    in the valid_values list.
 
-    Description of arguments:
-    var_value                       The value being validated.  This value
-                                    must be an integer.
-    valid_range                     A list comprised of one or two elements
-                                    which are the lower and upper ends of a
-                                    range.  These values must be integers
-                                    except where noted.  Valid specifications
-                                    may be of the following forms: [lower,
-                                    upper], [lower] or [None, upper].
-    var_name                        The name of the variable whose value is
-                                    passed in var_value.  This parameter is
-                                    normally unnecessary as this function can
-                                    figure out the var_name.  This is provided
-                                    for Robot callers.  In this scenario, we
-                                    are unable to get the variable name
-                                    ourselves.
-    """
-
-    error_message = ""
-
-    # Validate this function's parms:
-    # First, ensure that the value is an integer.
-    error_message = svalid_integer(var_value, var_name)
-    if not error_message == "":
-        return error_message
-    var_value = int(var_value)
-
-    len_valid_range = len(valid_range)
-    if len_valid_range == 0 or len_valid_range > 2:
-        error_message += "Programmer error - For the valid_range parameter," +\
-                         " you must provide a list consisting of one or two" +\
-                         " elements.\n" +\
-                         gp.sprint_var(valid_range)
-        return error_message
-
-    if len_valid_range == 1 or valid_range[0] is not None:
-        # Make sure lower valid_range value is an integer.
-        error_message = svalid_integer(valid_range[0], "valid_range[0]")
-        if not error_message == "":
-            error_message = "Programmer error:\n" + error_message
-            return error_message
-    if valid_range[0] is not None:
-        valid_range[0] = int(valid_range[0])
-    if len_valid_range == 2:
-        # Make sure upper valid_range value is an integer.
-        error_message = svalid_integer(valid_range[1], "valid_range[1]")
-        if not error_message == "":
-            error_message = "Programmer error:\n" + error_message
-            return error_message
-        valid_range[1] = int(valid_range[1])
-        if valid_range[0] is not None and valid_range[0] > valid_range[1]:
-            error_message = "Programmer error - In the following range, the" +\
-                            " lower limit is greater than the upper" +\
-                            " limit:\n" + gp.sprint_var(valid_range)
-            return error_message
-
-    if len_valid_range == 1:
-        if var_value < valid_range[0]:
-            error_message += "The following variable is not within the" +\
-                             " expected range:\n" +\
-                             gp.sprint_varx(get_var_name(var_name),
-                                            var_value) +\
-                             gp.sprint_varx("valid_range",
-                                            str(valid_range[0]) + "..")
-            return error_message
-        return error_message
-
-    if valid_range[0] is None:
-        if var_value > valid_range[1]:
-            error_message += "The following variable is not within the" +\
-                             " expected range:\n" +\
-                             gp.sprint_varx(get_var_name(var_name),
-                                            var_value) +\
-                             gp.sprint_varx("valid_range",
-                                            ".." + str(valid_range[1]))
-            return error_message
-
-    if var_value < valid_range[0] or var_value > valid_range[1]:
-        error_message += "The following variable is not within the expected" +\
-                         " range:\n" +\
-                         gp.sprint_varx(get_var_name(var_name), var_value) +\
-                         gp.sprint_varx("valid_range",
-                                        str(valid_range[0]) + ".."
-                                        + str(valid_range[1]))
-        return error_message
-
-    return error_message
-
-
-def valid_range(var_value,
-                valid_range=[],
-                var_name=""):
-    r"""
-    Return True if var_value is within range.  Otherwise, print an error
-    message and either return False or exit(1) depending on the value of
-    exit_on_error.
-
-    Description of arguments:
-    (See description of arguments for svalid_range (above)).
-    """
-
-    error_message = svalid_range(var_value, valid_range, var_name)
-    return process_error_message(error_message)
-
-
-def svalid_list(var_value,
-                valid_values=[],
-                var_name=""):
-    r"""
-    Return an empty string if var_value is a valid list.  Otherwise, return an
-    error string.
-
-    Description of arguments:
-    var_value                       The value (i.e. list) being validated.
+    Description of argument(s):
+    var_value                       The value being validated.
     valid_values                    A list of valid values.  Each element in
                                     the var_value list must be equal to one of
                                     these values to be considered valid.
-    var_name                        The name of the variable whose value is
-                                    passed in var_value.  This parameter is
-                                    normally unnecessary as this function can
-                                    figure out the var_name.  This is provided
-                                    for Robot callers.  In this scenario, we
-                                    are unable to get the variable name
-                                    ourselves.
+    fail_on_empty                   Indicates that an empty list for the
+                                    variable value should be considered an
+                                    error.
     """
 
     error_message = ""
-    if len(var_value) == 0:
-        error_message += "The \"" + get_var_name(var_name)
-        error_message += "\" list is empty and is therefore invalid:\n"
-        return error_message
+
+    if type(var_value) is not list:
+        var_name = get_var_name(*args, **kwargs)
+        error_message = valid_type(var_value, list, var_name=var_name)
+        if error_message:
+            return process_error_message(error_message)
+
+    if fail_on_empty and len(var_value) == 0:
+        var_name = get_var_name(*args, **kwargs)
+        error_message += "Invalid empty list:\n"
+        error_message += gp.sprint_varx(var_name, var_value, gp.show_type())
+        return process_error_message(error_message)
 
     found_error = 0
     display_var_value = list(var_value)
@@ -520,77 +464,56 @@
             display_var_value[ix] = var_value[ix] + "*"
 
     if found_error:
-        error_message += "The list entries marked with \"*\" are not valid:\n"
-        error_message += gp.sprint_varx(get_var_name(var_name),
-                                        display_var_value, gp.blank())
-        error_message += gp.sprint_var(valid_values)
-        return error_message
+        var_name = get_var_name(*args, **kwargs)
+        error_message += "The following list is invalid (see entries marked"
+        error_message += " with \"*\"):\n"
+        error_message += gp.sprint_varx(var_name, display_var_value,
+                                        gp.blank() | gp.show_type())
+        error_message += "\n"
+        error_message += gp.sprint_var(valid_values | gp.show_type())
+        return process_error_message(error_message)
 
-    return ""
-
-
-def valid_list(var_value,
-               valid_values=[],
-               var_name=""):
-    r"""
-    Return True if var_value is a valid list.  Otherwise, print an error
-    message and either return False or exit(1) depending on the value of
-    exit_on_error.
-
-    Description of arguments:
-    (See description of arguments for svalid_list (above)).
-    """
-
-    error_message = svalid_list(var_value, valid_values, var_name)
     return process_error_message(error_message)
 
 
-def svalid_dict(var_value,
-                required_keys=[],
-                var_name=""):
+def valid_dict(var_value, required_keys=[], *args, **kwargs):
     r"""
-    Return an empty string if var_value is a valid dictionary.  Otherwise,
-    return an error string.
+    The variable value is valid if it is a dictionary containing all of the
+    required keys.
 
-    Description of arguments:
-    var_value                       The value (i.e. dictionary) being
-                                    validated.
+    Description of argument(s):
+    var_value                       The value being validated.
     required_keys                   A list of keys which must be found in the
                                     dictionary for it to be considered valid.
-    var_name                        The name of the variable whose value is
-                                    passed in var_value.  This parameter is
-                                    normally unnecessary as this function can
-                                    figure out the var_name.  This is provided
-                                    for Robot callers.  In this scenario, we
-                                    are unable to get the variable name
-                                    ourselves.
     """
 
     error_message = ""
-
-    keys_missing = list(set(required_keys) - set(var_value.keys()))
-    if len(keys_missing) > 0:
-        var_name = get_var_name(var_name)
-        error_message = "The following key fields are missing from "
-        error_message += var_name + ":\n"
-        error_message += gp.sprint_var(keys_missing)
-        error_message += gp.sprint_varx(var_name, var_value, gp.blank())
-        return error_message
-
-    return ""
-
-
-def valid_dict(var_value,
-               required_keys=[],
-               var_name=""):
-    r"""
-    Return True if var_value is a valid dictionary.  Otherwise, print an error
-    message and either return False or exit(1) depending on the value of
-    exit_on_error.
-
-    Description of arguments:
-    (See description of arguments for svalid_list (above)).
-    """
-
-    error_message = svalid_dict(var_value, required_keys, var_name)
+    missing_keys = list(set(required_keys) - set(var_value.keys()))
+    if len(missing_keys) > 0:
+        var_name = get_var_name(*args, **kwargs)
+        error_message += "The following dictionary is invalid because it is"
+        error_message += " missing required keys:\n"
+        error_message += gp.sprint_varx(var_name, var_value,
+                                        gp.blank() | gp.show_type())
+        error_message += "\n"
+        error_message += gp.sprint_var(missing_keys | gp.show_type())
     return process_error_message(error_message)
+
+
+# Modify selected function docstrings by adding headers/footers.
+
+func_names = [
+    "valid_type", "valid_value", "valid_range", "valid_integer",
+    "valid_dir_path", "valid_file_path", "valid_path", "valid_list",
+    "valid_dict"
+]
+
+raw_doc_strings = {}
+
+for func_name in func_names:
+    cmd_buf = "raw_doc_strings['" + func_name + "'] = " + func_name
+    cmd_buf += ".__doc__"
+    exec(cmd_buf)
+    cmd_buf = func_name + ".__doc__ = docstring_header + " + func_name
+    cmd_buf += ".__doc__.rstrip(\" \\n\") + additional_args_docstring_footer"
+    exec(cmd_buf)