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