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