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