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
1079func_line_style_std = None
1080func_line_style_short = 1
1081
1082
1083def sprint_func_line(stack_frame, style=None):
1084    r"""
1085    For the given stack_frame, return a formatted string containing the
1086    function name and all its arguments.
1087
1088    Example:
1089
1090    func1(last_name = 'walsh', first_name = 'mikey')
1091
1092    Description of argument(s):
1093    stack_frame                     A stack frame (such as is returned by
1094                                    inspect.stack()).
1095    style                           Indicates the style or formatting of the
1096                                    result string.  Acceptable values are
1097                                    shown above.
1098
1099    Description of styles:
1100    func_line_style_std             The standard formatting.
1101    func_line_style_short           1) The self parm (associated with methods)
1102                                    will be dropped. 2) The args and kwargs
1103                                    values will be treated as special.  In
1104                                    both cases the arg name ('args' or
1105                                    'kwargs') will be dropped and only the
1106                                    values will be shown.
1107    """
1108
1109    func_name = str(stack_frame[3])
1110    if func_name == "?":
1111        # "?" is the name used when code is not in a function.
1112        func_name = "(none)"
1113
1114    if func_name == "<module>":
1115        # If the func_name is the "main" program, we simply get the command
1116        # line call string.
1117        func_and_args = ' '.join(sys.argv)
1118    else:
1119        # Get the program arguments.
1120        (args, varargs, keywords, locals) =\
1121            inspect.getargvalues(stack_frame[0])
1122
1123        args_list = []
1124        for arg_name in filter(None, args + [varargs, keywords]):
1125            # Get the arg value from frame locals.
1126            arg_value = locals[arg_name]
1127            if arg_name == 'self':
1128                if style == func_line_style_short:
1129                    continue
1130                # Manipulations to improve output for class methods.
1131                func_name = arg_value.__class__.__name__ + "." + func_name
1132                args_list.append(arg_name + " = <self>")
1133            elif (style == func_line_style_short
1134                  and arg_name == 'args'
1135                  and type(arg_value) in (list, tuple)):
1136                if len(arg_value) == 0:
1137                    continue
1138                args_list.append(repr(', '.join(arg_value)))
1139            elif (style == func_line_style_short
1140                  and arg_name == 'kwargs'
1141                  and type(arg_value) is dict):
1142                for key, value in arg_value.items():
1143                    args_list.append(key + "=" + repr(value))
1144            else:
1145                args_list.append(arg_name + " = " + repr(arg_value))
1146        args_str = "(" + ', '.join(map(str, args_list)) + ")"
1147
1148        # Now we need to print this in a nicely-wrapped way.
1149        func_and_args = func_name + args_str
1150
1151    return func_and_args
1152
1153
1154def sprint_call_stack(indent=0,
1155                      stack_frame_ix=0,
1156                      style=None):
1157    r"""
1158    Return a call stack report for the given point in the program with line
1159    numbers, function names and function parameters and arguments.
1160
1161    Sample output:
1162
1163    -------------------------------------------------------------------------
1164    Python function call stack
1165
1166    Line # Function name and arguments
1167    ------ ------------------------------------------------------------------
1168       424 sprint_call_stack()
1169         4 print_call_stack()
1170        31 func1(last_name = 'walsh', first_name = 'mikey')
1171        59 /tmp/scr5.py
1172    -------------------------------------------------------------------------
1173
1174    Description of arguments:
1175    indent                          The number of characters to indent each
1176                                    line of output.
1177    stack_frame_ix                  The index of the first stack frame which
1178                                    is to be returned.
1179    style                           See the sprint_line_func prolog above for
1180                                    details.
1181    """
1182
1183    buffer = ""
1184    buffer += sprint_dashes(indent)
1185    buffer += sindent("Python function call stack\n\n", indent)
1186    buffer += sindent("Line # Function name and arguments\n", indent)
1187    buffer += sprint_dashes(indent, 6, 0) + " " + sprint_dashes(0, 73)
1188
1189    # Grab the current program stack.
1190    current_stack = inspect.stack()
1191
1192    # Process each frame in turn.
1193    format_string = "%6s %s\n"
1194    ix = 0
1195    for stack_frame in current_stack:
1196        if ix < stack_frame_ix:
1197            ix += 1
1198            continue
1199        # I want the line number shown to be the line where you find the line
1200        # shown.
1201        try:
1202            line_num = str(current_stack[ix + 1][2])
1203        except IndexError:
1204            line_num = ""
1205        func_and_args = sprint_func_line(stack_frame, style=style)
1206
1207        buffer += sindent(format_string % (line_num, func_and_args), indent)
1208        ix += 1
1209
1210    buffer += sprint_dashes(indent)
1211
1212    return buffer
1213
1214
1215def sprint_executing(stack_frame_ix=None, style=None):
1216    r"""
1217    Print a line indicating what function is executing and with what parameter
1218    values.  This is useful for debugging.
1219
1220    Sample output:
1221
1222    #(CDT) 2016/08/25 17:54:27 - Executing: func1(x = 1)
1223
1224    Description of arguments:
1225    stack_frame_ix                  The index of the stack frame whose
1226                                    function info should be returned.  If the
1227                                    caller does not specify a value, this
1228                                    function will set the value to 1 which is
1229                                    the index of the caller's stack frame.  If
1230                                    the caller is the wrapper function
1231                                    "print_executing", this function will bump
1232                                    it up by 1.
1233    style                           See the sprint_line_func prolog above for
1234                                    details.
1235    """
1236
1237    # If user wants default stack_frame_ix.
1238    if stack_frame_ix is None:
1239        func_name = sys._getframe().f_code.co_name
1240        caller_func_name = sys._getframe(1).f_code.co_name
1241        if caller_func_name.endswith(func_name[1:]):
1242            stack_frame_ix = 2
1243        else:
1244            stack_frame_ix = 1
1245
1246    stack_frame = inspect.stack()[stack_frame_ix]
1247
1248    func_and_args = sprint_func_line(stack_frame, style)
1249
1250    return sprint_time() + "Executing: " + func_and_args + "\n"
1251
1252
1253def sprint_pgm_header(indent=0,
1254                      linefeed=1):
1255    r"""
1256    Return a standardized header that programs should print at the beginning
1257    of the run.  It includes useful information like command line, pid,
1258    userid, program parameters, etc.
1259
1260    Description of arguments:
1261    indent                          The number of characters to indent each
1262                                    line of output.
1263    linefeed                        Indicates whether a line feed be included
1264                                    at the beginning and end of the report.
1265    """
1266
1267    loc_col1_width = col1_width + indent
1268
1269    buffer = ""
1270    if linefeed:
1271        buffer = "\n"
1272
1273    if robot_env:
1274        suite_name = BuiltIn().get_variable_value("${suite_name}")
1275        buffer += sindent(sprint_time("Running test suite \"" + suite_name
1276                                      + "\".\n"), indent)
1277
1278    buffer += sindent(sprint_time() + "Running " + pgm_name + ".\n", indent)
1279    buffer += sindent(sprint_time() + "Program parameter values, etc.:\n\n",
1280                      indent)
1281    buffer += sprint_varx("command_line", ' '.join(sys.argv), 0, indent,
1282                          loc_col1_width)
1283    # We want the output to show a customized name for the pid and pgid but
1284    # we want it to look like a valid variable name.  Therefore, we'll use
1285    # pgm_name_var_name which was set when this module was imported.
1286    buffer += sprint_varx(pgm_name_var_name + "_pid", os.getpid(), 0, indent,
1287                          loc_col1_width)
1288    buffer += sprint_varx(pgm_name_var_name + "_pgid", os.getpgrp(), 0, indent,
1289                          loc_col1_width)
1290    userid_num = str(os.geteuid())
1291    try:
1292        username = os.getlogin()
1293    except OSError:
1294        if userid_num == "0":
1295            username = "root"
1296        else:
1297            username = "?"
1298    buffer += sprint_varx("uid", userid_num + " (" + username
1299                          + ")", 0, indent, loc_col1_width)
1300    buffer += sprint_varx("gid", str(os.getgid()) + " ("
1301                          + str(grp.getgrgid(os.getgid()).gr_name) + ")", 0,
1302                          indent, loc_col1_width)
1303    buffer += sprint_varx("host_name", socket.gethostname(), 0, indent,
1304                          loc_col1_width)
1305    try:
1306        DISPLAY = os.environ['DISPLAY']
1307    except KeyError:
1308        DISPLAY = ""
1309    buffer += sprint_varx("DISPLAY", DISPLAY, 0, indent,
1310                          loc_col1_width)
1311    # I want to add code to print caller's parms.
1312
1313    # __builtin__.arg_obj is created by the get_arg module function,
1314    # gen_get_options.
1315    try:
1316        buffer += ga.sprint_args(__builtin__.arg_obj, indent)
1317    except AttributeError:
1318        pass
1319
1320    if robot_env:
1321        # Get value of global parm_list.
1322        parm_list = BuiltIn().get_variable_value("${parm_list}")
1323
1324        for parm in parm_list:
1325            parm_value = BuiltIn().get_variable_value("${" + parm + "}")
1326            buffer += sprint_varx(parm, parm_value, 0, indent, loc_col1_width)
1327
1328        # Setting global program_pid.
1329        BuiltIn().set_global_variable("${program_pid}", os.getpid())
1330
1331    if linefeed:
1332        buffer += "\n"
1333
1334    return buffer
1335
1336
1337def sprint_error_report(error_text="\n",
1338                        indent=2,
1339                        format=None):
1340    r"""
1341    Return a string with a standardized report which includes the caller's
1342    error text, the call stack and the program header.
1343
1344    Description of args:
1345    error_text                      The error text to be included in the
1346                                    report.  The caller should include any
1347                                    needed linefeeds.
1348    indent                          The number of characters to indent each
1349                                    line of output.
1350    format                          Long or short format.  Long includes
1351                                    extras like lines of dashes, call stack,
1352                                    etc.
1353    """
1354
1355    # Process input.
1356    indent = int(indent)
1357    if format is None:
1358        if robot_env:
1359            format = 'short'
1360        else:
1361            format = 'long'
1362    error_text = error_text.rstrip('\n') + '\n'
1363
1364    if format == 'short':
1365        return sprint_error(error_text)
1366
1367    buffer = ""
1368    buffer += sprint_dashes(width=120, char="=")
1369    buffer += sprint_error(error_text)
1370    buffer += "\n"
1371    # Calling sprint_call_stack with stack_frame_ix of 0 causes it to show
1372    # itself and this function in the call stack.  This is not helpful to a
1373    # debugger and is therefore clutter.  We will adjust the stack_frame_ix to
1374    # hide that information.
1375    stack_frame_ix = 1
1376    caller_func_name = sprint_func_name(2)
1377    if caller_func_name.endswith("print_error_report"):
1378        stack_frame_ix += 1
1379    buffer += sprint_call_stack(indent, stack_frame_ix)
1380    buffer += sprint_pgm_header(indent)
1381    buffer += sprint_dashes(width=120, char="=")
1382
1383    return buffer
1384
1385
1386def sprint_issuing(cmd_buf,
1387                   test_mode=0):
1388    r"""
1389    Return a line indicating a command that the program is about to execute.
1390
1391    Sample output for a cmd_buf of "ls"
1392
1393    #(CDT) 2016/08/25 17:57:36 - Issuing: ls
1394
1395    Description of args:
1396    cmd_buf                         The command to be executed by caller.
1397    test_mode                       With test_mode set, your output will look
1398                                    like this:
1399
1400    #(CDT) 2016/08/25 17:57:36 - (test_mode) Issuing: ls
1401
1402    """
1403
1404    buffer = sprint_time()
1405    if test_mode:
1406        buffer += "(test_mode) "
1407    buffer += "Issuing: " + cmd_buf + "\n"
1408
1409    return buffer
1410
1411
1412def sprint_pgm_footer():
1413    r"""
1414    Return a standardized footer that programs should print at the end of the
1415    program run.  It includes useful information like total run time, etc.
1416    """
1417
1418    buffer = "\n" + sprint_time() + "Finished running " + pgm_name + ".\n\n"
1419
1420    total_time = time.time() - start_time
1421    total_time_string = "%0.6f" % total_time
1422
1423    buffer += sprint_varx(pgm_name_var_name + "_runtime", total_time_string)
1424    buffer += "\n"
1425
1426    return buffer
1427
1428
1429def sprint(buffer=""):
1430    r"""
1431    Simply return the user's buffer.  This function is used by the qprint and
1432    dprint functions defined dynamically below, i.e. it would not normally be
1433    called for general use.
1434
1435    Description of arguments.
1436    buffer                          This will be returned to the caller.
1437    """
1438
1439    try:
1440        return str(buffer)
1441    except UnicodeEncodeError:
1442        return buffer
1443
1444
1445def sprintn(buffer=""):
1446    r"""
1447    Simply return the user's buffer with a line feed.  This function is used
1448    by the qprint and dprint functions defined dynamically below, i.e. it
1449    would not normally be called for general use.
1450
1451    Description of arguments.
1452    buffer                          This will be returned to the caller.
1453    """
1454
1455    try:
1456        buffer = str(buffer) + "\n"
1457    except UnicodeEncodeError:
1458        buffer = buffer + "\n"
1459
1460    return buffer
1461
1462
1463def gp_print(buffer,
1464             stream='stdout'):
1465    r"""
1466    Print the buffer using either sys.stdout.write or BuiltIn().log_to_console
1467    depending on whether we are running in a robot environment.
1468
1469    This function is intended for use only by other functions in this module.
1470
1471    Description of arguments:
1472    buffer                          The string to be printed.
1473    stream                          Either "stdout" or "stderr".
1474    """
1475
1476    if robot_env:
1477        BuiltIn().log_to_console(buffer, stream=stream, no_newline=True)
1478    else:
1479        if stream == "stdout":
1480            sys.stdout.write(buffer)
1481            sys.stdout.flush()
1482        else:
1483            sys.stderr.write(buffer)
1484            sys.stderr.flush()
1485
1486
1487def gp_log(buffer):
1488    r"""
1489    Log the buffer using either python logging or BuiltIn().log depending on
1490    whether we are running in a robot environment.
1491
1492    This function is intended for use only by other functions in this module.
1493
1494    Description of arguments:
1495    buffer                          The string to be logged.
1496    """
1497
1498    if robot_env:
1499        BuiltIn().log(buffer)
1500    else:
1501        logging.warning(buffer)
1502
1503
1504def gp_debug_print(buffer):
1505    r"""
1506    Print with gp_print only if gen_print_debug is set.
1507
1508    This function is intended for use only by other functions in this module.
1509
1510    Description of arguments:
1511    buffer                          The string to be printed.
1512    """
1513
1514    if not gen_print_debug:
1515        return
1516
1517    gp_print(buffer)
1518
1519
1520def get_var_value(var_value=None,
1521                  default=1,
1522                  var_name=None):
1523    r"""
1524    Return either var_value, the corresponding global value or default.
1525
1526    If var_value is not None, it will simply be returned.
1527
1528    If var_value is None, this function will return the corresponding global
1529    value of the variable in question.
1530
1531    Note: For global values, if we are in a robot environment,
1532    get_variable_value will be used.  Otherwise, the __builtin__ version of
1533    the variable is returned (which are set by gen_arg.py functions).
1534
1535    If there is no global value associated with the variable, default is
1536    returned.
1537
1538    This function is useful for other functions in setting default values for
1539    parameters.
1540
1541    Example use:
1542
1543    def my_func(quiet=None):
1544
1545      quiet = int(get_var_value(quiet, 0))
1546
1547    Example calls to my_func():
1548
1549    In the following example, the caller is explicitly asking to have quiet be
1550    set to 1.
1551
1552    my_func(quiet=1)
1553
1554    In the following example, quiet will be set to the global value of quiet,
1555    if defined, or to 0 (the default).
1556
1557    my_func()
1558
1559    Description of arguments:
1560    var_value                       The value to be returned (if not equal to
1561                                    None).
1562    default                         The value that is returned if var_value is
1563                                    None and there is no corresponding global
1564                                    value defined.
1565    var_name                        The name of the variable whose value is to
1566                                    be returned.  Under most circumstances,
1567                                    this value need not be provided.  This
1568                                    function can figure out the name of the
1569                                    variable passed as var_value.  One
1570                                    exception to this would be if this
1571                                    function is called directly from a .robot
1572                                    file.
1573    """
1574
1575    if var_value is not None:
1576        return var_value
1577
1578    if var_name is None:
1579        var_name = get_arg_name(None, 1, 2)
1580
1581    if robot_env:
1582        var_value = BuiltIn().get_variable_value("${" + var_name + "}",
1583                                                 default)
1584    else:
1585        var_value = getattr(__builtin__, var_name, default)
1586
1587    return var_value
1588
1589
1590def get_stack_var(var_name,
1591                  default="",
1592                  init_stack_ix=2):
1593    r"""
1594    Starting with the caller's stack level, search upward in the call stack,
1595    for a variable named var_name and return its value.  If the variable
1596    cannot be found, return default.
1597
1598    Example code:
1599
1600    def func12():
1601        my_loc_var1 = get_stack_var('my_var1', "default value")
1602
1603    def func11():
1604        my_var1 = 11
1605        func12()
1606
1607    In this example, get_stack_var will find the value of my_var1 in func11's
1608    stack and will therefore return the value 11.  Therefore, my_loc_var1
1609    would get set to 11.
1610
1611    Description of argument(s):
1612    var_name                        The name of the variable to be searched
1613                                    for.
1614    default                         The value to return if the the variable
1615                                    cannot be found.
1616    init_stack_ix                   The initial stack index from which to
1617                                    begin the search.  0 would be the index of
1618                                    this func1tion ("get_stack_var"), 1 would
1619                                    be the index of the function calling this
1620                                    function, etc.
1621    """
1622
1623    return next((frame[0].f_locals[var_name]
1624                 for frame in inspect.stack()[init_stack_ix:]
1625                 if var_name in frame[0].f_locals), default)
1626
1627
1628# hidden_text is a list of passwords which are to be replaced with asterisks
1629# by print functions defined in this module.
1630hidden_text = []
1631# password_regex is created based on the contents of hidden_text.
1632password_regex = ""
1633
1634
1635def register_passwords(*args):
1636    r"""
1637    Register one or more passwords which are to be hidden in output produced
1638    by the print functions in this module.
1639
1640    Note:  Blank password values are NOT registered.  They are simply ignored.
1641
1642    Description of argument(s):
1643    args                            One or more password values.  If a given
1644                                    password value is already registered, this
1645                                    function will simply do nothing.
1646    """
1647
1648    global hidden_text
1649    global password_regex
1650
1651    for password in args:
1652        if password == "":
1653            break
1654        if password in hidden_text:
1655            break
1656
1657        # Place the password into the hidden_text list.
1658        hidden_text.append(password)
1659        # Create a corresponding password regular expression.  Escape regex
1660        # special characters too.
1661        password_regex = '(' +\
1662            '|'.join([re.escape(x) for x in hidden_text]) + ')'
1663
1664
1665def replace_passwords(buffer):
1666    r"""
1667    Return the buffer but with all registered passwords replaced by a string
1668    of asterisks.
1669
1670
1671    Description of argument(s):
1672    buffer                          The string to be returned but with
1673                                    passwords replaced.
1674    """
1675
1676    global password_regex
1677
1678    if int(os.environ.get("DEBUG_SHOW_PASSWORDS", "0")):
1679        return buffer
1680
1681    if password_regex == "":
1682        # No passwords to replace.
1683        return buffer
1684
1685    return re.sub(password_regex, "********", buffer)
1686
1687
1688def create_print_wrapper_funcs(func_names,
1689                               stderr_func_names,
1690                               replace_dict):
1691    r"""
1692    Generate code for print wrapper functions and return the generated code as
1693    a string.
1694
1695    To illustrate, suppose there is a "print_foo_bar" function in the
1696    func_names list.
1697    This function will...
1698    - Expect that there is an sprint_foo_bar function already in existence.
1699    - Create a print_foo_bar function which calls sprint_foo_bar and prints
1700      the result.
1701    - Create a qprint_foo_bar function which calls upon sprint_foo_bar only if
1702      global value quiet is 0.
1703    - Create a dprint_foo_bar function which calls upon sprint_foo_bar only if
1704      global value debug is 1.
1705
1706    Also, code will be generated to define aliases for each function as well.
1707    Each alias will be created by replacing "print_" in the function name with
1708    "p"  For example, the alias for print_foo_bar will be pfoo_bar.
1709
1710    Description of argument(s):
1711    func_names                      A list of functions for which print
1712                                    wrapper function code is to be generated.
1713    stderr_func_names               A list of functions whose generated code
1714                                    should print to stderr rather than to
1715                                    stdout.
1716    replace_dict                    Please see the create_func_def_string
1717                                    function in wrap_utils.py for details on
1718                                    this parameter.  This parameter will be
1719                                    passed directly to create_func_def_string.
1720    """
1721
1722    buffer = ""
1723
1724    for func_name in func_names:
1725        if func_name in stderr_func_names:
1726            replace_dict['output_stream'] = "stderr"
1727        else:
1728            replace_dict['output_stream'] = "stdout"
1729
1730        s_func_name = "s" + func_name
1731        q_func_name = "q" + func_name
1732        d_func_name = "d" + func_name
1733
1734        # We don't want to try to redefine the "print" function, thus the
1735        # following if statement.
1736        if func_name != "print":
1737            func_def = create_func_def_string(s_func_name, func_name,
1738                                              print_func_template,
1739                                              replace_dict)
1740            buffer += func_def
1741
1742        func_def = create_func_def_string(s_func_name, "q" + func_name,
1743                                          qprint_func_template, replace_dict)
1744        buffer += func_def
1745
1746        func_def = create_func_def_string(s_func_name, "d" + func_name,
1747                                          dprint_func_template, replace_dict)
1748        buffer += func_def
1749
1750        func_def = create_func_def_string(s_func_name, "l" + func_name,
1751                                          lprint_func_template, replace_dict)
1752        buffer += func_def
1753
1754        # Create abbreviated aliases (e.g. spvar is an alias for sprint_var).
1755        alias = re.sub("print_", "p", func_name)
1756        alias = re.sub("print", "p", alias)
1757        prefixes = ["", "s", "q", "d", "l"]
1758        for prefix in prefixes:
1759            if alias == "p":
1760                continue
1761            func_def = prefix + alias + " = " + prefix + func_name
1762            buffer += func_def + "\n"
1763
1764    return buffer
1765
1766
1767# In the following section of code, we will dynamically create print versions
1768# for each of the sprint functions defined above.  So, for example, where we
1769# have an sprint_time() function defined above that returns the time to the
1770# caller in a string, we will create a corresponding print_time() function
1771# that will print that string directly to stdout.
1772
1773# It can be complicated to follow what's being created by below.  Here is an
1774# example of the print_time() function that will be created:
1775
1776# def print_time(buffer=''):
1777#     sys.stdout.write(replace_passwords(sprint_time(buffer=buffer)))
1778#     sys.stdout.flush()
1779
1780# Templates for the various print wrapper functions.
1781print_func_template = \
1782    [
1783        "    <mod_qualifier>gp_print(<mod_qualifier>replace_passwords("
1784        + "<call_line>), stream='<output_stream>')"
1785    ]
1786
1787qprint_func_template = \
1788    [
1789        "    quiet_default = <mod_qualifier>get_var_value(None, 0, \"quiet\")",
1790        "    quiet = <mod_qualifier>get_stack_var(\"quiet\", quiet_default)",
1791        "    if int(quiet): return"
1792    ] + print_func_template
1793
1794dprint_func_template = \
1795    [
1796        "    debug_default = <mod_qualifier>get_var_value(None, 0, \"debug\")",
1797        "    debug = <mod_qualifier>get_stack_var(\"debug\", debug_default)",
1798        "    if not int(debug): return"
1799    ] + print_func_template
1800
1801lprint_func_template = \
1802    [
1803        "    global sprint_time_last_seconds",
1804        "    global last_seconds_ix",
1805        "    if len(sprint_time_last_seconds) <= lprint_last_seconds_ix():",
1806        "        sprint_time_last_seconds.append(start_time)",
1807        "    save_last_seconds_ix = last_seconds_ix",
1808        "    last_seconds_ix = lprint_last_seconds_ix()",
1809        "    gp_log(<mod_qualifier>replace_passwords(<call_line>))",
1810        "    last_seconds_ix = save_last_seconds_ix",
1811    ]
1812
1813replace_dict = {'output_stream': 'stdout', 'mod_qualifier': ''}
1814
1815
1816gp_debug_print("robot_env: " + str(robot_env))
1817
1818# func_names contains a list of all print functions which should be created
1819# from their sprint counterparts.
1820func_names = ['print_time', 'print_timen', 'print_error', 'print_varx',
1821              'print_var', 'print_vars', 'print_dashes', 'indent',
1822              'print_call_stack', 'print_func_name', 'print_executing',
1823              'print_pgm_header', 'print_issuing', 'print_pgm_footer',
1824              'print_error_report', 'print', 'printn']
1825
1826# stderr_func_names is a list of functions whose output should go to stderr
1827# rather than stdout.
1828stderr_func_names = ['print_error', 'print_error_report']
1829
1830
1831func_defs = create_print_wrapper_funcs(func_names, stderr_func_names,
1832                                       replace_dict)
1833gp_debug_print(func_defs)
1834exec(func_defs)
1835