1#!/usr/bin/env python
2
3r"""
4This module provides many print functions such as sprint_var, sprint_time, sprint_error, sprint_call_stack.
5"""
6
7import sys
8import os
9import time
10import inspect
11import re
12import grp
13import socket
14import argparse
15import copy
16try:
17    import __builtin__
18except ImportError:
19    import builtins as __builtin__
20import logging
21import collections
22from wrap_utils import *
23
24try:
25    robot_env = 1
26    from robot.utils import DotDict
27    from robot.utils import NormalizedDict
28    from robot.libraries.BuiltIn import BuiltIn
29    # Having access to the robot libraries alone does not indicate that we are in a robot environment.  The
30    # following try block should confirm that.
31    try:
32        var_value = BuiltIn().get_variable_value("${SUITE_NAME}", "")
33    except BaseException:
34        robot_env = 0
35except ImportError:
36    robot_env = 0
37
38import gen_arg as ga
39
40# Setting these variables for use both inside this module and by programs importing this module.
41pgm_file_path = sys.argv[0]
42pgm_name = os.path.basename(pgm_file_path)
43pgm_dir_path = os.path.normpath(re.sub("/" + pgm_name, "", pgm_file_path)) +\
44    os.path.sep
45
46
47# Some functions (e.g. sprint_pgm_header) have need of a program name value that looks more like a valid
48# variable name.  Therefore, we'll swap odd characters like "." out for underscores.
49pgm_name_var_name = pgm_name.replace(".", "_")
50
51# Initialize global values used as defaults by print_time, print_var, etc.
52dft_indent = 0
53
54# Calculate default column width for print_var functions based on environment variable settings.  The
55# objective is to make the variable values line up nicely with the time stamps.
56dft_col1_width = 29
57
58NANOSECONDS = os.environ.get('NANOSECONDS', '1')
59
60if NANOSECONDS == "1":
61    dft_col1_width = dft_col1_width + 7
62
63SHOW_ELAPSED_TIME = os.environ.get('SHOW_ELAPSED_TIME', '1')
64
65if SHOW_ELAPSED_TIME == "1":
66    if NANOSECONDS == "1":
67        dft_col1_width = dft_col1_width + 14
68    else:
69        dft_col1_width = dft_col1_width + 7
70
71# Initialize some time variables used in module functions.
72start_time = time.time()
73# sprint_time_last_seconds is used to calculate elapsed seconds.
74sprint_time_last_seconds = [start_time, start_time]
75# Define global index for the sprint_time_last_seconds list.
76last_seconds_ix = 0
77
78
79def set_last_seconds_ix(ix):
80    r"""
81    Set the "last_seconds_ix" module variable to the index value.
82
83    Description of argument(s):
84    ix                              The index value to be set into the module global last_seconds_ix variable.
85    """
86    global last_seconds_ix
87    last_seconds_ix = ix
88
89
90# Since output from the lprint_ functions goes to a different location than the output from the print_
91# functions (e.g. a file vs. the console), sprint_time_last_seconds has been created as a list rather than a
92# simple integer so that it can store multiple sprint_time_last_seconds values.  Standard print_ functions
93# defined in this file will use sprint_time_last_seconds[0] and the lprint_ functions will use
94# sprint_time_last_seconds[1].
95def standard_print_last_seconds_ix():
96    r"""
97    Return the standard print last_seconds index value to the caller.
98    """
99    return 0
100
101
102def lprint_last_seconds_ix():
103    r"""
104    Return lprint last_seconds index value to the caller.
105    """
106    return 1
107
108
109# The user can set environment variable "GEN_PRINT_DEBUG" to get debug output from this module.
110gen_print_debug = int(os.environ.get('GEN_PRINT_DEBUG', 0))
111
112
113def sprint_func_name(stack_frame_ix=None):
114    r"""
115    Return the function name associated with the indicated stack frame.
116
117    Description of argument(s):
118    stack_frame_ix                  The index of the stack frame whose function name should be returned.  If
119                                    the caller does not specify a value, this function will set the value to
120                                    1 which is the index of the caller's stack frame.  If the caller is the
121                                    wrapper function "print_func_name", this function will bump it up by 1.
122    """
123
124    # If user specified no stack_frame_ix, we'll set it to a proper default value.
125    if stack_frame_ix is None:
126        func_name = sys._getframe().f_code.co_name
127        caller_func_name = sys._getframe(1).f_code.co_name
128        if func_name[1:] == caller_func_name:
129            stack_frame_ix = 2
130        else:
131            stack_frame_ix = 1
132
133    func_name = sys._getframe(stack_frame_ix).f_code.co_name
134
135    return func_name
136
137
138def work_around_inspect_stack_cwd_failure():
139    r"""
140    Work around the inspect.stack() getcwd() failure by making "/tmp" the current working directory.
141
142    NOTES: If the current working directory has been deleted, inspect.stack() will fail with "OSError: [Errno
143    2] No such file or directory" because it tries to do a getcwd().
144
145    This function will try to prevent this failure by detecting the scenario in advance and making "/tmp" the
146    current working directory.
147    """
148    try:
149        os.getcwd()
150    except OSError:
151        os.chdir("/tmp")
152
153
154def get_line_indent(line):
155    r"""
156    Return the number of spaces at the beginning of the line.
157    """
158
159    return len(line) - len(line.lstrip(' '))
160
161
162# get_arg_name is not a print function per se.  It has been included in this module because it is used by
163# sprint_var which is defined in this module.
164def get_arg_name(var,
165                 arg_num=1,
166                 stack_frame_ix=1):
167    r"""
168    Return the "name" of an argument passed to a function.  This could be a literal or a variable name.
169
170    Description of argument(s):
171    var                             The variable whose name is to be returned.
172    arg_num                         The arg number whose name is to be returned.  To illustrate how arg_num
173                                    is processed, suppose that a programmer codes this line: "rc, outbuf =
174                                    my_func(var1, var2)" and suppose that my_func has this line of code:
175                                    "result = gp.get_arg_name(0, arg_num, 2)".  If arg_num is positive, the
176                                    indicated argument is returned.  For example, if arg_num is 1, "var1"
177                                    would be returned, If arg_num is 2, "var2" would be returned.  If arg_num
178                                    exceeds the number of arguments, get_arg_name will simply return a
179                                    complete list of the arguments.  If arg_num is 0, get_arg_name will
180                                    return the name of the target function as specified in the calling line
181                                    ("my_func" in this case).  To clarify, if the caller of the target
182                                    function uses an alias function name, the alias name would be returned.
183                                    If arg_num is negative, an lvalue variable name is returned.  Continuing
184                                    with the given example, if arg_num is -2 the 2nd parm to the left of the
185                                    "=" ("rc" in this case) should be returned.  If arg_num is -1, the 1st
186                                    parm to the left of the "=" ("out_buf" in this case) should be returned.
187                                    If arg_num is less than -2, an entire dictionary is returned.  The keys
188                                    to the dictionary for this example would be -2 and -1.
189    stack_frame_ix                  The stack frame index of the target function.  This value must be 1 or
190                                    greater.  1 would indicate get_arg_name's stack frame.  2 would be the
191                                    caller of get_arg_name's stack frame, etc.
192
193    Example 1:
194
195    my_var = "mike"
196    var_name = get_arg_name(my_var)
197
198    In this example, var_name will receive the value "my_var".
199
200    Example 2:
201
202    def test1(var):
203        # Getting the var name of the first arg to this function, test1.  Note, in this case, it doesn't
204        # matter what is passed as the first arg to get_arg_name since it is the caller's variable name that
205        # matters.
206        dummy = 1
207        arg_num = 1
208        stack_frame = 2
209        var_name = get_arg_name(dummy, arg_num, stack_frame)
210
211    # Mainline...
212
213    another_var = "whatever"
214    test1(another_var)
215
216    In this example, var_name will be set to "another_var".
217
218    """
219
220    # Note: To avoid infinite recursion, avoid calling any function that calls this function (e.g.
221    # sprint_var, valid_value, etc.).
222
223    # The user can set environment variable "GET_ARG_NAME_DEBUG" to get debug output from this function.
224    local_debug = int(os.environ.get('GET_ARG_NAME_DEBUG', 0))
225    # In addition to GET_ARG_NAME_DEBUG, the user can set environment variable "GET_ARG_NAME_SHOW_SOURCE" to
226    # have this function include source code in the debug output.
227    local_debug_show_source = int(
228        os.environ.get('GET_ARG_NAME_SHOW_SOURCE', 0))
229
230    if stack_frame_ix < 1:
231        print_error("Programmer error - Variable \"stack_frame_ix\" has an"
232                    + " invalid value of \"" + str(stack_frame_ix) + "\".  The"
233                    + " value must be an integer that is greater than or equal"
234                    + " to 1.\n")
235        return
236
237    if local_debug:
238        debug_indent = 2
239        print("")
240        print_dashes(0, 120)
241        print(sprint_func_name() + "() parms:")
242        print_varx("var", var, indent=debug_indent)
243        print_varx("arg_num", arg_num, indent=debug_indent)
244        print_varx("stack_frame_ix", stack_frame_ix, indent=debug_indent)
245        print("")
246        print_call_stack(debug_indent, 2)
247
248    work_around_inspect_stack_cwd_failure()
249    for count in range(0, 2):
250        try:
251            frame, filename, cur_line_no, function_name, lines, index = \
252                inspect.stack()[stack_frame_ix]
253        except IndexError:
254            print_error("Programmer error - The caller has asked for"
255                        + " information about the stack frame at index \""
256                        + str(stack_frame_ix) + "\".  However, the stack"
257                        + " only contains " + str(len(inspect.stack()))
258                        + " entries.  Therefore the stack frame index is out"
259                        + " of range.\n")
260            return
261        if filename != "<string>":
262            break
263        # filename of "<string>" may mean that the function in question was defined dynamically and
264        # therefore its code stack is inaccessible.  This may happen with functions like "rqprint_var".  In
265        # this case, we'll increment the stack_frame_ix and try again.
266        stack_frame_ix += 1
267        if local_debug:
268            print("Adjusted stack_frame_ix...")
269            print_varx("stack_frame_ix", stack_frame_ix, indent=debug_indent)
270
271    real_called_func_name = sprint_func_name(stack_frame_ix)
272
273    module = inspect.getmodule(frame)
274
275    # Though one would expect inspect.getsourcelines(frame) to get all module source lines if the frame is
276    # "<module>", it doesn't do that.  Therefore, for this special case, do inspect.getsourcelines(module).
277    if function_name == "<module>":
278        source_lines, source_line_num =\
279            inspect.getsourcelines(module)
280        line_ix = cur_line_no - source_line_num - 1
281    else:
282        source_lines, source_line_num =\
283            inspect.getsourcelines(frame)
284        line_ix = cur_line_no - source_line_num
285
286    if local_debug:
287        print("\n  Variables retrieved from inspect.stack() function:")
288        print_varx("frame", frame, indent=debug_indent + 2)
289        print_varx("filename", filename, indent=debug_indent + 2)
290        print_varx("cur_line_no", cur_line_no, indent=debug_indent + 2)
291        print_varx("function_name", function_name, indent=debug_indent + 2)
292        print_varx("lines", lines, indent=debug_indent + 2)
293        print_varx("index", index, indent=debug_indent + 2)
294        print_varx("source_line_num", source_line_num, indent=debug_indent)
295        print_varx("line_ix", line_ix, indent=debug_indent)
296        if local_debug_show_source:
297            print_varx("source_lines", source_lines, indent=debug_indent)
298        print_varx("real_called_func_name", real_called_func_name,
299                   indent=debug_indent)
300
301    # Get a list of all functions defined for the module.  Note that this doesn't work consistently when
302    # _run_exitfuncs is at the top of the stack (i.e. if we're running an exit function).  I've coded a
303    # work-around below for this deficiency.
304    all_functions = inspect.getmembers(module, inspect.isfunction)
305
306    # Get called_func_id by searching for our function in the list of all functions.
307    called_func_id = None
308    for func_name, function in all_functions:
309        if func_name == real_called_func_name:
310            called_func_id = id(function)
311            break
312    # NOTE: The only time I've found that called_func_id can't be found is when we're running from an exit
313    # function.
314
315    # Look for other functions in module with matching id.
316    aliases = set([real_called_func_name])
317    for func_name, function in all_functions:
318        if func_name == real_called_func_name:
319            continue
320        func_id = id(function)
321        if func_id == called_func_id:
322            aliases.add(func_name)
323
324    # In most cases, my general purpose code above will find all aliases.  However, for the odd case (i.e.
325    # running from exit function), I've added code to handle pvar, qpvar, dpvar, etc. aliases explicitly
326    # since they are defined in this module and used frequently.
327    # pvar is an alias for print_var.
328    aliases.add(re.sub("print_var", "pvar", real_called_func_name))
329
330    # The call to the function could be encased in a recast (e.g. int(func_name())).
331    recast_regex = "([^ ]+\\([ ]*)?"
332    import_name_regex = "([a-zA-Z0-9_]+\\.)?"
333    func_name_regex = recast_regex + import_name_regex + "(" +\
334        '|'.join(aliases) + ")"
335    pre_args_regex = ".*" + func_name_regex + "[ ]*\\("
336
337    # Search backward through source lines looking for the calling function name.
338    found = False
339    for start_line_ix in range(line_ix, 0, -1):
340        # Skip comment lines.
341        if re.match(r"[ ]*#", source_lines[start_line_ix]):
342            continue
343        if re.match(pre_args_regex, source_lines[start_line_ix]):
344            found = True
345            break
346    if not found:
347        print_error("Programmer error - Could not find the source line with"
348                    + " a reference to function \"" + real_called_func_name
349                    + "\".\n")
350        return
351
352    # Search forward through the source lines looking for a line whose indentation is the same or less than
353    # the start line.  The end of our composite line should be the line preceding that line.
354    start_indent = get_line_indent(source_lines[start_line_ix])
355    end_line_ix = line_ix
356    for end_line_ix in range(line_ix + 1, len(source_lines)):
357        if source_lines[end_line_ix].strip() == "":
358            continue
359        line_indent = get_line_indent(source_lines[end_line_ix])
360        if line_indent <= start_indent:
361            end_line_ix -= 1
362            break
363    if start_line_ix != 0:
364        # Check to see whether the start line is a continuation of the prior line.
365        prior_line = source_lines[start_line_ix - 1]
366        prior_line_stripped = re.sub(r"[ ]*\\([\r\n]$)", " \\1", prior_line)
367        prior_line_indent = get_line_indent(prior_line)
368        if prior_line != prior_line_stripped and\
369           prior_line_indent < start_indent:
370            start_line_ix -= 1
371            # Remove the backslash (continuation char) from prior line.
372            source_lines[start_line_ix] = prior_line_stripped
373
374    # Join the start line through the end line into a composite line.
375    composite_line = ''.join(map(str.strip,
376                                 source_lines[start_line_ix:end_line_ix + 1]))
377    # Insert one space after first "=" if there isn't one already.
378    composite_line = re.sub("=[ ]*([^ ])", "= \\1", composite_line, 1)
379
380    lvalue_regex = "[ ]*=[ ]+" + func_name_regex + ".*"
381    lvalue_string = re.sub(lvalue_regex, "", composite_line)
382    if lvalue_string == composite_line:
383        # i.e. the regex did not match so there are no lvalues.
384        lvalue_string = ""
385    lvalues_list = list(filter(None, map(str.strip, lvalue_string.split(","))))
386    try:
387        lvalues = collections.OrderedDict()
388    except AttributeError:
389        # A non-ordered dict doesn't look as nice when printed but it will do.
390        lvalues = {}
391    ix = len(lvalues_list) * -1
392    for lvalue in lvalues_list:
393        lvalues[ix] = lvalue
394        ix += 1
395    lvalue_prefix_regex = "(.*=[ ]+)?"
396    called_func_name_regex = lvalue_prefix_regex + func_name_regex +\
397        "[ ]*\\(.*"
398    called_func_name = re.sub(called_func_name_regex, "\\4", composite_line)
399    arg_list_etc = "(" + re.sub(pre_args_regex, "", composite_line)
400    if local_debug:
401        print_varx("aliases", aliases, indent=debug_indent)
402        print_varx("import_name_regex", import_name_regex, indent=debug_indent)
403        print_varx("func_name_regex", func_name_regex, indent=debug_indent)
404        print_varx("pre_args_regex", pre_args_regex, indent=debug_indent)
405        print_varx("start_line_ix", start_line_ix, indent=debug_indent)
406        print_varx("end_line_ix", end_line_ix, indent=debug_indent)
407        print_varx("composite_line", composite_line, indent=debug_indent)
408        print_varx("lvalue_regex", lvalue_regex, indent=debug_indent)
409        print_varx("lvalue_string", lvalue_string, indent=debug_indent)
410        print_varx("lvalues", lvalues, indent=debug_indent)
411        print_varx("called_func_name_regex", called_func_name_regex,
412                   indent=debug_indent)
413        print_varx("called_func_name", called_func_name, indent=debug_indent)
414        print_varx("arg_list_etc", arg_list_etc, indent=debug_indent)
415
416    # Parse arg list...
417    # Initialize...
418    nest_level = -1
419    arg_ix = 0
420    args_list = [""]
421    for ix in range(0, len(arg_list_etc)):
422        char = arg_list_etc[ix]
423        # Set the nest_level based on whether we've encounted a parenthesis.
424        if char == "(":
425            nest_level += 1
426            if nest_level == 0:
427                continue
428        elif char == ")":
429            nest_level -= 1
430            if nest_level < 0:
431                break
432
433        # If we reach a comma at base nest level, we are done processing an argument so we increment arg_ix
434        # and initialize a new args_list entry.
435        if char == "," and nest_level == 0:
436            arg_ix += 1
437            args_list.append("")
438            continue
439
440        # For any other character, we append it it to the current arg list entry.
441        args_list[arg_ix] += char
442
443    # Trim whitespace from each list entry.
444    args_list = [arg.strip() for arg in args_list]
445
446    if arg_num < 0:
447        if abs(arg_num) > len(lvalues):
448            argument = lvalues
449        else:
450            argument = lvalues[arg_num]
451    elif arg_num == 0:
452        argument = called_func_name
453    else:
454        if arg_num > len(args_list):
455            argument = args_list
456        else:
457            argument = args_list[arg_num - 1]
458
459    if local_debug:
460        print_varx("args_list", args_list, indent=debug_indent)
461        print_varx("argument", argument, indent=debug_indent)
462        print_dashes(0, 120)
463
464    return argument
465
466
467def sprint_time(buffer=""):
468    r"""
469    Return the time in the following format.
470
471    Example:
472
473    The following python code...
474
475    sys.stdout.write(sprint_time())
476    sys.stdout.write("Hi.\n")
477
478    Will result in the following type of output:
479
480    #(CDT) 2016/07/08 15:25:35 - Hi.
481
482    Example:
483
484    The following python code...
485
486    sys.stdout.write(sprint_time("Hi.\n"))
487
488    Will result in the following type of output:
489
490    #(CDT) 2016/08/03 17:12:05 - Hi.
491
492    The following environment variables will affect the formatting as described:
493    NANOSECONDS                     This will cause the time stamps to be precise to the microsecond (Yes, it
494                                    probably should have been named MICROSECONDS but the convention was set
495                                    long ago so we're sticking with it).  Example of the output when
496                                    environment variable NANOSECONDS=1.
497
498    #(CDT) 2016/08/03 17:16:25.510469 - Hi.
499
500    SHOW_ELAPSED_TIME               This will cause the elapsed time to be included in the output.  This is
501                                    the amount of time that has elapsed since the last time this function was
502                                    called.  The precision of the elapsed time field is also affected by the
503                                    value of the NANOSECONDS environment variable.  Example of the output
504                                    when environment variable NANOSECONDS=0 and SHOW_ELAPSED_TIME=1.
505
506    #(CDT) 2016/08/03 17:17:40 -    0 - Hi.
507
508    Example of the output when environment variable NANOSECONDS=1 and SHOW_ELAPSED_TIME=1.
509
510    #(CDT) 2016/08/03 17:18:47.317339 -    0.000046 - Hi.
511
512    Description of argument(s).
513    buffer                          This will be appended to the formatted time string.
514    """
515
516    global NANOSECONDS
517    global SHOW_ELAPSED_TIME
518    global sprint_time_last_seconds
519    global last_seconds_ix
520
521    seconds = time.time()
522    loc_time = time.localtime(seconds)
523    nanoseconds = "%0.6f" % seconds
524    pos = nanoseconds.find(".")
525    nanoseconds = nanoseconds[pos:]
526
527    time_string = time.strftime("#(%Z) %Y/%m/%d %H:%M:%S", loc_time)
528    if NANOSECONDS == "1":
529        time_string = time_string + nanoseconds
530
531    if SHOW_ELAPSED_TIME == "1":
532        cur_time_seconds = seconds
533        math_string = "%9.9f" % cur_time_seconds + " - " + "%9.9f" % \
534            sprint_time_last_seconds[last_seconds_ix]
535        elapsed_seconds = eval(math_string)
536        if NANOSECONDS == "1":
537            elapsed_seconds = "%11.6f" % elapsed_seconds
538        else:
539            elapsed_seconds = "%4i" % elapsed_seconds
540        sprint_time_last_seconds[last_seconds_ix] = cur_time_seconds
541        time_string = time_string + " - " + elapsed_seconds
542
543    return time_string + " - " + buffer
544
545
546def sprint_timen(buffer=""):
547    r"""
548    Append a line feed to the buffer, pass it to sprint_time and return the result.
549    """
550
551    return sprint_time(buffer + "\n")
552
553
554def sprint_error(buffer=""):
555    r"""
556    Return a standardized error string.  This includes:
557      - A time stamp
558      - The "**ERROR**" string
559      - The caller's buffer string.
560
561    Example:
562
563    The following python code...
564
565    print(sprint_error("Oops.\n"))
566
567    Will result in the following type of output:
568
569    #(CDT) 2016/08/03 17:12:05 - **ERROR** Oops.
570
571    Description of argument(s).
572    buffer                          This will be appended to the formatted error string.
573    """
574
575    return sprint_time() + "**ERROR** " + buffer
576
577
578# Implement "constants" with functions.
579def digit_length_in_bits():
580    r"""
581    Return the digit length in bits.
582    """
583
584    return 4
585
586
587def word_length_in_digits():
588    r"""
589    Return the word length in digits.
590    """
591
592    return 8
593
594
595def bit_length(number):
596    r"""
597    Return the bit length of the number.
598
599    Description of argument(s):
600    number                          The number to be analyzed.
601    """
602
603    if number < 0:
604        # Convert negative numbers to positive and subtract one.  The following example illustrates the
605        # reason for this:
606        # Consider a single nibble whose signed values can range from -8 to 7 (0x8 to 0x7).  A value of 0x7
607        # equals 0b0111.  Therefore, its length in bits is 3.  Since the negative bit (i.e. 0b1000) is not
608        # set, the value 7 clearly will fit in one nibble.  With -8 = 0x8 = 0b1000, one has the smallest
609        # negative value that will fit.  Note that it requires 3 bits of 0.  So by converting a number value
610        # of -8 to a working_number of 7, this function can accurately calculate the number of bits and
611        # therefore nibbles required to represent the number in print.
612        working_number = abs(number) - 1
613    else:
614        working_number = number
615
616    # Handle the special case of the number 0.
617    if working_number == 0:
618        return 0
619
620    return len(bin(working_number)) - 2
621
622
623def get_req_num_hex_digits(number):
624    r"""
625    Return the required number of hex digits required to display the given number.
626
627    The returned value will always be rounded up to the nearest multiple of 8.
628
629    Description of argument(s):
630    number                          The number to be analyzed.
631    """
632
633    if number < 0:
634        # Convert negative numbers to positive and subtract one.  The following example illustrates the
635        # reason for this:
636        # Consider a single nibble whose signed values can range from -8 to 7 (0x8 to 0x7).  A value of 0x7
637        # equals 0b0111.  Therefore, its length in bits is 3.  Since the negative bit (i.e. 0b1000) is not
638        # set, the value 7 clearly will fit in one nibble.  With -8 = 0x8 = 0b1000, one has the smallest
639        # negative value that will fit.  Note that it requires 3 bits of 0.  So by converting a number value
640        # of -8 to a working_number of 7, this function can accurately calculate the number of bits and
641        # therefore nibbles required to represent the number in print.
642        working_number = abs(number) - 1
643    else:
644        working_number = number
645
646    # Handle the special case of the number 0.
647    if working_number == 0:
648        return word_length_in_digits()
649
650    num_length_in_bits = bit_length(working_number)
651    num_hex_digits, remainder = divmod(num_length_in_bits,
652                                       digit_length_in_bits())
653    if remainder > 0:
654        # Example: the number 7 requires 3 bits.  The divmod above produces, 0 with remainder of 3.  So
655        # because we have a remainder, we increment num_hex_digits from 0 to 1.
656        num_hex_digits += 1
657
658    # Check to see whether the negative bit is set.  This is the left-most bit in the highest order digit.
659    negative_mask = 2 ** (num_hex_digits * 4 - 1)
660    if working_number & negative_mask:
661        # If a number that is intended to be positive has its negative bit on, an additional digit will be
662        # required to represent it correctly in print.
663        num_hex_digits += 1
664
665    num_words, remainder = divmod(num_hex_digits, word_length_in_digits())
666    if remainder > 0 or num_words == 0:
667        num_words += 1
668
669    # Round up to the next word length in digits.
670    return num_words * word_length_in_digits()
671
672
673def dft_num_hex_digits():
674    r"""
675    Return the default number of hex digits to be used to represent a hex number in print.
676
677    The value returned is a function of sys.maxsize.
678    """
679
680    global _gen_print_dft_num_hex_digits_
681    try:
682        return _gen_print_dft_num_hex_digits_
683    except NameError:
684        _gen_print_dft_num_hex_digits_ = get_req_num_hex_digits(sys.maxsize)
685        return _gen_print_dft_num_hex_digits_
686
687
688# Create constant functions to describe various types of dictionaries.
689def dict_type():
690    return 1
691
692
693def ordered_dict_type():
694    return 2
695
696
697def dot_dict_type():
698    return 3
699
700
701def normalized_dict_type():
702    return 4
703
704
705def proxy_dict_type():
706    return 5
707
708
709def is_dict(var_value):
710    r"""
711    Return non-zero if var_value is a type of dictionary and 0 if it is not.
712
713    The specific non-zero value returned will indicate what type of dictionary var_value is (see constant
714    functions above).
715
716    Description of argument(s):
717    var_value                       The object to be analyzed to determine whether it is a dictionary and if
718                                    so, what type of dictionary.
719    """
720
721    if isinstance(var_value, dict):
722        return dict_type()
723    try:
724        if isinstance(var_value, collections.OrderedDict):
725            return ordered_dict_type()
726    except AttributeError:
727        pass
728    try:
729        if isinstance(var_value, DotDict):
730            return dot_dict_type()
731    except NameError:
732        pass
733    try:
734        if isinstance(var_value, NormalizedDict):
735            return normalized_dict_type()
736    except NameError:
737        pass
738    try:
739        if str(type(var_value)).split("'")[1] == "dictproxy":
740            return proxy_dict_type()
741    except NameError:
742        pass
743    return 0
744
745
746def get_int_types():
747    r"""
748    Return a tuple consisting of the valid integer data types for the system and version of python being run.
749
750    Example:
751    (int, long)
752    """
753
754    try:
755        int_types = (int, long)
756    except NameError:
757        int_types = (int,)
758    return int_types
759
760
761def get_string_types():
762    r"""
763    Return a tuple consisting of the valid string data types for the system and version of python being run.
764
765    Example:
766    (str, unicode)
767    """
768
769    try:
770        string_types = (str, unicode)
771    except NameError:
772        string_types = (bytes, str)
773    return string_types
774
775
776def valid_fmts():
777    r"""
778    Return a list of the valid formats that can be specified for the fmt argument of the sprint_varx function
779    (defined below).
780    """
781
782    return [
783        'hexa',
784        'octal',
785        'binary',
786        'blank',
787        'verbose',
788        'quote_keys',
789        'show_type',
790        'strip_brackets',
791        'no_header',
792        'quote_values']
793
794
795def create_fmt_definition():
796    r"""
797    Create a string consisting of function-definition code that can be executed to create constant fmt
798    definition functions.
799
800    These functions can be used by callers of sprint_var/sprint_varx to set the fmt argument correctly.
801
802    Likewise, the sprint_varx function will use these generated functions to correctly interpret the fmt
803    argument.
804
805    Example output from this function:
806
807    def hexa():
808        return 0x00000001
809    def octal_fmt():
810        return 0x00000002
811    etc.
812    """
813
814    buffer = ""
815    bits = 0x00000001
816    for fmt_name in valid_fmts():
817        buffer += "def " + fmt_name + "():\n"
818        buffer += "    return " + "0x%08x" % bits + "\n"
819        bits = bits << 1
820    return buffer
821
822
823# Dynamically create fmt definitions (for use with the fmt argument of sprint_varx function):
824exec(create_fmt_definition())
825
826
827def terse():
828    r"""
829    Constant function to return fmt value of 0.
830
831    Now that sprint_varx defaults to printing in terse format, the terse option is deprecated.  This function
832    is here for backward compatibility.
833
834    Once the repo has been purged of the use of terse, this function can be removed.
835    """
836
837    return 0
838
839
840def list_pop(a_list, index=0, default=None):
841    r"""
842    Pop the list entry indicated by the index and return the entry.  If no such entry exists, return default.
843
844    Note that the list passed to this function will be modified.
845
846    Description of argument(s):
847    a_list                          The list from which an entry is to be popped.
848    index                           The index indicating which entry is to be popped.
849    default                         The value to be returned if there is no entry at the given index location.
850    """
851    try:
852        return a_list.pop(index)
853    except IndexError:
854        return default
855
856
857def parse_fmt(fmt):
858    r"""
859    Parse the fmt argument and return a tuple consisting of a format and a child format.
860
861    This function was written for use by the sprint_varx function defined in this module.
862
863    When sprint_varx is processing a multi-level object such as a list or dictionary (which in turn may
864    contain other lists or dictionaries), it will use the fmt value to dictate the print formatting of the
865    current level and the child_fmt value to dictate the print formatting of subordinate levels.  Consider
866    the following example:
867
868    python code example:
869
870    ord_dict = \
871        collections.OrderedDict([
872            ('one', 1),
873            ('two', 2),
874            ('sub',
875             collections.OrderedDict([
876                ('three', 3), ('four', 4)]))])
877
878    print_var(ord_dict)
879
880    This would generate the following output:
881
882    ord_dict:
883      [one]:                     1
884      [two]:                     2
885      [sub]:
886        [three]:                 3
887        [four]:                  4
888
889    The first level in this example is the line that simply says "ord_dict".  The second level is comprised
890    of the dictionary entries with the keys 'one', 'two' and 'sub'.  The third level is comprised of the last
891    2 lines (i.e. printed values 3 and 4).
892
893    Given the data structure shown above, the programmer could code the following where fmt is a simple
894    integer value set by calling the verbose() function.
895
896    print_var(ord_dict, fmt=verbose())
897
898    The output would look like this:
899
900    ord_dict:
901      ord_dict[one]:             1
902      ord_dict[two]:             2
903      ord_dict[sub]:
904        ord_dict[sub][three]:    3
905        ord_dict[sub][four]:     4
906
907    Note the verbose format where the name of the object ("ord_dict") is repeated on every line.
908
909    If the programmer wishes to get more granular with the fmt argument, he/she can specify it as a list
910    where each entry corresponds to a level of the object being printed.  The last such list entry governs
911    the print formatting of all subordinate parts of the given object.
912
913    Look at each of the following code examples and their corresponding output.  See how the show_type()
914    formatting affects the printing depending on which position it occupies in the fmt list argument:
915
916    print_var(ord_dict, fmt=[show_type()])
917
918    ord_dict: <collections.OrderedDict>
919      ord_dict[one]:             1 <int>
920      ord_dict[two]:             2 <int>
921      ord_dict[sub]: <collections.OrderedDict>
922        ord_dict[sub][three]:    3 <int>
923        ord_dict[sub][four]:     4 <int>
924
925    print_var(ord_dict, fmt=[0, show_type()])
926
927    ord_dict:
928      ord_dict[one]:             1 <int>
929      ord_dict[two]:             2 <int>
930      ord_dict[sub]: <collections.OrderedDict>
931        ord_dict[sub][three]:    3 <int>
932        ord_dict[sub][four]:     4 <int>
933
934    print_var(ord_dict, fmt=[0, 0, show_type()])
935
936    ord_dict:
937      ord_dict[one]:             1
938      ord_dict[two]:             2
939      ord_dict[sub]:
940        ord_dict[sub][three]:    3 <int>
941        ord_dict[sub][four]:     4 <int>
942
943    Description of argument(s):
944    fmt                             The format argument such as is passed to sprint_varx.  This argument may
945                                    be an integer or a list of integers.  See the prolog of sprint_varx for
946                                    more details.
947    """
948
949    # Make a deep copy of the fmt argument in order to avoid modifying the caller's fmt value when it is a
950    # list.
951    fmt = copy.deepcopy(fmt)
952    try:
953        # Assume fmt is a list.  Pop the first element from the list.
954        first_element = list_pop(fmt, index=0, default=0)
955        # Return the first list element along with either 1) the remainder of the fmt list if not null or 2)
956        # another copy of the first element.
957        return first_element, fmt if len(fmt) else first_element
958    except AttributeError:
959        # fmt is not a list so treat it as a simple integer value.
960        return fmt, fmt
961
962
963def sprint_varx(var_name,
964                var_value,
965                fmt=0,
966                indent=dft_indent,
967                col1_width=dft_col1_width,
968                trailing_char="\n",
969                key_list=None,
970                delim=":"):
971    r"""
972    Print the var name/value passed to it.  If the caller lets col1_width default, the printing lines up
973    nicely with output generated by the print_time functions.
974
975    Note that the sprint_var function (defined below) can be used to call this function so that the
976    programmer does not need to pass the var_name.  sprint_var will figure out the var_name.  The sprint_var
977    function is the one that would normally be used by the general user.
978
979    For example, the following python code:
980
981    first_name = "Mike"
982    print_time("Doing this...\n")
983    print_varx("first_name", first_name)
984    print_time("Doing that...\n")
985
986    Will generate output like this:
987
988    #(CDT) 2016/08/10 17:34:42.847374 -    0.001285 - Doing this...
989    first_name:                                       Mike
990    #(CDT) 2016/08/10 17:34:42.847510 -    0.000136 - Doing that...
991
992    This function recognizes several complex types of data such as dict, list or tuple.
993
994    For example, the following python code:
995
996    my_dict = dict(one=1, two=2, three=3)
997    print_var(my_dict)
998
999    Will generate the following output:
1000
1001    my_dict:
1002      my_dict[three]:                                 3
1003      my_dict[two]:                                   2
1004      my_dict[one]:                                   1
1005
1006    Description of argument(s).
1007    var_name                        The name of the variable to be printed.
1008    var_value                       The value of the variable to be printed.
1009    fmt                             A bit map to dictate the format of the output.  For printing multi-level
1010                                    objects like lists and dictionaries, this argument may also be a list of
1011                                    bit maps.  The first list element pertains to the highest level of
1012                                    output, the second element pertains to the 2nd level of output, etc.  The
1013                                    last element in the list pertains to all subordinate levels.  The bits
1014                                    can be set using the dynamically created functionhs above.  Example:
1015                                    sprint_varx("var1", var1, fmt=verbose()).  Note that these values can be
1016                                    OR'ed together: print_var(var1, hexa() | verbose()).  If the caller ORs
1017                                    mutually exclusive bits (hexa() | octal()), behavior is not guaranteed.
1018                                    The following features are supported:
1019        hexa                        Print all integer values in hexadecimal format.
1020        octal                       Print all integer values in octal format.
1021        binary                      Print all integer values in binary format.
1022        blank                       For blank string values, print "<blank>" instead of an actual blank.
1023        verbose                     For structured values like dictionaries, lists, etc. repeat the name of
1024                                    the variable on each line to the right of the key or subscript value.
1025                                    Example: print "my_dict[key1]" instead of just "[key1]".
1026        quote_keys                  Quote dictionary keys in the output.  Example: my_dict['key1'] instead of
1027                                    my_dict[key1].
1028        show_type                   Show the type of the data in angled brackets just to the right of the
1029                                    data.
1030        strip_brackets              Strip the brackets from the variable name portion of the output.  This is
1031                                    applicable when printing complex objects like lists or dictionaries.
1032        no_header                   For complex objects like dictionaries, do not include a header line.
1033                                    This necessarily means that the member lines will be indented 2
1034                                    characters less than they otherwise would have been.
1035        quote_values                Quote the values printed.
1036    indent                          The number of spaces to indent the output.
1037    col1_width                      The width of the output column containing the variable name.  The default
1038                                    value of this is adjusted so that the var_value lines up with text
1039                                    printed via the print_time function.
1040    trailing_char                   The character to be used at the end of the returned string.  The default
1041                                    value is a line feed.
1042    key_list                        A list of which dictionary keys should be printed.  All others keys will
1043                                    be skipped.  Each value in key_list will be regarded as a regular
1044                                    expression and it will be regarded as anchored to the beginning and ends
1045                                    of the dictionary key being referenced.  For example if key_list is
1046                                    ["one", "two"], the resulting regex used will be "^one|two$", i.e. only
1047                                    keys "one" and "two" from the var_value dictionary will be printed.  As
1048                                    another example, if the caller were to specify a key_list of ["one.*"],
1049                                    then only dictionary keys whose names begin with "one" will be printed.
1050                                    Note: This argument pertains only to var_values which are dictionaries.
1051    delim                           The value to be used to delimit the variable name from the variable value
1052                                    in the output.
1053    """
1054
1055    fmt, child_fmt = parse_fmt(fmt)
1056
1057    if fmt & show_type():
1058        type_str = "<" + str(type(var_value)).split("'")[1] + ">"
1059    # Compose object type categories.
1060    int_types = get_int_types()
1061    string_types = get_string_types()
1062    simple_types = int_types + string_types + (float, bool, type, type(None))
1063    # Determine the type.
1064    if type(var_value) in simple_types:
1065        # The data type is simple in the sense that it has no subordinate parts.
1066        # Adjust col1_width.
1067        col1_width = col1_width - indent
1068        # Set default value for value_format.
1069        value_format = "%s"
1070        # Process format requests.
1071        if type(var_value) in int_types:
1072            # Process format values pertaining to int types.
1073            if fmt & hexa():
1074                num_hex_digits = max(dft_num_hex_digits(),
1075                                     get_req_num_hex_digits(var_value))
1076                # Convert a negative number to its positive twos complement for proper printing.  For
1077                # example, instead of printing -1 as "0x-000000000000001" it will be printed as
1078                # "0xffffffffffffffff".
1079                var_value = var_value & (2 ** (num_hex_digits * 4) - 1)
1080                value_format = "0x%0" + str(num_hex_digits) + "x"
1081            elif fmt & octal():
1082                value_format = "0o%016o"
1083            elif fmt & binary():
1084                num_digits, remainder = \
1085                    divmod(max(bit_length(var_value), 1), 8)
1086                num_digits *= 8
1087                if remainder:
1088                    num_digits += 8
1089                num_digits += 2
1090                value_format = '#0' + str(num_digits) + 'b'
1091                var_value = format(var_value, value_format)
1092                value_format = "%s"
1093        elif type(var_value) in string_types:
1094            # Process format values pertaining to string types.
1095            if fmt & blank() and var_value == "":
1096                value_format = "%s"
1097                var_value = "<blank>"
1098        elif type(var_value) is type:
1099            var_value = str(var_value).split("'")[1]
1100        format_string = "%" + str(indent) + "s%-" + str(col1_width) + "s" \
1101            + value_format
1102        if fmt & show_type():
1103            if var_value != "":
1104                format_string += " "
1105            format_string += type_str
1106        format_string += trailing_char
1107        if fmt & quote_values():
1108            var_value = "'" + var_value + "'"
1109        if not (fmt & verbose()):
1110            # Strip everything leading up to the first left square brace.
1111            var_name = re.sub(r".*\[", "[", var_name)
1112        if (fmt & strip_brackets()):
1113            var_name = re.sub(r"[\[\]]", "", var_name)
1114        if value_format == "0x%08x":
1115            return format_string % ("", str(var_name) + delim,
1116                                    var_value & 0xffffffff)
1117        else:
1118            return format_string % ("", str(var_name) + delim, var_value)
1119    else:
1120        # The data type is complex in the sense that it has subordinate parts.
1121        if (fmt & no_header()):
1122            buffer = ""
1123        else:
1124            # Create header line.
1125            if not (fmt & verbose()):
1126                # Strip everything leading up to the first square brace.
1127                loc_var_name = re.sub(r".*\[", "[", var_name)
1128            else:
1129                loc_var_name = var_name
1130            if (fmt & strip_brackets()):
1131                loc_var_name = re.sub(r"[\[\]]", "", loc_var_name)
1132            format_string = "%" + str(indent) + "s%s\n"
1133            buffer = format_string % ("", loc_var_name + ":")
1134            if fmt & show_type():
1135                buffer = buffer.replace("\n", " " + type_str + "\n")
1136            indent += 2
1137        try:
1138            length = len(var_value)
1139        except TypeError:
1140            length = 0
1141        ix = 0
1142        loc_trailing_char = "\n"
1143        if is_dict(var_value):
1144            if type(child_fmt) is list:
1145                child_quote_keys = (child_fmt[0] & quote_keys())
1146            else:
1147                child_quote_keys = (child_fmt & quote_keys())
1148            for key, value in var_value.items():
1149                if key_list is not None:
1150                    key_list_regex = "^" + "|".join(key_list) + "$"
1151                    if not re.match(key_list_regex, key):
1152                        continue
1153                ix += 1
1154                if ix == length:
1155                    loc_trailing_char = trailing_char
1156                if child_quote_keys:
1157                    key = "'" + key + "'"
1158                key = "[" + str(key) + "]"
1159                buffer += sprint_varx(var_name + key, value, child_fmt, indent,
1160                                      col1_width, loc_trailing_char, key_list,
1161                                      delim)
1162        elif type(var_value) in (list, tuple, set):
1163            for key, value in enumerate(var_value):
1164                ix += 1
1165                if ix == length:
1166                    loc_trailing_char = trailing_char
1167                key = "[" + str(key) + "]"
1168                buffer += sprint_varx(var_name + key, value, child_fmt, indent,
1169                                      col1_width, loc_trailing_char, key_list,
1170                                      delim)
1171        elif isinstance(var_value, argparse.Namespace):
1172            for key in var_value.__dict__:
1173                ix += 1
1174                if ix == length:
1175                    loc_trailing_char = trailing_char
1176                cmd_buf = "buffer += sprint_varx(var_name + \".\" + str(key)" \
1177                          + ", var_value." + key + ", child_fmt, indent," \
1178                          + " col1_width, loc_trailing_char, key_list," \
1179                          + " delim)"
1180                exec(cmd_buf)
1181        else:
1182            var_type = type(var_value).__name__
1183            func_name = sys._getframe().f_code.co_name
1184            var_value = "<" + var_type + " type not supported by " + \
1185                        func_name + "()>"
1186            value_format = "%s"
1187            indent -= 2
1188            # Adjust col1_width.
1189            col1_width = col1_width - indent
1190            format_string = "%" + str(indent) + "s%-" \
1191                + str(col1_width) + "s" + value_format + trailing_char
1192            return format_string % ("", str(var_name) + ":", var_value)
1193
1194        return buffer
1195
1196    return ""
1197
1198
1199def sprint_var(*args, **kwargs):
1200    r"""
1201    Figure out the name of the first argument for the caller and then call sprint_varx with it.  Therefore,
1202    the following 2 calls are equivalent:
1203    sprint_varx("var1", var1)
1204    sprint_var(var1)
1205
1206    See sprint_varx for description of arguments.
1207    """
1208
1209    stack_frame = 2
1210    caller_func_name = sprint_func_name(2)
1211    if caller_func_name.endswith("print_var"):
1212        stack_frame += 1
1213    # Get the name of the first variable passed to this function.
1214    var_name = get_arg_name(None, 1, stack_frame)
1215    return sprint_varx(var_name, *args, **kwargs)
1216
1217
1218def sprint_vars(*args, **kwargs):
1219    r"""
1220    Sprint the values of one or more variables.
1221
1222    Description of argument(s):
1223    args                            The variable values which are to be printed.
1224    kwargs                          See sprint_varx (above) for description of additional arguments.
1225    """
1226
1227    stack_frame = 2
1228    caller_func_name = sprint_func_name(2)
1229    if caller_func_name.endswith("print_vars"):
1230        stack_frame += 1
1231
1232    buffer = ""
1233    arg_num = 1
1234    for var_value in args:
1235        var_name = get_arg_name(None, arg_num, stack_frame)
1236        buffer += sprint_varx(var_name, var_value, **kwargs)
1237        arg_num += 1
1238
1239    return buffer
1240
1241
1242def sprint_dashes(indent=dft_indent,
1243                  width=80,
1244                  line_feed=1,
1245                  char="-"):
1246    r"""
1247    Return a string of dashes to the caller.
1248
1249    Description of argument(s):
1250    indent                          The number of characters to indent the output.
1251    width                           The width of the string of dashes.
1252    line_feed                       Indicates whether the output should end with a line feed.
1253    char                            The character to be repeated in the output string.
1254    """
1255
1256    width = int(width)
1257    buffer = " " * int(indent) + char * width
1258    if line_feed:
1259        buffer += "\n"
1260
1261    return buffer
1262
1263
1264def sindent(text="",
1265            indent=0):
1266    r"""
1267    Pre-pend the specified number of characters to the text string (i.e. indent it) and return it.
1268
1269    Description of argument(s):
1270    text                            The string to be indented.
1271    indent                          The number of characters to indent the string.
1272    """
1273
1274    format_string = "%" + str(indent) + "s%s"
1275    buffer = format_string % ("", text)
1276
1277    return buffer
1278
1279
1280func_line_style_std = None
1281func_line_style_short = 1
1282
1283
1284def sprint_func_line(stack_frame, style=None, max_width=160):
1285    r"""
1286    For the given stack_frame, return a formatted string containing the function name and all its arguments.
1287
1288    Example:
1289
1290    func1(last_name = 'walsh', first_name = 'mikey')
1291
1292    Description of argument(s):
1293    stack_frame                     A stack frame (such as is returned by inspect.stack()).
1294    style                           Indicates the style or formatting of the result string.  Acceptable
1295                                    values are shown above.
1296    max_width                       The max width of the result.  If it exceeds this length, it will be
1297                                    truncated on the right.
1298
1299    Description of styles:
1300    func_line_style_std             The standard formatting.
1301    func_line_style_short           1) The self parm (associated with methods) will be dropped. 2) The args
1302                                    and kwargs values will be treated as special.  In both cases the arg name
1303                                    ('args' or 'kwargs') will be dropped and only the values will be shown.
1304    """
1305
1306    func_name = str(stack_frame[3])
1307    if func_name == "?":
1308        # "?" is the name used when code is not in a function.
1309        func_name = "(none)"
1310
1311    if func_name == "<module>":
1312        # If the func_name is the "main" program, we simply get the command line call string.
1313        func_and_args = ' '.join(sys.argv)
1314    else:
1315        # Get the program arguments.
1316        (args, varargs, keywords, locals) =\
1317            inspect.getargvalues(stack_frame[0])
1318
1319        args_list = []
1320        for arg_name in filter(None, args + [varargs, keywords]):
1321            # Get the arg value from frame locals.
1322            arg_value = locals[arg_name]
1323            if arg_name == 'self':
1324                if style == func_line_style_short:
1325                    continue
1326                # Manipulations to improve output for class methods.
1327                func_name = arg_value.__class__.__name__ + "." + func_name
1328                args_list.append(arg_name + " = <self>")
1329            elif (style == func_line_style_short
1330                  and arg_name == 'args'
1331                  and type(arg_value) in (list, tuple)):
1332                if len(arg_value) == 0:
1333                    continue
1334                args_list.append(repr(', '.join(arg_value)))
1335            elif (style == func_line_style_short
1336                  and arg_name == 'kwargs'
1337                  and type(arg_value) is dict):
1338                for key, value in arg_value.items():
1339                    args_list.append(key + "=" + repr(value))
1340            else:
1341                args_list.append(arg_name + " = " + repr(arg_value))
1342        args_str = "(" + ', '.join(map(str, args_list)) + ")"
1343
1344        # Now we need to print this in a nicely-wrapped way.
1345        func_and_args = func_name + args_str
1346
1347    if len(func_and_args) > max_width:
1348        func_and_args = func_and_args[0:max_width] + "..."
1349    return func_and_args
1350
1351
1352def sprint_call_stack(indent=0,
1353                      stack_frame_ix=0,
1354                      style=None):
1355    r"""
1356    Return a call stack report for the given point in the program with line numbers, function names and
1357    function parameters and arguments.
1358
1359    Sample output:
1360
1361    -------------------------------------------------------------------------
1362    Python function call stack
1363
1364    Line # Function name and arguments
1365    ------ ------------------------------------------------------------------
1366       424 sprint_call_stack()
1367         4 print_call_stack()
1368        31 func1(last_name = 'walsh', first_name = 'mikey')
1369        59 /tmp/scr5.py
1370    -------------------------------------------------------------------------
1371
1372    Description of argument(s):
1373    indent                          The number of characters to indent each line of output.
1374    stack_frame_ix                  The index of the first stack frame which is to be returned.
1375    style                           See the sprint_line_func prolog above for details.
1376    """
1377
1378    buffer = ""
1379    buffer += sprint_dashes(indent)
1380    buffer += sindent("Python function call stack\n\n", indent)
1381    buffer += sindent("Line # Function name and arguments\n", indent)
1382    buffer += sprint_dashes(indent, 6, 0) + " " + sprint_dashes(0, 73)
1383
1384    # Grab the current program stack.
1385    work_around_inspect_stack_cwd_failure()
1386    current_stack = inspect.stack()
1387
1388    # Process each frame in turn.
1389    format_string = "%6s %s\n"
1390    ix = 0
1391    for stack_frame in current_stack:
1392        if ix < stack_frame_ix:
1393            ix += 1
1394            continue
1395        # Make the line number shown to be the line where one finds the line shown.
1396        try:
1397            line_num = str(current_stack[ix + 1][2])
1398        except IndexError:
1399            line_num = ""
1400        func_and_args = sprint_func_line(stack_frame, style=style)
1401
1402        buffer += sindent(format_string % (line_num, func_and_args), indent)
1403        ix += 1
1404
1405    buffer += sprint_dashes(indent)
1406
1407    return buffer
1408
1409
1410def sprint_executing(stack_frame_ix=None, style=None, max_width=None):
1411    r"""
1412    Print a line indicating what function is executing and with what parameter values.  This is useful for
1413    debugging.
1414
1415    Sample output:
1416
1417    #(CDT) 2016/08/25 17:54:27 - Executing: func1(x = 1)
1418
1419    Description of argument(s):
1420    stack_frame_ix                  The index of the stack frame whose function info should be returned.  If
1421                                    the caller does not specify a value, this function will set the value to
1422                                    1 which is the index of the caller's stack frame.  If the caller is the
1423                                    wrapper function "print_executing", this function will bump it up by 1.
1424    style                           See the sprint_line_func prolog above for details.
1425    max_width                       See the sprint_line_func prolog above for details.
1426    """
1427
1428    # If user wants default stack_frame_ix.
1429    if stack_frame_ix is None:
1430        func_name = sys._getframe().f_code.co_name
1431        caller_func_name = sys._getframe(1).f_code.co_name
1432        if caller_func_name.endswith(func_name[1:]):
1433            stack_frame_ix = 2
1434        else:
1435            stack_frame_ix = 1
1436
1437    work_around_inspect_stack_cwd_failure()
1438    stack_frame = inspect.stack()[stack_frame_ix]
1439
1440    if max_width is None:
1441        max_width = 160 - (dft_col1_width + 11)
1442    func_and_args = sprint_func_line(stack_frame, style, max_width=max_width)
1443
1444    return sprint_time() + "Executing: " + func_and_args + "\n"
1445
1446
1447def sprint_pgm_header(indent=0,
1448                      linefeed=1):
1449    r"""
1450    Return a standardized header that programs should print at the beginning of the run.  It includes useful
1451    information like command line, pid, userid, program parameters, etc.
1452
1453    Description of argument(s):
1454    indent                          The number of characters to indent each line of output.
1455    linefeed                        Indicates whether a line feed be included at the beginning and end of the
1456                                    report.
1457    """
1458
1459    col1_width = dft_col1_width + indent
1460
1461    buffer = ""
1462    if linefeed:
1463        buffer = "\n"
1464
1465    if robot_env:
1466        suite_name = BuiltIn().get_variable_value("${suite_name}")
1467        buffer += sindent(sprint_time("Running test suite \"" + suite_name
1468                                      + "\".\n"), indent)
1469
1470    buffer += sindent(sprint_time() + "Running " + pgm_name + ".\n", indent)
1471    buffer += sindent(sprint_time() + "Program parameter values, etc.:\n\n",
1472                      indent)
1473    buffer += sprint_varx("command_line", ' '.join(sys.argv), 0, indent,
1474                          col1_width)
1475    # We want the output to show a customized name for the pid and pgid but we want it to look like a valid
1476    # variable name.  Therefore, we'll use pgm_name_var_name which was set when this module was imported.
1477    buffer += sprint_varx(pgm_name_var_name + "_pid", os.getpid(), 0, indent,
1478                          col1_width)
1479    buffer += sprint_varx(pgm_name_var_name + "_pgid", os.getpgrp(), 0, indent,
1480                          col1_width)
1481    userid_num = str(os.geteuid())
1482    try:
1483        username = os.getlogin()
1484    except OSError:
1485        if userid_num == "0":
1486            username = "root"
1487        else:
1488            username = "?"
1489    buffer += sprint_varx("uid", userid_num + " (" + username
1490                          + ")", 0, indent, col1_width)
1491    buffer += sprint_varx("gid", str(os.getgid()) + " ("
1492                          + str(grp.getgrgid(os.getgid()).gr_name) + ")", 0,
1493                          indent, col1_width)
1494    buffer += sprint_varx("host_name", socket.gethostname(), 0, indent,
1495                          col1_width)
1496    try:
1497        DISPLAY = os.environ['DISPLAY']
1498    except KeyError:
1499        DISPLAY = ""
1500    buffer += sprint_var(DISPLAY, 0, indent, col1_width)
1501    PYTHON_VERSION = os.environ.get('PYTHON_VERSION', None)
1502    if PYTHON_VERSION is not None:
1503        buffer += sprint_var(PYTHON_VERSION, 0, indent, col1_width)
1504    PYTHON_PGM_PATH = os.environ.get('PYTHON_PGM_PATH', None)
1505    if PYTHON_PGM_PATH is not None:
1506        buffer += sprint_var(PYTHON_PGM_PATH, 0, indent, col1_width)
1507    python_version = sys.version.replace("\n", "")
1508    buffer += sprint_var(python_version, 0, indent, col1_width)
1509    ROBOT_VERSION = os.environ.get('ROBOT_VERSION', None)
1510    if ROBOT_VERSION is not None:
1511        buffer += sprint_var(ROBOT_VERSION, 0, indent, col1_width)
1512    ROBOT_PGM_PATH = os.environ.get('ROBOT_PGM_PATH', None)
1513    if ROBOT_PGM_PATH is not None:
1514        buffer += sprint_var(ROBOT_PGM_PATH, 0, indent, col1_width)
1515
1516    # TODO: Add code to print caller's parms.
1517
1518    # __builtin__.arg_obj is created by the get_arg module function, gen_get_options.
1519    try:
1520        buffer += ga.sprint_args(__builtin__.arg_obj, indent)
1521    except AttributeError:
1522        pass
1523
1524    if robot_env:
1525        # Get value of global parm_list.
1526        parm_list = BuiltIn().get_variable_value("${parm_list}")
1527
1528        for parm in parm_list:
1529            parm_value = BuiltIn().get_variable_value("${" + parm + "}")
1530            buffer += sprint_varx(parm, parm_value, 0, indent, col1_width)
1531
1532        # Setting global program_pid.
1533        BuiltIn().set_global_variable("${program_pid}", os.getpid())
1534
1535    if linefeed:
1536        buffer += "\n"
1537
1538    return buffer
1539
1540
1541def sprint_error_report(error_text="\n",
1542                        indent=2,
1543                        format=None,
1544                        stack_frame_ix=None):
1545    r"""
1546    Return a string with a standardized report which includes the caller's error text, the call stack and the
1547    program header.
1548
1549    Description of argument(s):
1550    error_text                      The error text to be included in the report.  The caller should include
1551                                    any needed linefeeds.
1552    indent                          The number of characters to indent each line of output.
1553    format                          Long or short format.  Long includes extras like lines of dashes, call
1554                                    stack, etc.
1555    stack_frame_ix                  The index of the first stack frame which is to be shown in the
1556                                    print_call_stack portion of the error report.
1557    """
1558
1559    # Process input.
1560    indent = int(indent)
1561    if format is None:
1562        if robot_env:
1563            format = 'short'
1564        else:
1565            format = 'long'
1566    error_text = error_text.rstrip('\n') + '\n'
1567
1568    if format == 'short':
1569        return sprint_error(error_text)
1570
1571    buffer = ""
1572    buffer += sprint_dashes(width=120, char="=")
1573    buffer += sprint_error(error_text)
1574    buffer += "\n"
1575    if not stack_frame_ix:
1576        # Calling sprint_call_stack with stack_frame_ix of 0 causes it to show itself and this function in
1577        # the call stack.  This is not helpful to a debugger and is therefore clutter.  We will adjust the
1578        # stack_frame_ix to hide that information.
1579        stack_frame_ix = 1
1580        caller_func_name = sprint_func_name(1)
1581        if caller_func_name.endswith("print_error_report"):
1582            stack_frame_ix += 1
1583        caller_func_name = sprint_func_name(2)
1584        if caller_func_name.endswith("print_error_report"):
1585            stack_frame_ix += 1
1586    buffer += sprint_call_stack(indent, stack_frame_ix)
1587    buffer += sprint_pgm_header(indent)
1588    buffer += sprint_dashes(width=120, char="=")
1589
1590    return buffer
1591
1592
1593def sprint_issuing(cmd_buf,
1594                   test_mode=0):
1595    r"""
1596    Return a line indicating a command that the program is about to execute.
1597
1598    Sample output for a cmd_buf of "ls"
1599
1600    #(CDT) 2016/08/25 17:57:36 - Issuing: ls
1601
1602    Description of argument(s):
1603    cmd_buf                         The command to be executed by caller.
1604    test_mode                       With test_mode set, the output will look like this:
1605
1606    #(CDT) 2016/08/25 17:57:36 - (test_mode) Issuing: ls
1607
1608    """
1609
1610    buffer = sprint_time()
1611    if test_mode:
1612        buffer += "(test_mode) "
1613    if type(cmd_buf) is list:
1614        # Assume this is a robot command in the form of a list.
1615        cmd_buf = '  '.join([str(element) for element in cmd_buf])
1616    buffer += "Issuing: " + cmd_buf + "\n"
1617
1618    return buffer
1619
1620
1621def sprint_pgm_footer():
1622    r"""
1623    Return a standardized footer that programs should print at the end of the program run.  It includes
1624    useful information like total run time, etc.
1625    """
1626
1627    buffer = "\n" + sprint_time() + "Finished running " + pgm_name + ".\n\n"
1628
1629    total_time = time.time() - start_time
1630    total_time_string = "%0.6f" % total_time
1631
1632    buffer += sprint_varx(pgm_name_var_name + "_runtime", total_time_string)
1633    buffer += "\n"
1634
1635    return buffer
1636
1637
1638def sprint_file(file_path):
1639    r"""
1640    Return the file data as a string.
1641
1642    Description of argument(s):
1643    file_path                       The path to a file (e.g. "/tmp/file1").
1644    """
1645
1646    with open(file_path, 'r') as file:
1647        buffer = file.read()
1648    return buffer
1649
1650
1651def sprint(buffer=""):
1652    r"""
1653    Simply return the user's buffer.  This function is used by the qprint and dprint functions defined
1654    dynamically below, i.e. it would not normally be called for general use.
1655
1656    Description of argument(s).
1657    buffer                          This will be returned to the caller.
1658    """
1659
1660    try:
1661        return str(buffer)
1662    except UnicodeEncodeError:
1663        return buffer
1664
1665
1666def sprintn(buffer=""):
1667    r"""
1668    Simply return the user's buffer with a line feed.  This function is used by the qprint and dprint
1669    functions defined dynamically below, i.e. it would not normally be called for general use.
1670
1671    Description of argument(s).
1672    buffer                          This will be returned to the caller.
1673    """
1674
1675    try:
1676        buffer = str(buffer) + "\n"
1677    except UnicodeEncodeError:
1678        buffer = buffer + "\n"
1679
1680    return buffer
1681
1682
1683def gp_print(buffer,
1684             stream='stdout'):
1685    r"""
1686    Print the buffer using either sys.stdout.write or BuiltIn().log_to_console depending on whether we are
1687    running in a robot environment.
1688
1689    This function is intended for use only by other functions in this module.
1690
1691    Description of argument(s):
1692    buffer                          The string to be printed.
1693    stream                          Either "stdout" or "stderr".
1694    """
1695
1696    if robot_env:
1697        BuiltIn().log_to_console(buffer, stream=stream, no_newline=True)
1698    else:
1699        if stream == "stdout":
1700            sys.stdout.write(buffer)
1701            sys.stdout.flush()
1702        else:
1703            sys.stderr.write(buffer)
1704            sys.stderr.flush()
1705
1706
1707def gp_log(buffer):
1708    r"""
1709    Log the buffer using either python logging or BuiltIn().log depending on whether we are running in a
1710    robot environment.
1711
1712    This function is intended for use only by other functions in this module.
1713
1714    Description of argument(s):
1715    buffer                          The string to be logged.
1716    """
1717
1718    if robot_env:
1719        BuiltIn().log(buffer)
1720    else:
1721        logging.warning(buffer)
1722
1723
1724def gp_debug_print(buffer):
1725    r"""
1726    Print with gp_print only if gen_print_debug is set.
1727
1728    This function is intended for use only by other functions in this module.
1729
1730    Description of argument(s):
1731    buffer                          The string to be printed.
1732    """
1733
1734    if not gen_print_debug:
1735        return
1736
1737    gp_print(buffer)
1738
1739
1740def get_var_value(var_value=None,
1741                  default=1,
1742                  var_name=None):
1743    r"""
1744    Return either var_value, the corresponding global value or default.
1745
1746    If var_value is not None, it will simply be returned.
1747
1748    If var_value is None, this function will return the corresponding global value of the variable in
1749    question.
1750
1751    Note: For global values, if we are in a robot environment, get_variable_value will be used.  Otherwise,
1752    the __builtin__ version of the variable is returned (which are set by gen_arg.py functions).
1753
1754    If there is no global value associated with the variable, default is returned.
1755
1756    This function is useful for other functions in setting default values for parameters.
1757
1758    Example use:
1759
1760    def my_func(quiet=None):
1761
1762      quiet = int(get_var_value(quiet, 0))
1763
1764    Example calls to my_func():
1765
1766    In the following example, the caller is explicitly asking to have quiet be set to 1.
1767
1768    my_func(quiet=1)
1769
1770    In the following example, quiet will be set to the global value of quiet, if defined, or to 0 (the
1771    default).
1772
1773    my_func()
1774
1775    Description of argument(s):
1776    var_value                       The value to be returned (if not equal to None).
1777    default                         The value that is returned if var_value is None and there is no
1778                                    corresponding global value defined.
1779    var_name                        The name of the variable whose value is to be returned.  Under most
1780                                    circumstances, this value need not be provided.  This function can figure
1781                                    out the name of the variable passed as var_value.  One exception to this
1782                                    would be if this function is called directly from a .robot file.
1783    """
1784
1785    if var_value is not None:
1786        return var_value
1787
1788    if var_name is None:
1789        var_name = get_arg_name(None, 1, 2)
1790
1791    if robot_env:
1792        var_value = BuiltIn().get_variable_value("${" + var_name + "}",
1793                                                 default)
1794    else:
1795        var_value = getattr(__builtin__, var_name, default)
1796
1797    return var_value
1798
1799
1800def get_stack_var(var_name,
1801                  default="",
1802                  init_stack_ix=2):
1803    r"""
1804    Starting with the caller's stack level, search upward in the call stack for a variable named var_name and
1805    return its value.  If the variable cannot be found in the stack, attempt to get the global value.  If the
1806    variable still cannot be found, return default.
1807
1808    Example code:
1809
1810    def func12():
1811        my_loc_var1 = get_stack_var('my_var1', "default value")
1812
1813    def func11():
1814        my_var1 = 11
1815        func12()
1816
1817    In this example, get_stack_var will find the value of my_var1 in func11's stack and will therefore return
1818    the value 11.  Therefore, my_loc_var1 would get set to 11.
1819
1820    Description of argument(s):
1821    var_name                        The name of the variable to be searched for.
1822    default                         The value to return if the the variable cannot be found.
1823    init_stack_ix                   The initial stack index from which to begin the search.  0 would be the
1824                                    index of this func1tion ("get_stack_var"), 1 would be the index of the
1825                                    function calling this function, etc.
1826    """
1827
1828    work_around_inspect_stack_cwd_failure()
1829    default = get_var_value(var_name=var_name, default=default)
1830    return next((frame[0].f_locals[var_name]
1831                 for frame in inspect.stack()[init_stack_ix:]
1832                 if var_name in frame[0].f_locals), default)
1833
1834
1835# hidden_text is a list of passwords which are to be replaced with asterisks by print functions defined in
1836# this module.
1837hidden_text = []
1838# password_regex is created based on the contents of hidden_text.
1839password_regex = ""
1840
1841
1842def register_passwords(*args):
1843    r"""
1844    Register one or more passwords which are to be hidden in output produced by the print functions in this
1845    module.
1846
1847    Note:  Blank password values are NOT registered.  They are simply ignored.
1848
1849    Description of argument(s):
1850    args                            One or more password values.  If a given password value is already
1851                                    registered, this function will simply do nothing.
1852    """
1853
1854    global hidden_text
1855    global password_regex
1856
1857    for password in args:
1858        if password == "":
1859            break
1860        if password in hidden_text:
1861            break
1862
1863        # Place the password into the hidden_text list.
1864        hidden_text.append(password)
1865        # Create a corresponding password regular expression.  Escape regex special characters too.
1866        password_regex = '(' +\
1867            '|'.join([re.escape(x) for x in hidden_text]) + ')'
1868
1869
1870def replace_passwords(buffer):
1871    r"""
1872    Return the buffer but with all registered passwords replaced by a string of asterisks.
1873
1874
1875    Description of argument(s):
1876    buffer                          The string to be returned but with passwords replaced.
1877    """
1878
1879    global password_regex
1880
1881    if int(os.environ.get("DEBUG_SHOW_PASSWORDS", "0")):
1882        return buffer
1883
1884    if password_regex == "":
1885        # No passwords to replace.
1886        return buffer
1887
1888    return re.sub(password_regex, "********", buffer)
1889
1890
1891def create_print_wrapper_funcs(func_names,
1892                               stderr_func_names,
1893                               replace_dict,
1894                               func_prefix=""):
1895    r"""
1896    Generate code for print wrapper functions and return the generated code as a string.
1897
1898    To illustrate, suppose there is a "print_foo_bar" function in the func_names list.
1899    This function will...
1900    - Expect that there is an sprint_foo_bar function already in existence.
1901    - Create a print_foo_bar function which calls sprint_foo_bar and prints the result.
1902    - Create a qprint_foo_bar function which calls upon sprint_foo_bar only if global value quiet is 0.
1903    - Create a dprint_foo_bar function which calls upon sprint_foo_bar only if global value debug is 1.
1904
1905    Also, code will be generated to define aliases for each function as well.  Each alias will be created by
1906    replacing "print_" in the function name with "p"  For example, the alias for print_foo_bar will be
1907    pfoo_bar.
1908
1909    Description of argument(s):
1910    func_names                      A list of functions for which print wrapper function code is to be
1911                                    generated.
1912    stderr_func_names               A list of functions whose generated code should print to stderr rather
1913                                    than to stdout.
1914    replace_dict                    Please see the create_func_def_string function in wrap_utils.py for
1915                                    details on this parameter.  This parameter will be passed directly to
1916                                    create_func_def_string.
1917    func_prefix                     Prefix to be pre-pended to the generated function name.
1918    """
1919
1920    buffer = ""
1921
1922    for func_name in func_names:
1923        if func_name in stderr_func_names:
1924            replace_dict['output_stream'] = "stderr"
1925        else:
1926            replace_dict['output_stream'] = "stdout"
1927
1928        s_func_name = "s" + func_name
1929        q_func_name = "q" + func_name
1930        d_func_name = "d" + func_name
1931
1932        # We don't want to try to redefine the "print" function, thus the following if statement.
1933        if func_name != "print":
1934            func_def = create_func_def_string(s_func_name,
1935                                              func_prefix + func_name,
1936                                              print_func_template,
1937                                              replace_dict)
1938            buffer += func_def
1939
1940        func_def = create_func_def_string(s_func_name,
1941                                          func_prefix + "q" + func_name,
1942                                          qprint_func_template, replace_dict)
1943        buffer += func_def
1944
1945        func_def = create_func_def_string(s_func_name,
1946                                          func_prefix + "d" + func_name,
1947                                          dprint_func_template, replace_dict)
1948        buffer += func_def
1949
1950        func_def = create_func_def_string(s_func_name,
1951                                          func_prefix + "l" + func_name,
1952                                          lprint_func_template, replace_dict)
1953        buffer += func_def
1954
1955        # Create abbreviated aliases (e.g. spvar is an alias for sprint_var).
1956        alias = re.sub("print_", "p", func_name)
1957        alias = re.sub("print", "p", alias)
1958        prefixes = [func_prefix + "", "s", func_prefix + "q",
1959                    func_prefix + "d", func_prefix + "l"]
1960        for prefix in prefixes:
1961            if alias == "p":
1962                continue
1963            func_def = prefix + alias + " = " + prefix + func_name
1964            buffer += func_def + "\n"
1965
1966    return buffer
1967
1968
1969# In the following section of code, we will dynamically create print versions for each of the sprint
1970# functions defined above.  So, for example, where we have an sprint_time() function defined above that
1971# returns the time to the caller in a string, we will create a corresponding print_time() function that will
1972# print that string directly to stdout.
1973
1974# It can be complicated to follow what's being created below.  Here is an example of the print_time()
1975# function that will be created:
1976
1977# def print_time(buffer=''):
1978#     gp_print(replace_passwords(sprint_time(buffer=buffer)), stream='stdout')
1979
1980# For each print function defined below, there will also be a qprint, a dprint and an lprint version defined
1981# (e.g. qprint_time, dprint_time, lprint_time).
1982
1983# The q version of each print function will only print if the quiet variable is 0.
1984# The d version of each print function will only print if the debug variable is 1.
1985# The l version of each print function will print the contents as log data.  For conventional programs, this
1986# means use of the logging module.  For robot programs it means use of the BuiltIn().log() function.
1987
1988# Templates for the various print wrapper functions.
1989print_func_template = \
1990    [
1991        "    <mod_qualifier>gp_print(<mod_qualifier>replace_passwords("
1992        + "<call_line>), stream='<output_stream>')"
1993    ]
1994
1995qprint_func_template = \
1996    [
1997        "    quiet = <mod_qualifier>get_stack_var(\"quiet\", 0)",
1998        "    if int(quiet): return"
1999    ] + print_func_template
2000
2001dprint_func_template = \
2002    [
2003        "    debug = <mod_qualifier>get_stack_var(\"debug\", 0)",
2004        "    if not int(debug): return"
2005    ] + print_func_template
2006
2007lprint_func_template = \
2008    [
2009        "    <mod_qualifier>set_last_seconds_ix(<mod_qualifier>"
2010        + "lprint_last_seconds_ix())",
2011        "    <mod_qualifier>gp_log(<mod_qualifier>replace_passwords"
2012        + "(<call_line>))",
2013        "    <mod_qualifier>set_last_seconds_ix(<mod_qualifier>"
2014        + "standard_print_last_seconds_ix())"
2015    ]
2016
2017replace_dict = {'output_stream': 'stdout', 'mod_qualifier': ''}
2018
2019gp_debug_print("robot_env: " + str(robot_env) + "\n")
2020
2021# func_names contains a list of all print functions which should be created from their sprint counterparts.
2022func_names = ['print_time', 'print_timen', 'print_error', 'print_varx',
2023              'print_var', 'print_vars', 'print_dashes', 'indent',
2024              'print_call_stack', 'print_func_name', 'print_executing',
2025              'print_pgm_header', 'print_issuing', 'print_pgm_footer',
2026              'print_file', 'print_error_report', 'print', 'printn']
2027
2028# stderr_func_names is a list of functions whose output should go to stderr rather than stdout.
2029stderr_func_names = ['print_error', 'print_error_report']
2030
2031func_defs = create_print_wrapper_funcs(func_names, stderr_func_names,
2032                                       replace_dict)
2033gp_debug_print(func_defs)
2034exec(func_defs)
2035