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