xref: /openbmc/openbmc-test-automation/lib/gen_print.py (revision 0fd3b249bfcdf993f88e38828da2e8bd66844ecf)
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
19
20try:
21    from robot.utils import DotDict
22except ImportError:
23    pass
24
25import gen_arg as ga
26
27# Setting these variables for use both inside this module and by programs
28# importing this module.
29pgm_dir_path = sys.argv[0]
30pgm_name = os.path.basename(pgm_dir_path)
31pgm_dir_name = re.sub("/" + pgm_name, "", pgm_dir_path) + "/"
32
33
34# Some functions (e.g. sprint_pgm_header) have need of a program name value
35# that looks more like a valid variable name.  Therefore, we'll swap odd
36# characters like "." out for underscores.
37pgm_name_var_name = pgm_name.replace(".", "_")
38
39# Initialize global values used as defaults by print_time, print_var, etc.
40col1_indent = 0
41
42# Calculate default column width for print_var functions based on environment
43# variable settings.  The objective is to make the variable values line up
44# nicely with the time stamps.
45col1_width = 29
46if 'NANOSECONDS' in os.environ:
47    NANOSECONDS = os.environ['NANOSECONDS']
48else:
49    NANOSECONDS = 0
50
51if NANOSECONDS == "1":
52    col1_width = col1_width + 7
53
54if 'SHOW_ELAPSED_TIME' in os.environ:
55    SHOW_ELAPSED_TIME = os.environ['SHOW_ELAPSED_TIME']
56else:
57    SHOW_ELAPSED_TIME = 0
58
59if SHOW_ELAPSED_TIME == "1":
60    if NANOSECONDS == "1":
61        col1_width = col1_width + 14
62    else:
63        col1_width = col1_width + 7
64
65# Initialize some time variables used in module functions.
66start_time = time.time()
67sprint_time_last_seconds = start_time
68
69try:
70    # The user can set environment variable "GEN_PRINT_DEBUG" to get debug
71    # output from this module.
72    gen_print_debug = int(os.environ['GEN_PRINT_DEBUG'])
73except KeyError:
74    gen_print_debug = 0
75
76
77###############################################################################
78def sprint_func_name(stack_frame_ix=None):
79
80    r"""
81    Return the function name associated with the indicated stack frame.
82
83    Description of arguments:
84    stack_frame_ix                  The index of the stack frame whose
85                                    function name should be returned.  If the
86                                    caller does not specifiy a value, this
87                                    function will set the value to 1 which is
88                                    the index of the caller's stack frame.  If
89                                    the caller is the wrapper function
90                                    "print_func_name", this function will bump
91                                    it up by 1.
92    """
93
94    # If user specified no stack_frame_ix, we'll set it to a proper default
95    # value.
96    if stack_frame_ix is None:
97        func_name = sys._getframe().f_code.co_name
98        caller_func_name = sys._getframe(1).f_code.co_name
99        if func_name[1:] == caller_func_name:
100            stack_frame_ix = 2
101        else:
102            stack_frame_ix = 1
103
104    func_name = sys._getframe(stack_frame_ix).f_code.co_name
105
106    return func_name
107
108###############################################################################
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.
113###############################################################################
114def get_arg_name(var,
115                 arg_num=1,
116                 stack_frame_ix=1):
117
118    r"""
119    Return the "name" of an argument passed to a function.  This could be a
120    literal or a variable name.
121
122    Description of arguements:
123    var                             The variable whose name you want returned.
124    arg_num                         The arg number (1 through n) whose name
125                                    you wish to have returned.  This value
126                                    should not exceed the number of arguments
127                                    allowed by the target function.
128    stack_frame_ix                  The stack frame index of the target
129                                    function.  This value must be 1 or
130                                    greater.  1 would indicate get_arg_name's
131                                    stack frame.  2 would be the caller of
132                                    get_arg_name's stack frame, etc.
133
134    Example 1:
135
136    my_var = "mike"
137    var_name = get_arg_name(my_var)
138
139    In this example, var_name will receive the value "my_var".
140
141    Example 2:
142
143    def test1(var):
144        # Getting the var name of the first arg to this function, test1.
145        # Note, in this case, it doesn't matter what you pass as the first arg
146        # to get_arg_name since it is the caller's variable name that matters.
147        dummy = 1
148        arg_num = 1
149        stack_frame = 2
150        var_name = get_arg_name(dummy, arg_num, stack_frame)
151
152    # Mainline...
153
154    another_var = "whatever"
155    test1(another_var)
156
157    In this example, var_name will be set to "another_var".
158
159    """
160
161    # Note: I wish to avoid recursion so I refrain from calling any function
162    # that calls this function (i.e. sprint_var, valid_value, etc.).
163
164    try:
165        # The user can set environment variable "GET_ARG_NAME_DEBUG" to get
166        # debug output from this function.
167        local_debug = os.environ['GET_ARG_NAME_DEBUG']
168    except KeyError:
169        local_debug = 0
170
171    if arg_num < 1:
172        print_error("Programmer error - Variable \"arg_num\" has an invalid" +
173                    " value of \"" + str(arg_num) + "\".  The value must be" +
174                    " an integer that is greater than 0.\n")
175        # What is the best way to handle errors?  Raise exception?  I'll
176        # revisit later.
177        return
178    if stack_frame_ix < 1:
179        print_error("Programmer error - Variable \"stack_frame_ix\" has an" +
180                    " invalid value of \"" + str(stack_frame_ix) + "\".  The" +
181                    " value must be an integer that is greater than or equal" +
182                    " to 1.\n")
183        return
184
185    if local_debug:
186        debug_indent = 2
187        print(sprint_func_name() + "() parms:")
188        print_varx("var", var, 0, debug_indent)
189        print_varx("arg_num", arg_num, 0, debug_indent)
190        print_varx("stack_frame_ix", stack_frame_ix, 0, debug_indent)
191
192    try:
193        frame, filename, cur_line_no, function_name, lines, index = \
194            inspect.stack()[stack_frame_ix]
195    except IndexError:
196        print_error("Programmer error - The caller has asked for information" +
197                    " about the stack frame at index \"" +
198                    str(stack_frame_ix) + "\".  However, the stack only" +
199                    " contains " + str(len(inspect.stack())) + " entries." +
200                    "  Therefore the stack frame index is out of range.\n")
201        return
202
203    if local_debug:
204        print("\nVariables retrieved from inspect.stack() function:")
205        print_varx("frame", frame, 0, debug_indent)
206        print_varx("filename", filename, 0, debug_indent)
207        print_varx("cur_line_no", cur_line_no, 0, debug_indent)
208        print_varx("function_name", function_name, 0, debug_indent)
209        print_varx("lines", lines, 0, debug_indent)
210        print_varx("index", index, 0, debug_indent)
211
212    composite_line = lines[0].strip()
213
214    called_func_name = sprint_func_name(stack_frame_ix)
215    # Needed to add a right anchor to func_regex for cases like this where
216    # there is an arg whose name is a substring of the function name.  So the
217    # function name needs to be bounded on the right by zero or more spaces
218    # and a left parenthesis.
219    # if not valid_value(whatever, valid_values=["one", "two"]):
220    func_regex = ".*" + called_func_name + "[ ]*\("
221    # if not re.match(r".*" + called_func_name, composite_line):
222    if not re.match(func_regex, composite_line):
223        # The called function name was not found in the composite line.  The
224        # caller may be using a function alias.
225        # I added code to handle pvar, qpvar, dpvar, etc. aliases.
226        # pvar is an alias for print_var.  However, when it is used,
227        # sprint_func_name() returns the non-alias version, i.e. "print_var".
228        # Adjusting for that here.
229        alias = re.sub("print_var", "pvar", called_func_name)
230        if local_debug:
231            print_varx("alias", alias, 0, debug_indent)
232        called_func_name = alias
233        func_regex = ".*" + called_func_name + "[ ]*\("
234
235    # arg_list_etc = re.sub(".*" + called_func_name, "", composite_line)
236    arg_list_etc = "(" + re.sub(func_regex, "", composite_line)
237    if local_debug:
238        print_varx("func_regex", func_regex, 0, debug_indent)
239        print_varx("called_func_name", called_func_name, 0, debug_indent)
240        print_varx("composite_line", composite_line, 0, debug_indent)
241        print_varx("arg_list_etc", arg_list_etc, 0, debug_indent)
242
243    # Parse arg list...
244    # Initialize...
245    nest_level = -1
246    arg_ix = 0
247    args_list = [""]
248    for ix in range(0, len(arg_list_etc)):
249        char = arg_list_etc[ix]
250        # Set the nest_level based on whether we've encounted a parenthesis.
251        if char == "(":
252            nest_level += 1
253            if nest_level == 0:
254                continue
255        elif char == ")":
256            nest_level -= 1
257            if nest_level < 0:
258                break
259
260        # If we reach a comma at base nest level, we are done processing an
261        # argument so we increment arg_ix and initialize a new args_list entry.
262        if char == "," and nest_level == 0:
263            arg_ix += 1
264            args_list.append("")
265            continue
266
267        # For any other character, we append it it to the current arg list
268        # entry.
269        args_list[arg_ix] += char
270
271    # Trim whitespace from each list entry.
272    args_list = [arg.strip() for arg in args_list]
273
274    if arg_num > len(args_list):
275        print_error("Programmer error - The caller has asked for the name of" +
276                    " argument number \"" + str(arg_num) + "\" but there " +
277                    "were only \"" + str(len(args_list)) + "\" args used:\n" +
278                    sprint_varx("args_list", args_list))
279        return
280
281    argument = args_list[arg_num - 1]
282
283    if local_debug:
284        print_varx("args_list", args_list, 0, debug_indent)
285        print_varx("argument", argument, 0, debug_indent)
286
287    return argument
288
289###############################################################################
290
291
292###############################################################################
293def sprint_time(buffer=""):
294
295    r"""
296    Return the time in the following format.
297
298    Example:
299
300    The following python code...
301
302    sys.stdout.write(sprint_time())
303    sys.stdout.write("Hi.\n")
304
305    Will result in the following type of output:
306
307    #(CDT) 2016/07/08 15:25:35 - Hi.
308
309    Example:
310
311    The following python code...
312
313    sys.stdout.write(sprint_time("Hi.\n"))
314
315    Will result in the following type of output:
316
317    #(CDT) 2016/08/03 17:12:05 - Hi.
318
319    The following environment variables will affect the formatting as
320    described:
321    NANOSECONDS                     This will cause the time stamps to be
322                                    precise to the microsecond (Yes, it
323                                    probably should have been named
324                                    MICROSECONDS but the convention was set
325                                    long ago so we're sticking with it).
326                                    Example of the output when environment
327                                    variable NANOSECONDS=1.
328
329    #(CDT) 2016/08/03 17:16:25.510469 - Hi.
330
331    SHOW_ELAPSED_TIME               This will cause the elapsed time to be
332                                    included in the output.  This is the
333                                    amount of time that has elapsed since the
334                                    last time this function was called.  The
335                                    precision of the elapsed time field is
336                                    also affected by the value of the
337                                    NANOSECONDS environment variable.  Example
338                                    of the output when environment variable
339                                    NANOSECONDS=0 and SHOW_ELAPSED_TIME=1.
340
341    #(CDT) 2016/08/03 17:17:40 -    0 - Hi.
342
343    Example of the output when environment variable NANOSECONDS=1 and
344    SHOW_ELAPSED_TIME=1.
345
346    #(CDT) 2016/08/03 17:18:47.317339 -    0.000046 - Hi.
347
348    Description of arguments.
349    buffer                          This will be appended to the formatted
350                                    time string.
351    """
352
353    global NANOSECONDS
354    global SHOW_ELAPSED_TIME
355    global sprint_time_last_seconds
356
357    seconds = time.time()
358    loc_time = time.localtime(seconds)
359    nanoseconds = "%0.6f" % seconds
360    pos = nanoseconds.find(".")
361    nanoseconds = nanoseconds[pos:]
362
363    time_string = time.strftime("#(%Z) %Y/%m/%d %H:%M:%S", loc_time)
364    if NANOSECONDS == "1":
365        time_string = time_string + nanoseconds
366
367    if SHOW_ELAPSED_TIME == "1":
368        cur_time_seconds = seconds
369        math_string = "%9.9f" % cur_time_seconds + " - " + "%9.9f" % \
370            sprint_time_last_seconds
371        elapsed_seconds = eval(math_string)
372        if NANOSECONDS == "1":
373            elapsed_seconds = "%11.6f" % elapsed_seconds
374        else:
375            elapsed_seconds = "%4i" % elapsed_seconds
376        sprint_time_last_seconds = cur_time_seconds
377        time_string = time_string + " - " + elapsed_seconds
378
379    return time_string + " - " + buffer
380
381###############################################################################
382
383
384###############################################################################
385def sprint_timen(buffer=""):
386
387    r"""
388    Append a line feed to the buffer, pass it to sprint_time and return the
389    result.
390    """
391
392    return sprint_time(buffer + "\n")
393
394###############################################################################
395
396
397###############################################################################
398def sprint_error(buffer=""):
399
400    r"""
401    Return a standardized error string.  This includes:
402      - A time stamp
403      - The "**ERROR**" string
404      - The caller's buffer string.
405
406    Example:
407
408    The following python code...
409
410    print(sprint_error("Oops.\n"))
411
412    Will result in the following type of output:
413
414    #(CDT) 2016/08/03 17:12:05 - **ERROR** Oops.
415
416    Description of arguments.
417    buffer                          This will be appended to the formatted
418                                    error string.
419    """
420
421    return sprint_time() + "**ERROR** " + buffer
422
423###############################################################################
424
425
426###############################################################################
427def sprint_varx(var_name,
428                var_value,
429                hex=0,
430                loc_col1_indent=col1_indent,
431                loc_col1_width=col1_width,
432                trailing_char="\n"):
433
434    r"""
435    Print the var name/value passed to it.  If the caller lets loc_col1_width
436    default, the printing lines up nicely with output generated by the
437    print_time functions.
438
439    Note that the sprint_var function (defined below) can be used to call this
440    function so that the programmer does not need to pass the var_name.
441    sprint_var will figure out the var_name.  The sprint_var function is the
442    one that would normally be used by the general user.
443
444    For example, the following python code:
445
446    first_name = "Mike"
447    print_time("Doing this...\n")
448    print_varx("first_name", first_name)
449    print_time("Doing that...\n")
450
451    Will generate output like this:
452
453    #(CDT) 2016/08/10 17:34:42.847374 -    0.001285 - Doing this...
454    first_name:                                       Mike
455    #(CDT) 2016/08/10 17:34:42.847510 -    0.000136 - Doing that...
456
457    This function recognizes several complex types of data such as dict, list
458    or tuple.
459
460    For example, the following python code:
461
462    my_dict = dict(one=1, two=2, three=3)
463    print_var(my_dict)
464
465    Will generate the following output:
466
467    my_dict:
468      my_dict[three]:                                 3
469      my_dict[two]:                                   2
470      my_dict[one]:                                   1
471
472    Description of arguments.
473    var_name                        The name of the variable to be printed.
474    var_value                       The value of the variable to be printed.
475    hex                             This indicates that the value should be
476                                    printed in hex format.  It is the user's
477                                    responsibility to ensure that a var_value
478                                    contains a valid hex number.  For string
479                                    var_values, this will be interpreted as
480                                    show_blanks which means that blank values
481                                    will be printed as "<blank>".
482    loc_col1_indent                 The number of spaces to indent the output.
483    loc_col1_width                  The width of the output column containing
484                                    the variable name.  The default value of
485                                    this is adjusted so that the var_value
486                                    lines up with text printed via the
487                                    print_time function.
488    trailing_char                   The character to be used at the end of the
489                                    returned string.  The default value is a
490                                    line feed.
491    """
492
493    # Determine the type
494    if type(var_value) in (int, float, bool, str, unicode) \
495       or var_value is None:
496        # The data type is simple in the sense that it has no subordinate
497        # parts.
498        # Adjust loc_col1_width.
499        loc_col1_width = loc_col1_width - loc_col1_indent
500        # See if the user wants the output in hex format.
501        if hex:
502            if type(var_value) not in (int, long):
503                value_format = "%s"
504                if var_value is "":
505                    var_value = "<blank>"
506            else:
507                value_format = "0x%08x"
508        else:
509            value_format = "%s"
510        format_string = "%" + str(loc_col1_indent) + "s%-" \
511            + str(loc_col1_width) + "s" + value_format + trailing_char
512        return format_string % ("", var_name + ":", var_value)
513    else:
514        # The data type is complex in the sense that it has subordinate parts.
515        format_string = "%" + str(loc_col1_indent) + "s%s\n"
516        buffer = format_string % ("", var_name + ":")
517        loc_col1_indent += 2
518        try:
519            length = len(var_value)
520        except TypeError:
521            pass
522        ix = 0
523        loc_trailing_char = "\n"
524        type_is_dict = 0
525        try:
526            if type(var_value) in (dict, collections.OrderedDict):
527                type_is_dict = 1
528        except AttributeError:
529            try:
530                if type(var_value) is DotDict:
531                    type_is_dict = 1
532            except NameError:
533                pass
534        if type_is_dict:
535            for key, value in var_value.iteritems():
536                ix += 1
537                if ix == length:
538                    loc_trailing_char = trailing_char
539                buffer += sprint_varx(var_name + "[" + key + "]", value, hex,
540                                      loc_col1_indent, loc_col1_width,
541                                      loc_trailing_char)
542        elif type(var_value) in (list, tuple, set):
543            for key, value in enumerate(var_value):
544                ix += 1
545                if ix == length:
546                    loc_trailing_char = trailing_char
547                buffer += sprint_varx(var_name + "[" + str(key) + "]", value,
548                                      hex, loc_col1_indent, loc_col1_width,
549                                      loc_trailing_char)
550        elif type(var_value) is argparse.Namespace:
551            for key in var_value.__dict__:
552                ix += 1
553                if ix == length:
554                    loc_trailing_char = trailing_char
555                cmd_buf = "buffer += sprint_varx(var_name + \".\" + str(key)" \
556                          + ", var_value." + key + ", hex, loc_col1_indent," \
557                          + " loc_col1_width, loc_trailing_char)"
558                exec(cmd_buf)
559        else:
560            var_type = type(var_value).__name__
561            func_name = sys._getframe().f_code.co_name
562            var_value = "<" + var_type + " type not supported by " + \
563                        func_name + "()>"
564            value_format = "%s"
565            loc_col1_indent -= 2
566            # Adjust loc_col1_width.
567            loc_col1_width = loc_col1_width - loc_col1_indent
568            format_string = "%" + str(loc_col1_indent) + "s%-" \
569                + str(loc_col1_width) + "s" + value_format + trailing_char
570            return format_string % ("", var_name + ":", var_value)
571        return buffer
572
573    return ""
574
575###############################################################################
576
577
578###############################################################################
579def sprint_var(*args):
580
581    r"""
582    Figure out the name of the first argument for you and then call
583    sprint_varx with it.  Therefore, the following 2 calls are equivalent:
584    sprint_varx("var1", var1)
585    sprint_var(var1)
586    """
587
588    # Get the name of the first variable passed to this function.
589    stack_frame = 2
590    caller_func_name = sprint_func_name(2)
591    if caller_func_name.endswith("print_var"):
592        stack_frame += 1
593    var_name = get_arg_name(None, 1, stack_frame)
594    return sprint_varx(var_name, *args)
595
596###############################################################################
597
598
599###############################################################################
600def sprint_vars(*args):
601
602    r"""
603    Sprint the values of one or more variables.
604
605    Description of args:
606    args:
607        If the first argument is an integer, it will be interpreted to be the
608        "indent" value.
609        If the second argument is an integer, it will be interpreted to be the
610        "col1_width" value.
611        If the third argument is an integer, it will be interpreted to be the
612        "hex" value.
613        All remaining parms are considered variable names which are to be
614        sprinted.
615    """
616
617    if len(args) == 0:
618        return
619
620    # Get the name of the first variable passed to this function.
621    stack_frame = 2
622    caller_func_name = sprint_func_name(2)
623    if caller_func_name.endswith("print_vars"):
624        stack_frame += 1
625
626    parm_num = 1
627
628    # Create list from args (which is a tuple) so that it can be modified.
629    args_list = list(args)
630
631    var_name = get_arg_name(None, parm_num, stack_frame)
632    # See if parm 1 is to be interpreted as "indent".
633    try:
634        if type(int(var_name)) is int:
635            indent = int(var_name)
636            args_list.pop(0)
637            parm_num += 1
638    except ValueError:
639        indent = 0
640
641    var_name = get_arg_name(None, parm_num, stack_frame)
642    # See if parm 1 is to be interpreted as "col1_width".
643    try:
644        if type(int(var_name)) is int:
645            loc_col1_width = int(var_name)
646            args_list.pop(0)
647            parm_num += 1
648    except ValueError:
649        loc_col1_width = col1_width
650
651    var_name = get_arg_name(None, parm_num, stack_frame)
652    # See if parm 1 is to be interpreted as "hex".
653    try:
654        if type(int(var_name)) is int:
655            hex = int(var_name)
656            args_list.pop(0)
657            parm_num += 1
658    except ValueError:
659        hex = 0
660
661    buffer = ""
662    for var_value in args_list:
663        var_name = get_arg_name(None, parm_num, stack_frame)
664        buffer += sprint_varx(var_name, var_value, hex, indent, loc_col1_width)
665        parm_num += 1
666
667    return buffer
668
669###############################################################################
670
671
672###############################################################################
673def lprint_varx(var_name,
674                var_value,
675                hex=0,
676                loc_col1_indent=col1_indent,
677                loc_col1_width=col1_width,
678                log_level=getattr(logging, 'INFO')):
679
680    r"""
681    Send sprint_varx output to logging.
682    """
683
684    logging.log(log_level, sprint_varx(var_name, var_value, hex,
685                loc_col1_indent, loc_col1_width, ""))
686
687###############################################################################
688
689
690###############################################################################
691def lprint_var(*args):
692
693    r"""
694    Figure out the name of the first argument for you and then call
695    lprint_varx with it.  Therefore, the following 2 calls are equivalent:
696    lprint_varx("var1", var1)
697    lprint_var(var1)
698    """
699
700    # Get the name of the first variable passed to this function.
701    stack_frame = 2
702    caller_func_name = sprint_func_name(2)
703    if caller_func_name.endswith("print_var"):
704        stack_frame += 1
705    var_name = get_arg_name(None, 1, stack_frame)
706    lprint_varx(var_name, *args)
707
708###############################################################################
709
710
711###############################################################################
712def sprint_dashes(indent=col1_indent,
713                  width=80,
714                  line_feed=1,
715                  char="-"):
716
717    r"""
718    Return a string of dashes to the caller.
719
720    Description of arguements:
721    indent                          The number of characters to indent the
722                                    output.
723    width                           The width of the string of dashes.
724    line_feed                       Indicates whether the output should end
725                                    with a line feed.
726    char                            The character to be repeated in the output
727                                    string.
728    """
729
730    width = int(width)
731    buffer = " "*int(indent) + char*width
732    if line_feed:
733        buffer += "\n"
734
735    return buffer
736
737###############################################################################
738
739
740###############################################################################
741def sindent(text="",
742            indent=0):
743
744    r"""
745    Pre-pend the specified number of characters to the text string (i.e.
746    indent it) and return it.
747
748    Description of arguments:
749    text                            The string to be indented.
750    indent                          The number of characters to indent the
751                                    string.
752    """
753
754    format_string = "%" + str(indent) + "s%s"
755    buffer = format_string % ("", text)
756
757    return buffer
758
759###############################################################################
760
761
762###############################################################################
763def sprint_call_stack(indent=0,
764                      stack_frame_ix=0):
765
766    r"""
767    Return a call stack report for the given point in the program with line
768    numbers, function names and function parameters and arguments.
769
770    Sample output:
771
772    -------------------------------------------------------------------------
773    Python function call stack
774
775    Line # Function name and arguments
776    ------ ------------------------------------------------------------------
777       424 sprint_call_stack ()
778         4 print_call_stack ()
779        31 func1 (last_name = 'walsh', first_name = 'mikey')
780        59 /tmp/scr5.py
781    -------------------------------------------------------------------------
782
783    Description of arguments:
784    indent                          The number of characters to indent each
785                                    line of output.
786    stack_frame_ix                  The index of the first stack frame which
787                                    is to be returned.
788    """
789
790    buffer = ""
791    buffer += sprint_dashes(indent)
792    buffer += sindent("Python function call stack\n\n", indent)
793    buffer += sindent("Line # Function name and arguments\n", indent)
794    buffer += sprint_dashes(indent, 6, 0) + " " + sprint_dashes(0, 73)
795
796    # Grab the current program stack.
797    current_stack = inspect.stack()
798
799    # Process each frame in turn.
800    format_string = "%6s %s\n"
801    ix = 0
802    for stack_frame in current_stack:
803        if ix < stack_frame_ix:
804            ix += 1
805            continue
806        lineno = str(stack_frame[2])
807        func_name = str(stack_frame[3])
808        if func_name == "?":
809            # "?" is the name used when code is not in a function.
810            func_name = "(none)"
811
812        if func_name == "<module>":
813            # If the func_name is the "main" program, we simply get the
814            # command line call string.
815            func_and_args = ' '.join(sys.argv)
816        else:
817            # Get the program arguments.
818            arg_vals = inspect.getargvalues(stack_frame[0])
819            function_parms = arg_vals[0]
820            frame_locals = arg_vals[3]
821
822            args_list = []
823            for arg_name in function_parms:
824                # Get the arg value from frame locals.
825                arg_value = frame_locals[arg_name]
826                args_list.append(arg_name + " = " + repr(arg_value))
827            args_str = "(" + ', '.join(map(str, args_list)) + ")"
828
829            # Now we need to print this in a nicely-wrapped way.
830            func_and_args = func_name + " " + args_str
831
832        buffer += sindent(format_string % (lineno, func_and_args), indent)
833        ix += 1
834
835    buffer += sprint_dashes(indent)
836
837    return buffer
838
839###############################################################################
840
841
842###############################################################################
843def sprint_executing(stack_frame_ix=None):
844
845    r"""
846    Print a line indicating what function is executing and with what parameter
847    values.  This is useful for debugging.
848
849    Sample output:
850
851    #(CDT) 2016/08/25 17:54:27 - Executing: func1 (x = 1)
852
853    Description of arguments:
854    stack_frame_ix                  The index of the stack frame whose
855                                    function info should be returned.  If the
856                                    caller does not specifiy a value, this
857                                    function will set the value to 1 which is
858                                    the index of the caller's stack frame.  If
859                                    the caller is the wrapper function
860                                    "print_executing", this function will bump
861                                    it up by 1.
862    """
863
864    # If user wants default stack_frame_ix.
865    if stack_frame_ix is None:
866        func_name = sys._getframe().f_code.co_name
867        caller_func_name = sys._getframe(1).f_code.co_name
868        if caller_func_name.endswith(func_name[1:]):
869            stack_frame_ix = 2
870        else:
871            stack_frame_ix = 1
872
873    stack_frame = inspect.stack()[stack_frame_ix]
874
875    func_name = str(stack_frame[3])
876    if func_name == "?":
877        # "?" is the name used when code is not in a function.
878        func_name = "(none)"
879
880    if func_name == "<module>":
881        # If the func_name is the "main" program, we simply get the command
882        # line call string.
883        func_and_args = ' '.join(sys.argv)
884    else:
885        # Get the program arguments.
886        arg_vals = inspect.getargvalues(stack_frame[0])
887        function_parms = arg_vals[0]
888        frame_locals = arg_vals[3]
889
890        args_list = []
891        for arg_name in function_parms:
892            # Get the arg value from frame locals.
893            arg_value = frame_locals[arg_name]
894            args_list.append(arg_name + " = " + repr(arg_value))
895        args_str = "(" + ', '.join(map(str, args_list)) + ")"
896
897        # Now we need to print this in a nicely-wrapped way.
898        func_and_args = func_name + " " + args_str
899
900    return sprint_time() + "Executing: " + func_and_args + "\n"
901
902###############################################################################
903
904
905###############################################################################
906def sprint_pgm_header(indent=0,
907                      linefeed=1):
908
909    r"""
910    Return a standardized header that programs should print at the beginning
911    of the run.  It includes useful information like command line, pid,
912    userid, program parameters, etc.
913
914    Description of arguments:
915    indent                          The number of characters to indent each
916                                    line of output.
917    linefeed                        Indicates whether a line feed be included
918                                    at the beginning and end of the report.
919    """
920
921    loc_col1_width = col1_width + indent
922
923    buffer = ""
924    if linefeed:
925        buffer = "\n"
926
927    buffer += sindent(sprint_time() + "Running " + pgm_name + ".\n", indent)
928    buffer += sindent(sprint_time() + "Program parameter values, etc.:\n\n",
929                      indent)
930    buffer += sprint_varx("command_line", ' '.join(sys.argv), 0, indent,
931                          loc_col1_width)
932    # We want the output to show a customized name for the pid and pgid but
933    # we want it to look like a valid variable name.  Therefore, we'll use
934    # pgm_name_var_name which was set when this module was imported.
935    buffer += sprint_varx(pgm_name_var_name + "_pid", os.getpid(), 0, indent,
936                          loc_col1_width)
937    buffer += sprint_varx(pgm_name_var_name + "_pgid", os.getpgrp(), 0, indent,
938                          loc_col1_width)
939    buffer += sprint_varx("uid", str(os.geteuid()) + " (" + os.getlogin() +
940                          ")", 0, indent, loc_col1_width)
941    buffer += sprint_varx("gid", str(os.getgid()) + " (" +
942                          str(grp.getgrgid(os.getgid()).gr_name) + ")", 0,
943                          indent, loc_col1_width)
944    buffer += sprint_varx("host_name", socket.gethostname(), 0, indent,
945                          loc_col1_width)
946    buffer += sprint_varx("DISPLAY", os.environ['DISPLAY'], 0, indent,
947                          loc_col1_width)
948    # I want to add code to print caller's parms.
949
950    # __builtin__.arg_obj is created by the get_arg module function,
951    # gen_get_options.
952    try:
953        buffer += ga.sprint_args(__builtin__.arg_obj, indent)
954    except AttributeError:
955        pass
956
957    if linefeed:
958        buffer += "\n"
959
960    return buffer
961
962###############################################################################
963
964
965###############################################################################
966def sprint_error_report(error_text="\n",
967                        indent=2):
968
969    r"""
970    Return a string with a standardized report which includes the caller's
971    error text, the call stack and the program header.
972
973    Description of args:
974    error_text                      The error text to be included in the
975                                    report.  The caller should include any
976                                    needed linefeeds.
977    indent                          The number of characters to indent each
978                                    line of output.
979    """
980
981    buffer = ""
982    buffer += sprint_dashes(width=120, char="=")
983    buffer += sprint_error(error_text)
984    buffer += "\n"
985    # Calling sprint_call_stack with stack_frame_ix of 0 causes it to show
986    # itself and this function in the call stack.  This is not helpful to a
987    # debugger and is therefore clutter.  We will adjust the stack_frame_ix to
988    # hide that information.
989    stack_frame_ix = 2
990    caller_func_name = sprint_func_name(2)
991    if caller_func_name.endswith("print_error_report"):
992        stack_frame_ix += 1
993    buffer += sprint_call_stack(indent, stack_frame_ix)
994    buffer += sprint_pgm_header(indent)
995    buffer += sprint_dashes(width=120, char="=")
996
997    return buffer
998
999###############################################################################
1000
1001
1002###############################################################################
1003def sprint_issuing(cmd_buf,
1004                   test_mode=0):
1005
1006    r"""
1007    Return a line indicating a command that the program is about to execute.
1008
1009    Sample output for a cmd_buf of "ls"
1010
1011    #(CDT) 2016/08/25 17:57:36 - Issuing: ls
1012
1013    Description of args:
1014    cmd_buf                         The command to be executed by caller.
1015    test_mode                       With test_mode set, your output will look
1016                                    like this:
1017
1018    #(CDT) 2016/08/25 17:57:36 - (test_mode) Issuing: ls
1019
1020    """
1021
1022    buffer = sprint_time()
1023    if test_mode:
1024        buffer += "(test_mode) "
1025    buffer += "Issuing: " + cmd_buf + "\n"
1026
1027    return buffer
1028
1029###############################################################################
1030
1031
1032###############################################################################
1033def sprint_pgm_footer():
1034
1035    r"""
1036    Return a standardized footer that programs should print at the end of the
1037    program run.  It includes useful information like total run time, etc.
1038    """
1039
1040    buffer = "\n" + sprint_time() + "Finished running " + pgm_name + ".\n\n"
1041
1042    total_time = time.time() - start_time
1043    total_time_string = "%0.6f" % total_time
1044
1045    buffer += sprint_varx(pgm_name_var_name + "_runtime", total_time_string)
1046    buffer += "\n"
1047
1048    return buffer
1049
1050###############################################################################
1051
1052
1053###############################################################################
1054def sprint(buffer=""):
1055
1056    r"""
1057    Simply return the user's buffer.  This function is used by the qprint and
1058    dprint functions defined dynamically below, i.e. it would not normally be
1059    called for general use.
1060
1061    Description of arguments.
1062    buffer                          This will be returned to the caller.
1063    """
1064
1065    return str(buffer)
1066
1067###############################################################################
1068
1069
1070###############################################################################
1071def sprintn(buffer=""):
1072
1073    r"""
1074    Simply return the user's buffer with a line feed.  This function is used
1075    by the qprint and dprint functions defined dynamically below, i.e. it
1076    would not normally be called for general use.
1077
1078    Description of arguments.
1079    buffer                          This will be returned to the caller.
1080    """
1081
1082    buffer = str(buffer) + "\n"
1083
1084    return buffer
1085
1086###############################################################################
1087
1088
1089###############################################################################
1090# In the following section of code, we will dynamically create print versions
1091# for each of the sprint functions defined above.  So, for example, where we
1092# have an sprint_time() function defined above that returns the time to the
1093# caller in a string, we will create a corresponding print_time() function
1094# that will print that string directly to stdout.
1095
1096# It can be complicated to follow what's being creaed by the exec statement
1097# below.  Here is an example of the print_time() function that will be created:
1098
1099# def print_time(*args):
1100#   s_funcname = "s" + sys._getframe().f_code.co_name
1101#   s_func = getattr(sys.modules[__name__], s_funcname)
1102#   sys.stdout.write(s_func(*args))
1103
1104# Here are comments describing the 3 lines in the body of the created function.
1105# Calculate the "s" version of this function name (e.g. if this function name
1106# is print_time, we want s_funcname to be "sprint_time".
1107# Put a reference to the "s" version of this function in s_func.
1108# Call the "s" version of this function passing it all of our arguments.
1109# Write the result to stdout.
1110
1111# func_names contains a list of all print functions which should be created
1112# from their sprint counterparts.
1113func_names = ['print_time', 'print_timen', 'print_error', 'print_varx',
1114              'print_var', 'print_vars', 'print_dashes', 'indent',
1115              'print_call_stack', 'print_func_name', 'print_executing',
1116              'print_pgm_header', 'print_issuing', 'print_pgm_footer',
1117              'print_error_report', 'print', 'printn']
1118
1119for func_name in func_names:
1120    if func_name == "print":
1121        continue
1122    # Create abbreviated aliases (e.g. spvar is an alias for sprint_var).
1123    alias = re.sub("print_", "p", func_name)
1124    pgm_definition_string = "s" + alias + " = s" + func_name
1125    if gen_print_debug:
1126        print(pgm_definition_string)
1127    exec(pgm_definition_string)
1128
1129for func_name in func_names:
1130    if func_name == "print_error" or func_name == "print_error_report":
1131        output_stream = "stderr"
1132    else:
1133        output_stream = "stdout"
1134    func_def = \
1135        [
1136            "def " + func_name + "(*args):",
1137            "  s_func_name = \"s\" + sys._getframe().f_code.co_name",
1138            "  s_func = getattr(sys.modules[__name__], s_func_name)",
1139            "  sys." + output_stream + ".write(s_func(*args))",
1140            "  sys." + output_stream + ".flush()"
1141        ]
1142    if func_name != "print":
1143        pgm_definition_string = '\n'.join(func_def)
1144        if gen_print_debug:
1145            print(pgm_definition_string)
1146        exec(pgm_definition_string)
1147
1148    # Now define "q" versions of each print function.
1149    func_def = \
1150        [
1151            "def q" + func_name + "(*args):",
1152            "  if __builtin__.quiet: return",
1153            "  s_func_name = \"s" + func_name + "\"",
1154            "  s_func = getattr(sys.modules[__name__], s_func_name)",
1155            "  sys." + output_stream + ".write(s_func(*args))",
1156            "  sys." + output_stream + ".flush()"
1157        ]
1158
1159    pgm_definition_string = '\n'.join(func_def)
1160    if gen_print_debug:
1161        print(pgm_definition_string)
1162    exec(pgm_definition_string)
1163
1164    # Now define "d" versions of each print function.
1165    func_def = \
1166        [
1167            "def d" + func_name + "(*args):",
1168            "  if not __builtin__.debug: return",
1169            "  s_func_name = \"s" + func_name + "\"",
1170            "  s_func = getattr(sys.modules[__name__], s_func_name)",
1171            "  sys." + output_stream + ".write(s_func(*args))",
1172            "  sys." + output_stream + ".flush()"
1173        ]
1174
1175    pgm_definition_string = '\n'.join(func_def)
1176    if gen_print_debug:
1177        print(pgm_definition_string)
1178    exec(pgm_definition_string)
1179
1180    # Now define "l" versions of each print function.
1181    func_def = \
1182        [
1183            "def l" + func_name + "(*args):",
1184            "  s_func_name = \"s" + func_name + "\"",
1185            "  s_func = getattr(sys.modules[__name__], s_func_name)",
1186            "  logging.log(getattr(logging, 'INFO'), s_func(*args))",
1187        ]
1188
1189    if func_name != "print_varx" and func_name != "print_var":
1190        pgm_definition_string = '\n'.join(func_def)
1191        if gen_print_debug:
1192            print(pgm_definition_string)
1193        exec(pgm_definition_string)
1194
1195    if func_name == "print":
1196        continue
1197
1198    # Create abbreviated aliases (e.g. pvar is an alias for print_var).
1199    alias = re.sub("print_", "p", func_name)
1200    pgm_definition_string = alias + " = " + func_name
1201    if gen_print_debug:
1202        print(pgm_definition_string)
1203    exec(pgm_definition_string)
1204
1205    # Create abbreviated aliases (e.g. qpvar is an alias for qprint_var).
1206    alias = re.sub("print_", "p", func_name)
1207    pgm_definition_string = "q" + alias + " = q" + func_name
1208    if gen_print_debug:
1209        print(pgm_definition_string)
1210    exec(pgm_definition_string)
1211
1212    # Create abbreviated aliases (e.g. dpvar is an alias for dprint_var).
1213    alias = re.sub("print_", "p", func_name)
1214    pgm_definition_string = "d" + alias + " = d" + func_name
1215    if gen_print_debug:
1216        print(pgm_definition_string)
1217    exec(pgm_definition_string)
1218
1219    # Create abbreviated aliases (e.g. lpvar is an alias for lprint_var).
1220    alias = re.sub("print_", "p", func_name)
1221    pgm_definition_string = "l" + alias + " = l" + func_name
1222    if gen_print_debug:
1223        print(pgm_definition_string)
1224    exec(pgm_definition_string)
1225
1226###############################################################################
1227