Adding new function and fixing some bugs in general purpose py files.

gen_args.py:
  sprint_args()
    I made corrections to col1_width processing when indent is not zero.

gen_print.py:
  get_arg_name:
    I fixed a bug.
  sprint_varx:
    I added support for printing OrderedDict objects and robot DotDict objects.
    I added support for having the hex arg double as a "print None" for string objects.
  sprint_pgm_header:
    I added support for linefeed arg.
  sissuing:
    I added support for test_mode parm.
  sprintn:
    New function.

gen_valid.py:
  svalid_value:
    New function:
  valid_value:
    Now calls svalid_value.

  svalid_integer:
    New function:
  valid_integer:
    Now calls svalid_integer.

Change-Id: I161086d1148e4559fcc57b7d749cc3fb810dc19f
Signed-off-by: Michael Walsh <micwalsh@us.ibm.com>
diff --git a/lib/gen_arg.py b/lib/gen_arg.py
index 1a57411..72638cc 100755
--- a/lib/gen_arg.py
+++ b/lib/gen_arg.py
@@ -177,10 +177,13 @@
                                     of output.
     """
 
+    loc_col1_width = gp.col1_width + indent
+
     buffer = ""
 
     for key in arg_obj.__dict__:
-        buffer += gp.sprint_varx(key, getattr(arg_obj, key), 0, indent)
+        buffer += gp.sprint_varx(key, getattr(arg_obj, key), 0, indent,
+                                 loc_col1_width)
 
     return buffer
 
diff --git a/lib/gen_print.py b/lib/gen_print.py
index 6310122..e7efb16 100755
--- a/lib/gen_print.py
+++ b/lib/gen_print.py
@@ -15,6 +15,13 @@
 import argparse
 import __builtin__
 import logging
+import collections
+
+running_from_robot = 1
+try:
+    from robot.utils import DotDict
+except ImportError:
+    running_from_robot = 0
 
 import gen_arg as ga
 
@@ -206,7 +213,14 @@
     composite_line = lines[0].strip()
 
     called_func_name = sprint_func_name(stack_frame_ix)
-    if not re.match(r".*" + called_func_name, composite_line):
+    # Needed to add a right anchor to func_regex for cases like this where
+    # there is an arg whose name is a substring of the function name.  So the
+    # function name needs to be bounded on the right by zero or more spaces
+    # and a left parenthesis.
+    # if not valid_value(whatever, valid_values=["one", "two"]):
+    func_regex = ".*" + called_func_name + "[ ]*\("
+    # if not re.match(r".*" + called_func_name, composite_line):
+    if not re.match(func_regex, composite_line):
         # The called function name was not found in the composite line.  The
         # caller may be using a function alias.
         # I added code to handle pvar, qpvar, dpvar, etc. aliases.
@@ -214,10 +228,15 @@
         # sprint_func_name() returns the non-alias version, i.e. "print_var".
         # Adjusting for that here.
         alias = re.sub("print_var", "pvar", called_func_name)
+        if local_debug:
+            print_varx("alias", alias, 0, debug_indent)
         called_func_name = alias
+        func_regex = ".*" + called_func_name + "[ ]*\("
 
-    arg_list_etc = re.sub(".*" + called_func_name, "", composite_line)
+    # arg_list_etc = re.sub(".*" + called_func_name, "", composite_line)
+    arg_list_etc = "(" + re.sub(func_regex, "", composite_line)
     if local_debug:
+        print_varx("func_regex", func_regex, 0, debug_indent)
         print_varx("called_func_name", called_func_name, 0, debug_indent)
         print_varx("composite_line", composite_line, 0, debug_indent)
         print_varx("arg_list_etc", arg_list_etc, 0, debug_indent)
@@ -457,7 +476,10 @@
     hex                             This indicates that the value should be
                                     printed in hex format.  It is the user's
                                     responsibility to ensure that a var_value
-                                    contains a valid hex number.
+                                    contains a valid hex number.  For string
+                                    var_values, this will be interpreted as
+                                    show_blanks which means that blank values
+                                    will be printed as "<blank>".
     loc_col1_indent                 The number of spaces to indent the output.
     loc_col1_width                  The width of the output column containing
                                     the variable name.  The default value of
@@ -478,7 +500,12 @@
         loc_col1_width = loc_col1_width - loc_col1_indent
         # See if the user wants the output in hex format.
         if hex:
-            value_format = "0x%08x"
+            if type(var_value) in (str, unicode):
+                value_format = "%s"
+                if var_value is "":
+                    var_value = "<blank>"
+            else:
+                value_format = "0x%08x"
         else:
             value_format = "%s"
         format_string = "%" + str(loc_col1_indent) + "s%-" \
@@ -495,7 +522,16 @@
             pass
         ix = 0
         loc_trailing_char = "\n"
-        if type(var_value) is dict:
+        type_is_dict = 0
+        try:
+            if type(var_value) in (dict, collections.OrderedDict):
+                type_is_dict = 1
+        except AttributeError:
+                type_is_dict = 0
+        if running_from_robot:
+            if type(var_value) is DotDict:
+                type_is_dict = 1
+        if type_is_dict:
             for key, value in var_value.iteritems():
                 ix += 1
                 if ix == length:
@@ -794,7 +830,8 @@
 
 
 ###############################################################################
-def sprint_pgm_header(indent=0):
+def sprint_pgm_header(indent=0,
+                      linefeed=1):
 
     r"""
     Return a standardized header that programs should print at the beginning
