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