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