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 " if int(<mod_qualifier>get_var_value(None, 0, \"quiet\")): return" 1790 ] + print_func_template 1791 1792dprint_func_template = \ 1793 [ 1794 " if not int(<mod_qualifier>get_var_value(None, 0, \"debug\")):" 1795 + " return" 1796 ] + print_func_template 1797 1798lprint_func_template = \ 1799 [ 1800 " global sprint_time_last_seconds", 1801 " global last_seconds_ix", 1802 " if len(sprint_time_last_seconds) <= lprint_last_seconds_ix():", 1803 " sprint_time_last_seconds.append(start_time)", 1804 " save_last_seconds_ix = last_seconds_ix", 1805 " last_seconds_ix = lprint_last_seconds_ix()", 1806 " gp_log(<mod_qualifier>replace_passwords(<call_line>))", 1807 " last_seconds_ix = save_last_seconds_ix", 1808 ] 1809 1810replace_dict = {'output_stream': 'stdout', 'mod_qualifier': ''} 1811 1812 1813gp_debug_print("robot_env: " + str(robot_env)) 1814 1815# func_names contains a list of all print functions which should be created 1816# from their sprint counterparts. 1817func_names = ['print_time', 'print_timen', 'print_error', 'print_varx', 1818 'print_var', 'print_vars', 'print_dashes', 'indent', 1819 'print_call_stack', 'print_func_name', 'print_executing', 1820 'print_pgm_header', 'print_issuing', 'print_pgm_footer', 1821 'print_error_report', 'print', 'printn'] 1822 1823# stderr_func_names is a list of functions whose output should go to stderr 1824# rather than stdout. 1825stderr_func_names = ['print_error', 'print_error_report'] 1826 1827 1828func_defs = create_print_wrapper_funcs(func_names, stderr_func_names, 1829 replace_dict) 1830gp_debug_print(func_defs) 1831exec(func_defs) 1832