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 == "":
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            pass
530        if not type_is_dict:
531            try:
532                if type(var_value) is DotDict:
533                    type_is_dict = 1
534            except NameError:
535                pass
536        if type_is_dict:
537            for key, value in var_value.iteritems():
538                ix += 1
539                if ix == length:
540                    loc_trailing_char = trailing_char
541                buffer += sprint_varx(var_name + "[" + key + "]", value, hex,
542                                      loc_col1_indent, loc_col1_width,
543                                      loc_trailing_char)
544        elif type(var_value) in (list, tuple, set):
545            for key, value in enumerate(var_value):
546                ix += 1
547                if ix == length:
548                    loc_trailing_char = trailing_char
549                buffer += sprint_varx(var_name + "[" + str(key) + "]", value,
550                                      hex, loc_col1_indent, loc_col1_width,
551                                      loc_trailing_char)
552        elif type(var_value) is argparse.Namespace:
553            for key in var_value.__dict__:
554                ix += 1
555                if ix == length:
556                    loc_trailing_char = trailing_char
557                cmd_buf = "buffer += sprint_varx(var_name + \".\" + str(key)" \
558                          + ", var_value." + key + ", hex, loc_col1_indent," \
559                          + " loc_col1_width, loc_trailing_char)"
560                exec(cmd_buf)
561        else:
562            var_type = type(var_value).__name__
563            func_name = sys._getframe().f_code.co_name
564            var_value = "<" + var_type + " type not supported by " + \
565                        func_name + "()>"
566            value_format = "%s"
567            loc_col1_indent -= 2
568            # Adjust loc_col1_width.
569            loc_col1_width = loc_col1_width - loc_col1_indent
570            format_string = "%" + str(loc_col1_indent) + "s%-" \
571                + str(loc_col1_width) + "s" + value_format + trailing_char
572            return format_string % ("", var_name + ":", var_value)
573        return buffer
574
575    return ""
576
577###############################################################################
578
579
580###############################################################################
581def sprint_var(*args):
582
583    r"""
584    Figure out the name of the first argument for you and then call
585    sprint_varx with it.  Therefore, the following 2 calls are equivalent:
586    sprint_varx("var1", var1)
587    sprint_var(var1)
588    """
589
590    # Get the name of the first variable passed to this function.
591    stack_frame = 2
592    caller_func_name = sprint_func_name(2)
593    if caller_func_name.endswith("print_var"):
594        stack_frame += 1
595    var_name = get_arg_name(None, 1, stack_frame)
596    return sprint_varx(var_name, *args)
597
598###############################################################################
599
600
601###############################################################################
602def sprint_vars(*args):
603
604    r"""
605    Sprint the values of one or more variables.
606
607    Description of args:
608    args:
609        If the first argument is an integer, it will be interpreted to be the
610        "indent" value.
611        If the second argument is an integer, it will be interpreted to be the
612        "col1_width" value.
613        If the third argument is an integer, it will be interpreted to be the
614        "hex" value.
615        All remaining parms are considered variable names which are to be
616        sprinted.
617    """
618
619    if len(args) == 0:
620        return
621
622    # Get the name of the first variable passed to this function.
623    stack_frame = 2
624    caller_func_name = sprint_func_name(2)
625    if caller_func_name.endswith("print_vars"):
626        stack_frame += 1
627
628    parm_num = 1
629
630    # Create list from args (which is a tuple) so that it can be modified.
631    args_list = list(args)
632
633    var_name = get_arg_name(None, parm_num, stack_frame)
634    # See if parm 1 is to be interpreted as "indent".
635    try:
636        if type(int(var_name)) is int:
637            indent = int(var_name)
638            args_list.pop(0)
639            parm_num += 1
640    except ValueError:
641        indent = 0
642
643    var_name = get_arg_name(None, parm_num, stack_frame)
644    # See if parm 1 is to be interpreted as "col1_width".
645    try:
646        if type(int(var_name)) is int:
647            loc_col1_width = int(var_name)
648            args_list.pop(0)
649            parm_num += 1
650    except ValueError:
651        loc_col1_width = col1_width
652
653    var_name = get_arg_name(None, parm_num, stack_frame)
654    # See if parm 1 is to be interpreted as "hex".
655    try:
656        if type(int(var_name)) is int:
657            hex = int(var_name)
658            args_list.pop(0)
659            parm_num += 1
660    except ValueError:
661        hex = 0
662
663    buffer = ""
664    for var_value in args_list:
665        var_name = get_arg_name(None, parm_num, stack_frame)
666        buffer += sprint_varx(var_name, var_value, hex, indent, loc_col1_width)
667        parm_num += 1
668
669    return buffer
670
671###############################################################################
672
673
674###############################################################################
675def lprint_varx(var_name,
676                var_value,
677                hex=0,
678                loc_col1_indent=col1_indent,
679                loc_col1_width=col1_width,
680                log_level=getattr(logging, 'INFO')):
681
682    r"""
683    Send sprint_varx output to logging.
684    """
685
686    logging.log(log_level, sprint_varx(var_name, var_value, hex,
687                loc_col1_indent, loc_col1_width, ""))
688
689###############################################################################
690
691
692###############################################################################
693def lprint_var(*args):
694
695    r"""
696    Figure out the name of the first argument for you and then call
697    lprint_varx with it.  Therefore, the following 2 calls are equivalent:
698    lprint_varx("var1", var1)
699    lprint_var(var1)
700    """
701
702    # Get the name of the first variable passed to this function.
703    stack_frame = 2
704    caller_func_name = sprint_func_name(2)
705    if caller_func_name.endswith("print_var"):
706        stack_frame += 1
707    var_name = get_arg_name(None, 1, stack_frame)
708    lprint_varx(var_name, *args)
709
710###############################################################################
711
712
713###############################################################################
714def sprint_dashes(indent=col1_indent,
715                  width=80,
716                  line_feed=1,
717                  char="-"):
718
719    r"""
720    Return a string of dashes to the caller.
721
722    Description of arguements:
723    indent                          The number of characters to indent the
724                                    output.
725    width                           The width of the string of dashes.
726    line_feed                       Indicates whether the output should end
727                                    with a line feed.
728    char                            The character to be repeated in the output
729                                    string.
730    """
731
732    width = int(width)
733    buffer = " "*int(indent) + char*width
734    if line_feed:
735        buffer += "\n"
736
737    return buffer
738
739###############################################################################
740
741
742###############################################################################
743def sindent(text="",
744            indent=0):
745
746    r"""
747    Pre-pend the specified number of characters to the text string (i.e.
748    indent it) and return it.
749
750    Description of arguments:
751    text                            The string to be indented.
752    indent                          The number of characters to indent the
753                                    string.
754    """
755
756    format_string = "%" + str(indent) + "s%s"
757    buffer = format_string % ("", text)
758
759    return buffer
760
761###############################################################################
762
763
764###############################################################################
765def sprint_call_stack(indent=0,
766                      stack_frame_ix=0):
767
768    r"""
769    Return a call stack report for the given point in the program with line
770    numbers, function names and function parameters and arguments.
771
772    Sample output:
773
774    -------------------------------------------------------------------------
775    Python function call stack
776
777    Line # Function name and arguments
778    ------ ------------------------------------------------------------------
779       424 sprint_call_stack ()
780         4 print_call_stack ()
781        31 func1 (last_name = 'walsh', first_name = 'mikey')
782        59 /tmp/scr5.py
783    -------------------------------------------------------------------------
784
785    Description of arguments:
786    indent                          The number of characters to indent each
787                                    line of output.
788    stack_frame_ix                  The index of the first stack frame which
789                                    is to be returned.
790    """
791
792    buffer = ""
793    buffer += sprint_dashes(indent)
794    buffer += sindent("Python function call stack\n\n", indent)
795    buffer += sindent("Line # Function name and arguments\n", indent)
796    buffer += sprint_dashes(indent, 6, 0) + " " + sprint_dashes(0, 73)
797
798    # Grab the current program stack.
799    current_stack = inspect.stack()
800
801    # Process each frame in turn.
802    format_string = "%6s %s\n"
803    ix = 0
804    for stack_frame in current_stack:
805        if ix < stack_frame_ix:
806            ix += 1
807            continue
808        lineno = str(stack_frame[2])
809        func_name = str(stack_frame[3])
810        if func_name == "?":
811            # "?" is the name used when code is not in a function.
812            func_name = "(none)"
813
814        if func_name == "<module>":
815            # If the func_name is the "main" program, we simply get the
816            # command line call string.
817            func_and_args = ' '.join(sys.argv)
818        else:
819            # Get the program arguments.
820            arg_vals = inspect.getargvalues(stack_frame[0])
821            function_parms = arg_vals[0]
822            frame_locals = arg_vals[3]
823
824            args_list = []
825            for arg_name in function_parms:
826                # Get the arg value from frame locals.
827                arg_value = frame_locals[arg_name]
828                args_list.append(arg_name + " = " + repr(arg_value))
829            args_str = "(" + ', '.join(map(str, args_list)) + ")"
830
831            # Now we need to print this in a nicely-wrapped way.
832            func_and_args = func_name + " " + args_str
833
834        buffer += sindent(format_string % (lineno, func_and_args), indent)
835        ix += 1
836
837    buffer += sprint_dashes(indent)
838
839    return buffer
840
841###############################################################################
842
843
844###############################################################################
845def sprint_executing(stack_frame_ix=None):
846
847    r"""
848    Print a line indicating what function is executing and with what parameter
849    values.  This is useful for debugging.
850
851    Sample output:
852
853    #(CDT) 2016/08/25 17:54:27 - Executing: func1 (x = 1)
854
855    Description of arguments:
856    stack_frame_ix                  The index of the stack frame whose
857                                    function info should be returned.  If the
858                                    caller does not specifiy a value, this
859                                    function will set the value to 1 which is
860                                    the index of the caller's stack frame.  If
861                                    the caller is the wrapper function
862                                    "print_executing", this function will bump
863                                    it up by 1.
864    """
865
866    # If user wants default stack_frame_ix.
867    if stack_frame_ix is None:
868        func_name = sys._getframe().f_code.co_name
869        caller_func_name = sys._getframe(1).f_code.co_name
870        if caller_func_name.endswith(func_name[1:]):
871            stack_frame_ix = 2
872        else:
873            stack_frame_ix = 1
874
875    stack_frame = inspect.stack()[stack_frame_ix]
876
877    func_name = str(stack_frame[3])
878    if func_name == "?":
879        # "?" is the name used when code is not in a function.
880        func_name = "(none)"
881
882    if func_name == "<module>":
883        # If the func_name is the "main" program, we simply get the command
884        # line call string.
885        func_and_args = ' '.join(sys.argv)
886    else:
887        # Get the program arguments.
888        arg_vals = inspect.getargvalues(stack_frame[0])
889        function_parms = arg_vals[0]
890        frame_locals = arg_vals[3]
891
892        args_list = []
893        for arg_name in function_parms:
894            # Get the arg value from frame locals.
895            arg_value = frame_locals[arg_name]
896            args_list.append(arg_name + " = " + repr(arg_value))
897        args_str = "(" + ', '.join(map(str, args_list)) + ")"
898
899        # Now we need to print this in a nicely-wrapped way.
900        func_and_args = func_name + " " + args_str
901
902    return sprint_time() + "Executing: " + func_and_args + "\n"
903
904###############################################################################
905
906
907###############################################################################
908def sprint_pgm_header(indent=0,
909                      linefeed=1):
910
911    r"""
912    Return a standardized header that programs should print at the beginning
913    of the run.  It includes useful information like command line, pid,
914    userid, program parameters, etc.
915
916    Description of arguments:
917    indent                          The number of characters to indent each
918                                    line of output.
919    linefeed                        Indicates whether a line feed be included
920                                    at the beginning and end of the report.
921    """
922
923    loc_col1_width = col1_width + indent
924
925    buffer = ""
926    if linefeed:
927        buffer = "\n"
928
929    buffer += sindent(sprint_time() + "Running " + pgm_name + ".\n", indent)
930    buffer += sindent(sprint_time() + "Program parameter values, etc.:\n\n",
931                      indent)
932    buffer += sprint_varx("command_line", ' '.join(sys.argv), 0, indent,
933                          loc_col1_width)
934    # We want the output to show a customized name for the pid and pgid but
935    # we want it to look like a valid variable name.  Therefore, we'll use
936    # pgm_name_var_name which was set when this module was imported.
937    buffer += sprint_varx(pgm_name_var_name + "_pid", os.getpid(), 0, indent,
938                          loc_col1_width)
939    buffer += sprint_varx(pgm_name_var_name + "_pgid", os.getpgrp(), 0, indent,
940                          loc_col1_width)
941    userid_num = str(os.geteuid())
942    try:
943        username = os.getlogin()
944    except OSError:
945        if userid_num == "0":
946            username = "root"
947        else:
948            username = "?"
949    buffer += sprint_varx("uid", userid_num + " (" + username +
950                          ")", 0, indent, loc_col1_width)
951    buffer += sprint_varx("gid", str(os.getgid()) + " (" +
952                          str(grp.getgrgid(os.getgid()).gr_name) + ")", 0,
953                          indent, loc_col1_width)
954    buffer += sprint_varx("host_name", socket.gethostname(), 0, indent,
955                          loc_col1_width)
956    try:
957        DISPLAY = os.environ['DISPLAY']
958    except KeyError:
959        DISPLAY = ""
960    buffer += sprint_varx("DISPLAY", DISPLAY, 0, indent,
961                          loc_col1_width)
962    # I want to add code to print caller's parms.
963
964    # __builtin__.arg_obj is created by the get_arg module function,
965    # gen_get_options.
966    try:
967        buffer += ga.sprint_args(__builtin__.arg_obj, indent)
968    except AttributeError:
969        pass
970
971    if linefeed:
972        buffer += "\n"
973
974    return buffer
975
976###############################################################################
977
978
979###############################################################################
980def sprint_error_report(error_text="\n",
981                        indent=2):
982
983    r"""
984    Return a string with a standardized report which includes the caller's
985    error text, the call stack and the program header.
986
987    Description of args:
988    error_text                      The error text to be included in the
989                                    report.  The caller should include any
990                                    needed linefeeds.
991    indent                          The number of characters to indent each
992                                    line of output.
993    """
994
995    buffer = ""
996    buffer += sprint_dashes(width=120, char="=")
997    buffer += sprint_error(error_text)
998    buffer += "\n"
999    # Calling sprint_call_stack with stack_frame_ix of 0 causes it to show
1000    # itself and this function in the call stack.  This is not helpful to a
1001    # debugger and is therefore clutter.  We will adjust the stack_frame_ix to
1002    # hide that information.
1003    stack_frame_ix = 2
1004    caller_func_name = sprint_func_name(2)
1005    if caller_func_name.endswith("print_error_report"):
1006        stack_frame_ix += 1
1007    buffer += sprint_call_stack(indent, stack_frame_ix)
1008    buffer += sprint_pgm_header(indent)
1009    buffer += sprint_dashes(width=120, char="=")
1010
1011    return buffer
1012
1013###############################################################################
1014
1015
1016###############################################################################
1017def sprint_issuing(cmd_buf,
1018                   test_mode=0):
1019
1020    r"""
1021    Return a line indicating a command that the program is about to execute.
1022
1023    Sample output for a cmd_buf of "ls"
1024
1025    #(CDT) 2016/08/25 17:57:36 - Issuing: ls
1026
1027    Description of args:
1028    cmd_buf                         The command to be executed by caller.
1029    test_mode                       With test_mode set, your output will look
1030                                    like this:
1031
1032    #(CDT) 2016/08/25 17:57:36 - (test_mode) Issuing: ls
1033
1034    """
1035
1036    buffer = sprint_time()
1037    if test_mode:
1038        buffer += "(test_mode) "
1039    buffer += "Issuing: " + cmd_buf + "\n"
1040
1041    return buffer
1042
1043###############################################################################
1044
1045
1046###############################################################################
1047def sprint_pgm_footer():
1048
1049    r"""
1050    Return a standardized footer that programs should print at the end of the
1051    program run.  It includes useful information like total run time, etc.
1052    """
1053
1054    buffer = "\n" + sprint_time() + "Finished running " + pgm_name + ".\n\n"
1055
1056    total_time = time.time() - start_time
1057    total_time_string = "%0.6f" % total_time
1058
1059    buffer += sprint_varx(pgm_name_var_name + "_runtime", total_time_string)
1060    buffer += "\n"
1061
1062    return buffer
1063
1064###############################################################################
1065
1066
1067###############################################################################
1068def sprint(buffer=""):
1069
1070    r"""
1071    Simply return the user's buffer.  This function is used by the qprint and
1072    dprint functions defined dynamically below, i.e. it would not normally be
1073    called for general use.
1074
1075    Description of arguments.
1076    buffer                          This will be returned to the caller.
1077    """
1078
1079    return str(buffer)
1080
1081###############################################################################
1082
1083
1084###############################################################################
1085def sprintn(buffer=""):
1086
1087    r"""
1088    Simply return the user's buffer with a line feed.  This function is used
1089    by the qprint and dprint functions defined dynamically below, i.e. it
1090    would not normally be called for general use.
1091
1092    Description of arguments.
1093    buffer                          This will be returned to the caller.
1094    """
1095
1096    buffer = str(buffer) + "\n"
1097
1098    return buffer
1099
1100###############################################################################
1101
1102
1103###############################################################################
1104# In the following section of code, we will dynamically create print versions
1105# for each of the sprint functions defined above.  So, for example, where we
1106# have an sprint_time() function defined above that returns the time to the
1107# caller in a string, we will create a corresponding print_time() function
1108# that will print that string directly to stdout.
1109
1110# It can be complicated to follow what's being creaed by the exec statement
1111# below.  Here is an example of the print_time() function that will be created:
1112
1113# def print_time(*args):
1114#   s_funcname = "s" + sys._getframe().f_code.co_name
1115#   s_func = getattr(sys.modules[__name__], s_funcname)
1116#   sys.stdout.write(s_func(*args))
1117
1118# Here are comments describing the 3 lines in the body of the created function.
1119# Calculate the "s" version of this function name (e.g. if this function name
1120# is print_time, we want s_funcname to be "sprint_time".
1121# Put a reference to the "s" version of this function in s_func.
1122# Call the "s" version of this function passing it all of our arguments.
1123# Write the result to stdout.
1124
1125# func_names contains a list of all print functions which should be created
1126# from their sprint counterparts.
1127func_names = ['print_time', 'print_timen', 'print_error', 'print_varx',
1128              'print_var', 'print_vars', 'print_dashes', 'indent',
1129              'print_call_stack', 'print_func_name', 'print_executing',
1130              'print_pgm_header', 'print_issuing', 'print_pgm_footer',
1131              'print_error_report', 'print', 'printn']
1132
1133for func_name in func_names:
1134    if func_name == "print":
1135        continue
1136    # Create abbreviated aliases (e.g. spvar is an alias for sprint_var).
1137    alias = re.sub("print_", "p", func_name)
1138    pgm_definition_string = "s" + alias + " = s" + func_name
1139    if gen_print_debug:
1140        print(pgm_definition_string)
1141    exec(pgm_definition_string)
1142
1143for func_name in func_names:
1144    if func_name == "print_error" or func_name == "print_error_report":
1145        output_stream = "stderr"
1146    else:
1147        output_stream = "stdout"
1148    func_def = \
1149        [
1150            "def " + func_name + "(*args):",
1151            "  s_func_name = \"s\" + sys._getframe().f_code.co_name",
1152            "  s_func = getattr(sys.modules[__name__], s_func_name)",
1153            "  sys." + output_stream + ".write(s_func(*args))",
1154            "  sys." + output_stream + ".flush()"
1155        ]
1156    if func_name != "print":
1157        pgm_definition_string = '\n'.join(func_def)
1158        if gen_print_debug:
1159            print(pgm_definition_string)
1160        exec(pgm_definition_string)
1161
1162    # Now define "q" versions of each print function.
1163    func_def = \
1164        [
1165            "def q" + func_name + "(*args):",
1166            "  if __builtin__.quiet: return",
1167            "  s_func_name = \"s" + func_name + "\"",
1168            "  s_func = getattr(sys.modules[__name__], s_func_name)",
1169            "  sys." + output_stream + ".write(s_func(*args))",
1170            "  sys." + output_stream + ".flush()"
1171        ]
1172
1173    pgm_definition_string = '\n'.join(func_def)
1174    if gen_print_debug:
1175        print(pgm_definition_string)
1176    exec(pgm_definition_string)
1177
1178    # Now define "d" versions of each print function.
1179    func_def = \
1180        [
1181            "def d" + func_name + "(*args):",
1182            "  if not __builtin__.debug: return",
1183            "  s_func_name = \"s" + func_name + "\"",
1184            "  s_func = getattr(sys.modules[__name__], s_func_name)",
1185            "  sys." + output_stream + ".write(s_func(*args))",
1186            "  sys." + output_stream + ".flush()"
1187        ]
1188
1189    pgm_definition_string = '\n'.join(func_def)
1190    if gen_print_debug:
1191        print(pgm_definition_string)
1192    exec(pgm_definition_string)
1193
1194    # Now define "l" versions of each print function.
1195    func_def = \
1196        [
1197            "def l" + func_name + "(*args):",
1198            "  s_func_name = \"s" + func_name + "\"",
1199            "  s_func = getattr(sys.modules[__name__], s_func_name)",
1200            "  logging.log(getattr(logging, 'INFO'), s_func(*args))",
1201        ]
1202
1203    if func_name != "print_varx" and func_name != "print_var":
1204        pgm_definition_string = '\n'.join(func_def)
1205        if gen_print_debug:
1206            print(pgm_definition_string)
1207        exec(pgm_definition_string)
1208
1209    if func_name == "print":
1210        continue
1211
1212    # Create abbreviated aliases (e.g. pvar is an alias for print_var).
1213    alias = re.sub("print_", "p", func_name)
1214    pgm_definition_string = alias + " = " + func_name
1215    if gen_print_debug:
1216        print(pgm_definition_string)
1217    exec(pgm_definition_string)
1218
1219    # Create abbreviated aliases (e.g. qpvar is an alias for qprint_var).
1220    alias = re.sub("print_", "p", func_name)
1221    pgm_definition_string = "q" + alias + " = q" + func_name
1222    if gen_print_debug:
1223        print(pgm_definition_string)
1224    exec(pgm_definition_string)
1225
1226    # Create abbreviated aliases (e.g. dpvar is an alias for dprint_var).
1227    alias = re.sub("print_", "p", func_name)
1228    pgm_definition_string = "d" + alias + " = d" + func_name
1229    if gen_print_debug:
1230        print(pgm_definition_string)
1231    exec(pgm_definition_string)
1232
1233    # Create abbreviated aliases (e.g. lpvar is an alias for lprint_var).
1234    alias = re.sub("print_", "p", func_name)
1235    pgm_definition_string = "l" + alias + " = l" + func_name
1236    if gen_print_debug:
1237        print(pgm_definition_string)
1238    exec(pgm_definition_string)
1239
1240###############################################################################
1241