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