@@ -804,26 +841,37 @@
     Description of arguments:
     indent                          The number of characters to indent each
                                     line of output.
+    linefeed                        Indicates whether a line feed be included
+                                    at the beginning and end of the report.
     """
 
-    buffer = "\n"
+    loc_col1_width = col1_width + indent
+
+    buffer = ""
+    if linefeed:
+        buffer = "\n"
 
     buffer += sindent(sprint_time() + "Running " + pgm_name + ".\n", indent)
     buffer += sindent(sprint_time() + "Program parameter values, etc.:\n\n",
                       indent)
-    buffer += sprint_varx("command_line", ' '.join(sys.argv), 0, indent)
+    buffer += sprint_varx("command_line", ' '.join(sys.argv), 0, indent,
+                          loc_col1_width)
     # We want the output to show a customized name for the pid and pgid but
     # we want it to look like a valid variable name.  Therefore, we'll use
     # pgm_name_var_name which was set when this module was imported.
-    buffer += sprint_varx(pgm_name_var_name + "_pid", os.getpid(), 0, indent)
-    buffer += sprint_varx(pgm_name_var_name + "_pgid", os.getpgrp(), 0, indent)
+    buffer += sprint_varx(pgm_name_var_name + "_pid", os.getpid(), 0, indent,
+                          loc_col1_width)
+    buffer += sprint_varx(pgm_name_var_name + "_pgid", os.getpgrp(), 0, indent,
+                          loc_col1_width)
     buffer += sprint_varx("uid", str(os.geteuid()) + " (" + os.getlogin() +
-                          ")", 0, indent)
+                          ")", 0, indent, loc_col1_width)
     buffer += sprint_varx("gid", str(os.getgid()) + " (" +
                           str(grp.getgrgid(os.getgid()).gr_name) + ")", 0,
-                          indent)
-    buffer += sprint_varx("host_name", socket.gethostname(), 0, indent)
-    buffer += sprint_varx("DISPLAY", os.environ['DISPLAY'], 0, indent)
+                          indent, loc_col1_width)
+    buffer += sprint_varx("host_name", socket.gethostname(), 0, indent,
+                          loc_col1_width)
+    buffer += sprint_varx("DISPLAY", os.environ['DISPLAY'], 0, indent,
+                          loc_col1_width)
     # I want to add code to print caller's parms.
 
     # __builtin__.arg_obj is created by the get_arg module function,
@@ -833,7 +881,8 @@
     except AttributeError:
         pass
 
-    buffer += "\n"
+    if linefeed:
+        buffer += "\n"
 
     return buffer
 
@@ -878,7 +927,8 @@
 
 
 ###############################################################################
-def sissuing(cmd_buf):
+def sissuing(cmd_buf,
+             test_mode=0):
 
     r"""
     Return a line indicating a command that the program is about to execute.
