Adding the following support functions for use with dvt robot tests:
sprint_func_name
get_arg_name
sprint_time
sprint_timen
sprint_error
sprint_varx
sprint_var
sprint_dashes
sprint_call_stack
sprint_executing
sprint_pgm_header
sissuing
sprint_pgm_footer
create_temp_file_name
my_parm_file
rprint
rprintn
rprint_auto_vars
rpvars
Change-Id: I30f022e7bfdb92bdf083305e87aeccd94b9bd49e
Signed-off-by: Michael Walsh <micwalsh@us.ibm.com>
diff --git a/lib/gen_misc.py b/lib/gen_misc.py
new file mode 100755
index 0000000..167a3d9
--- /dev/null
+++ b/lib/gen_misc.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python
+
+# This module provides many valuable functions such as my_parm_file.
+
+# sys and os are needed to get the program dir path and program name.
+import sys
+import os
+import ConfigParser
+import StringIO
+
+
+###############################################################################
+def my_parm_file(prop_file_path):
+
+ r"""
+ Read a properties file, put the keys/values into a dictionary and return
+ the dictionary.
+
+ The properties file must have the following format:
+ var_name<= or :>var_value
+ Comment lines (those beginning with a "#") and blank lines are allowed and
+ will be ignored. Leading and trailing single or double quotes will be
+ stripped from the value. E.g.
+ var1="This one"
+ Quotes are stripped so the resulting value for var1 is:
+ This one
+
+ Description of arguments:
+ prop_file_path The caller should pass the path to the properties file.
+ """
+
+ # ConfigParser expects at least one section header in the file (or you
+ # get ConfigParser.MissingSectionHeaderError). Properties files don't
+ # need those so I'll write a dummy section header.
+
+ string_file = StringIO.StringIO()
+ # Write the dummy section header to the string file.
+ string_file.write('[dummysection]\n')
+ # Write the entire contents of the properties file to the string file.
+ string_file.write(open(prop_file_path).read())
+ # Rewind the string file.
+ string_file.seek(0, os.SEEK_SET)
+
+ # Create the ConfigParser object.
+ config_parser = ConfigParser.ConfigParser()
+ # Make the property names case-sensitive.
+ config_parser.optionxform = str
+ # Read the properties from the string file.
+ config_parser.readfp(string_file)
+ # Return the properties as a dictionary.
+ return dict(config_parser.items('dummysection'))
+
+###############################################################################
diff --git a/lib/gen_print.py b/lib/gen_print.py
new file mode 100755
index 0000000..c4618cc
--- /dev/null
+++ b/lib/gen_print.py
@@ -0,0 +1,816 @@
+#!/usr/bin/env python
+
+r"""
+This module provides many valuable print functions such as sprint_var,
+sprint_time, sprint_error, sprint_call_stack.
+"""
+
+import sys
+import os
+import time
+import inspect
+import re
+import grp
+import socket
+import argparse
+
+# Setting these variables for use both inside this module and by programs
+# importing this module.
+pgm_dir_path = sys.argv[0]
+pgm_name = os.path.basename(pgm_dir_path)
+
+# Some functions (e.g. sprint_pgm_header) have need of a program name value
+# that looks more like a valid variable name. Therefore, we'll swap odd
+# characters like "." out for underscores.
+pgm_name_var_name = pgm_name.replace(".", "_")
+
+# Initialize global values used as defaults by print_time, print_var, etc.
+col1_indent = 0
+
+# Calculate default column width for print_var functions based on environment
+# variable settings. The objective is to make the variable values line up
+# nicely with the time stamps.
+col1_width = 29
+if 'NANOSECONDS' in os.environ:
+ NANOSECONDS = os.environ['NANOSECONDS']
+else:
+ NANOSECONDS = 0
+
+if NANOSECONDS == "1":
+ col1_width = col1_width + 7
+
+if 'SHOW_ELAPSED_TIME' in os.environ:
+ SHOW_ELAPSED_TIME = os.environ['SHOW_ELAPSED_TIME']
+else:
+ SHOW_ELAPSED_TIME = 0
+
+if SHOW_ELAPSED_TIME == "1":
+ if NANOSECONDS == "1":
+ col1_width = col1_width + 14
+ else:
+ col1_width = col1_width + 7
+
+# Initialize some time variables used in module functions.
+start_time = time.time()
+sprint_time_last_seconds = start_time
+
+
+###############################################################################
+def sprint_func_name(stack_frame_ix=None):
+
+ r"""
+ Return the function name associated with the indicated stack frame.
+
+ Description of arguments:
+ stack_frame_ix The index of the stack frame whose
+ function name should be returned. If the
+ caller does not specifiy a value, this
+ function will set the value to 1 which is
+ the index of the caller's stack frame. If
+ the caller is the wrapper function
+ "print_func_name", this function will bump
+ it up by 1.
+ """
+
+ # If user specified no stack_frame_ix, we'll set it to a proper default
+ # value.
+ if stack_frame_ix is None:
+ func_name = sys._getframe().f_code.co_name
+ caller_func_name = sys._getframe(1).f_code.co_name
+ if func_name[1:] == caller_func_name:
+ stack_frame_ix = 2
+ else:
+ stack_frame_ix = 1
+
+ func_name = sys._getframe(stack_frame_ix).f_code.co_name
+
+ return func_name
+
+###############################################################################
+
+
+# get_arg_name is not a print function per se. I have included it in this
+# module because it is used by sprint_var which is found in this module.
+###############################################################################
+def get_arg_name(var,
+ arg_num=1,
+ stack_frame_ix=1):
+
+ r"""
+ Return the "name" of an argument passed to a function. This could be a
+ literal or a variable name.
+
+ Description of arguements:
+ var The variable whose name you want returned.
+ arg_num The arg number (1 through n) whose name
+ you wish to have returned. This value
+ should not exceed the number of arguments
+ allowed by the target function.
+ stack_frame_ix The stack frame index of the target
+ function. This value must be 1 or
+ greater. 1 would indicate get_arg_name's
+ stack frame. 2 would be the caller of
+ get_arg_name's stack frame, etc.
+
+ Example 1:
+
+ my_var = "mike"
+ var_name = get_arg_name(my_var)
+
+ In this example, var_name will receive the value "my_var".
+
+ Example 2:
+
+ def test1(var):
+ # Getting the var name of the first arg to this function, test1.
+ # Note, in this case, it doesn't matter what you pass as the first arg
+ # to get_arg_name since it is the caller's variable name that matters.
+ dummy = 1
+ arg_num = 1
+ stack_frame = 2
+ var_name = get_arg_name(dummy, arg_num, stack_frame)
+
+ # Mainline...
+
+ another_var = "whatever"
+ test1(another_var)
+
+ In this example, var_name will be set to "another_var".
+
+ """
+
+ # Note: I wish to avoid recursion so I refrain from calling any function
+ # that calls this function (i.e. sprint_var, valid_value, etc.).
+
+ try:
+ # The user can set environment variable "GET_ARG_NAME_DEBUG" to get
+ # debug output from this function.
+ local_debug = os.environ['GET_ARG_NAME_DEBUG']
+ except KeyError:
+ local_debug = 0
+
+ if arg_num < 1:
+ print_error("Programmer error - Variable \"arg_num\" has an invalid" +
+ " value of \"" + str(arg_num) + "\". The value must be" +
+ " an integer that is greater than 0.\n")
+ # What is the best way to handle errors? Raise exception? I'll
+ # revisit later.
+ return
+ if stack_frame_ix < 1:
+ print_error("Programmer error - Variable \"stack_frame_ix\" has an" +
+ " invalid value of \"" + str(stack_frame_ix) + "\". The" +
+ " value must be an integer that is greater than or equal" +
+ " to 1.\n")
+ return
+
+ if local_debug:
+ debug_indent = 2
+ print(sprint_func_name() + "() parms:")
+ print_varx("var", var, 0, debug_indent)
+ print_varx("arg_num", arg_num, 0, debug_indent)
+ print_varx("stack_frame_ix", stack_frame_ix, 0, debug_indent)
+
+ try:
+ frame, filename, cur_line_no, function_name, lines, index = \
+ inspect.stack()[stack_frame_ix]
+ except IndexError:
+ print_error("Programmer error - The caller has asked for information" +
+ " about the stack frame at index \"" +
+ str(stack_frame_ix) + "\". However, the stack only" +
+ " contains " + str(len(inspect.stack())) + " entries." +
+ " Therefore the stack frame index is out of range.\n")
+ return
+
+ if local_debug:
+ print("\nVariables retrieved from inspect.stack() function:")
+ print_varx("frame", frame, 0, debug_indent)
+ print_varx("filename", filename, 0, debug_indent)
+ print_varx("cur_line_no", cur_line_no, 0, debug_indent)
+ print_varx("function_name", function_name, 0, debug_indent)
+ print_varx("lines", lines, 0, debug_indent)
+ print_varx("index", index, 0, debug_indent)
+
+ composite_line = lines[0].strip()
+
+ called_func_name = sprint_func_name(stack_frame_ix)
+ # 2016/09/01 Mike Walsh (xzy0065) - I added code to handle pvar alias.
+ # pvar is an alias for print_var. However, when it is used,
+ # sprint_func_name() returns the non-alias version, i.e. "print_var".
+ # Adjusting for that here.
+ substring = composite_line[0:4]
+ if substring == "pvar":
+ called_func_name = "pvar"
+ arg_list_etc = re.sub(".*" + called_func_name, "", composite_line)
+ if local_debug:
+ 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)
+
+ # Parse arg list...
+ # Initialize...
+ nest_level = -1
+ arg_ix = 0
+ args_arr = [""]
+ for ix in range(0, len(arg_list_etc)):
+ char = arg_list_etc[ix]
+ # Set the nest_level based on whether we've encounted a parenthesis.
+ if char == "(":
+ nest_level += 1
+ if nest_level == 0:
+ continue
+ elif char == ")":
+ nest_level -= 1
+ if nest_level < 0:
+ break
+
+ # If we reach a comma at base nest level, we are done processing an
+ # argument so we increment arg_ix and initialize a new args_arr entry.
+ if char == "," and nest_level == 0:
+ arg_ix += 1
+ args_arr.append("")
+ continue
+
+ # For any other character, we append it it to the current arg array
+ # entry.
+ args_arr[arg_ix] += char
+
+ # Trim whitespace from each list entry.
+ args_arr = [arg.strip() for arg in args_arr]
+
+ if arg_num > len(args_arr):
+ print_error("Programmer error - The caller has asked for the name of" +
+ " argument number \"" + str(arg_num) + "\" but there " +
+ "were only \"" + str(len(args_arr)) + "\" args used:\n" +
+ sprint_varx("args_arr", args_arr))
+ return
+
+ argument = args_arr[arg_num - 1]
+
+ if local_debug:
+ print_varx("args_arr", args_arr, 0, debug_indent)
+ print_varx("argument", argument, 0, debug_indent)
+
+ return argument
+
+###############################################################################
+
+
+###############################################################################
+def sprint_time(buffer=""):
+
+ r"""
+ Return the time in the following format.
+
+ Example:
+
+ The following python code...
+
+ sys.stdout.write(sprint_time())
+ sys.stdout.write("Hi.\n")
+
+ Will result in the following type of output:
+
+ #(CDT) 2016/07/08 15:25:35 - Hi.
+
+ Example:
+
+ The following python code...
+
+ sys.stdout.write(sprint_time("Hi.\n"))
+
+ Will result in the following type of output:
+
+ #(CDT) 2016/08/03 17:12:05 - Hi.
+
+ The following environment variables will affect the formatting as
+ described:
+ NANOSECONDS This will cause the time stamps to be
+ precise to the microsecond (Yes, it
+ probably should have been named
+ MICROSECONDS but the convention was set
+ long ago so we're sticking with it).
+ Example of the output when environment
+ variable NANOSECONDS=1.
+
+ #(CDT) 2016/08/03 17:16:25.510469 - Hi.
+
+ SHOW_ELAPSED_TIME This will cause the elapsed time to be
+ included in the output. This is the
+ amount of time that has elapsed since the
+ last time this function was called. The
+ precision of the elapsed time field is
+ also affected by the value of the
+ NANOSECONDS environment variable. Example
+ of the output when environment variable
+ NANOSECONDS=0 and SHOW_ELAPSED_TIME=1.
+
+ #(CDT) 2016/08/03 17:17:40 - 0 - Hi.
+
+ Example of the output when environment variable NANOSECONDS=1 and
+ SHOW_ELAPSED_TIME=1.
+
+ #(CDT) 2016/08/03 17:18:47.317339 - 0.000046 - Hi.
+
+ Description of arguments.
+ buffer This will be appended to the formatted
+ time string.
+ """
+
+ global NANOSECONDS
+ global SHOW_ELAPSED_TIME
+ global sprint_time_last_seconds
+
+ seconds = time.time()
+ loc_time = time.localtime(seconds)
+ nanoseconds = "%0.6f" % seconds
+ pos = nanoseconds.find(".")
+ nanoseconds = nanoseconds[pos:]
+
+ time_string = time.strftime("#(%Z) %Y/%m/%d %H:%M:%S", loc_time)
+ if NANOSECONDS == "1":
+ time_string = time_string + nanoseconds
+
+ if SHOW_ELAPSED_TIME == "1":
+ cur_time_seconds = seconds
+ math_string = "%9.9f" % cur_time_seconds + " - " + "%9.9f" % \
+ sprint_time_last_seconds
+ elapsed_seconds = eval(math_string)
+ if NANOSECONDS == "1":
+ elapsed_seconds = "%11.6f" % elapsed_seconds
+ else:
+ elapsed_seconds = "%4i" % elapsed_seconds
+ sprint_time_last_seconds = cur_time_seconds
+ time_string = time_string + " - " + elapsed_seconds
+
+ return time_string + " - " + buffer
+
+###############################################################################
+
+
+###############################################################################
+def sprint_timen(buffer=""):
+
+ r"""
+ Append a line feed to the buffer, pass it to sprint_time and return the
+ result.
+ """
+
+ return sprint_time(buffer + "\n")
+
+###############################################################################
+
+
+###############################################################################
+def sprint_error(buffer=""):
+
+ r"""
+ Return a standardized error string. This includes:
+ - A time stamp
+ - The "**ERROR**" string
+ - The caller's buffer string.
+
+ Example:
+
+ The following python code...
+
+ print(sprint_error("Oops.\n"))
+
+ Will result in the following type of output:
+
+ #(CDT) 2016/08/03 17:12:05 - **ERROR** Oops.
+
+ Description of arguments.
+ buffer This will be appended to the formatted
+ error string.
+ """
+
+ return sprint_time() + "**ERROR** " + buffer
+
+###############################################################################
+
+
+###############################################################################
+def sprint_varx(var_name,
+ var_value,
+ hex=0,
+ loc_col1_indent=col1_indent,
+ loc_col1_width=col1_width):
+
+ r"""
+ Print the var name/value passed to it. If the caller lets loc_col1_width
+ default, the printing lines up nicely with output generated by the
+ print_time functions.
+
+ Note that the sprint_var function (defined below) can be used to call this
+ function so that the programmer does not need to pass the var_name.
+ sprint_var will figure out the var_name. The sprint_var function is the
+ one that would normally be used by the general user.
+
+ For example, the following python code:
+
+ first_name = "Mike"
+ print_time("Doing this...\n")
+ print_varx("first_name", first_name)
+ print_time("Doing that...\n")
+
+ Will generate output like this:
+
+ #(CDT) 2016/08/10 17:34:42.847374 - 0.001285 - Doing this...
+ first_name: Mike
+ #(CDT) 2016/08/10 17:34:42.847510 - 0.000136 - Doing that...
+
+ This function recognizes several complex types of data such as dict, list
+ or tuple.
+
+ For example, the following python code:
+
+ my_dict = dict(one=1, two=2, three=3)
+ print_var(my_dict)
+
+ Will generate the following output:
+
+ my_dict:
+ my_dict[three]: 3
+ my_dict[two]: 2
+ my_dict[one]: 1
+
+ Description of arguments.
+ var_name The name of the variable to be printed.
+ var_value The value of the variable to be printed.
+ 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.
+ 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
+ this is adjusted so that the var_value
+ lines up with text printed via the
+ print_time function.
+ """
+
+ # Adjust loc_col1_width.
+ loc_col1_width = loc_col1_width - loc_col1_indent
+
+ # Determine the type
+ if type(var_value) in (int, float, bool, str, unicode) \
+ or var_value is None:
+ # The data type is simple in the sense that it has no subordinate
+ # parts.
+ # See if the user wants the output in hex format.
+ if hex:
+ value_format = "0x%08x"
+ else:
+ value_format = "%s"
+ format_string = "%" + str(loc_col1_indent) + "s%-" \
+ + str(loc_col1_width) + "s" + value_format + "\n"
+ return format_string % ("", var_name + ":", var_value)
+ else:
+ # The data type is complex in the sense that it has subordinate parts.
+ format_string = "%" + str(loc_col1_indent) + "s%s\n"
+ buffer = format_string % ("", var_name + ":")
+ loc_col1_indent += 2
+ if type(var_value) is dict:
+ for key, value in var_value.iteritems():
+ buffer += sprint_varx(var_name + "[" + key + "]", value, hex,
+ loc_col1_indent)
+ elif type(var_value) in (list, tuple):
+ for key, value in enumerate(var_value):
+ buffer += sprint_varx(var_name + "[" + str(key) + "]", value,
+ hex, loc_col1_indent)
+ elif type(var_value) is argparse.Namespace:
+ for key in var_value.__dict__:
+ cmd_buf = "buffer += sprint_varx(var_name + \".\" + str(key)" \
+ + ", var_value." + key + ", hex, loc_col1_indent)"
+ exec(cmd_buf)
+ else:
+ var_type = type(var_value).__name__
+ func_name = sys._getframe().f_code.co_name
+ var_value = "<" + var_type + " type not supported by " \
+ + func_name + "()>"
+ value_format = "%s"
+ loc_col1_indent -= 2
+ format_string = "%" + str(loc_col1_indent) + "s%-" \
+ + str(loc_col1_width) + "s" + value_format + "\n"
+ return format_string % ("", var_name + ":", var_value)
+ return buffer
+
+ return ""
+
+###############################################################################
+
+
+###############################################################################
+def sprint_var(*args):
+
+ r"""
+ Figure out the name of the first argument for you and then call
+ sprint_varx with it. Therefore, the following 2 calls are equivalent:
+ sprint_varx("var1", var1)
+ sprint_var(var1)
+ """
+
+ # Get the name of the first variable passed to this function.
+ stack_frame = 2
+ calling_func_name = sprint_func_name(2)
+ if calling_func_name == "print_var":
+ stack_frame += 1
+ var_name = get_arg_name(None, 1, stack_frame)
+ return sprint_varx(var_name, *args)
+
+###############################################################################
+
+
+###############################################################################
+def sprint_dashes(loc_col1_indent=col1_indent,
+ col_width=80,
+ line_feed=1):
+
+ r"""
+ Return a string of dashes to the caller.
+
+ Description of arguements:
+ indent The number of characters to indent the
+ output.
+ width The width of the string of dashes.
+ line_feed Indicates whether the output should end
+ with a line feed.
+ """
+
+ col_width = int(col_width)
+ buffer = " "*int(loc_col1_indent) + "-"*col_width
+ if line_feed:
+ buffer += "\n"
+
+ return buffer
+
+###############################################################################
+
+
+###############################################################################
+def sprint_call_stack():
+
+ r"""
+ Return a call stack report for the given point in the program with line
+ numbers, function names and function parameters and arguments.
+
+ Sample output:
+
+ -------------------------------------------------------------------------
+ Python function call stack
+
+ Line # Function name and arguments
+ ------ ------------------------------------------------------------------
+ 424 sprint_call_stack ()
+ 4 print_call_stack ()
+ 31 func1 (last_name = 'walsh', first_name = 'mikey')
+ 59 /tmp/scr5.py
+ -------------------------------------------------------------------------
+
+ Description of arguments:
+ indent The number of characters to indent each
+ line of output.
+ stack_frame_ix The index of the first stack frame which
+ is to be returned.
+ """
+
+ buffer = ""
+
+ buffer += sprint_dashes()
+ buffer += "Python function call stack\n\n"
+ buffer += "Line # Function name and arguments\n"
+ buffer += sprint_dashes(0, 6, 0) + " " + sprint_dashes(0, 73)
+
+ # Grab the current program stack.
+ current_stack = inspect.stack()
+
+ # Process each frame in turn.
+ format_string = "%6s %s\n"
+ for stack_frame in current_stack:
+ lineno = str(stack_frame[2])
+ func_name = str(stack_frame[3])
+ if func_name == "?":
+ # "?" is the name used when code is not in a function.
+ func_name = "(none)"
+
+ if func_name == "<module>":
+ # If the func_name is the "main" program, we simply get the command
+ # line call string.
+ func_and_args = ' '.join(sys.argv)
+ else:
+ # Get the program arguments.
+ arg_vals = inspect.getargvalues(stack_frame[0])
+ function_parms = arg_vals[0]
+ frame_locals = arg_vals[3]
+
+ args_arr = []
+ for arg_name in function_parms:
+ # Get the arg value from frame locals.
+ arg_value = frame_locals[arg_name]
+ args_arr.append(arg_name + " = " + repr(arg_value))
+ args_str = "(" + ', '.join(map(str, args_arr)) + ")"
+
+ # Now we need to print this in a nicely-wrapped way.
+ func_and_args = func_name + " " + args_str
+
+ buffer += format_string % (lineno, func_and_args)
+
+ buffer += sprint_dashes()
+
+ return buffer
+
+###############################################################################
+
+
+###############################################################################
+def sprint_executing(stack_frame_ix=None):
+
+ r"""
+ Print a line indicating what function is executing and with what parameter
+ values. This is useful for debugging.
+
+ Sample output:
+
+ #(CDT) 2016/08/25 17:54:27 - Executing: func1 (x = 1)
+
+ Description of arguments:
+ stack_frame_ix The index of the stack frame whose
+ function info should be returned. If the
+ caller does not specifiy a value, this
+ function will set the value to 1 which is
+ the index of the caller's stack frame. If
+ the caller is the wrapper function
+ "print_executing", this function will bump
+ it up by 1.
+ """
+
+ # If user wants default stack_frame_ix.
+ if stack_frame_ix is None:
+ func_name = sys._getframe().f_code.co_name
+ caller_func_name = sys._getframe(1).f_code.co_name
+ if func_name[1:] == caller_func_name:
+ stack_frame_ix = 2
+ else:
+ stack_frame_ix = 1
+
+ stack_frame = inspect.stack()[stack_frame_ix]
+
+ func_name = str(stack_frame[3])
+ if func_name == "?":
+ # "?" is the name used when code is not in a function.
+ func_name = "(none)"
+
+ if func_name == "<module>":
+ # If the func_name is the "main" program, we simply get the command
+ # line call string.
+ func_and_args = ' '.join(sys.argv)
+ else:
+ # Get the program arguments.
+ arg_vals = inspect.getargvalues(stack_frame[0])
+ function_parms = arg_vals[0]
+ frame_locals = arg_vals[3]
+
+ args_arr = []
+ for arg_name in function_parms:
+ # Get the arg value from frame locals.
+ arg_value = frame_locals[arg_name]
+ args_arr.append(arg_name + " = " + repr(arg_value))
+ args_str = "(" + ', '.join(map(str, args_arr)) + ")"
+
+ # Now we need to print this in a nicely-wrapped way.
+ func_and_args = func_name + " " + args_str
+
+ return sprint_time() + "Executing: " + func_and_args + "\n"
+
+###############################################################################
+
+
+###############################################################################
+def sprint_pgm_header():
+
+ r"""
+ Return a standardized header that programs should print at the beginning
+ of the run. It includes useful information like command line, pid,
+ userid, program parameters, etc.
+
+ """
+
+ buffer = "\n"
+ buffer += sprint_time() + "Running " + pgm_name + ".\n"
+ buffer += sprint_time() + "Program parameter values, etc.:\n\n"
+ buffer += sprint_varx("command_line", ' '.join(sys.argv))
+ # 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())
+ buffer += sprint_varx(pgm_name_var_name + "_pgid", os.getpgrp())
+ buffer += sprint_varx("uid", str(os.geteuid()) + " (" + os.getlogin() +
+ ")")
+ buffer += sprint_varx("gid", str(os.getgid()) + " (" +
+ str(grp.getgrgid(os.getgid()).gr_name) + ")")
+ buffer += sprint_varx("host_name", socket.gethostname())
+ buffer += sprint_varx("DISPLAY", os.environ['DISPLAY'])
+ # I want to add code to print caller's parms.
+
+ buffer += "\n"
+
+ return buffer
+
+###############################################################################
+
+
+###############################################################################
+def sissuing(cmd_buf):
+
+ r"""
+ Return a line indicating a command that the program is about to execute.
+
+ 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.
+ """
+
+ buffer = sprint_time() + "Issuing: " + cmd_buf + "\n"
+
+ return buffer
+
+###############################################################################
+
+
+###############################################################################
+def sprint_pgm_footer():
+
+ r"""
+ Return a standardized footer that programs should print at the end of the
+ program run. It includes useful information like total run time, etc.
+ """
+
+ buffer = "\n" + sprint_time() + "Finished running " + pgm_name + ".\n\n"
+
+ total_time = time.time() - start_time
+ total_time_string = "%0.6f" % total_time
+
+ buffer += sprint_varx(pgm_name_var_name + "runtime", total_time_string)
+
+ return buffer
+
+###############################################################################
+
+
+###############################################################################
+# In the following section of code, we will dynamically create print versions
+# for each of the sprint functions defined above. So, for example, where we
+# have an sprint_time() function defined above that returns the time to the
+# caller in a string, we will create a corresponding print_time() function that
+# will print that string directly to stdout.
+
+# It can be complicated to follow what's being creaed by the exec statement
+# below. Here is an example of the print_time() function that will be created:
+
+# def print_time(*args):
+# s_funcname = "s" + sys._getframe().f_code.co_name
+# s_func = getattr(sys.modules[__name__], s_funcname)
+# sys.stdout.write(s_func(*args))
+
+# Here are comments describing the 3 lines in the body of the created function.
+# Calculate the "s" version of this function name (e.g. if this function name
+# is print_time, we want s_funcname to be "sprint_time".
+# Put a reference to the "s" version of this function in s_func.
+# Call the "s" version of this function passing it all of our arguments. Write
+# the result to stdout.
+
+# 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_func_name', 'print_executing', 'print_pgm_header',
+ 'issuing', 'print_pgm_footer']
+
+for func_name in func_names:
+ # Create abbreviated aliases (e.g. spvar is an alias for sprint_var).
+ alias = re.sub("print_", "p", func_name)
+ exec("s" + alias + " = s" + func_name)
+
+for func_name in func_names:
+ if func_name == "print_error":
+ output_stream = "stderr"
+ else:
+ output_stream = "stdout"
+ func_def = \
+ [
+ "def " + func_name + "(*args):",
+ " s_func_name = \"s\" + sys._getframe().f_code.co_name",
+ " s_func = getattr(sys.modules[__name__], s_func_name)",
+ " sys." + output_stream + ".write(s_func(*args))",
+ " sys." + output_stream + ".flush()"
+ ]
+ pgm_definition_string = '\n'.join(func_def)
+ exec(pgm_definition_string)
+
+ # Create abbreviated aliases (e.g. pvar is an alias for print_var).
+ alias = re.sub("print_", "p", func_name)
+ exec(alias + " = " + func_name)
+
+###############################################################################
diff --git a/lib/gen_robot_print.py b/lib/gen_robot_print.py
new file mode 100755
index 0000000..803aa4a
--- /dev/null
+++ b/lib/gen_robot_print.py
@@ -0,0 +1,162 @@
+#!/usr/bin/env python
+
+r"""
+This file contains functions useful for printing to stdout from robot programs.
+"""
+
+import sys
+import re
+import gen_print as gp
+from robot.libraries.BuiltIn import BuiltIn
+from robot.api import logger
+
+
+###############################################################################
+# In the following section of code, we will dynamically create robot versions
+# of print functions for each of the sprint functions defined in the
+# gen_print.py module. So, for example, where we have an sprint_time()
+# function defined above that returns the time to the caller in a string, we
+# will create a corresponding rprint_time() function that will print that
+# string directly to stdout.
+
+# It can be complicated to follow what's being creaed by the exec statement
+# below. Here is an example of the rprint_time() function that will be created
+# (as of the time of this writing):
+
+# def rprint_time(*args):
+# s_func = getattr(gp, "sprint_time")
+# BuiltIn().log_to_console(s_func(*args),
+# stream='STDIN',
+# no_newline=True)
+
+# Here are comments describing the lines in the body of the created function.
+# Put a reference to the "s" version of this function in s_func.
+# Call the "s" version of this function passing it all of our arguments. Write
+# the result to stdout.
+
+robot_prefix = "r"
+for func_name in gp.func_names:
+ # The print_var function's job is to figure out the name of arg 1 and then
+ # call print_varx. This is not currently supported for robot programs.
+ if func_name == "print_error":
+ output_stream = "STDERR"
+ else:
+ output_stream = "STDIN"
+ func_def = \
+ [
+ "def " + robot_prefix + func_name + "(*args):",
+ " s_func = getattr(gp, \"s" + func_name + "\")",
+ " BuiltIn().log_to_console(s_func(*args),"
+ " stream = '" + output_stream + "',"
+ " no_newline=True)"
+ ]
+
+ pgm_definition_string = '\n'.join(func_def)
+ exec(pgm_definition_string)
+
+ # Create abbreviated aliases (e.g. rpvarx is an alias for rprint_varx).
+ alias = re.sub("print_", "p", func_name)
+ exec(robot_prefix + alias + " = " + robot_prefix + func_name)
+
+
+###############################################################################
+
+
+###############################################################################
+def rprint(buffer=""):
+
+ r"""
+ rprint stands for "Robot Print". This keyword will print the user's
+ buffer to the console. This keyword does not write a linefeed. It is the
+ responsibility of the caller to include a line feed if desired. This
+ keyword is essentially an alias for "Log to Console <string>
+ no_newline=True".
+
+ Description of arguments:
+ buffer The value that is to written to stdout.
+ """
+
+ BuiltIn().log_to_console(buffer, no_newline=True)
+
+###############################################################################
+
+
+###############################################################################
+def rprintn(buffer=""):
+
+ r"""
+ rprintn stands for "Robot print with linefeed". This keyword will print
+ the user's buffer to the console along with a linefeed. It is basically
+ an abbreviated form of "Log go Console <string>"
+
+ Description of arguments:
+ buffer The value that is to written to stdout.
+ """
+
+ BuiltIn().log_to_console(buffer, no_newline=False)
+
+###############################################################################
+
+
+###############################################################################
+def rprint_auto_vars(headers=0):
+
+ r"""
+ This keyword will print all of the Automatic Variables described in the
+ Robot User's Guide using rpvars.
+
+ NOTE: Not all automatic variables are guaranteed to exist.
+
+ Description of arguments:
+ headers This indicates that a header and footer
+ will be printed.
+ """
+
+ if int(headers) == 1:
+ BuiltIn().log_to_console(gp.sprint_dashes(), no_newline=True)
+ BuiltIn().log_to_console("Automatic Variables:", no_newline=False)
+
+ rpvars("TEST_NAME", "TEST_TAGS", "TEST_DOCUMENTATION", "TEST_STATUS",
+ "TEST_DOCUMENTATION", "TEST_STATUS", "TEST_MESSAGE",
+ "PREV_TEST_NAME", "PREV_TEST_STATUS", "PREV_TEST_MESSAGE",
+ "SUITE_NAME", "SUITE_SOURCE", "SUITE_DOCUMENTATION",
+ "SUITE_METADATA", "SUITE_STATUS", "SUITE_MESSAGE", "KEYWORD_STATUS",
+ "KEYWORD_MESSAGE", "LOG_LEVEL", "OUTPUT_FILE", "LOG_FILE",
+ "REPORT_FILE", "DEBUG_FILE", "OUTPUT_DIR")
+
+ if int(headers) == 1:
+ BuiltIn().log_to_console(gp.sprint_dashes(), no_newline=True)
+
+###############################################################################
+
+
+###############################################################################
+def rpvars(*var_names):
+
+ r"""
+ rpvars stands for "Robot Print Vars". Given a list of variable names,
+ this keyword will print each variable name and value such that the value
+ lines up in the same column as messages printed with rptime.
+
+ NOTE: This function should NOT be called for local variables. It is
+ incapable of obtaining their values.
+
+ NOTE: I intend to add code to allow the last several parms to be
+ recognized as hex, indent, etc. and passed on to rpvarx function. See the
+ sprint_var() function in gen_print.py for details.
+
+ Description of arguments:
+ var_names A list of the names of variables to be
+ printed.
+ """
+
+ for var_name in var_names:
+ var_value = BuiltIn().get_variable_value("${" + var_name + "}")
+ rpvarx(var_name, var_value)
+
+###############################################################################
+
+
+# Define an alias. rpvar is just a special case of rpvars where the var_names
+# list contains only one element.
+rpvar = rpvars