xref: /openbmc/openbmc-test-automation/lib/gen_print.py (revision 35aa8d53f99891f7fb7c8a1b02b6fb72ffd04727)
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:
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    called_func_name = re.sub(called_func_name_regex, "\\4", composite_line)
380    arg_list_etc = "(" + re.sub(pre_args_regex, "", composite_line)
381    if local_debug:
382        print_varx("aliases", aliases, 0, debug_indent)
383        print_varx("import_name_regex", import_name_regex, 0, debug_indent)
384        print_varx("func_name_regex", func_name_regex, 0, debug_indent)
385        print_varx("pre_args_regex", pre_args_regex, 0, debug_indent)
386        print_varx("start_line_ix", start_line_ix, 0, debug_indent)
387        print_varx("end_line_ix", end_line_ix, 0, debug_indent)
388        print_varx("composite_line", composite_line, 0, debug_indent)
389        print_varx("lvalue_regex", lvalue_regex, 0, debug_indent)
390        print_varx("lvalue_string", lvalue_string, 0, debug_indent)
391        print_varx("lvalues", lvalues, 0, debug_indent)
392        print_varx("called_func_name_regex", called_func_name_regex, 0,
393                   debug_indent)
394        print_varx("called_func_name", called_func_name, 0, debug_indent)
395        print_varx("arg_list_etc", arg_list_etc, 0, debug_indent)
396
397    # Parse arg list...
398    # Initialize...
399    nest_level = -1
400    arg_ix = 0
401    args_list = [""]
402    for ix in range(0, len(arg_list_etc)):
403        char = arg_list_etc[ix]
404        # Set the nest_level based on whether we've encounted a parenthesis.
405        if char == "(":
406            nest_level += 1
407            if nest_level == 0:
408                continue
409        elif char == ")":
410            nest_level -= 1
411            if nest_level < 0:
412                break
413
414        # If we reach a comma at base nest level, we are done processing an
415        # argument so we increment arg_ix and initialize a new args_list entry.
416        if char == "," and nest_level == 0:
417            arg_ix += 1
418            args_list.append("")
419            continue
420
421        # For any other character, we append it it to the current arg list
422        # entry.
423        args_list[arg_ix] += char
424
425    # Trim whitespace from each list entry.
426    args_list = [arg.strip() for arg in args_list]
427
428    if arg_num < 0:
429        if abs(arg_num) > len(lvalues):
430            argument = lvalues
431        else:
432            argument = lvalues[arg_num]
433    elif arg_num == 0:
434        argument = called_func_name
435    else:
436        if arg_num > len(args_list):
437            argument = args_list
438        else:
439            argument = args_list[arg_num - 1]
440
441    if local_debug:
442        print_varx("args_list", args_list, 0, debug_indent)
443        print_varx("argument", argument, 0, debug_indent)
444        print_dashes(0, 120)
445
446    return argument
447
448
449def sprint_time(buffer=""):
450    r"""
451    Return the time in the following format.
452
453    Example:
454
455    The following python code...
456
457    sys.stdout.write(sprint_time())
458    sys.stdout.write("Hi.\n")
459
460    Will result in the following type of output:
461
462    #(CDT) 2016/07/08 15:25:35 - Hi.
463
464    Example:
465
466    The following python code...
467
468    sys.stdout.write(sprint_time("Hi.\n"))
469
470    Will result in the following type of output:
471
472    #(CDT) 2016/08/03 17:12:05 - Hi.
473
474    The following environment variables will affect the formatting as
475    described:
476    NANOSECONDS                     This will cause the time stamps to be
477                                    precise to the microsecond (Yes, it
478                                    probably should have been named
479                                    MICROSECONDS but the convention was set
480                                    long ago so we're sticking with it).
481                                    Example of the output when environment
482                                    variable NANOSECONDS=1.
483
484    #(CDT) 2016/08/03 17:16:25.510469 - Hi.
485
486    SHOW_ELAPSED_TIME               This will cause the elapsed time to be
487                                    included in the output.  This is the
488                                    amount of time that has elapsed since the
489                                    last time this function was called.  The
490                                    precision of the elapsed time field is
491                                    also affected by the value of the
492                                    NANOSECONDS environment variable.  Example
493                                    of the output when environment variable
494                                    NANOSECONDS=0 and SHOW_ELAPSED_TIME=1.
495
496    #(CDT) 2016/08/03 17:17:40 -    0 - Hi.
497
498    Example of the output when environment variable NANOSECONDS=1 and
499    SHOW_ELAPSED_TIME=1.
500
501    #(CDT) 2016/08/03 17:18:47.317339 -    0.000046 - Hi.
502
503    Description of arguments.
504    buffer                          This will be appended to the formatted
505                                    time string.
506    """
507
508    global NANOSECONDS
509    global SHOW_ELAPSED_TIME
510    global sprint_time_last_seconds
511
512    seconds = time.time()
513    loc_time = time.localtime(seconds)
514    nanoseconds = "%0.6f" % seconds
515    pos = nanoseconds.find(".")
516    nanoseconds = nanoseconds[pos:]
517
518    time_string = time.strftime("#(%Z) %Y/%m/%d %H:%M:%S", loc_time)
519    if NANOSECONDS == "1":
520        time_string = time_string + nanoseconds
521
522    if SHOW_ELAPSED_TIME == "1":
523        cur_time_seconds = seconds
524        math_string = "%9.9f" % cur_time_seconds + " - " + "%9.9f" % \
525            sprint_time_last_seconds
526        elapsed_seconds = eval(math_string)
527        if NANOSECONDS == "1":
528            elapsed_seconds = "%11.6f" % elapsed_seconds
529        else:
530            elapsed_seconds = "%4i" % elapsed_seconds
531        sprint_time_last_seconds = cur_time_seconds
532        time_string = time_string + " - " + elapsed_seconds
533
534    return time_string + " - " + buffer
535
536
537def sprint_timen(buffer=""):
538    r"""
539    Append a line feed to the buffer, pass it to sprint_time and return the
540    result.
541    """
542
543    return sprint_time(buffer + "\n")
544
545
546def sprint_error(buffer=""):
547    r"""
548    Return a standardized error string.  This includes:
549      - A time stamp
550      - The "**ERROR**" string
551      - The caller's buffer string.
552
553    Example:
554
555    The following python code...
556
557    print(sprint_error("Oops.\n"))
558
559    Will result in the following type of output:
560
561    #(CDT) 2016/08/03 17:12:05 - **ERROR** Oops.
562
563    Description of arguments.
564    buffer                          This will be appended to the formatted
565                                    error string.
566    """
567
568    return sprint_time() + "**ERROR** " + buffer
569
570
571# Implement "constants" with functions.
572def digit_length_in_bits():
573    r"""
574    Return the digit length in bits.
575    """
576
577    return 4
578
579
580def word_length_in_digits():
581    r"""
582    Return the word length in digits.
583    """
584
585    return 8
586
587
588def bit_length(number):
589    r"""
590    Return the bit length of the number.
591
592    Description of argument(s):
593    number                          The number to be analyzed.
594    """
595
596    if number < 0:
597        # Convert negative numbers to positive and subtract one.  The
598        # following example illustrates the reason for this:
599        # Consider a single nibble whose signed values can range from -8 to 7
600        # (0x8 to 0x7).  A value of 0x7 equals 0b0111.  Therefore, its length
601        # in bits is 3.  Since the negative bit (i.e. 0b1000) is not set, the
602        # value 7 clearly will fit in one nibble.  With -8 = 0x8 = 0b1000, you
603        # have the smallest negative value that will fit.  Note that it
604        # requires 3 bits of 0.  So by converting a number value of -8 to a
605        # working_number of 7, this function can accurately calculate the
606        # number of bits and therefore nibbles required to represent the
607        # number in print.
608        working_number = abs(number) - 1
609    else:
610        working_number = number
611
612    # Handle the special case of the number 0.
613    if working_number == 0:
614        return 0
615
616    return len(bin(working_number)) - 2
617
618
619def get_req_num_hex_digits(number):
620    r"""
621    Return the required number of hex digits required to display the given
622    number.
623
624    The returned value will always be rounded up to the nearest multiple of 8.
625
626    Description of argument(s):
627    number                          The number to be analyzed.
628    """
629
630    if number < 0:
631        # Convert negative numbers to positive and subtract one.  The
632        # following example illustrates the reason for this:
633        # Consider a single nibble whose signed values can range from -8 to 7
634        # (0x8 to 0x7).  A value of 0x7 equals 0b0111.  Therefore, its length
635        # in bits is 3.  Since the negative bit (i.e. 0b1000) is not set, the
636        # value 7 clearly will fit in one nibble.  With -8 = 0x8 = 0b1000, you
637        # have the smallest negative value that will fit.  Note that it
638        # requires 3 bits of 0.  So by converting a number value of -8 to a
639        # working_number of 7, this function can accurately calculate the
640        # number of bits and therefore nibbles required to represent the
641        # number in print.
642        working_number = abs(number) - 1
643    else:
644        working_number = number
645
646    # Handle the special case of the number 0.
647    if working_number == 0:
648        return word_length_in_digits()
649
650    num_length_in_bits = bit_length(working_number)
651    num_hex_digits, remainder = divmod(num_length_in_bits,
652                                       digit_length_in_bits())
653    if remainder > 0:
654        # Example: the number 7 requires 3 bits.  The divmod above produces,
655        # 0 with remainder of 3.  So because we have a remainder, we increment
656        # num_hex_digits from 0 to 1.
657        num_hex_digits += 1
658
659    # Check to see whether the negative bit is set.  This is the left-most
660    # bit in the highest order digit.
661    negative_mask = 2 ** (num_hex_digits * 4 - 1)
662    if working_number & negative_mask:
663        # If a number that is intended to be positive has its negative bit
664        # on, an additional digit will be required to represent it correctly
665        # in print.
666        num_hex_digits += 1
667
668    num_words, remainder = divmod(num_hex_digits, word_length_in_digits())
669    if remainder > 0 or num_words == 0:
670        num_words += 1
671
672    # Round up to the next word length in digits.
673    return num_words * word_length_in_digits()
674
675
676def dft_num_hex_digits():
677    r"""
678    Return the default number of hex digits to be used to represent a hex
679    number in print.
680
681    The value returned is a function of sys.maxsize.
682    """
683
684    global _gen_print_dft_num_hex_digits_
685    try:
686        return _gen_print_dft_num_hex_digits_
687    except NameError:
688        _gen_print_dft_num_hex_digits_ = get_req_num_hex_digits(sys.maxsize)
689        return _gen_print_dft_num_hex_digits_
690
691
692def sprint_varx(var_name,
693                var_value,
694                hex=0,
695                loc_col1_indent=col1_indent,
696                loc_col1_width=col1_width,
697                trailing_char="\n",
698                key_list=None):
699    r"""
700    Print the var name/value passed to it.  If the caller lets loc_col1_width
701    default, the printing lines up nicely with output generated by the
702    print_time functions.
703
704    Note that the sprint_var function (defined below) can be used to call this
705    function so that the programmer does not need to pass the var_name.
706    sprint_var will figure out the var_name.  The sprint_var function is the
707    one that would normally be used by the general user.
708
709    For example, the following python code:
710
711    first_name = "Mike"
712    print_time("Doing this...\n")
713    print_varx("first_name", first_name)
714    print_time("Doing that...\n")
715
716    Will generate output like this:
717
718    #(CDT) 2016/08/10 17:34:42.847374 -    0.001285 - Doing this...
719    first_name:                                       Mike
720    #(CDT) 2016/08/10 17:34:42.847510 -    0.000136 - Doing that...
721
722    This function recognizes several complex types of data such as dict, list
723    or tuple.
724
725    For example, the following python code:
726
727    my_dict = dict(one=1, two=2, three=3)
728    print_var(my_dict)
729
730    Will generate the following output:
731
732    my_dict:
733      my_dict[three]:                                 3
734      my_dict[two]:                                   2
735      my_dict[one]:                                   1
736
737    Description of arguments.
738    var_name                        The name of the variable to be printed.
739    var_value                       The value of the variable to be printed.
740    hex                             This indicates that the value should be
741                                    printed in hex format.  It is the user's
742                                    responsibility to ensure that a var_value
743                                    contains a valid hex number.  For string
744                                    var_values, this will be interpreted as
745                                    show_blanks which means that blank values
746                                    will be printed as "<blank>".  For dict
747                                    var_values, this will be interpreted as
748                                    terse format where keys are not repeated
749                                    in the output.
750    loc_col1_indent                 The number of spaces to indent the output.
751    loc_col1_width                  The width of the output column containing
752                                    the variable name.  The default value of
753                                    this is adjusted so that the var_value
754                                    lines up with text printed via the
755                                    print_time function.
756    trailing_char                   The character to be used at the end of the
757                                    returned string.  The default value is a
758                                    line feed.
759    key_list                        A list of which dictionary keys should be
760                                    printed.  All others keys will be skipped.
761                                    Each value in key_list will be regarded
762                                    as a regular expression and it will be
763                                    regarded as anchored to the beginning and
764                                    ends of the dictionary key being
765                                    referenced.  For example if key_list is
766                                    ["one", "two"], the resulting regex used
767                                    will be "^one|two$", i.e. only keys "one"
768                                    and "two" from the var_value dictionary
769                                    will be printed.  As another example, if
770                                    the caller were to specify a key_list of
771                                    ["one.*"], then only dictionary keys whose
772                                    names begin with "one" will be printed.
773                                    Note: This argument pertains only to
774                                    var_values which are dictionaries.
775    """
776
777    # Determine the type
778    if type(var_value) in (int, long, float, bool, str, unicode) \
779       or var_value is None:
780        # The data type is simple in the sense that it has no subordinate
781        # parts.
782        # Adjust loc_col1_width.
783        loc_col1_width = loc_col1_width - loc_col1_indent
784        # See if the user wants the output in hex format.
785        if hex:
786            if type(var_value) not in (int, long):
787                value_format = "%s"
788                if var_value == "":
789                    var_value = "<blank>"
790            else:
791                num_hex_digits = max(dft_num_hex_digits(),
792                                     get_req_num_hex_digits(var_value))
793                # Convert a negative number to its positive twos complement
794                # for proper printing.  For example, instead of printing -1 as
795                # "0x-000000000000001" it will be printed as
796                # "0xffffffffffffffff".
797                var_value = var_value & (2 ** (num_hex_digits * 4) - 1)
798                value_format = "0x%0" + str(num_hex_digits) + "x"
799        else:
800            value_format = "%s"
801        format_string = "%" + str(loc_col1_indent) + "s%-" \
802            + str(loc_col1_width) + "s" + value_format + trailing_char
803        if value_format == "0x%08x":
804            return format_string % ("", str(var_name) + ":",
805                                    var_value & 0xffffffff)
806        else:
807            return format_string % ("", str(var_name) + ":", var_value)
808    elif type(var_value) is type:
809        return sprint_varx(var_name, str(var_value).split("'")[1], hex,
810                           loc_col1_indent, loc_col1_width, trailing_char,
811                           key_list)
812    else:
813        # The data type is complex in the sense that it has subordinate parts.
814        format_string = "%" + str(loc_col1_indent) + "s%s\n"
815        buffer = format_string % ("", var_name + ":")
816        loc_col1_indent += 2
817        try:
818            length = len(var_value)
819        except TypeError:
820            length = 0
821        ix = 0
822        loc_trailing_char = "\n"
823        type_is_dict = 0
824        if type(var_value) is dict:
825            type_is_dict = 1
826        try:
827            if type(var_value) is collections.OrderedDict:
828                type_is_dict = 1
829        except AttributeError:
830            pass
831        try:
832            if type(var_value) is DotDict:
833                type_is_dict = 1
834        except NameError:
835            pass
836        try:
837            if type(var_value) is NormalizedDict:
838                type_is_dict = 1
839        except NameError:
840            pass
841        if type_is_dict:
842            for key, value in var_value.iteritems():
843                if key_list is not None:
844                    key_list_regex = "^" + "|".join(key_list) + "$"
845                    if not re.match(key_list_regex, key):
846                        continue
847                ix += 1
848                if ix == length:
849                    loc_trailing_char = trailing_char
850                if hex:
851                    # Since hex is being used as a format type, we want it
852                    # turned off when processing integer dictionary values so
853                    # it is not interpreted as a hex indicator.
854                    loc_hex = not (type(value) is int)
855                    buffer += sprint_varx("[" + key + "]", value,
856                                          loc_hex, loc_col1_indent,
857                                          loc_col1_width,
858                                          loc_trailing_char,
859                                          key_list)
860                else:
861                    buffer += sprint_varx(var_name + "[" + str(key) + "]",
862                                          value, hex, loc_col1_indent,
863                                          loc_col1_width, loc_trailing_char,
864                                          key_list)
865        elif type(var_value) in (list, tuple, set):
866            for key, value in enumerate(var_value):
867                ix += 1
868                if ix == length:
869                    loc_trailing_char = trailing_char
870                buffer += sprint_varx(var_name + "[" + str(key) + "]", value,
871                                      hex, loc_col1_indent, loc_col1_width,
872                                      loc_trailing_char, key_list)
873        elif type(var_value) is argparse.Namespace:
874            for key in var_value.__dict__:
875                ix += 1
876                if ix == length:
877                    loc_trailing_char = trailing_char
878                cmd_buf = "buffer += sprint_varx(var_name + \".\" + str(key)" \
879                          + ", var_value." + key + ", hex, loc_col1_indent," \
880                          + " loc_col1_width, loc_trailing_char, key_list)"
881                exec(cmd_buf)
882        else:
883            var_type = type(var_value).__name__
884            func_name = sys._getframe().f_code.co_name
885            var_value = "<" + var_type + " type not supported by " + \
886                        func_name + "()>"
887            value_format = "%s"
888            loc_col1_indent -= 2
889            # Adjust loc_col1_width.
890            loc_col1_width = loc_col1_width - loc_col1_indent
891            format_string = "%" + str(loc_col1_indent) + "s%-" \
892                + str(loc_col1_width) + "s" + value_format + trailing_char
893            return format_string % ("", str(var_name) + ":", var_value)
894
895        return buffer
896
897    return ""
898
899
900def sprint_var(var_value,
901               hex=0,
902               loc_col1_indent=col1_indent,
903               loc_col1_width=col1_width,
904               trailing_char="\n",
905               key_list=None):
906    r"""
907    Figure out the name of the first argument for you and then call
908    sprint_varx with it.  Therefore, the following 2 calls are equivalent:
909    sprint_varx("var1", var1)
910    sprint_var(var1)
911    """
912
913    # Get the name of the first variable passed to this function.
914    stack_frame = 2
915    caller_func_name = sprint_func_name(2)
916    if caller_func_name.endswith("print_var"):
917        stack_frame += 1
918    var_name = get_arg_name(None, 1, stack_frame)
919    return sprint_varx(var_name, var_value=var_value, hex=hex,
920                       loc_col1_indent=loc_col1_indent,
921                       loc_col1_width=loc_col1_width,
922                       trailing_char=trailing_char,
923                       key_list=key_list)
924
925
926def sprint_vars(*args):
927    r"""
928    Sprint the values of one or more variables.
929
930    Description of args:
931    args:
932        If the first argument is an integer, it will be interpreted to be the
933        "indent" value.
934        If the second argument is an integer, it will be interpreted to be the
935        "col1_width" value.
936        If the third argument is an integer, it will be interpreted to be the
937        "hex" value.
938        All remaining parms are considered variable names which are to be
939        sprinted.
940    """
941
942    if len(args) == 0:
943        return
944
945    # Get the name of the first variable passed to this function.
946    stack_frame = 2
947    caller_func_name = sprint_func_name(2)
948    if caller_func_name.endswith("print_vars"):
949        stack_frame += 1
950
951    parm_num = 1
952
953    # Create list from args (which is a tuple) so that it can be modified.
954    args_list = list(args)
955
956    var_name = get_arg_name(None, parm_num, stack_frame)
957    # See if parm 1 is to be interpreted as "indent".
958    try:
959        if type(int(var_name)) is int:
960            indent = int(var_name)
961            args_list.pop(0)
962            parm_num += 1
963    except ValueError:
964        indent = 0
965
966    var_name = get_arg_name(None, parm_num, stack_frame)
967    # See if parm 1 is to be interpreted as "col1_width".
968    try:
969        if type(int(var_name)) is int:
970            loc_col1_width = int(var_name)
971            args_list.pop(0)
972            parm_num += 1
973    except ValueError:
974        loc_col1_width = col1_width
975
976    var_name = get_arg_name(None, parm_num, stack_frame)
977    # See if parm 1 is to be interpreted as "hex".
978    try:
979        if type(int(var_name)) is int:
980            hex = int(var_name)
981            args_list.pop(0)
982            parm_num += 1
983    except ValueError:
984        hex = 0
985
986    buffer = ""
987    for var_value in args_list:
988        var_name = get_arg_name(None, parm_num, stack_frame)
989        buffer += sprint_varx(var_name, var_value, hex, indent, loc_col1_width)
990        parm_num += 1
991
992    return buffer
993
994
995def sprint_dashes(indent=col1_indent,
996                  width=80,
997                  line_feed=1,
998                  char="-"):
999    r"""
1000    Return a string of dashes to the caller.
1001
1002    Description of arguments:
1003    indent                          The number of characters to indent the
1004                                    output.
1005    width                           The width of the string of dashes.
1006    line_feed                       Indicates whether the output should end
1007                                    with a line feed.
1008    char                            The character to be repeated in the output
1009                                    string.
1010    """
1011
1012    width = int(width)
1013    buffer = " " * int(indent) + char * width
1014    if line_feed:
1015        buffer += "\n"
1016
1017    return buffer
1018
1019
1020def sindent(text="",
1021            indent=0):
1022    r"""
1023    Pre-pend the specified number of characters to the text string (i.e.
1024    indent it) and return it.
1025
1026    Description of arguments:
1027    text                            The string to be indented.
1028    indent                          The number of characters to indent the
1029                                    string.
1030    """
1031
1032    format_string = "%" + str(indent) + "s%s"
1033    buffer = format_string % ("", text)
1034
1035    return buffer
1036
1037
1038def sprint_call_stack(indent=0,
1039                      stack_frame_ix=0):
1040    r"""
1041    Return a call stack report for the given point in the program with line
1042    numbers, function names and function parameters and arguments.
1043
1044    Sample output:
1045
1046    -------------------------------------------------------------------------
1047    Python function call stack
1048
1049    Line # Function name and arguments
1050    ------ ------------------------------------------------------------------
1051       424 sprint_call_stack ()
1052         4 print_call_stack ()
1053        31 func1 (last_name = 'walsh', first_name = 'mikey')
1054        59 /tmp/scr5.py
1055    -------------------------------------------------------------------------
1056
1057    Description of arguments:
1058    indent                          The number of characters to indent each
1059                                    line of output.
1060    stack_frame_ix                  The index of the first stack frame which
1061                                    is to be returned.
1062    """
1063
1064    buffer = ""
1065    buffer += sprint_dashes(indent)
1066    buffer += sindent("Python function call stack\n\n", indent)
1067    buffer += sindent("Line # Function name and arguments\n", indent)
1068    buffer += sprint_dashes(indent, 6, 0) + " " + sprint_dashes(0, 73)
1069
1070    # Grab the current program stack.
1071    current_stack = inspect.stack()
1072
1073    # Process each frame in turn.
1074    format_string = "%6s %s\n"
1075    ix = 0
1076    for stack_frame in current_stack:
1077        if ix < stack_frame_ix:
1078            ix += 1
1079            continue
1080        # I want the line number shown to be the line where you find the line
1081        # shown.
1082        try:
1083            line_num = str(current_stack[ix + 1][2])
1084        except IndexError:
1085            line_num = ""
1086        func_name = str(stack_frame[3])
1087        if func_name == "?":
1088            # "?" is the name used when code is not in a function.
1089            func_name = "(none)"
1090
1091        if func_name == "<module>":
1092            # If the func_name is the "main" program, we simply get the
1093            # command line call string.
1094            func_and_args = ' '.join(sys.argv)
1095        else:
1096            # Get the program arguments.
1097            arg_vals = inspect.getargvalues(stack_frame[0])
1098            function_parms = arg_vals[0]
1099            frame_locals = arg_vals[3]
1100
1101            args_list = []
1102            for arg_name in function_parms:
1103                # Get the arg value from frame locals.
1104                arg_value = frame_locals[arg_name]
1105                args_list.append(arg_name + " = " + repr(arg_value))
1106            args_str = "(" + ', '.join(map(str, args_list)) + ")"
1107
1108            # Now we need to print this in a nicely-wrapped way.
1109            func_and_args = func_name + " " + args_str
1110
1111        buffer += sindent(format_string % (line_num, func_and_args), indent)
1112        ix += 1
1113
1114    buffer += sprint_dashes(indent)
1115
1116    return buffer
1117
1118
1119def sprint_executing(stack_frame_ix=None):
1120    r"""
1121    Print a line indicating what function is executing and with what parameter
1122    values.  This is useful for debugging.
1123
1124    Sample output:
1125
1126    #(CDT) 2016/08/25 17:54:27 - Executing: func1 (x = 1)
1127
1128    Description of arguments:
1129    stack_frame_ix                  The index of the stack frame whose
1130                                    function info should be returned.  If the
1131                                    caller does not specify a value, this
1132                                    function will set the value to 1 which is
1133                                    the index of the caller's stack frame.  If
1134                                    the caller is the wrapper function
1135                                    "print_executing", this function will bump
1136                                    it up by 1.
1137    """
1138
1139    # If user wants default stack_frame_ix.
1140    if stack_frame_ix is None:
1141        func_name = sys._getframe().f_code.co_name
1142        caller_func_name = sys._getframe(1).f_code.co_name
1143        if caller_func_name.endswith(func_name[1:]):
1144            stack_frame_ix = 2
1145        else:
1146            stack_frame_ix = 1
1147
1148    stack_frame = inspect.stack()[stack_frame_ix]
1149
1150    func_name = str(stack_frame[3])
1151    if func_name == "?":
1152        # "?" is the name used when code is not in a function.
1153        func_name = "(none)"
1154
1155    if func_name == "<module>":
1156        # If the func_name is the "main" program, we simply get the command
1157        # line call string.
1158        func_and_args = ' '.join(sys.argv)
1159    else:
1160        # Get the program arguments.
1161        arg_vals = inspect.getargvalues(stack_frame[0])
1162        function_parms = arg_vals[0]
1163        frame_locals = arg_vals[3]
1164
1165        args_list = []
1166        for arg_name in function_parms:
1167            # Get the arg value from frame locals.
1168            arg_value = frame_locals[arg_name]
1169            args_list.append(arg_name + " = " + repr(arg_value))
1170        args_str = "(" + ', '.join(map(str, args_list)) + ")"
1171
1172        # Now we need to print this in a nicely-wrapped way.
1173        func_and_args = func_name + " " + args_str
1174
1175    return sprint_time() + "Executing: " + func_and_args + "\n"
1176
1177
1178def sprint_pgm_header(indent=0,
1179                      linefeed=1):
1180    r"""
1181    Return a standardized header that programs should print at the beginning
1182    of the run.  It includes useful information like command line, pid,
1183    userid, program parameters, etc.
1184
1185    Description of arguments:
1186    indent                          The number of characters to indent each
1187                                    line of output.
1188    linefeed                        Indicates whether a line feed be included
1189                                    at the beginning and end of the report.
1190    """
1191
1192    loc_col1_width = col1_width + indent
1193
1194    buffer = ""
1195    if linefeed:
1196        buffer = "\n"
1197
1198    if robot_env:
1199        suite_name = BuiltIn().get_variable_value("${suite_name}")
1200        buffer += sindent(sprint_time("Running test suite \"" + suite_name +
1201                                      "\".\n"), indent)
1202
1203    buffer += sindent(sprint_time() + "Running " + pgm_name + ".\n", indent)
1204    buffer += sindent(sprint_time() + "Program parameter values, etc.:\n\n",
1205                      indent)
1206    buffer += sprint_varx("command_line", ' '.join(sys.argv), 0, indent,
1207                          loc_col1_width)
1208    # We want the output to show a customized name for the pid and pgid but
1209    # we want it to look like a valid variable name.  Therefore, we'll use
1210    # pgm_name_var_name which was set when this module was imported.
1211    buffer += sprint_varx(pgm_name_var_name + "_pid", os.getpid(), 0, indent,
1212                          loc_col1_width)
1213    buffer += sprint_varx(pgm_name_var_name + "_pgid", os.getpgrp(), 0, indent,
1214                          loc_col1_width)
1215    userid_num = str(os.geteuid())
1216    try:
1217        username = os.getlogin()
1218    except OSError:
1219        if userid_num == "0":
1220            username = "root"
1221        else:
1222            username = "?"
1223    buffer += sprint_varx("uid", userid_num + " (" + username +
1224                          ")", 0, indent, loc_col1_width)
1225    buffer += sprint_varx("gid", str(os.getgid()) + " (" +
1226                          str(grp.getgrgid(os.getgid()).gr_name) + ")", 0,
1227                          indent, loc_col1_width)
1228    buffer += sprint_varx("host_name", socket.gethostname(), 0, indent,
1229                          loc_col1_width)
1230    try:
1231        DISPLAY = os.environ['DISPLAY']
1232    except KeyError:
1233        DISPLAY = ""
1234    buffer += sprint_varx("DISPLAY", DISPLAY, 0, indent,
1235                          loc_col1_width)
1236    # I want to add code to print caller's parms.
1237
1238    # __builtin__.arg_obj is created by the get_arg module function,
1239    # gen_get_options.
1240    try:
1241        buffer += ga.sprint_args(__builtin__.arg_obj, indent)
1242    except AttributeError:
1243        pass
1244
1245    if robot_env:
1246        # Get value of global parm_list.
1247        parm_list = BuiltIn().get_variable_value("${parm_list}")
1248
1249        for parm in parm_list:
1250            parm_value = BuiltIn().get_variable_value("${" + parm + "}")
1251            buffer += sprint_varx(parm, parm_value, 0, indent, loc_col1_width)
1252
1253        # Setting global program_pid.
1254        BuiltIn().set_global_variable("${program_pid}", os.getpid())
1255
1256    if linefeed:
1257        buffer += "\n"
1258
1259    return buffer
1260
1261
1262def sprint_error_report(error_text="\n",
1263                        indent=2,
1264                        format=None):
1265    r"""
1266    Return a string with a standardized report which includes the caller's
1267    error text, the call stack and the program header.
1268
1269    Description of args:
1270    error_text                      The error text to be included in the
1271                                    report.  The caller should include any
1272                                    needed linefeeds.
1273    indent                          The number of characters to indent each
1274                                    line of output.
1275    format                          Long or short format.  Long includes
1276                                    extras like lines of dashes, call stack,
1277                                    etc.
1278    """
1279
1280    # Process input.
1281    indent = int(indent)
1282    if format is None:
1283        if robot_env:
1284            format = 'short'
1285        else:
1286            format = 'long'
1287    error_text = error_text.rstrip('\n') + '\n'
1288
1289    if format == 'short':
1290        return sprint_error(error_text)
1291
1292    buffer = ""
1293    buffer += sprint_dashes(width=120, char="=")
1294    buffer += sprint_error(error_text)
1295    buffer += "\n"
1296    # Calling sprint_call_stack with stack_frame_ix of 0 causes it to show
1297    # itself and this function in the call stack.  This is not helpful to a
1298    # debugger and is therefore clutter.  We will adjust the stack_frame_ix to
1299    # hide that information.
1300    stack_frame_ix = 1
1301    caller_func_name = sprint_func_name(2)
1302    if caller_func_name.endswith("print_error_report"):
1303        stack_frame_ix += 1
1304    if not robot_env:
1305        buffer += sprint_call_stack(indent, stack_frame_ix)
1306    buffer += sprint_pgm_header(indent)
1307    buffer += sprint_dashes(width=120, char="=")
1308
1309    return buffer
1310
1311
1312def sprint_issuing(cmd_buf,
1313                   test_mode=0):
1314    r"""
1315    Return a line indicating a command that the program is about to execute.
1316
1317    Sample output for a cmd_buf of "ls"
1318
1319    #(CDT) 2016/08/25 17:57:36 - Issuing: ls
1320
1321    Description of args:
1322    cmd_buf                         The command to be executed by caller.
1323    test_mode                       With test_mode set, your output will look
1324                                    like this:
1325
1326    #(CDT) 2016/08/25 17:57:36 - (test_mode) Issuing: ls
1327
1328    """
1329
1330    buffer = sprint_time()
1331    if test_mode:
1332        buffer += "(test_mode) "
1333    buffer += "Issuing: " + cmd_buf + "\n"
1334
1335    return buffer
1336
1337
1338def sprint_pgm_footer():
1339    r"""
1340    Return a standardized footer that programs should print at the end of the
1341    program run.  It includes useful information like total run time, etc.
1342    """
1343
1344    buffer = "\n" + sprint_time() + "Finished running " + pgm_name + ".\n\n"
1345
1346    total_time = time.time() - start_time
1347    total_time_string = "%0.6f" % total_time
1348
1349    buffer += sprint_varx(pgm_name_var_name + "_runtime", total_time_string)
1350    buffer += "\n"
1351
1352    return buffer
1353
1354
1355def sprint(buffer=""):
1356    r"""
1357    Simply return the user's buffer.  This function is used by the qprint and
1358    dprint functions defined dynamically below, i.e. it would not normally be
1359    called for general use.
1360
1361    Description of arguments.
1362    buffer                          This will be returned to the caller.
1363    """
1364
1365    try:
1366        return str(buffer)
1367    except UnicodeEncodeError:
1368        return buffer
1369
1370
1371def sprintn(buffer=""):
1372    r"""
1373    Simply return the user's buffer with a line feed.  This function is used
1374    by the qprint and dprint functions defined dynamically below, i.e. it
1375    would not normally be called for general use.
1376
1377    Description of arguments.
1378    buffer                          This will be returned to the caller.
1379    """
1380
1381    try:
1382        buffer = str(buffer) + "\n"
1383    except UnicodeEncodeError:
1384        buffer = buffer + "\n"
1385
1386    return buffer
1387
1388
1389def gp_print(buffer,
1390             stream='stdout'):
1391    r"""
1392    Print the buffer using either sys.stdout.write or BuiltIn().log_to_console
1393    depending on whether we are running in a robot environment.
1394
1395    This function is intended for use only by other functions in this module.
1396
1397    Description of arguments:
1398    buffer                          The string to be printed.
1399    stream                          Either "stdout" or "stderr".
1400    """
1401
1402    if robot_env:
1403        BuiltIn().log_to_console(buffer, stream=stream, no_newline=True)
1404    else:
1405        if stream == "stdout":
1406            sys.stdout.write(buffer)
1407            sys.stdout.flush()
1408        else:
1409            sys.stderr.write(buffer)
1410            sys.stderr.flush()
1411
1412
1413def gp_log(buffer):
1414    r"""
1415    Log the buffer using either python logging or BuiltIn().log depending on
1416    whether we are running in a robot environment.
1417
1418    This function is intended for use only by other functions in this module.
1419
1420    Description of arguments:
1421    buffer                          The string to be logged.
1422    """
1423
1424    if robot_env:
1425        BuiltIn().log(buffer)
1426    else:
1427        logging.warning(buffer)
1428
1429
1430def gp_debug_print(buffer):
1431    r"""
1432    Print with gp_print only if gen_print_debug is set.
1433
1434    This function is intended for use only by other functions in this module.
1435
1436    Description of arguments:
1437    buffer                          The string to be printed.
1438    """
1439
1440    if not gen_print_debug:
1441        return
1442
1443    gp_print(buffer)
1444
1445
1446def get_var_value(var_value=None,
1447                  default=1,
1448                  var_name=None):
1449    r"""
1450    Return either var_value, the corresponding global value or default.
1451
1452    If var_value is not None, it will simply be returned.
1453
1454    If var_value is None, this function will return the corresponding global
1455    value of the variable in question.
1456
1457    Note: For global values, if we are in a robot environment,
1458    get_variable_value will be used.  Otherwise, the __builtin__ version of
1459    the variable is returned (which are set by gen_arg.py functions).
1460
1461    If there is no global value associated with the variable, default is
1462    returned.
1463
1464    This function is useful for other functions in setting default values for
1465    parameters.
1466
1467    Example use:
1468
1469    def my_func(quiet=None):
1470
1471      quiet = int(get_var_value(quiet, 0))
1472
1473    Example calls to my_func():
1474
1475    In the following example, the caller is explicitly asking to have quiet be
1476    set to 1.
1477
1478    my_func(quiet=1)
1479
1480    In the following example, quiet will be set to the global value of quiet,
1481    if defined, or to 0 (the default).
1482
1483    my_func()
1484
1485    Description of arguments:
1486    var_value                       The value to be returned (if not equal to
1487                                    None).
1488    default                         The value that is returned if var_value is
1489                                    None and there is no corresponding global
1490                                    value defined.
1491    var_name                        The name of the variable whose value is to
1492                                    be returned.  Under most circumstances,
1493                                    this value need not be provided.  This
1494                                    function can figure out the name of the
1495                                    variable passed as var_value.  One
1496                                    exception to this would be if this
1497                                    function is called directly from a .robot
1498                                    file.
1499    """
1500
1501    if var_value is not None:
1502        return var_value
1503
1504    if var_name is None:
1505        var_name = get_arg_name(None, 1, 2)
1506
1507    if robot_env:
1508        var_value = BuiltIn().get_variable_value("${" + var_name + "}",
1509                                                 default)
1510    else:
1511        var_value = getattr(__builtin__, var_name, default)
1512
1513    return var_value
1514
1515
1516def get_stack_var(var_name,
1517                  default="",
1518                  init_stack_ix=2):
1519
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