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