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