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