@@ -886,11 +936,20 @@
     Sample output for a cmd_buf of "ls"
 
     #(CDT) 2016/08/25 17:57:36 - Issuing: ls
+
     Description of args:
     cmd_buf                         The command to be executed by caller.
+    test_mode                       With test_mode set, your output will look
+                                    like this:
+
+    #(CDT) 2016/08/25 17:57:36 - (test_mode) Issuing: ls
+
     """
 
-    buffer = sprint_time() + "Issuing: " + cmd_buf + "\n"
+    buffer = sprint_time()
+    if test_mode:
+        buffer += "(test_mode) "
+    buffer += "Issuing: " + cmd_buf + "\n"
 
     return buffer
 
@@ -911,6 +970,7 @@
     total_time_string = "%0.6f" % total_time
 
     buffer += sprint_varx(pgm_name_var_name + "_runtime", total_time_string)
+    buffer += "\n"
 
     return buffer
 
@@ -929,6 +989,25 @@
     buffer                          This will be returned to the caller.
     """
 
+    return str(buffer)
+
+###############################################################################
+
+
+###############################################################################
+def sprintn(buffer=""):
+
+    r"""
+    Simply return the user's buffer with a line feed.  This function is used
+    by the qprint and dprint functions defined dynamically below, i.e. it
+    would not normally be called for general use.
+
+    Description of arguments.
+    buffer                          This will be returned to the caller.
+    """
+
+    buffer = str(buffer) + "\n"
+
     return buffer
 
 ###############################################################################
@@ -959,9 +1038,10 @@
 # func_names contains a list of all print functions which should be created
 # from their sprint counterparts.
 func_names = ['print_time', 'print_timen', 'print_error', 'print_varx',
-              'print_var', 'print_dashes', 'print_call_stack',
+              'print_var', 'print_dashes', 'indent', 'print_call_stack',
               'print_func_name', 'print_executing', 'print_pgm_header',
-              'issuing', 'print_pgm_footer', 'print_error_report', 'print']
+              'issuing', 'print_pgm_footer', 'print_error_report', 'print',
+              'printn']
 
 for func_name in func_names:
     if func_name == "print":
diff --git a/lib/gen_valid.py b/lib/gen_valid.py
index 1a52ace..e0337b4 100755
--- a/lib/gen_valid.py
+++ b/lib/gen_valid.py
@@ -10,15 +10,15 @@
 import gen_print as gp
 
 
-
 ###############################################################################
-def valid_value(var_value,
-                invalid_values=[""],
-                valid_values=[]):
+def svalid_value(var_value,
+                 invalid_values=[],
+                 valid_values=[],
+                 var_name=""):
 
     r"""
-    Return True if var_value is a valid value.  Otherwise, return False and
-    print an error message to stderr.
+    Return an empty string if var_value is a valid value.  Otherwise, return
+    an error string.
 
     Description of arguments:
     var_value                       The value being validated.
@@ -30,50 +30,135 @@
     valid_values                    A list of invalid 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.
     """
 
+    success_message = ""
+    error_message = ""
+    stack_frame_ix = 3
+
     len_valid_values = len(valid_values)
     len_invalid_values = len(invalid_values)
     if len_valid_values > 0 and len_invalid_values > 0:
-        gp.print_error_report("Programmer error - You must provide either an" +
-                              " invalid_values list or a valid_values" +
-                              " list but NOT both.")
-        return False
+        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
 
+    show_blanks = 1
     if len_valid_values > 0:
         # Processing the valid_values list.
         if var_value in valid_values:
