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 = 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    try:
783        int_types = (int, long)
784    except NameError:
785        int_types = (int,)
786    try:
787        string_types = (str, unicode)
788    except NameError:
789        string_types = (str,)
790    simple_types = int_types + string_types + (float, bool)
791    if type(var_value) in simple_types \
792       or var_value is None:
793        # The data type is simple in the sense that it has no subordinate
794        # parts.
795        # Adjust loc_col1_width.
796        loc_col1_width = loc_col1_width - loc_col1_indent
797        # See if the user wants the output in hex format.
798        if hex:
799            if type(var_value) not in int_types:
800                value_format = "%s"
801                if var_value == "":
802                    var_value = "<blank>"
803            else:
804                num_hex_digits = max(dft_num_hex_digits(),
805                                     get_req_num_hex_digits(var_value))
806                # Convert a negative number to its positive twos complement
807                # for proper printing.  For example, instead of printing -1 as
808                # "0x-000000000000001" it will be printed as
809                # "0xffffffffffffffff".
810                var_value = var_value & (2 ** (num_hex_digits * 4) - 1)
811                value_format = "0x%0" + str(num_hex_digits) + "x"
812        else:
813            value_format = "%s"
814        format_string = "%" + str(loc_col1_indent) + "s%-" \
815            + str(loc_col1_width) + "s" + value_format + trailing_char
816        if value_format == "0x%08x":
817            return format_string % ("", str(var_name) + ":",
818                                    var_value & 0xffffffff)
819        else:
820            return format_string % ("", str(var_name) + ":", var_value)
821    elif isinstance(var_value, type):
822        return sprint_varx(var_name, str(var_value).split("'")[1], hex,
823                           loc_col1_indent, loc_col1_width, trailing_char,
824                           key_list)
825    else:
826        # The data type is complex in the sense that it has subordinate parts.
827        format_string = "%" + str(loc_col1_indent) + "s%s\n"
828        buffer = format_string % ("", var_name + ":")
829        loc_col1_indent += 2
830        try:
831            length = len(var_value)
832        except TypeError:
833            length = 0
834        ix = 0
835        loc_trailing_char = "\n"
836        type_is_dict = 0
837        if isinstance(var_value, dict):
838            type_is_dict = 1
839        try:
840            if isinstance(var_value, collections.OrderedDict):
841                type_is_dict = 1
842        except AttributeError:
843            pass
844        try:
845            if isinstance(var_value, DotDict):
846                type_is_dict = 1
847        except NameError:
848            pass
849        try:
850            if isinstance(var_value, NormalizedDict):
851                type_is_dict = 1
852        except NameError:
853            pass
854        if type_is_dict:
855            for key, value in var_value.items():
856                if key_list is not None:
857                    key_list_regex = "^" + "|".join(key_list) + "$"
858                    if not re.match(key_list_regex, key):
859                        continue
860                ix += 1
861                if ix == length:
862                    loc_trailing_char = trailing_char
863                if hex:
864                    # Since hex is being used as a format type, we want it
865                    # turned off when processing integer dictionary values so
866                    # it is not interpreted as a hex indicator.
867                    loc_hex = not (isinstance(value, int))
868                    buffer += sprint_varx("[" + key + "]", value,
869                                          loc_hex, loc_col1_indent,
870                                          loc_col1_width,
871                                          loc_trailing_char,
872                                          key_list)
873                else:
874                    buffer += sprint_varx(var_name + "[" + str(key) + "]",
875                                          value, hex, loc_col1_indent,
876                                          loc_col1_width, loc_trailing_char,
877                                          key_list)
878        elif type(var_value) in (list, tuple, set):
879            for key, value in enumerate(var_value):
880                ix += 1
881                if ix == length:
882                    loc_trailing_char = trailing_char
883                buffer += sprint_varx(var_name + "[" + str(key) + "]", value,
884                                      hex, loc_col1_indent, loc_col1_width,
885                                      loc_trailing_char, key_list)
886        elif isinstance(var_value, argparse.Namespace):
887            for key in var_value.__dict__:
888                ix += 1
889                if ix == length:
890                    loc_trailing_char = trailing_char
891                cmd_buf = "buffer += sprint_varx(var_name + \".\" + str(key)" \
892                          + ", var_value." + key + ", hex, loc_col1_indent," \
893                          + " loc_col1_width, loc_trailing_char, key_list)"
894                exec(cmd_buf)
895        else:
896            var_type = type(var_value).__name__
897            func_name = sys._getframe().f_code.co_name
898            var_value = "<" + var_type + " type not supported by " + \
899                        func_name + "()>"
900            value_format = "%s"
901            loc_col1_indent -= 2
902            # Adjust loc_col1_width.
903            loc_col1_width = loc_col1_width - loc_col1_indent
904            format_string = "%" + str(loc_col1_indent) + "s%-" \
905                + str(loc_col1_width) + "s" + value_format + trailing_char
906            return format_string % ("", str(var_name) + ":", var_value)
907
908        return buffer
909
910    return ""
911
912
913def sprint_var(var_value,
914               hex=0,
915               loc_col1_indent=col1_indent,
916               loc_col1_width=col1_width,
917               trailing_char="\n",
918               key_list=None):
919    r"""
920    Figure out the name of the first argument for you and then call
921    sprint_varx with it.  Therefore, the following 2 calls are equivalent:
922    sprint_varx("var1", var1)
923    sprint_var(var1)
924    """
925
926    # Get the name of the first variable passed to this function.
927    stack_frame = 2
928    caller_func_name = sprint_func_name(2)
929    if caller_func_name.endswith("print_var"):
930        stack_frame += 1
931    var_name = get_arg_name(None, 1, stack_frame)
932    return sprint_varx(var_name, var_value=var_value, hex=hex,
933                       loc_col1_indent=loc_col1_indent,
934                       loc_col1_width=loc_col1_width,
935                       trailing_char=trailing_char,
936                       key_list=key_list)
937
938
939def sprint_vars(*args):
940    r"""
941    Sprint the values of one or more variables.
942
943    Description of args:
944    args:
945        If the first argument is an integer, it will be interpreted to be the
946        "indent" value.
947        If the second argument is an integer, it will be interpreted to be the
948        "col1_width" value.
949        If the third argument is an integer, it will be interpreted to be the
950        "hex" value.
951        All remaining parms are considered variable names which are to be
952        sprinted.
953    """
954
955    if len(args) == 0:
956        return
957
958    # Get the name of the first variable passed to this function.
959    stack_frame = 2
960    caller_func_name = sprint_func_name(2)
961    if caller_func_name.endswith("print_vars"):
962        stack_frame += 1
963
964    parm_num = 1
965
966    # Create list from args (which is a tuple) so that it can be modified.
967    args_list = list(args)
968
969    var_name = get_arg_name(None, parm_num, stack_frame)
970    # See if parm 1 is to be interpreted as "indent".
971    try:
972        if isinstance(int(var_name), int):
973            indent = int(var_name)
974            args_list.pop(0)
975            parm_num += 1
976    except ValueError:
977        indent = 0
978
979    var_name = get_arg_name(None, parm_num, stack_frame)
980    # See if parm 1 is to be interpreted as "col1_width".
981    try:
982        if isinstance(int(var_name), int):
983            loc_col1_width = int(var_name)
984            args_list.pop(0)
985            parm_num += 1
986    except ValueError:
987        loc_col1_width = col1_width
988
989    var_name = get_arg_name(None, parm_num, stack_frame)
990    # See if parm 1 is to be interpreted as "hex".
991    try:
992        if isinstance(int(var_name), int):
993            hex = int(var_name)
994            args_list.pop(0)
995            parm_num += 1
996    except ValueError:
997        hex = 0
998
999    buffer = ""
1000    for var_value in args_list:
1001        var_name = get_arg_name(None, parm_num, stack_frame)
1002        buffer += sprint_varx(var_name, var_value, hex, indent, loc_col1_width)
1003        parm_num += 1
1004
1005    return buffer
1006
1007
1008def sprint_dashes(indent=col1_indent,
1009                  width=80,
1010                  line_feed=1,
1011                  char="-"):
1012    r"""
1013    Return a string of dashes to the caller.
1014
1015    Description of arguments:
1016    indent                          The number of characters to indent the
1017                                    output.
1018    width                           The width of the string of dashes.
1019    line_feed                       Indicates whether the output should end
1020                                    with a line feed.
1021    char                            The character to be repeated in the output
1022                                    string.
1023    """
1024
1025    width = int(width)
1026    buffer = " " * int(indent) + char * width
1027    if line_feed:
1028        buffer += "\n"
1029
1030    return buffer
1031
1032
1033def sindent(text="",
1034            indent=0):
1035    r"""
1036    Pre-pend the specified number of characters to the text string (i.e.
1037    indent it) and return it.
1038
1039    Description of arguments:
1040    text                            The string to be indented.
1041    indent                          The number of characters to indent the
1042                                    string.
1043    """
1044
1045    format_string = "%" + str(indent) + "s%s"
1046    buffer = format_string % ("", text)
1047
1048    return buffer
1049
1050
1051def sprint_call_stack(indent=0,
1052                      stack_frame_ix=0):
1053    r"""
1054    Return a call stack report for the given point in the program with line
1055    numbers, function names and function parameters and arguments.
1056
1057    Sample output:
1058
1059    -------------------------------------------------------------------------
1060    Python function call stack
1061
1062    Line # Function name and arguments
1063    ------ ------------------------------------------------------------------
1064       424 sprint_call_stack ()
1065         4 print_call_stack ()
1066        31 func1 (last_name = 'walsh', first_name = 'mikey')
1067        59 /tmp/scr5.py
1068    -------------------------------------------------------------------------
1069
1070    Description of arguments:
1071    indent                          The number of characters to indent each
1072                                    line of output.
1073    stack_frame_ix                  The index of the first stack frame which
1074                                    is to be returned.
1075    """
1076
1077    buffer = ""
1078    buffer += sprint_dashes(indent)
1079    buffer += sindent("Python function call stack\n\n", indent)
1080    buffer += sindent("Line # Function name and arguments\n", indent)
1081    buffer += sprint_dashes(indent, 6, 0) + " " + sprint_dashes(0, 73)
1082
1083    # Grab the current program stack.
1084    current_stack = inspect.stack()
1085
1086    # Process each frame in turn.
1087    format_string = "%6s %s\n"
1088    ix = 0
1089    for stack_frame in current_stack:
1090        if ix < stack_frame_ix:
1091            ix += 1
1092            continue
1093        # I want the line number shown to be the line where you find the line
1094        # shown.
1095        try:
1096            line_num = str(current_stack[ix + 1][2])
1097        except IndexError:
1098            line_num = ""
1099        func_name = str(stack_frame[3])
1100        if func_name == "?":
1101            # "?" is the name used when code is not in a function.
1102            func_name = "(none)"
1103
1104        if func_name == "<module>":
1105            # If the func_name is the "main" program, we simply get the
1106            # command line call string.
1107            func_and_args = ' '.join(sys.argv)
1108        else:
1109            # Get the program arguments.
1110            arg_vals = inspect.getargvalues(stack_frame[0])
1111            function_parms = arg_vals[0]
1112            frame_locals = arg_vals[3]
1113
1114            args_list = []
1115            for arg_name in function_parms:
1116                # Get the arg value from frame locals.
1117                arg_value = frame_locals[arg_name]
1118                args_list.append(arg_name + " = " + repr(arg_value))
1119            args_str = "(" + ', '.join(map(str, args_list)) + ")"
1120
1121            # Now we need to print this in a nicely-wrapped way.
1122            func_and_args = func_name + " " + args_str
1123
1124        buffer += sindent(format_string % (line_num, func_and_args), indent)
1125        ix += 1
1126
1127    buffer += sprint_dashes(indent)
1128
1129    return buffer
1130
1131
1132def sprint_executing(stack_frame_ix=None):
1133    r"""
1134    Print a line indicating what function is executing and with what parameter
1135    values.  This is useful for debugging.
1136
1137    Sample output:
1138
1139    #(CDT) 2016/08/25 17:54:27 - Executing: func1 (x = 1)
1140
1141    Description of arguments:
1142    stack_frame_ix                  The index of the stack frame whose
1143                                    function info should be returned.  If the
1144                                    caller does not specify a value, this
1145                                    function will set the value to 1 which is
1146                                    the index of the caller's stack frame.  If
1147                                    the caller is the wrapper function
1148                                    "print_executing", this function will bump
1149                                    it up by 1.
1150    """
1151
1152    # If user wants default stack_frame_ix.
1153    if stack_frame_ix is None:
1154        func_name = sys._getframe().f_code.co_name
1155        caller_func_name = sys._getframe(1).f_code.co_name
1156        if caller_func_name.endswith(func_name[1:]):
1157            stack_frame_ix = 2
1158        else:
1159            stack_frame_ix = 1
1160
1161    stack_frame = inspect.stack()[stack_frame_ix]
1162
1163    func_name = str(stack_frame[3])
1164    if func_name == "?":
1165        # "?" is the name used when code is not in a function.
1166        func_name = "(none)"
1167
1168    if func_name == "<module>":
1169        # If the func_name is the "main" program, we simply get the command
1170        # line call string.
1171        func_and_args = ' '.join(sys.argv)
1172    else:
1173        # Get the program arguments.
1174        arg_vals = inspect.getargvalues(stack_frame[0])
1175        function_parms = arg_vals[0]
1176        frame_locals = arg_vals[3]
1177
1178        args_list = []
1179        for arg_name in function_parms:
1180            # Get the arg value from frame locals.
1181            arg_value = frame_locals[arg_name]
1182            args_list.append(arg_name + " = " + repr(arg_value))
1183        args_str = "(" + ', '.join(map(str, args_list)) + ")"
1184
1185        # Now we need to print this in a nicely-wrapped way.
1186        func_and_args = func_name + " " + args_str
1187
1188    return sprint_time() + "Executing: " + func_and_args + "\n"
1189
1190
1191def sprint_pgm_header(indent=0,
1192                      linefeed=1):
1193    r"""
1194    Return a standardized header that programs should print at the beginning
1195    of the run.  It includes useful information like command line, pid,
1196    userid, program parameters, etc.
1197
1198    Description of arguments:
1199    indent                          The number of characters to indent each
1200                                    line of output.
1201    linefeed                        Indicates whether a line feed be included
1202                                    at the beginning and end of the report.
1203    """
1204
1205    loc_col1_width = col1_width + indent
1206
1207    buffer = ""
1208    if linefeed:
1209        buffer = "\n"
1210
1211    if robot_env:
1212        suite_name = BuiltIn().get_variable_value("${suite_name}")
1213        buffer += sindent(sprint_time("Running test suite \"" + suite_name
1214                                      + "\".\n"), indent)
1215
1216    buffer += sindent(sprint_time() + "Running " + pgm_name + ".\n", indent)
1217    buffer += sindent(sprint_time() + "Program parameter values, etc.:\n\n",
1218                      indent)
1219    buffer += sprint_varx("command_line", ' '.join(sys.argv), 0, indent,
1220                          loc_col1_width)
1221    # We want the output to show a customized name for the pid and pgid but
1222    # we want it to look like a valid variable name.  Therefore, we'll use
1223    # pgm_name_var_name which was set when this module was imported.
1224    buffer += sprint_varx(pgm_name_var_name + "_pid", os.getpid(), 0, indent,
1225                          loc_col1_width)
1226    buffer += sprint_varx(pgm_name_var_name + "_pgid", os.getpgrp(), 0, indent,
1227                          loc_col1_width)
1228    userid_num = str(os.geteuid())
1229    try:
1230        username = os.getlogin()
1231    except OSError:
1232        if userid_num == "0":
1233            username = "root"
1234        else:
1235            username = "?"
1236    buffer += sprint_varx("uid", userid_num + " (" + username
1237                          + ")", 0, indent, loc_col1_width)
1238    buffer += sprint_varx("gid", str(os.getgid()) + " ("
1239                          + str(grp.getgrgid(os.getgid()).gr_name) + ")", 0,
1240                          indent, loc_col1_width)
1241    buffer += sprint_varx("host_name", socket.gethostname(), 0, indent,
1242                          loc_col1_width)
1243    try:
1244        DISPLAY = os.environ['DISPLAY']
1245    except KeyError:
1246        DISPLAY = ""
1247    buffer += sprint_varx("DISPLAY", DISPLAY, 0, indent,
1248                          loc_col1_width)
1249    # I want to add code to print caller's parms.
1250
1251    # __builtin__.arg_obj is created by the get_arg module function,
1252    # gen_get_options.
1253    try:
1254        buffer += ga.sprint_args(__builtin__.arg_obj, indent)
1255    except AttributeError:
1256        pass
1257
1258    if robot_env:
1259        # Get value of global parm_list.
1260        parm_list = BuiltIn().get_variable_value("${parm_list}")
1261
1262        for parm in parm_list:
1263            parm_value = BuiltIn().get_variable_value("${" + parm + "}")
1264            buffer += sprint_varx(parm, parm_value, 0, indent, loc_col1_width)
1265
1266        # Setting global program_pid.
1267        BuiltIn().set_global_variable("${program_pid}", os.getpid())
1268
1269    if linefeed:
1270        buffer += "\n"
1271
1272    return buffer
1273
1274
1275def sprint_error_report(error_text="\n",
1276                        indent=2,
1277                        format=None):
1278    r"""
1279    Return a string with a standardized report which includes the caller's
1280    error text, the call stack and the program header.
1281
1282    Description of args:
1283    error_text                      The error text to be included in the
1284                                    report.  The caller should include any
1285                                    needed linefeeds.
1286    indent                          The number of characters to indent each
1287                                    line of output.
1288    format                          Long or short format.  Long includes
1289                                    extras like lines of dashes, call stack,
1290                                    etc.
1291    """
1292
1293    # Process input.
1294    indent = int(indent)
1295    if format is None:
1296        if robot_env:
1297            format = 'short'
1298        else:
1299            format = 'long'
1300    error_text = error_text.rstrip('\n') + '\n'
1301
1302    if format == 'short':
1303        return sprint_error(error_text)
1304
1305    buffer = ""
1306    buffer += sprint_dashes(width=120, char="=")
1307    buffer += sprint_error(error_text)
1308    buffer += "\n"
1309    # Calling sprint_call_stack with stack_frame_ix of 0 causes it to show
1310    # itself and this function in the call stack.  This is not helpful to a
1311    # debugger and is therefore clutter.  We will adjust the stack_frame_ix to
1312    # hide that information.
1313    stack_frame_ix = 1
1314    caller_func_name = sprint_func_name(2)
1315    if caller_func_name.endswith("print_error_report"):
1316        stack_frame_ix += 1
1317    if not robot_env:
1318        buffer += sprint_call_stack(indent, stack_frame_ix)
1319    buffer += sprint_pgm_header(indent)
1320    buffer += sprint_dashes(width=120, char="=")
1321
1322    return buffer
1323
1324
1325def sprint_issuing(cmd_buf,
1326                   test_mode=0):
1327    r"""
1328    Return a line indicating a command that the program is about to execute.
1329
1330    Sample output for a cmd_buf of "ls"
1331
1332    #(CDT) 2016/08/25 17:57:36 - Issuing: ls
1333
1334    Description of args:
1335    cmd_buf                         The command to be executed by caller.
1336    test_mode                       With test_mode set, your output will look
1337                                    like this:
1338
1339    #(CDT) 2016/08/25 17:57:36 - (test_mode) Issuing: ls
1340
1341    """
1342
1343    buffer = sprint_time()
1344    if test_mode:
1345        buffer += "(test_mode) "
1346    buffer += "Issuing: " + cmd_buf + "\n"
1347
1348    return buffer
1349
1350
1351def sprint_pgm_footer():
1352    r"""
1353    Return a standardized footer that programs should print at the end of the
1354    program run.  It includes useful information like total run time, etc.
1355    """
1356
1357    buffer = "\n" + sprint_time() + "Finished running " + pgm_name + ".\n\n"
1358
1359    total_time = time.time() - start_time
1360    total_time_string = "%0.6f" % total_time
1361
1362    buffer += sprint_varx(pgm_name_var_name + "_runtime", total_time_string)
1363    buffer += "\n"
1364
1365    return buffer
1366
1367
1368def sprint(buffer=""):
1369    r"""
1370    Simply return the user's buffer.  This function is used by the qprint and
1371    dprint functions defined dynamically below, i.e. it would not normally be
1372    called for general use.
1373
1374    Description of arguments.
1375    buffer                          This will be returned to the caller.
1376    """
1377
1378    try:
1379        return str(buffer)
1380    except UnicodeEncodeError:
1381        return buffer
1382
1383
1384def sprintn(buffer=""):
1385    r"""
1386    Simply return the user's buffer with a line feed.  This function is used
1387    by the qprint and dprint functions defined dynamically below, i.e. it
1388    would not normally be called for general use.
1389
1390    Description of arguments.
1391    buffer                          This will be returned to the caller.
1392    """
1393
1394    try:
1395        buffer = str(buffer) + "\n"
1396    except UnicodeEncodeError:
1397        buffer = buffer + "\n"
1398
1399    return buffer
1400
1401
1402def gp_print(buffer,
1403             stream='stdout'):
1404    r"""
1405    Print the buffer using either sys.stdout.write or BuiltIn().log_to_console
1406    depending on whether we are running in a robot environment.
1407
1408    This function is intended for use only by other functions in this module.
1409
1410    Description of arguments:
1411    buffer                          The string to be printed.
1412    stream                          Either "stdout" or "stderr".
1413    """
1414
1415    if robot_env:
1416        BuiltIn().log_to_console(buffer, stream=stream, no_newline=True)
1417    else:
1418        if stream == "stdout":
1419            sys.stdout.write(buffer)
1420            sys.stdout.flush()
1421        else:
1422            sys.stderr.write(buffer)
1423            sys.stderr.flush()
1424
1425
1426def gp_log(buffer):
1427    r"""
1428    Log the buffer using either python logging or BuiltIn().log depending on
1429    whether we are running in a robot environment.
1430
1431    This function is intended for use only by other functions in this module.
1432
1433    Description of arguments:
1434    buffer                          The string to be logged.
1435    """
1436
1437    if robot_env:
1438        BuiltIn().log(buffer)
1439    else:
1440        logging.warning(buffer)
1441
1442
1443def gp_debug_print(buffer):
1444    r"""
1445    Print with gp_print only if gen_print_debug is set.
1446
1447    This function is intended for use only by other functions in this module.
1448
1449    Description of arguments:
1450    buffer                          The string to be printed.
1451    """
1452
1453    if not gen_print_debug:
1454        return
1455
1456    gp_print(buffer)
1457
1458
1459def get_var_value(var_value=None,
1460                  default=1,
1461                  var_name=None):
1462    r"""
1463    Return either var_value, the corresponding global value or default.
1464
1465    If var_value is not None, it will simply be returned.
1466
1467    If var_value is None, this function will return the corresponding global
1468    value of the variable in question.
1469
1470    Note: For global values, if we are in a robot environment,
1471    get_variable_value will be used.  Otherwise, the __builtin__ version of
1472    the variable is returned (which are set by gen_arg.py functions).
1473
1474    If there is no global value associated with the variable, default is
1475    returned.
1476
1477    This function is useful for other functions in setting default values for
1478    parameters.
1479
1480    Example use:
1481
1482    def my_func(quiet=None):
1483
1484      quiet = int(get_var_value(quiet, 0))
1485
1486    Example calls to my_func():
1487
1488    In the following example, the caller is explicitly asking to have quiet be
1489    set to 1.
1490
1491    my_func(quiet=1)
1492
1493    In the following example, quiet will be set to the global value of quiet,
1494    if defined, or to 0 (the default).
1495
1496    my_func()
1497
1498    Description of arguments:
1499    var_value                       The value to be returned (if not equal to
1500                                    None).
1501    default                         The value that is returned if var_value is
1502                                    None and there is no corresponding global
1503                                    value defined.
1504    var_name                        The name of the variable whose value is to
1505                                    be returned.  Under most circumstances,
1506                                    this value need not be provided.  This
1507                                    function can figure out the name of the
1508                                    variable passed as var_value.  One
1509                                    exception to this would be if this
1510                                    function is called directly from a .robot
1511                                    file.
1512    """
1513
1514    if var_value is not None:
1515        return var_value
1516
1517    if var_name is None:
1518        var_name = get_arg_name(None, 1, 2)
1519
1520    if robot_env:
1521        var_value = BuiltIn().get_variable_value("${" + var_name + "}",
1522                                                 default)
1523    else:
1524        var_value = getattr(__builtin__, var_name, default)
1525
1526    return var_value
1527
1528
1529def get_stack_var(var_name,
1530                  default="",
1531                  init_stack_ix=2):
1532    r"""
1533    Starting with the caller's stack level, search upward in the call stack,
1534    for a variable named var_name and return its value.  If the variable
1535    cannot be found, return default.
1536
1537    Example code:
1538
1539    def func12():
1540        my_loc_var1 = get_stack_var('my_var1', "default value")
1541
1542    def func11():
1543        my_var1 = 11
1544        func12()
1545
1546    In this example, get_stack_var will find the value of my_var1 in func11's
1547    stack and will therefore return the value 11.  Therefore, my_loc_var1
1548    would get set to 11.
1549
1550    Description of argument(s):
1551    var_name                        The name of the variable to be searched
1552                                    for.
1553    default                         The value to return if the the variable
1554                                    cannot be found.
1555    init_stack_ix                   The initial stack index from which to
1556                                    begin the search.  0 would be the index of
1557                                    this func1tion ("get_stack_var"), 1 would
1558                                    be the index of the function calling this
1559                                    function, etc.
1560    """
1561
1562    return next((frame[0].f_locals[var_name]
1563                 for frame in inspect.stack()[init_stack_ix:]
1564                 if var_name in frame[0].f_locals), default)
1565
1566
1567# hidden_text is a list of passwords which are to be replaced with asterisks
1568# by print functions defined in this module.
1569hidden_text = []
1570# password_regex is created based on the contents of hidden_text.
1571password_regex = ""
1572
1573
1574def register_passwords(*args):
1575    r"""
1576    Register one or more passwords which are to be hidden in output produced
1577    by the print functions in this module.
1578
1579    Note:  Blank password values are NOT registered.  They are simply ignored.
1580
1581    Description of argument(s):
1582    args                            One or more password values.  If a given
1583                                    password value is already registered, this
1584                                    function will simply do nothing.
1585    """
1586
1587    global hidden_text
1588    global password_regex
1589
1590    for password in args:
1591        if password == "":
1592            break
1593        if password in hidden_text:
1594            break
1595
1596        # Place the password into the hidden_text list.
1597        hidden_text.append(password)
1598        # Create a corresponding password regular expression.  Escape regex
1599        # special characters too.
1600        password_regex = '(' +\
1601            '|'.join([re.escape(x) for x in hidden_text]) + ')'
1602
1603
1604def replace_passwords(buffer):
1605    r"""
1606    Return the buffer but with all registered passwords replaced by a string
1607    of asterisks.
1608
1609
1610    Description of argument(s):
1611    buffer                          The string to be returned but with
1612                                    passwords replaced.
1613    """
1614
1615    global password_regex
1616
1617    if int(os.environ.get("DEBUG_SHOW_PASSWORDS", "0")):
1618        return buffer
1619
1620    if password_regex == "":
1621        # No passwords to replace.
1622        return buffer
1623
1624    return re.sub(password_regex, "********", buffer)
1625
1626
1627def create_print_wrapper_funcs(func_names,
1628                               stderr_func_names,
1629                               replace_dict):
1630    r"""
1631    Generate code for print wrapper functions and return the generated code as
1632    a string.
1633
1634    To illustrate, suppose there is a "print_foo_bar" function in the
1635    func_names list.
1636    This function will...
1637    - Expect that there is an sprint_foo_bar function already in existence.
1638    - Create a print_foo_bar function which calls sprint_foo_bar and prints
1639      the result.
1640    - Create a qprint_foo_bar function which calls upon sprint_foo_bar only if
1641      global value quiet is 0.
1642    - Create a dprint_foo_bar function which calls upon sprint_foo_bar only if
1643      global value debug is 1.
1644
1645    Also, code will be generated to define aliases for each function as well.
1646    Each alias will be created by replacing "print_" in the function name with
1647    "p"  For example, the alias for print_foo_bar will be pfoo_bar.
1648
1649    Description of argument(s):
1650    func_names                      A list of functions for which print
1651                                    wrapper function code is to be generated.
1652    stderr_func_names               A list of functions whose generated code
1653                                    should print to stderr rather than to
1654                                    stdout.
1655    replace_dict                    Please see the create_func_def_string
1656                                    function in wrap_utils.py for details on
1657                                    this parameter.  This parameter will be
1658                                    passed directly to create_func_def_string.
1659    """
1660
1661    buffer = ""
1662
1663    for func_name in func_names:
1664        if func_name in stderr_func_names:
1665            replace_dict['output_stream'] = "stderr"
1666        else:
1667            replace_dict['output_stream'] = "stdout"
1668
1669        s_func_name = "s" + func_name
1670        q_func_name = "q" + func_name
1671        d_func_name = "d" + func_name
1672
1673        # We don't want to try to redefine the "print" function, thus the
1674        # following if statement.
1675        if func_name != "print":
1676            func_def = create_func_def_string(s_func_name, func_name,
1677                                              print_func_template,
1678                                              replace_dict)
1679            buffer += func_def
1680
1681        func_def = create_func_def_string(s_func_name, "q" + func_name,
1682                                          qprint_func_template, replace_dict)
1683        buffer += func_def
1684
1685        func_def = create_func_def_string(s_func_name, "d" + func_name,
1686                                          dprint_func_template, replace_dict)
1687        buffer += func_def
1688
1689        func_def = create_func_def_string(s_func_name, "l" + func_name,
1690                                          lprint_func_template, replace_dict)
1691        buffer += func_def
1692
1693        # Create abbreviated aliases (e.g. spvar is an alias for sprint_var).
1694        alias = re.sub("print_", "p", func_name)
1695        alias = re.sub("print", "p", alias)
1696        prefixes = ["", "s", "q", "d", "l"]
1697        for prefix in prefixes:
1698            if alias == "p":
1699                continue
1700            func_def = prefix + alias + " = " + prefix + func_name
1701            buffer += func_def + "\n"
1702
1703    return buffer
1704
1705
1706# In the following section of code, we will dynamically create print versions
1707# for each of the sprint functions defined above.  So, for example, where we
1708# have an sprint_time() function defined above that returns the time to the
1709# caller in a string, we will create a corresponding print_time() function
1710# that will print that string directly to stdout.
1711
1712# It can be complicated to follow what's being created by below.  Here is an
1713# example of the print_time() function that will be created:
1714
1715# def print_time(buffer=''):
1716#     sys.stdout.write(replace_passwords(sprint_time(buffer=buffer)))
1717#     sys.stdout.flush()
1718
1719# Templates for the various print wrapper functions.
1720print_func_template = \
1721    [
1722        "    <mod_qualifier>gp_print(<mod_qualifier>replace_passwords("
1723        + "<call_line>), stream='<output_stream>')"
1724    ]
1725
1726qprint_func_template = \
1727    [
1728        "    if int(<mod_qualifier>get_var_value(None, 0, \"quiet\")): return"
1729    ] + print_func_template
1730
1731dprint_func_template = \
1732    [
1733        "    if not int(<mod_qualifier>get_var_value(None, 0, \"debug\")):"
1734        + " return"
1735    ] + print_func_template
1736
1737lprint_func_template = \
1738    [
1739        "    gp_log(<mod_qualifier>replace_passwords(<call_line>))"
1740    ]
1741
1742replace_dict = {'output_stream': 'stdout', 'mod_qualifier': ''}
1743
1744
1745gp_debug_print("robot_env: " + str(robot_env))
1746
1747# func_names contains a list of all print functions which should be created
1748# from their sprint counterparts.
1749func_names = ['print_time', 'print_timen', 'print_error', 'print_varx',
1750              'print_var', 'print_vars', 'print_dashes', 'indent',
1751              'print_call_stack', 'print_func_name', 'print_executing',
1752              'print_pgm_header', 'print_issuing', 'print_pgm_footer',
1753              'print_error_report', 'print', 'printn']
1754
1755# stderr_func_names is a list of functions whose output should go to stderr
1756# rather than stdout.
1757stderr_func_names = ['print_error', 'print_error_report']
1758
1759
1760func_defs = create_print_wrapper_funcs(func_names, stderr_func_names,
1761                                       replace_dict)
1762gp_debug_print(func_defs)
1763exec(func_defs)
1764