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