-            return True
-        var_name = gp.get_arg_name(0, 1, 2)
-        gp.print_error_report("The following variable has an invalid" +
-                              " value:\n" +
-                              gp.sprint_varx(var_name, var_value) +
-                              "\nIt must be one of the following values:\n" +
-                              gp.sprint_varx("valid_values", valid_values))
-        return False
+            return success_message
+        if var_name == "":
+            var_name = gp.get_arg_name(0, 1, stack_frame_ix)
+        error_message += "The following variable has an invalid" +\
+                         " value:\n" +\
+                         gp.sprint_varx(var_name, var_value, show_blanks) +\
+                         "\nIt must be one of the following values:\n" +\
+                         gp.sprint_varx("valid_values", valid_values,
+                                        show_blanks)
+        return error_message
 
     if len_invalid_values == 0:
-        gp.print_error_report("Programmer error - You must provide either an" +
-                              " invalid_values list or a valid_values" +
-                              " list.  Both are empty.")
-        return False
+        # Assign default value.
+        invalid_values = [""]
 
     # Assertion: We have an invalid_values list.  Processing it now.
     if var_value not in invalid_values:
-        return True
+        return success_message
 
-    var_name = gp.get_arg_name(0, 1, 2)
-    gp.print_error_report("The following variable has an invalid value:\n" +
-                          gp.sprint_varx(var_name, var_value) + "\nIt must" +
-                          " NOT be one of the following values:\n" +
-                          gp.sprint_varx("invalid_values", invalid_values))
-    return False
+    if var_name == "":
+        var_name = gp.get_arg_name(0, 1, stack_frame_ix)
+    error_message += "The following variable has an invalid value:\n" +\
+                     gp.sprint_varx(var_name, var_value, show_blanks) +\
+                     "\nIt must NOT be one of the following values:\n" +\
+                     gp.sprint_varx("invalid_values", invalid_values,
+                                    show_blanks)
+    return error_message
 
 ###############################################################################
 
 
 ###############################################################################
-def valid_integer(var_value):
+def valid_value(var_value,
+                invalid_values=[],
+                valid_values=[],
+                var_name=""):
+
+    r"""
+    Return True if var_value is a valid value.  Otherwise, return False and
+    print an error message to stderr.
+
+    Description of arguments:
+    (See description of arguments for svalid_value (above).
+    """
+
+    error_message = svalid_value(var_value, invalid_values, valid_values,
+                                 var_name)
+
+    if not error_message == "":
+        gp.print_error_report(error_message)
+        return False
+    return True
+
+###############################################################################
+
+
+###############################################################################
+def svalid_integer(var_value,
+                   var_name=""):
+
+    r"""
+    Return an empty string if var_value is a valid integer.  Otherwise, return
+    an error string.
+
+    Description of arguments:
+    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.
+    """
+
+    # This currently allows floats which is not good.
+
+    success_message = ""
+    error_message = ""
+    try:
+        if type(int(var_value)) is int:
+            return success_message
+    except ValueError:
+        pass
+
+    # If we get to this point, the validation has failed.
+    if var_name is "":
+        stack_index = 3
+        var_name = gp.get_arg_name(0, 1, stack_index)
+
+    show_blanks = 1
+    error_message += "Invalid integer value:\n" +\
+                     gp.sprint_varx(var_name, var_value, show_blanks)
+
+    return error_message
+
+###############################################################################
+
+
+###############################################################################
+def valid_integer(var_value,
+                  var_name=""):
 
     r"""
     Return True if var_value is a valid integer.  Otherwise, return False and
@@ -85,20 +170,11 @@
 
     # This currently allows floats which is not good.
 
-    try:
-        if type(int(var_value)) is int:
-            return True
-    except ValueError:
-        pass
+    error_message = svalid_integer(var_value, var_name)
 
-    # If we get to this point, the validation has failed.
-
-    var_name = gp.get_arg_name(0, 1, 2)
-    gp.print_varx("var_name", var_name)
-
-    gp.print_error_report("Invalid integer value:\n" +
-                          gp.sprint_varx(var_name, var_value))
-
-    return False
+    if not error_message == "":
+        gp.print_error_report(error_message)
+        return False
+    return True
 
 ###############################################################################