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