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        'verbose',
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 terse():
897    r"""
898    Constant function to return fmt value of 0.
899
900    Now that sprint_varx defaults to printing in terse format, the terse
901    option is deprecated.  This function is here for backward compatibility.
902
903    Once the repo has been purged of the use of terse, this function can be
904    removed.
905    """
906
907    return 0
908
909
910def list_pop(a_list, index=0, default=None):
911    r"""
912    Pop the list entry indicated by the index and return the entry.  If no
913    such entry exists, return default.
914
915    Note that the list passed to this function will be modified.
916
917    Description of argument(s):
918    a_list                          The list from which an entry is to be
919                                    popped.
920    index                           The index indicating which entry is to be
921                                    popped.
922    default                         The value to be returned if there is no
923                                    entry at the given index location.
924    """
925    try:
926        return a_list.pop(index)
927    except IndexError:
928        return default
929
930
931def parse_fmt(fmt):
932    r"""
933    Parse the fmt argument and return a tuple consisting of a format and a
934    child format.
935
936    This function was written for use by the sprint_varx function defined in
937    this module.
938
939    When sprint_varx is processing a multi-level object such as a list or
940    dictionary (which in turn may contain other lists or dictionaries), it
941    will use the fmt value to dictate the print formatting of the current
942    level and the child_fmt value to dictate the print formatting of
943    subordinate levels.  Consider the following example:
944
945    python code example:
946
947    ord_dict = \
948        collections.OrderedDict([
949            ('one', 1),
950            ('two', 2),
951            ('sub',
952             collections.OrderedDict([
953                ('three', 3), ('four', 4)]))])
954
955    print_var(ord_dict)
956
957    This would generate the following output:
958
959    ord_dict:
960      [one]:                     1
961      [two]:                     2
962      [sub]:
963        [three]:                 3
964        [four]:                  4
965
966    The first level in this example is the line that simply says "ord_dict".
967    The second level is comprised of the dictionary entries with the keys
968    'one', 'two' and 'sub'.  The third level is comprised of the last 2 lines
969    (i.e. printed values 3 and 4).
970
971    Given the data structure shown above, the programmer could code the
972    following where fmt is a simple integer value set by calling the verbose()
973    function.
974
975    print_var(ord_dict, fmt=verbose())
976
977    The output would look like this:
978
979    ord_dict:
980      ord_dict[one]:             1
981      ord_dict[two]:             2
982      ord_dict[sub]:
983        ord_dict[sub][three]:    3
984        ord_dict[sub][four]:     4
985
986    Note the verbose format where the name of the object ("ord_dict") is
987    repeated on every line.
988
989    If the programmer wishes to get more granular with the fmt argument,
990    he/she can specify it as a list where each entry corresponds to a level of
991    the object being printed.  The last such list entry governs the print
992    formatting of all subordinate parts of the given object.
993
994    Look at each of the following code examples and their corresponding
995    output.  See how the show_type() formatting affects the printing depending
996    on which position it occupies in the fmt list argument:
997
998    print_var(ord_dict, fmt=[show_type()])
999
1000    ord_dict: <collections.OrderedDict>
1001      ord_dict[one]:             1 <int>
1002      ord_dict[two]:             2 <int>
1003      ord_dict[sub]: <collections.OrderedDict>
1004        ord_dict[sub][three]:    3 <int>
1005        ord_dict[sub][four]:     4 <int>
1006
1007    print_var(ord_dict, fmt=[0, show_type()])
1008
1009    ord_dict:
1010      ord_dict[one]:             1 <int>
1011      ord_dict[two]:             2 <int>
1012      ord_dict[sub]: <collections.OrderedDict>
1013        ord_dict[sub][three]:    3 <int>
1014        ord_dict[sub][four]:     4 <int>
1015
1016    print_var(ord_dict, fmt=[0, 0, show_type()])
1017
1018    ord_dict:
1019      ord_dict[one]:             1
1020      ord_dict[two]:             2
1021      ord_dict[sub]:
1022        ord_dict[sub][three]:    3 <int>
1023        ord_dict[sub][four]:     4 <int>
1024
1025    Description of argument(s):
1026    fmt                             The format argument such as is passed to
1027                                    sprint_varx.  This argument may be an
1028                                    integer or a list of integers.  See the
1029                                    prolog of sprint_varx for more details.
1030    """
1031
1032    # Make a deep copy of the fmt argument in order to avoid modifying the
1033    # caller's fmt value when it is a list.
1034    fmt = copy.deepcopy(fmt)
1035    try:
1036        # Assume fmt is a list.  Pop the first element from the list.
1037        first_element = list_pop(fmt, index=0, default=0)
1038        # Return the first list element along with either 1) the remainder of
1039        # the fmt list if not null or 2) another copy of the first element.
1040        return first_element, fmt if len(fmt) else first_element
1041    except AttributeError:
1042        # fmt is not a list so treat it as a simple integer value.
1043        return fmt, fmt
1044
1045
1046def sprint_varx(var_name,
1047                var_value,
1048                fmt=0,
1049                indent=dft_indent,
1050                col1_width=dft_col1_width,
1051                trailing_char="\n",
1052                key_list=None,
1053                delim=":"):
1054    r"""
1055    Print the var name/value passed to it.  If the caller lets col1_width
1056    default, the printing lines up nicely with output generated by the
1057    print_time functions.
1058
1059    Note that the sprint_var function (defined below) can be used to call this
1060    function so that the programmer does not need to pass the var_name.
1061    sprint_var will figure out the var_name.  The sprint_var function is the
1062    one that would normally be used by the general user.
1063
1064    For example, the following python code:
1065
1066    first_name = "Mike"
1067    print_time("Doing this...\n")
1068    print_varx("first_name", first_name)
1069    print_time("Doing that...\n")
1070
1071    Will generate output like this:
1072
1073    #(CDT) 2016/08/10 17:34:42.847374 -    0.001285 - Doing this...
1074    first_name:                                       Mike
1075    #(CDT) 2016/08/10 17:34:42.847510 -    0.000136 - Doing that...
1076
1077    This function recognizes several complex types of data such as dict, list
1078    or tuple.
1079
1080    For example, the following python code:
1081
1082    my_dict = dict(one=1, two=2, three=3)
1083    print_var(my_dict)
1084
1085    Will generate the following output:
1086
1087    my_dict:
1088      my_dict[three]:                                 3
1089      my_dict[two]:                                   2
1090      my_dict[one]:                                   1
1091
1092    Description of argument(s).
1093    var_name                        The name of the variable to be printed.
1094    var_value                       The value of the variable to be printed.
1095    fmt                             A bit map to dictate the format of the
1096                                    output.  For printing multi-level objects
1097                                    like lists and dictionaries, this argument
1098                                    may also be a list of bit maps.  The first
1099                                    list element pertains to the highest level
1100                                    of output, the second element pertains to
1101                                    the 2nd level of output, etc.  The last
1102                                    element in the list pertains to all
1103                                    subordinate levels.  The bits can be set
1104                                    using the dynamically created functionhs
1105                                    above.  Example: sprint_varx("var1", var1,
1106                                    fmt=verbose()).  Note that these values
1107                                    can be OR'ed together: print_var(var1,
1108                                    hexa() | verbose()).  If the caller ORs
1109                                    mutually exclusive bits (hexa() |
1110                                    octal()), behavior is not guaranteed.  The
1111                                    following features are supported:
1112        hexa                        Print all integer values in hexadecimal
1113                                    format.
1114        octal                       Print all integer values in octal format.
1115        binary                      Print all integer values in binary format.
1116        blank                       For blank string values, print "<blank>"
1117                                    instead of an actual blank.
1118        verbose                     For structured values like dictionaries,
1119                                    lists, etc. repeat the name of the
1120                                    variable on each line to the right of the
1121                                    key or subscript value.  Example: print
1122                                    "my_dict[key1]" instead of just "[key1]".
1123        quote_keys                  Quote dictionary keys in the output.
1124                                    Example: my_dict['key1'] instead of
1125                                    my_dict[key1].
1126        show_type                   Show the type of the data in angled
1127                                    brackets just to the right of the data.
1128    indent                          The number of spaces to indent the output.
1129    col1_width                      The width of the output column containing
1130                                    the variable name.  The default value of
1131                                    this is adjusted so that the var_value
1132                                    lines up with text printed via the
1133                                    print_time function.
1134    trailing_char                   The character to be used at the end of the
1135                                    returned string.  The default value is a
1136                                    line feed.
1137    key_list                        A list of which dictionary keys should be
1138                                    printed.  All others keys will be skipped.
1139                                    Each value in key_list will be regarded
1140                                    as a regular expression and it will be
1141                                    regarded as anchored to the beginning and
1142                                    ends of the dictionary key being
1143                                    referenced.  For example if key_list is
1144                                    ["one", "two"], the resulting regex used
1145                                    will be "^one|two$", i.e. only keys "one"
1146                                    and "two" from the var_value dictionary
1147                                    will be printed.  As another example, if
1148                                    the caller were to specify a key_list of
1149                                    ["one.*"], then only dictionary keys whose
1150                                    names begin with "one" will be printed.
1151                                    Note: This argument pertains only to
1152                                    var_values which are dictionaries.
1153    delim                           The value to be used to delimit the
1154                                    variable name from the variable value in
1155                                    the output.
1156    """
1157
1158    fmt, child_fmt = parse_fmt(fmt)
1159
1160    if fmt & show_type():
1161        type_str = "<" + str(type(var_value)).split("'")[1] + ">"
1162    # Compose object type categories.
1163    int_types = get_int_types()
1164    string_types = get_string_types()
1165    simple_types = int_types + string_types + (float, bool, type, type(None))
1166    # Determine the type.
1167    if type(var_value) in simple_types:
1168        # The data type is simple in the sense that it has no subordinate
1169        # parts.
1170        # Adjust col1_width.
1171        col1_width = col1_width - indent
1172        # Set default value for value_format.
1173        value_format = "%s"
1174        # Process format requests.
1175        if type(var_value) in int_types:
1176            # Process format values pertaining to int types.
1177            if fmt & hexa():
1178                num_hex_digits = max(dft_num_hex_digits(),
1179                                     get_req_num_hex_digits(var_value))
1180                # Convert a negative number to its positive twos complement
1181                # for proper printing.  For example, instead of printing -1 as
1182                # "0x-000000000000001" it will be printed as
1183                # "0xffffffffffffffff".
1184                var_value = var_value & (2 ** (num_hex_digits * 4) - 1)
1185                value_format = "0x%0" + str(num_hex_digits) + "x"
1186            elif fmt & octal():
1187                value_format = "0o%016o"
1188            elif fmt & binary():
1189                num_digits, remainder = \
1190                    divmod(max(bit_length(var_value), 1), 8)
1191                num_digits *= 8
1192                if remainder:
1193                    num_digits += 8
1194                num_digits += 2
1195                value_format = '#0' + str(num_digits) + 'b'
1196                var_value = format(var_value, value_format)
1197                value_format = "%s"
1198        elif type(var_value) in string_types:
1199            # Process format values pertaining to string types.
1200            if fmt & blank() and var_value == "":
1201                value_format = "%s"
1202                var_value = "<blank>"
1203        elif type(var_value) is type:
1204            var_value = str(var_value).split("'")[1]
1205        format_string = "%" + str(indent) + "s%-" + str(col1_width) + "s" \
1206            + value_format
1207        if fmt & show_type():
1208            if var_value != "":
1209                format_string += " "
1210            format_string += type_str
1211        format_string += trailing_char
1212        if not (fmt & verbose()):
1213            # Strip everything leading up to the first left square brace.
1214            var_name = re.sub(r".*\[", "[", var_name)
1215        if value_format == "0x%08x":
1216            return format_string % ("", str(var_name) + delim,
1217                                    var_value & 0xffffffff)
1218        else:
1219            return format_string % ("", str(var_name) + delim, var_value)
1220    else:
1221        # The data type is complex in the sense that it has subordinate parts.
1222        if not (fmt & verbose()):
1223            # Strip everything leading up to the first square brace.
1224            loc_var_name = re.sub(r".*\[", "[", var_name)
1225        else:
1226            loc_var_name = var_name
1227        format_string = "%" + str(indent) + "s%s\n"
1228        buffer = format_string % ("", loc_var_name + ":")
1229        if fmt & show_type():
1230            buffer = buffer.replace("\n", " " + type_str + "\n")
1231        indent += 2
1232        try:
1233            length = len(var_value)
1234        except TypeError:
1235            length = 0
1236        ix = 0
1237        loc_trailing_char = "\n"
1238        if is_dict(var_value):
1239            if type(child_fmt) is list:
1240                child_quote_keys = (child_fmt[0] & quote_keys())
1241            else:
1242                child_quote_keys = (child_fmt & quote_keys())
1243            for key, value in var_value.items():
1244                if key_list is not None:
1245                    key_list_regex = "^" + "|".join(key_list) + "$"
1246                    if not re.match(key_list_regex, key):
1247                        continue
1248                ix += 1
1249                if ix == length:
1250                    loc_trailing_char = trailing_char
1251                if child_quote_keys:
1252                    key = "'" + key + "'"
1253                key = "[" + str(key) + "]"
1254                buffer += sprint_varx(var_name + key, value, child_fmt, indent,
1255                                      col1_width, loc_trailing_char, key_list)
1256        elif type(var_value) in (list, tuple, set):
1257            for key, value in enumerate(var_value):
1258                ix += 1
1259                if ix == length:
1260                    loc_trailing_char = trailing_char
1261                key = "[" + str(key) + "]"
1262                buffer += sprint_varx(var_name + key, value, child_fmt, indent,
1263                                      col1_width, loc_trailing_char, key_list)
1264        elif isinstance(var_value, argparse.Namespace):
1265            for key in var_value.__dict__:
1266                ix += 1
1267                if ix == length:
1268                    loc_trailing_char = trailing_char
1269                cmd_buf = "buffer += sprint_varx(var_name + \".\" + str(key)" \
1270                          + ", var_value." + key + ", child_fmt, indent," \
1271                          + " col1_width, loc_trailing_char, key_list)"
1272                exec(cmd_buf)
1273        else:
1274            var_type = type(var_value).__name__
1275            func_name = sys._getframe().f_code.co_name
1276            var_value = "<" + var_type + " type not supported by " + \
1277                        func_name + "()>"
1278            value_format = "%s"
1279            indent -= 2
1280            # Adjust col1_width.
1281            col1_width = col1_width - indent
1282            format_string = "%" + str(indent) + "s%-" \
1283                + str(col1_width) + "s" + value_format + trailing_char
1284            return format_string % ("", str(var_name) + ":", var_value)
1285
1286        return buffer
1287
1288    return ""
1289
1290
1291def sprint_var(*args, **kwargs):
1292    r"""
1293    Figure out the name of the first argument for the caller and then call
1294    sprint_varx with it.  Therefore, the following 2 calls are equivalent:
1295    sprint_varx("var1", var1)
1296    sprint_var(var1)
1297
1298    See sprint_varx for description of arguments.
1299    """
1300
1301    stack_frame = 2
1302    caller_func_name = sprint_func_name(2)
1303    if caller_func_name.endswith("print_var"):
1304        stack_frame += 1
1305    # Get the name of the first variable passed to this function.
1306    var_name = get_arg_name(None, 1, stack_frame)
1307    return sprint_varx(var_name, *args, **kwargs)
1308
1309
1310def sprint_vars(*args, **kwargs):
1311    r"""
1312    Sprint the values of one or more variables.
1313
1314    Description of argument(s):
1315    args                            The variable values which are to be
1316                                    printed.
1317    kwargs                          See sprint_varx (above) for description of
1318                                    additional arguments.
1319    """
1320
1321    stack_frame = 2
1322    caller_func_name = sprint_func_name(2)
1323    if caller_func_name.endswith("print_vars"):
1324        stack_frame += 1
1325
1326    buffer = ""
1327    arg_num = 1
1328    for var_value in args:
1329        var_name = get_arg_name(None, arg_num, stack_frame)
1330        buffer += sprint_varx(var_name, var_value, **kwargs)
1331        arg_num += 1
1332
1333    return buffer
1334
1335
1336def sprint_dashes(indent=dft_indent,
1337                  width=80,
1338                  line_feed=1,
1339                  char="-"):
1340    r"""
1341    Return a string of dashes to the caller.
1342
1343    Description of argument(s):
1344    indent                          The number of characters to indent the
1345                                    output.
1346    width                           The width of the string of dashes.
1347    line_feed                       Indicates whether the output should end
1348                                    with a line feed.
1349    char                            The character to be repeated in the output
1350                                    string.
1351    """
1352
1353    width = int(width)
1354    buffer = " " * int(indent) + char * width
1355    if line_feed:
1356        buffer += "\n"
1357
1358    return buffer
1359
1360
1361def sindent(text="",
1362            indent=0):
1363    r"""
1364    Pre-pend the specified number of characters to the text string (i.e.
1365    indent it) and return it.
1366
1367    Description of argument(s):
1368    text                            The string to be indented.
1369    indent                          The number of characters to indent the
1370                                    string.
1371    """
1372
1373    format_string = "%" + str(indent) + "s%s"
1374    buffer = format_string % ("", text)
1375
1376    return buffer
1377
1378
1379func_line_style_std = None
1380func_line_style_short = 1
1381
1382
1383def sprint_func_line(stack_frame, style=None):
1384    r"""
1385    For the given stack_frame, return a formatted string containing the
1386    function name and all its arguments.
1387
1388    Example:
1389
1390    func1(last_name = 'walsh', first_name = 'mikey')
1391
1392    Description of argument(s):
1393    stack_frame                     A stack frame (such as is returned by
1394                                    inspect.stack()).
1395    style                           Indicates the style or formatting of the
1396                                    result string.  Acceptable values are
1397                                    shown above.
1398
1399    Description of styles:
1400    func_line_style_std             The standard formatting.
1401    func_line_style_short           1) The self parm (associated with methods)
1402                                    will be dropped. 2) The args and kwargs
1403                                    values will be treated as special.  In
1404                                    both cases the arg name ('args' or
1405                                    'kwargs') will be dropped and only the
1406                                    values will be shown.
1407    """
1408
1409    func_name = str(stack_frame[3])
1410    if func_name == "?":
1411        # "?" is the name used when code is not in a function.
1412        func_name = "(none)"
1413
1414    if func_name == "<module>":
1415        # If the func_name is the "main" program, we simply get the command
1416        # line call string.
1417        func_and_args = ' '.join(sys.argv)
1418    else:
1419        # Get the program arguments.
1420        (args, varargs, keywords, locals) =\
1421            inspect.getargvalues(stack_frame[0])
1422
1423        args_list = []
1424        for arg_name in filter(None, args + [varargs, keywords]):
1425            # Get the arg value from frame locals.
1426            arg_value = locals[arg_name]
1427            if arg_name == 'self':
1428                if style == func_line_style_short:
1429                    continue
1430                # Manipulations to improve output for class methods.
1431                func_name = arg_value.__class__.__name__ + "." + func_name
1432                args_list.append(arg_name + " = <self>")
1433            elif (style == func_line_style_short
1434                  and arg_name == 'args'
1435                  and type(arg_value) in (list, tuple)):
1436                if len(arg_value) == 0:
1437                    continue
1438                args_list.append(repr(', '.join(arg_value)))
1439            elif (style == func_line_style_short
1440                  and arg_name == 'kwargs'
1441                  and type(arg_value) is dict):
1442                for key, value in arg_value.items():
1443                    args_list.append(key + "=" + repr(value))
1444            else:
1445                args_list.append(arg_name + " = " + repr(arg_value))
1446        args_str = "(" + ', '.join(map(str, args_list)) + ")"
1447
1448        # Now we need to print this in a nicely-wrapped way.
1449        func_and_args = func_name + args_str
1450
1451    return func_and_args
1452
1453
1454def sprint_call_stack(indent=0,
1455                      stack_frame_ix=0,
1456                      style=None):
1457    r"""
1458    Return a call stack report for the given point in the program with line
1459    numbers, function names and function parameters and arguments.
1460
1461    Sample output:
1462
1463    -------------------------------------------------------------------------
1464    Python function call stack
1465
1466    Line # Function name and arguments
1467    ------ ------------------------------------------------------------------
1468       424 sprint_call_stack()
1469         4 print_call_stack()
1470        31 func1(last_name = 'walsh', first_name = 'mikey')
1471        59 /tmp/scr5.py
1472    -------------------------------------------------------------------------
1473
1474    Description of argument(s):
1475    indent                          The number of characters to indent each
1476                                    line of output.
1477    stack_frame_ix                  The index of the first stack frame which
1478                                    is to be returned.
1479    style                           See the sprint_line_func prolog above for
1480                                    details.
1481    """
1482
1483    buffer = ""
1484    buffer += sprint_dashes(indent)
1485    buffer += sindent("Python function call stack\n\n", indent)
1486    buffer += sindent("Line # Function name and arguments\n", indent)
1487    buffer += sprint_dashes(indent, 6, 0) + " " + sprint_dashes(0, 73)
1488
1489    # Grab the current program stack.
1490    work_around_inspect_stack_cwd_failure()
1491    current_stack = inspect.stack()
1492
1493    # Process each frame in turn.
1494    format_string = "%6s %s\n"
1495    ix = 0
1496    for stack_frame in current_stack:
1497        if ix < stack_frame_ix:
1498            ix += 1
1499            continue
1500        # Make the line number shown to be the line where one finds the line
1501        # shown.
1502        try:
1503            line_num = str(current_stack[ix + 1][2])
1504        except IndexError:
1505            line_num = ""
1506        func_and_args = sprint_func_line(stack_frame, style=style)
1507
1508        buffer += sindent(format_string % (line_num, func_and_args), indent)
1509        ix += 1
1510
1511    buffer += sprint_dashes(indent)
1512
1513    return buffer
1514
1515
1516def sprint_executing(stack_frame_ix=None, style=None):
1517    r"""
1518    Print a line indicating what function is executing and with what parameter
1519    values.  This is useful for debugging.
1520
1521    Sample output:
1522
1523    #(CDT) 2016/08/25 17:54:27 - Executing: func1(x = 1)
1524
1525    Description of argument(s):
1526    stack_frame_ix                  The index of the stack frame whose
1527                                    function info should be returned.  If the
1528                                    caller does not specify a value, this
1529                                    function will set the value to 1 which is
1530                                    the index of the caller's stack frame.  If
1531                                    the caller is the wrapper function
1532                                    "print_executing", this function will bump
1533                                    it up by 1.
1534    style                           See the sprint_line_func prolog above for
1535                                    details.
1536    """
1537
1538    # If user wants default stack_frame_ix.
1539    if stack_frame_ix is None:
1540        func_name = sys._getframe().f_code.co_name
1541        caller_func_name = sys._getframe(1).f_code.co_name
1542        if caller_func_name.endswith(func_name[1:]):
1543            stack_frame_ix = 2
1544        else:
1545            stack_frame_ix = 1
1546
1547    work_around_inspect_stack_cwd_failure()
1548    stack_frame = inspect.stack()[stack_frame_ix]
1549
1550    func_and_args = sprint_func_line(stack_frame, style)
1551
1552    return sprint_time() + "Executing: " + func_and_args + "\n"
1553
1554
1555def sprint_pgm_header(indent=0,
1556                      linefeed=1):
1557    r"""
1558    Return a standardized header that programs should print at the beginning
1559    of the run.  It includes useful information like command line, pid,
1560    userid, program parameters, etc.
1561
1562    Description of argument(s):
1563    indent                          The number of characters to indent each
1564                                    line of output.
1565    linefeed                        Indicates whether a line feed be included
1566                                    at the beginning and end of the report.
1567    """
1568
1569    col1_width = dft_col1_width + indent
1570
1571    buffer = ""
1572    if linefeed:
1573        buffer = "\n"
1574
1575    if robot_env:
1576        suite_name = BuiltIn().get_variable_value("${suite_name}")
1577        buffer += sindent(sprint_time("Running test suite \"" + suite_name
1578                                      + "\".\n"), indent)
1579
1580    buffer += sindent(sprint_time() + "Running " + pgm_name + ".\n", indent)
1581    buffer += sindent(sprint_time() + "Program parameter values, etc.:\n\n",
1582                      indent)
1583    buffer += sprint_varx("command_line", ' '.join(sys.argv), 0, indent,
1584                          col1_width)
1585    # We want the output to show a customized name for the pid and pgid but
1586    # we want it to look like a valid variable name.  Therefore, we'll use
1587    # pgm_name_var_name which was set when this module was imported.
1588    buffer += sprint_varx(pgm_name_var_name + "_pid", os.getpid(), 0, indent,
1589                          col1_width)
1590    buffer += sprint_varx(pgm_name_var_name + "_pgid", os.getpgrp(), 0, indent,
1591                          col1_width)
1592    userid_num = str(os.geteuid())
1593    try:
1594        username = os.getlogin()
1595    except OSError:
1596        if userid_num == "0":
1597            username = "root"
1598        else:
1599            username = "?"
1600    buffer += sprint_varx("uid", userid_num + " (" + username
1601                          + ")", 0, indent, col1_width)
1602    buffer += sprint_varx("gid", str(os.getgid()) + " ("
1603                          + str(grp.getgrgid(os.getgid()).gr_name) + ")", 0,
1604                          indent, col1_width)
1605    buffer += sprint_varx("host_name", socket.gethostname(), 0, indent,
1606                          col1_width)
1607    try:
1608        DISPLAY = os.environ['DISPLAY']
1609    except KeyError:
1610        DISPLAY = ""
1611    buffer += sprint_var(DISPLAY, 0, indent, col1_width)
1612    PYTHON_VERSION = os.environ.get('PYTHON_VERSION', None)
1613    if PYTHON_VERSION is not None:
1614        buffer += sprint_var(PYTHON_VERSION)
1615    PYTHON_PGM_PATH = os.environ.get('PYTHON_PGM_PATH', None)
1616    if PYTHON_PGM_PATH is not None:
1617        buffer += sprint_var(PYTHON_PGM_PATH)
1618    python_version = sys.version.replace("\n", "")
1619    buffer += sprint_var(python_version)
1620    ROBOT_VERSION = os.environ.get('ROBOT_VERSION', None)
1621    if ROBOT_VERSION is not None:
1622        buffer += sprint_var(ROBOT_VERSION)
1623    ROBOT_PGM_PATH = os.environ.get('ROBOT_PGM_PATH', None)
1624    if ROBOT_PGM_PATH is not None:
1625        buffer += sprint_var(ROBOT_PGM_PATH)
1626
1627    # TODO: Add code to print caller's parms.
1628
1629    # __builtin__.arg_obj is created by the get_arg module function,
1630    # gen_get_options.
1631    try:
1632        buffer += ga.sprint_args(__builtin__.arg_obj, indent)
1633    except AttributeError:
1634        pass
1635
1636    if robot_env:
1637        # Get value of global parm_list.
1638        parm_list = BuiltIn().get_variable_value("${parm_list}")
1639
1640        for parm in parm_list:
1641            parm_value = BuiltIn().get_variable_value("${" + parm + "}")
1642            buffer += sprint_varx(parm, parm_value, 0, indent, col1_width)
1643
1644        # Setting global program_pid.
1645        BuiltIn().set_global_variable("${program_pid}", os.getpid())
1646
1647    if linefeed:
1648        buffer += "\n"
1649
1650    return buffer
1651
1652
1653def sprint_error_report(error_text="\n",
1654                        indent=2,
1655                        format=None):
1656    r"""
1657    Return a string with a standardized report which includes the caller's
1658    error text, the call stack and the program header.
1659
1660    Description of argument(s):
1661    error_text                      The error text to be included in the
1662                                    report.  The caller should include any
1663                                    needed linefeeds.
1664    indent                          The number of characters to indent each
1665                                    line of output.
1666    format                          Long or short format.  Long includes
1667                                    extras like lines of dashes, call stack,
1668                                    etc.
1669    """
1670
1671    # Process input.
1672    indent = int(indent)
1673    if format is None:
1674        if robot_env:
1675            format = 'short'
1676        else:
1677            format = 'long'
1678    error_text = error_text.rstrip('\n') + '\n'
1679
1680    if format == 'short':
1681        return sprint_error(error_text)
1682
1683    buffer = ""
1684    buffer += sprint_dashes(width=120, char="=")
1685    buffer += sprint_error(error_text)
1686    buffer += "\n"
1687    # Calling sprint_call_stack with stack_frame_ix of 0 causes it to show
1688    # itself and this function in the call stack.  This is not helpful to a
1689    # debugger and is therefore clutter.  We will adjust the stack_frame_ix to
1690    # hide that information.
1691    stack_frame_ix = 1
1692    caller_func_name = sprint_func_name(2)
1693    if caller_func_name.endswith("print_error_report"):
1694        stack_frame_ix += 1
1695    buffer += sprint_call_stack(indent, stack_frame_ix)
1696    buffer += sprint_pgm_header(indent)
1697    buffer += sprint_dashes(width=120, char="=")
1698
1699    return buffer
1700
1701
1702def sprint_issuing(cmd_buf,
1703                   test_mode=0):
1704    r"""
1705    Return a line indicating a command that the program is about to execute.
1706
1707    Sample output for a cmd_buf of "ls"
1708
1709    #(CDT) 2016/08/25 17:57:36 - Issuing: ls
1710
1711    Description of argument(s):
1712    cmd_buf                         The command to be executed by caller.
1713    test_mode                       With test_mode set, the output will look
1714                                    like this:
1715
1716    #(CDT) 2016/08/25 17:57:36 - (test_mode) Issuing: ls
1717
1718    """
1719
1720    buffer = sprint_time()
1721    if test_mode:
1722        buffer += "(test_mode) "
1723    if type(cmd_buf) is list:
1724        # Assume this is a robot command in the form of a list.
1725        cmd_buf = '  '.join([str(element) for element in cmd_buf])
1726    buffer += "Issuing: " + cmd_buf + "\n"
1727
1728    return buffer
1729
1730
1731def sprint_pgm_footer():
1732    r"""
1733    Return a standardized footer that programs should print at the end of the
1734    program run.  It includes useful information like total run time, etc.
1735    """
1736
1737    buffer = "\n" + sprint_time() + "Finished running " + pgm_name + ".\n\n"
1738
1739    total_time = time.time() - start_time
1740    total_time_string = "%0.6f" % total_time
1741
1742    buffer += sprint_varx(pgm_name_var_name + "_runtime", total_time_string)
1743    buffer += "\n"
1744
1745    return buffer
1746
1747
1748def sprint(buffer=""):
1749    r"""
1750    Simply return the user's buffer.  This function is used by the qprint and
1751    dprint functions defined dynamically below, i.e. it would not normally be
1752    called for general use.
1753
1754    Description of argument(s).
1755    buffer                          This will be returned to the caller.
1756    """
1757
1758    try:
1759        return str(buffer)
1760    except UnicodeEncodeError:
1761        return buffer
1762
1763
1764def sprintn(buffer=""):
1765    r"""
1766    Simply return the user's buffer with a line feed.  This function is used
1767    by the qprint and dprint functions defined dynamically below, i.e. it
1768    would not normally be called for general use.
1769
1770    Description of argument(s).
1771    buffer                          This will be returned to the caller.
1772    """
1773
1774    try:
1775        buffer = str(buffer) + "\n"
1776    except UnicodeEncodeError:
1777        buffer = buffer + "\n"
1778
1779    return buffer
1780
1781
1782def gp_print(buffer,
1783             stream='stdout'):
1784    r"""
1785    Print the buffer using either sys.stdout.write or BuiltIn().log_to_console
1786    depending on whether we are running in a robot environment.
1787
1788    This function is intended for use only by other functions in this module.
1789
1790    Description of argument(s):
1791    buffer                          The string to be printed.
1792    stream                          Either "stdout" or "stderr".
1793    """
1794
1795    if robot_env:
1796        BuiltIn().log_to_console(buffer, stream=stream, no_newline=True)
1797    else:
1798        if stream == "stdout":
1799            sys.stdout.write(buffer)
1800            sys.stdout.flush()
1801        else:
1802            sys.stderr.write(buffer)
1803            sys.stderr.flush()
1804
1805
1806def gp_log(buffer):
1807    r"""
1808    Log the buffer using either python logging or BuiltIn().log depending on
1809    whether we are running in a robot environment.
1810
1811    This function is intended for use only by other functions in this module.
1812
1813    Description of argument(s):
1814    buffer                          The string to be logged.
1815    """
1816
1817    if robot_env:
1818        BuiltIn().log(buffer)
1819    else:
1820        logging.warning(buffer)
1821
1822
1823def gp_debug_print(buffer):
1824    r"""
1825    Print with gp_print only if gen_print_debug is set.
1826
1827    This function is intended for use only by other functions in this module.
1828
1829    Description of argument(s):
1830    buffer                          The string to be printed.
1831    """
1832
1833    if not gen_print_debug:
1834        return
1835
1836    gp_print(buffer)
1837
1838
1839def get_var_value(var_value=None,
1840                  default=1,
1841                  var_name=None):
1842    r"""
1843    Return either var_value, the corresponding global value or default.
1844
1845    If var_value is not None, it will simply be returned.
1846
1847    If var_value is None, this function will return the corresponding global
1848    value of the variable in question.
1849
1850    Note: For global values, if we are in a robot environment,
1851    get_variable_value will be used.  Otherwise, the __builtin__ version of
1852    the variable is returned (which are set by gen_arg.py functions).
1853
1854    If there is no global value associated with the variable, default is
1855    returned.
1856
1857    This function is useful for other functions in setting default values for
1858    parameters.
1859
1860    Example use:
1861
1862    def my_func(quiet=None):
1863
1864      quiet = int(get_var_value(quiet, 0))
1865
1866    Example calls to my_func():
1867
1868    In the following example, the caller is explicitly asking to have quiet be
1869    set to 1.
1870
1871    my_func(quiet=1)
1872
1873    In the following example, quiet will be set to the global value of quiet,
1874    if defined, or to 0 (the default).
1875
1876    my_func()
1877
1878    Description of argument(s):
1879    var_value                       The value to be returned (if not equal to
1880                                    None).
1881    default                         The value that is returned if var_value is
1882                                    None and there is no corresponding global
1883                                    value defined.
1884    var_name                        The name of the variable whose value is to
1885                                    be returned.  Under most circumstances,
1886                                    this value need not be provided.  This
1887                                    function can figure out the name of the
1888                                    variable passed as var_value.  One
1889                                    exception to this would be if this
1890                                    function is called directly from a .robot
1891                                    file.
1892    """
1893
1894    if var_value is not None:
1895        return var_value
1896
1897    if var_name is None:
1898        var_name = get_arg_name(None, 1, 2)
1899
1900    if robot_env:
1901        var_value = BuiltIn().get_variable_value("${" + var_name + "}",
1902                                                 default)
1903    else:
1904        var_value = getattr(__builtin__, var_name, default)
1905
1906    return var_value
1907
1908
1909def get_stack_var(var_name,
1910                  default="",
1911                  init_stack_ix=2):
1912    r"""
1913    Starting with the caller's stack level, search upward in the call stack,
1914    for a variable named var_name and return its value.  If the variable
1915    cannot be found, return default.
1916
1917    Example code:
1918
1919    def func12():
1920        my_loc_var1 = get_stack_var('my_var1', "default value")
1921
1922    def func11():
1923        my_var1 = 11
1924        func12()
1925
1926    In this example, get_stack_var will find the value of my_var1 in func11's
1927    stack and will therefore return the value 11.  Therefore, my_loc_var1
1928    would get set to 11.
1929
1930    Description of argument(s):
1931    var_name                        The name of the variable to be searched
1932                                    for.
1933    default                         The value to return if the the variable
1934                                    cannot be found.
1935    init_stack_ix                   The initial stack index from which to
1936                                    begin the search.  0 would be the index of
1937                                    this func1tion ("get_stack_var"), 1 would
1938                                    be the index of the function calling this
1939                                    function, etc.
1940    """
1941
1942    work_around_inspect_stack_cwd_failure()
1943    return next((frame[0].f_locals[var_name]
1944                 for frame in inspect.stack()[init_stack_ix:]
1945                 if var_name in frame[0].f_locals), default)
1946
1947
1948# hidden_text is a list of passwords which are to be replaced with asterisks
1949# by print functions defined in this module.
1950hidden_text = []
1951# password_regex is created based on the contents of hidden_text.
1952password_regex = ""
1953
1954
1955def register_passwords(*args):
1956    r"""
1957    Register one or more passwords which are to be hidden in output produced
1958    by the print functions in this module.
1959
1960    Note:  Blank password values are NOT registered.  They are simply ignored.
1961
1962    Description of argument(s):
1963    args                            One or more password values.  If a given
1964                                    password value is already registered, this
1965                                    function will simply do nothing.
1966    """
1967
1968    global hidden_text
1969    global password_regex
1970
1971    for password in args:
1972        if password == "":
1973            break
1974        if password in hidden_text:
1975            break
1976
1977        # Place the password into the hidden_text list.
1978        hidden_text.append(password)
1979        # Create a corresponding password regular expression.  Escape regex
1980        # special characters too.
1981        password_regex = '(' +\
1982            '|'.join([re.escape(x) for x in hidden_text]) + ')'
1983
1984
1985def replace_passwords(buffer):
1986    r"""
1987    Return the buffer but with all registered passwords replaced by a string
1988    of asterisks.
1989
1990
1991    Description of argument(s):
1992    buffer                          The string to be returned but with
1993                                    passwords replaced.
1994    """
1995
1996    global password_regex
1997
1998    if int(os.environ.get("DEBUG_SHOW_PASSWORDS", "0")):
1999        return buffer
2000
2001    if password_regex == "":
2002        # No passwords to replace.
2003        return buffer
2004
2005    return re.sub(password_regex, "********", buffer)
2006
2007
2008def create_print_wrapper_funcs(func_names,
2009                               stderr_func_names,
2010                               replace_dict,
2011                               func_prefix=""):
2012    r"""
2013    Generate code for print wrapper functions and return the generated code as
2014    a string.
2015
2016    To illustrate, suppose there is a "print_foo_bar" function in the
2017    func_names list.
2018    This function will...
2019    - Expect that there is an sprint_foo_bar function already in existence.
2020    - Create a print_foo_bar function which calls sprint_foo_bar and prints
2021      the result.
2022    - Create a qprint_foo_bar function which calls upon sprint_foo_bar only if
2023      global value quiet is 0.
2024    - Create a dprint_foo_bar function which calls upon sprint_foo_bar only if
2025      global value debug is 1.
2026
2027    Also, code will be generated to define aliases for each function as well.
2028    Each alias will be created by replacing "print_" in the function name with
2029    "p"  For example, the alias for print_foo_bar will be pfoo_bar.
2030
2031    Description of argument(s):
2032    func_names                      A list of functions for which print
2033                                    wrapper function code is to be generated.
2034    stderr_func_names               A list of functions whose generated code
2035                                    should print to stderr rather than to
2036                                    stdout.
2037    replace_dict                    Please see the create_func_def_string
2038                                    function in wrap_utils.py for details on
2039                                    this parameter.  This parameter will be
2040                                    passed directly to create_func_def_string.
2041    func_prefix                     Prefix to be pre-pended to the generated
2042                                    function name.
2043    """
2044
2045    buffer = ""
2046
2047    for func_name in func_names:
2048        if func_name in stderr_func_names:
2049            replace_dict['output_stream'] = "stderr"
2050        else:
2051            replace_dict['output_stream'] = "stdout"
2052
2053        s_func_name = "s" + func_name
2054        q_func_name = "q" + func_name
2055        d_func_name = "d" + func_name
2056
2057        # We don't want to try to redefine the "print" function, thus the
2058        # following if statement.
2059        if func_name != "print":
2060            func_def = create_func_def_string(s_func_name,
2061                                              func_prefix + func_name,
2062                                              print_func_template,
2063                                              replace_dict)
2064            buffer += func_def
2065
2066        func_def = create_func_def_string(s_func_name,
2067                                          func_prefix + "q" + func_name,
2068                                          qprint_func_template, replace_dict)
2069        buffer += func_def
2070
2071        func_def = create_func_def_string(s_func_name,
2072                                          func_prefix + "d" + func_name,
2073                                          dprint_func_template, replace_dict)
2074        buffer += func_def
2075
2076        func_def = create_func_def_string(s_func_name,
2077                                          func_prefix + "l" + func_name,
2078                                          lprint_func_template, replace_dict)
2079        buffer += func_def
2080
2081        # Create abbreviated aliases (e.g. spvar is an alias for sprint_var).
2082        alias = re.sub("print_", "p", func_name)
2083        alias = re.sub("print", "p", alias)
2084        prefixes = [func_prefix + "", "s", func_prefix + "q",
2085                    func_prefix + "d", func_prefix + "l"]
2086        for prefix in prefixes:
2087            if alias == "p":
2088                continue
2089            func_def = prefix + alias + " = " + prefix + func_name
2090            buffer += func_def + "\n"
2091
2092    return buffer
2093
2094
2095# In the following section of code, we will dynamically create print versions
2096# for each of the sprint functions defined above.  So, for example, where we
2097# have an sprint_time() function defined above that returns the time to the
2098# caller in a string, we will create a corresponding print_time() function
2099# that will print that string directly to stdout.
2100
2101# It can be complicated to follow what's being created below.  Here is an
2102# example of the print_time() function that will be created:
2103
2104# def print_time(buffer=''):
2105#     gp_print(replace_passwords(sprint_time(buffer=buffer)), stream='stdout')
2106
2107# For each print function defined below, there will also be a qprint, a
2108# dprint and an lprint version defined (e.g. qprint_time, dprint_time,
2109# lprint_time).
2110
2111# The q version of each print function will only print if the quiet variable
2112# is 0.
2113# The d version of each print function will only print if the debug variable
2114# is 1.
2115# The l version of each print function will print the contents as log data.
2116# For conventional programs, this means use of the logging module.  For robot
2117# programs it means use of the BuiltIn().log() function.
2118
2119# Templates for the various print wrapper functions.
2120print_func_template = \
2121    [
2122        "    <mod_qualifier>gp_print(<mod_qualifier>replace_passwords("
2123        + "<call_line>), stream='<output_stream>')"
2124    ]
2125
2126qprint_func_template = \
2127    [
2128        "    quiet_default = <mod_qualifier>get_var_value(None, 0, \"quiet\")",
2129        "    quiet = <mod_qualifier>get_stack_var(\"quiet\", quiet_default)",
2130        "    if int(quiet): return"
2131    ] + print_func_template
2132
2133dprint_func_template = \
2134    [
2135        "    debug_default = <mod_qualifier>get_var_value(None, 0, \"debug\")",
2136        "    debug = <mod_qualifier>get_stack_var(\"debug\", debug_default)",
2137        "    if not int(debug): return"
2138    ] + print_func_template
2139
2140lprint_func_template = \
2141    [
2142        "    <mod_qualifier>set_last_seconds_ix(<mod_qualifier>"
2143        + "lprint_last_seconds_ix())",
2144        "    <mod_qualifier>gp_log(<mod_qualifier>replace_passwords"
2145        + "(<call_line>))",
2146        "    <mod_qualifier>set_last_seconds_ix(<mod_qualifier>"
2147        + "standard_print_last_seconds_ix())"
2148    ]
2149
2150replace_dict = {'output_stream': 'stdout', 'mod_qualifier': ''}
2151
2152gp_debug_print("robot_env: " + str(robot_env) + "\n")
2153
2154# func_names contains a list of all print functions which should be created
2155# from their sprint counterparts.
2156func_names = ['print_time', 'print_timen', 'print_error', 'print_varx',
2157              'print_var', 'print_vars', 'print_dashes', 'indent',
2158              'print_call_stack', 'print_func_name', 'print_executing',
2159              'print_pgm_header', 'print_issuing', 'print_pgm_footer',
2160              'print_error_report', 'print', 'printn']
2161
2162# stderr_func_names is a list of functions whose output should go to stderr
2163# rather than stdout.
2164stderr_func_names = ['print_error', 'print_error_report']
2165
2166func_defs = create_print_wrapper_funcs(func_names, stderr_func_names,
2167                                       replace_dict)
2168gp_debug_print(func_defs)
2169exec(func_defs)
2170