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