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