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