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 'strip_brackets', 861 'no_header'] 862 863 864def create_fmt_definition(): 865 r""" 866 Create a string consisting of function-definition code that can be 867 executed to create constant fmt definition functions. 868 869 These functions can be used by callers of sprint_var/sprint_varx to set 870 the fmt argument correctly. 871 872 Likewise, the sprint_varx function will use these generated functions to 873 correctly interpret the fmt argument. 874 875 Example output from this function: 876 877 def hexa(): 878 return 0x00000001 879 def octal_fmt(): 880 return 0x00000002 881 etc. 882 """ 883 884 buffer = "" 885 bits = 0x00000001 886 for fmt_name in valid_fmts(): 887 buffer += "def " + fmt_name + "():\n" 888 buffer += " return " + "0x%08x" % bits + "\n" 889 bits = bits << 1 890 return buffer 891 892 893# Dynamically create fmt definitions (for use with the fmt argument of 894# sprint_varx function): 895exec(create_fmt_definition()) 896 897 898def terse(): 899 r""" 900 Constant function to return fmt value of 0. 901 902 Now that sprint_varx defaults to printing in terse format, the terse 903 option is deprecated. This function is here for backward compatibility. 904 905 Once the repo has been purged of the use of terse, this function can be 906 removed. 907 """ 908 909 return 0 910 911 912def list_pop(a_list, index=0, default=None): 913 r""" 914 Pop the list entry indicated by the index and return the entry. If no 915 such entry exists, return default. 916 917 Note that the list passed to this function will be modified. 918 919 Description of argument(s): 920 a_list The list from which an entry is to be 921 popped. 922 index The index indicating which entry is to be 923 popped. 924 default The value to be returned if there is no 925 entry at the given index location. 926 """ 927 try: 928 return a_list.pop(index) 929 except IndexError: 930 return default 931 932 933def parse_fmt(fmt): 934 r""" 935 Parse the fmt argument and return a tuple consisting of a format and a 936 child format. 937 938 This function was written for use by the sprint_varx function defined in 939 this module. 940 941 When sprint_varx is processing a multi-level object such as a list or 942 dictionary (which in turn may contain other lists or dictionaries), it 943 will use the fmt value to dictate the print formatting of the current 944 level and the child_fmt value to dictate the print formatting of 945 subordinate levels. Consider the following example: 946 947 python code example: 948 949 ord_dict = \ 950 collections.OrderedDict([ 951 ('one', 1), 952 ('two', 2), 953 ('sub', 954 collections.OrderedDict([ 955 ('three', 3), ('four', 4)]))]) 956 957 print_var(ord_dict) 958 959 This would generate the following output: 960 961 ord_dict: 962 [one]: 1 963 [two]: 2 964 [sub]: 965 [three]: 3 966 [four]: 4 967 968 The first level in this example is the line that simply says "ord_dict". 969 The second level is comprised of the dictionary entries with the keys 970 'one', 'two' and 'sub'. The third level is comprised of the last 2 lines 971 (i.e. printed values 3 and 4). 972 973 Given the data structure shown above, the programmer could code the 974 following where fmt is a simple integer value set by calling the verbose() 975 function. 976 977 print_var(ord_dict, fmt=verbose()) 978 979 The output would look like this: 980 981 ord_dict: 982 ord_dict[one]: 1 983 ord_dict[two]: 2 984 ord_dict[sub]: 985 ord_dict[sub][three]: 3 986 ord_dict[sub][four]: 4 987 988 Note the verbose format where the name of the object ("ord_dict") is 989 repeated on every line. 990 991 If the programmer wishes to get more granular with the fmt argument, 992 he/she can specify it as a list where each entry corresponds to a level of 993 the object being printed. The last such list entry governs the print 994 formatting of all subordinate parts of the given object. 995 996 Look at each of the following code examples and their corresponding 997 output. See how the show_type() formatting affects the printing depending 998 on which position it occupies in the fmt list argument: 999 1000 print_var(ord_dict, fmt=[show_type()]) 1001 1002 ord_dict: <collections.OrderedDict> 1003 ord_dict[one]: 1 <int> 1004 ord_dict[two]: 2 <int> 1005 ord_dict[sub]: <collections.OrderedDict> 1006 ord_dict[sub][three]: 3 <int> 1007 ord_dict[sub][four]: 4 <int> 1008 1009 print_var(ord_dict, fmt=[0, show_type()]) 1010 1011 ord_dict: 1012 ord_dict[one]: 1 <int> 1013 ord_dict[two]: 2 <int> 1014 ord_dict[sub]: <collections.OrderedDict> 1015 ord_dict[sub][three]: 3 <int> 1016 ord_dict[sub][four]: 4 <int> 1017 1018 print_var(ord_dict, fmt=[0, 0, show_type()]) 1019 1020 ord_dict: 1021 ord_dict[one]: 1 1022 ord_dict[two]: 2 1023 ord_dict[sub]: 1024 ord_dict[sub][three]: 3 <int> 1025 ord_dict[sub][four]: 4 <int> 1026 1027 Description of argument(s): 1028 fmt The format argument such as is passed to 1029 sprint_varx. This argument may be an 1030 integer or a list of integers. See the 1031 prolog of sprint_varx for more details. 1032 """ 1033 1034 # Make a deep copy of the fmt argument in order to avoid modifying the 1035 # caller's fmt value when it is a list. 1036 fmt = copy.deepcopy(fmt) 1037 try: 1038 # Assume fmt is a list. Pop the first element from the list. 1039 first_element = list_pop(fmt, index=0, default=0) 1040 # Return the first list element along with either 1) the remainder of 1041 # the fmt list if not null or 2) another copy of the first element. 1042 return first_element, fmt if len(fmt) else first_element 1043 except AttributeError: 1044 # fmt is not a list so treat it as a simple integer value. 1045 return fmt, fmt 1046 1047 1048def sprint_varx(var_name, 1049 var_value, 1050 fmt=0, 1051 indent=dft_indent, 1052 col1_width=dft_col1_width, 1053 trailing_char="\n", 1054 key_list=None, 1055 delim=":"): 1056 r""" 1057 Print the var name/value passed to it. If the caller lets col1_width 1058 default, the printing lines up nicely with output generated by the 1059 print_time functions. 1060 1061 Note that the sprint_var function (defined below) can be used to call this 1062 function so that the programmer does not need to pass the var_name. 1063 sprint_var will figure out the var_name. The sprint_var function is the 1064 one that would normally be used by the general user. 1065 1066 For example, the following python code: 1067 1068 first_name = "Mike" 1069 print_time("Doing this...\n") 1070 print_varx("first_name", first_name) 1071 print_time("Doing that...\n") 1072 1073 Will generate output like this: 1074 1075 #(CDT) 2016/08/10 17:34:42.847374 - 0.001285 - Doing this... 1076 first_name: Mike 1077 #(CDT) 2016/08/10 17:34:42.847510 - 0.000136 - Doing that... 1078 1079 This function recognizes several complex types of data such as dict, list 1080 or tuple. 1081 1082 For example, the following python code: 1083 1084 my_dict = dict(one=1, two=2, three=3) 1085 print_var(my_dict) 1086 1087 Will generate the following output: 1088 1089 my_dict: 1090 my_dict[three]: 3 1091 my_dict[two]: 2 1092 my_dict[one]: 1 1093 1094 Description of argument(s). 1095 var_name The name of the variable to be printed. 1096 var_value The value of the variable to be printed. 1097 fmt A bit map to dictate the format of the 1098 output. For printing multi-level objects 1099 like lists and dictionaries, this argument 1100 may also be a list of bit maps. The first 1101 list element pertains to the highest level 1102 of output, the second element pertains to 1103 the 2nd level of output, etc. The last 1104 element in the list pertains to all 1105 subordinate levels. The bits can be set 1106 using the dynamically created functionhs 1107 above. Example: sprint_varx("var1", var1, 1108 fmt=verbose()). Note that these values 1109 can be OR'ed together: print_var(var1, 1110 hexa() | verbose()). If the caller ORs 1111 mutually exclusive bits (hexa() | 1112 octal()), behavior is not guaranteed. The 1113 following features are supported: 1114 hexa Print all integer values in hexadecimal 1115 format. 1116 octal Print all integer values in octal format. 1117 binary Print all integer values in binary format. 1118 blank For blank string values, print "<blank>" 1119 instead of an actual blank. 1120 verbose For structured values like dictionaries, 1121 lists, etc. repeat the name of the 1122 variable on each line to the right of the 1123 key or subscript value. Example: print 1124 "my_dict[key1]" instead of just "[key1]". 1125 quote_keys Quote dictionary keys in the output. 1126 Example: my_dict['key1'] instead of 1127 my_dict[key1]. 1128 show_type Show the type of the data in angled 1129 brackets just to the right of the data. 1130 strip_brackets Strip the brackets from the variable name 1131 portion of the output. This is applicable 1132 when printing complex objects like lists 1133 or dictionaries. 1134 no_header For complex objects like dictionaries, do 1135 not include a header line. This 1136 necessarily means that the member lines 1137 will be indented 2 characters less than 1138 they otherwise would have been. 1139 indent The number of spaces to indent the output. 1140 col1_width The width of the output column containing 1141 the variable name. The default value of 1142 this is adjusted so that the var_value 1143 lines up with text printed via the 1144 print_time function. 1145 trailing_char The character to be used at the end of the 1146 returned string. The default value is a 1147 line feed. 1148 key_list A list of which dictionary keys should be 1149 printed. All others keys will be skipped. 1150 Each value in key_list will be regarded 1151 as a regular expression and it will be 1152 regarded as anchored to the beginning and 1153 ends of the dictionary key being 1154 referenced. For example if key_list is 1155 ["one", "two"], the resulting regex used 1156 will be "^one|two$", i.e. only keys "one" 1157 and "two" from the var_value dictionary 1158 will be printed. As another example, if 1159 the caller were to specify a key_list of 1160 ["one.*"], then only dictionary keys whose 1161 names begin with "one" will be printed. 1162 Note: This argument pertains only to 1163 var_values which are dictionaries. 1164 delim The value to be used to delimit the 1165 variable name from the variable value in 1166 the output. 1167 """ 1168 1169 fmt, child_fmt = parse_fmt(fmt) 1170 1171 if fmt & show_type(): 1172 type_str = "<" + str(type(var_value)).split("'")[1] + ">" 1173 # Compose object type categories. 1174 int_types = get_int_types() 1175 string_types = get_string_types() 1176 simple_types = int_types + string_types + (float, bool, type, type(None)) 1177 # Determine the type. 1178 if type(var_value) in simple_types: 1179 # The data type is simple in the sense that it has no subordinate 1180 # parts. 1181 # Adjust col1_width. 1182 col1_width = col1_width - indent 1183 # Set default value for value_format. 1184 value_format = "%s" 1185 # Process format requests. 1186 if type(var_value) in int_types: 1187 # Process format values pertaining to int types. 1188 if fmt & hexa(): 1189 num_hex_digits = max(dft_num_hex_digits(), 1190 get_req_num_hex_digits(var_value)) 1191 # Convert a negative number to its positive twos complement 1192 # for proper printing. For example, instead of printing -1 as 1193 # "0x-000000000000001" it will be printed as 1194 # "0xffffffffffffffff". 1195 var_value = var_value & (2 ** (num_hex_digits * 4) - 1) 1196 value_format = "0x%0" + str(num_hex_digits) + "x" 1197 elif fmt & octal(): 1198 value_format = "0o%016o" 1199 elif fmt & binary(): 1200 num_digits, remainder = \ 1201 divmod(max(bit_length(var_value), 1), 8) 1202 num_digits *= 8 1203 if remainder: 1204 num_digits += 8 1205 num_digits += 2 1206 value_format = '#0' + str(num_digits) + 'b' 1207 var_value = format(var_value, value_format) 1208 value_format = "%s" 1209 elif type(var_value) in string_types: 1210 # Process format values pertaining to string types. 1211 if fmt & blank() and var_value == "": 1212 value_format = "%s" 1213 var_value = "<blank>" 1214 elif type(var_value) is type: 1215 var_value = str(var_value).split("'")[1] 1216 format_string = "%" + str(indent) + "s%-" + str(col1_width) + "s" \ 1217 + value_format 1218 if fmt & show_type(): 1219 if var_value != "": 1220 format_string += " " 1221 format_string += type_str 1222 format_string += trailing_char 1223 if not (fmt & verbose()): 1224 # Strip everything leading up to the first left square brace. 1225 var_name = re.sub(r".*\[", "[", var_name) 1226 if (fmt & strip_brackets()): 1227 var_name = re.sub(r"[\[\]]", "", var_name) 1228 if value_format == "0x%08x": 1229 return format_string % ("", str(var_name) + delim, 1230 var_value & 0xffffffff) 1231 else: 1232 return format_string % ("", str(var_name) + delim, var_value) 1233 else: 1234 # The data type is complex in the sense that it has subordinate parts. 1235 if (fmt & no_header()): 1236 buffer = "" 1237 else: 1238 # Create header line. 1239 if not (fmt & verbose()): 1240 # Strip everything leading up to the first square brace. 1241 loc_var_name = re.sub(r".*\[", "[", var_name) 1242 else: 1243 loc_var_name = var_name 1244 if (fmt & strip_brackets()): 1245 loc_var_name = re.sub(r"[\[\]]", "", loc_var_name) 1246 format_string = "%" + str(indent) + "s%s\n" 1247 buffer = format_string % ("", loc_var_name + ":") 1248 if fmt & show_type(): 1249 buffer = buffer.replace("\n", " " + type_str + "\n") 1250 indent += 2 1251 try: 1252 length = len(var_value) 1253 except TypeError: 1254 length = 0 1255 ix = 0 1256 loc_trailing_char = "\n" 1257 if is_dict(var_value): 1258 if type(child_fmt) is list: 1259 child_quote_keys = (child_fmt[0] & quote_keys()) 1260 else: 1261 child_quote_keys = (child_fmt & quote_keys()) 1262 for key, value in var_value.items(): 1263 if key_list is not None: 1264 key_list_regex = "^" + "|".join(key_list) + "$" 1265 if not re.match(key_list_regex, key): 1266 continue 1267 ix += 1 1268 if ix == length: 1269 loc_trailing_char = trailing_char 1270 if child_quote_keys: 1271 key = "'" + key + "'" 1272 key = "[" + str(key) + "]" 1273 buffer += sprint_varx(var_name + key, value, child_fmt, indent, 1274 col1_width, loc_trailing_char, key_list, 1275 delim) 1276 elif type(var_value) in (list, tuple, set): 1277 for key, value in enumerate(var_value): 1278 ix += 1 1279 if ix == length: 1280 loc_trailing_char = trailing_char 1281 key = "[" + str(key) + "]" 1282 buffer += sprint_varx(var_name + key, value, child_fmt, indent, 1283 col1_width, loc_trailing_char, key_list, 1284 delim) 1285 elif isinstance(var_value, argparse.Namespace): 1286 for key in var_value.__dict__: 1287 ix += 1 1288 if ix == length: 1289 loc_trailing_char = trailing_char 1290 cmd_buf = "buffer += sprint_varx(var_name + \".\" + str(key)" \ 1291 + ", var_value." + key + ", child_fmt, indent," \ 1292 + " col1_width, loc_trailing_char, key_list," \ 1293 + " delim)" 1294 exec(cmd_buf) 1295 else: 1296 var_type = type(var_value).__name__ 1297 func_name = sys._getframe().f_code.co_name 1298 var_value = "<" + var_type + " type not supported by " + \ 1299 func_name + "()>" 1300 value_format = "%s" 1301 indent -= 2 1302 # Adjust col1_width. 1303 col1_width = col1_width - indent 1304 format_string = "%" + str(indent) + "s%-" \ 1305 + str(col1_width) + "s" + value_format + trailing_char 1306 return format_string % ("", str(var_name) + ":", var_value) 1307 1308 return buffer 1309 1310 return "" 1311 1312 1313def sprint_var(*args, **kwargs): 1314 r""" 1315 Figure out the name of the first argument for the caller and then call 1316 sprint_varx with it. Therefore, the following 2 calls are equivalent: 1317 sprint_varx("var1", var1) 1318 sprint_var(var1) 1319 1320 See sprint_varx for description of arguments. 1321 """ 1322 1323 stack_frame = 2 1324 caller_func_name = sprint_func_name(2) 1325 if caller_func_name.endswith("print_var"): 1326 stack_frame += 1 1327 # Get the name of the first variable passed to this function. 1328 var_name = get_arg_name(None, 1, stack_frame) 1329 return sprint_varx(var_name, *args, **kwargs) 1330 1331 1332def sprint_vars(*args, **kwargs): 1333 r""" 1334 Sprint the values of one or more variables. 1335 1336 Description of argument(s): 1337 args The variable values which are to be 1338 printed. 1339 kwargs See sprint_varx (above) for description of 1340 additional arguments. 1341 """ 1342 1343 stack_frame = 2 1344 caller_func_name = sprint_func_name(2) 1345 if caller_func_name.endswith("print_vars"): 1346 stack_frame += 1 1347 1348 buffer = "" 1349 arg_num = 1 1350 for var_value in args: 1351 var_name = get_arg_name(None, arg_num, stack_frame) 1352 buffer += sprint_varx(var_name, var_value, **kwargs) 1353 arg_num += 1 1354 1355 return buffer 1356 1357 1358def sprint_dashes(indent=dft_indent, 1359 width=80, 1360 line_feed=1, 1361 char="-"): 1362 r""" 1363 Return a string of dashes to the caller. 1364 1365 Description of argument(s): 1366 indent The number of characters to indent the 1367 output. 1368 width The width of the string of dashes. 1369 line_feed Indicates whether the output should end 1370 with a line feed. 1371 char The character to be repeated in the output 1372 string. 1373 """ 1374 1375 width = int(width) 1376 buffer = " " * int(indent) + char * width 1377 if line_feed: 1378 buffer += "\n" 1379 1380 return buffer 1381 1382 1383def sindent(text="", 1384 indent=0): 1385 r""" 1386 Pre-pend the specified number of characters to the text string (i.e. 1387 indent it) and return it. 1388 1389 Description of argument(s): 1390 text The string to be indented. 1391 indent The number of characters to indent the 1392 string. 1393 """ 1394 1395 format_string = "%" + str(indent) + "s%s" 1396 buffer = format_string % ("", text) 1397 1398 return buffer 1399 1400 1401func_line_style_std = None 1402func_line_style_short = 1 1403 1404 1405def sprint_func_line(stack_frame, style=None): 1406 r""" 1407 For the given stack_frame, return a formatted string containing the 1408 function name and all its arguments. 1409 1410 Example: 1411 1412 func1(last_name = 'walsh', first_name = 'mikey') 1413 1414 Description of argument(s): 1415 stack_frame A stack frame (such as is returned by 1416 inspect.stack()). 1417 style Indicates the style or formatting of the 1418 result string. Acceptable values are 1419 shown above. 1420 1421 Description of styles: 1422 func_line_style_std The standard formatting. 1423 func_line_style_short 1) The self parm (associated with methods) 1424 will be dropped. 2) The args and kwargs 1425 values will be treated as special. In 1426 both cases the arg name ('args' or 1427 'kwargs') will be dropped and only the 1428 values will be shown. 1429 """ 1430 1431 func_name = str(stack_frame[3]) 1432 if func_name == "?": 1433 # "?" is the name used when code is not in a function. 1434 func_name = "(none)" 1435 1436 if func_name == "<module>": 1437 # If the func_name is the "main" program, we simply get the command 1438 # line call string. 1439 func_and_args = ' '.join(sys.argv) 1440 else: 1441 # Get the program arguments. 1442 (args, varargs, keywords, locals) =\ 1443 inspect.getargvalues(stack_frame[0]) 1444 1445 args_list = [] 1446 for arg_name in filter(None, args + [varargs, keywords]): 1447 # Get the arg value from frame locals. 1448 arg_value = locals[arg_name] 1449 if arg_name == 'self': 1450 if style == func_line_style_short: 1451 continue 1452 # Manipulations to improve output for class methods. 1453 func_name = arg_value.__class__.__name__ + "." + func_name 1454 args_list.append(arg_name + " = <self>") 1455 elif (style == func_line_style_short 1456 and arg_name == 'args' 1457 and type(arg_value) in (list, tuple)): 1458 if len(arg_value) == 0: 1459 continue 1460 args_list.append(repr(', '.join(arg_value))) 1461 elif (style == func_line_style_short 1462 and arg_name == 'kwargs' 1463 and type(arg_value) is dict): 1464 for key, value in arg_value.items(): 1465 args_list.append(key + "=" + repr(value)) 1466 else: 1467 args_list.append(arg_name + " = " + repr(arg_value)) 1468 args_str = "(" + ', '.join(map(str, args_list)) + ")" 1469 1470 # Now we need to print this in a nicely-wrapped way. 1471 func_and_args = func_name + args_str 1472 1473 return func_and_args 1474 1475 1476def sprint_call_stack(indent=0, 1477 stack_frame_ix=0, 1478 style=None): 1479 r""" 1480 Return a call stack report for the given point in the program with line 1481 numbers, function names and function parameters and arguments. 1482 1483 Sample output: 1484 1485 ------------------------------------------------------------------------- 1486 Python function call stack 1487 1488 Line # Function name and arguments 1489 ------ ------------------------------------------------------------------ 1490 424 sprint_call_stack() 1491 4 print_call_stack() 1492 31 func1(last_name = 'walsh', first_name = 'mikey') 1493 59 /tmp/scr5.py 1494 ------------------------------------------------------------------------- 1495 1496 Description of argument(s): 1497 indent The number of characters to indent each 1498 line of output. 1499 stack_frame_ix The index of the first stack frame which 1500 is to be returned. 1501 style See the sprint_line_func prolog above for 1502 details. 1503 """ 1504 1505 buffer = "" 1506 buffer += sprint_dashes(indent) 1507 buffer += sindent("Python function call stack\n\n", indent) 1508 buffer += sindent("Line # Function name and arguments\n", indent) 1509 buffer += sprint_dashes(indent, 6, 0) + " " + sprint_dashes(0, 73) 1510 1511 # Grab the current program stack. 1512 work_around_inspect_stack_cwd_failure() 1513 current_stack = inspect.stack() 1514 1515 # Process each frame in turn. 1516 format_string = "%6s %s\n" 1517 ix = 0 1518 for stack_frame in current_stack: 1519 if ix < stack_frame_ix: 1520 ix += 1 1521 continue 1522 # Make the line number shown to be the line where one finds the line 1523 # shown. 1524 try: 1525 line_num = str(current_stack[ix + 1][2]) 1526 except IndexError: 1527 line_num = "" 1528 func_and_args = sprint_func_line(stack_frame, style=style) 1529 1530 buffer += sindent(format_string % (line_num, func_and_args), indent) 1531 ix += 1 1532 1533 buffer += sprint_dashes(indent) 1534 1535 return buffer 1536 1537 1538def sprint_executing(stack_frame_ix=None, style=None): 1539 r""" 1540 Print a line indicating what function is executing and with what parameter 1541 values. This is useful for debugging. 1542 1543 Sample output: 1544 1545 #(CDT) 2016/08/25 17:54:27 - Executing: func1(x = 1) 1546 1547 Description of argument(s): 1548 stack_frame_ix The index of the stack frame whose 1549 function info should be returned. If the 1550 caller does not specify a value, this 1551 function will set the value to 1 which is 1552 the index of the caller's stack frame. If 1553 the caller is the wrapper function 1554 "print_executing", this function will bump 1555 it up by 1. 1556 style See the sprint_line_func prolog above for 1557 details. 1558 """ 1559 1560 # If user wants default stack_frame_ix. 1561 if stack_frame_ix is None: 1562 func_name = sys._getframe().f_code.co_name 1563 caller_func_name = sys._getframe(1).f_code.co_name 1564 if caller_func_name.endswith(func_name[1:]): 1565 stack_frame_ix = 2 1566 else: 1567 stack_frame_ix = 1 1568 1569 work_around_inspect_stack_cwd_failure() 1570 stack_frame = inspect.stack()[stack_frame_ix] 1571 1572 func_and_args = sprint_func_line(stack_frame, style) 1573 1574 return sprint_time() + "Executing: " + func_and_args + "\n" 1575 1576 1577def sprint_pgm_header(indent=0, 1578 linefeed=1): 1579 r""" 1580 Return a standardized header that programs should print at the beginning 1581 of the run. It includes useful information like command line, pid, 1582 userid, program parameters, etc. 1583 1584 Description of argument(s): 1585 indent The number of characters to indent each 1586 line of output. 1587 linefeed Indicates whether a line feed be included 1588 at the beginning and end of the report. 1589 """ 1590 1591 col1_width = dft_col1_width + indent 1592 1593 buffer = "" 1594 if linefeed: 1595 buffer = "\n" 1596 1597 if robot_env: 1598 suite_name = BuiltIn().get_variable_value("${suite_name}") 1599 buffer += sindent(sprint_time("Running test suite \"" + suite_name 1600 + "\".\n"), indent) 1601 1602 buffer += sindent(sprint_time() + "Running " + pgm_name + ".\n", indent) 1603 buffer += sindent(sprint_time() + "Program parameter values, etc.:\n\n", 1604 indent) 1605 buffer += sprint_varx("command_line", ' '.join(sys.argv), 0, indent, 1606 col1_width) 1607 # We want the output to show a customized name for the pid and pgid but 1608 # we want it to look like a valid variable name. Therefore, we'll use 1609 # pgm_name_var_name which was set when this module was imported. 1610 buffer += sprint_varx(pgm_name_var_name + "_pid", os.getpid(), 0, indent, 1611 col1_width) 1612 buffer += sprint_varx(pgm_name_var_name + "_pgid", os.getpgrp(), 0, indent, 1613 col1_width) 1614 userid_num = str(os.geteuid()) 1615 try: 1616 username = os.getlogin() 1617 except OSError: 1618 if userid_num == "0": 1619 username = "root" 1620 else: 1621 username = "?" 1622 buffer += sprint_varx("uid", userid_num + " (" + username 1623 + ")", 0, indent, col1_width) 1624 buffer += sprint_varx("gid", str(os.getgid()) + " (" 1625 + str(grp.getgrgid(os.getgid()).gr_name) + ")", 0, 1626 indent, col1_width) 1627 buffer += sprint_varx("host_name", socket.gethostname(), 0, indent, 1628 col1_width) 1629 try: 1630 DISPLAY = os.environ['DISPLAY'] 1631 except KeyError: 1632 DISPLAY = "" 1633 buffer += sprint_var(DISPLAY, 0, indent, col1_width) 1634 PYTHON_VERSION = os.environ.get('PYTHON_VERSION', None) 1635 if PYTHON_VERSION is not None: 1636 buffer += sprint_var(PYTHON_VERSION, 0, indent, col1_width) 1637 PYTHON_PGM_PATH = os.environ.get('PYTHON_PGM_PATH', None) 1638 if PYTHON_PGM_PATH is not None: 1639 buffer += sprint_var(PYTHON_PGM_PATH, 0, indent, col1_width) 1640 python_version = sys.version.replace("\n", "") 1641 buffer += sprint_var(python_version, 0, indent, col1_width) 1642 ROBOT_VERSION = os.environ.get('ROBOT_VERSION', None) 1643 if ROBOT_VERSION is not None: 1644 buffer += sprint_var(ROBOT_VERSION, 0, indent, col1_width) 1645 ROBOT_PGM_PATH = os.environ.get('ROBOT_PGM_PATH', None) 1646 if ROBOT_PGM_PATH is not None: 1647 buffer += sprint_var(ROBOT_PGM_PATH, 0, indent, col1_width) 1648 1649 # TODO: Add code to print caller's parms. 1650 1651 # __builtin__.arg_obj is created by the get_arg module function, 1652 # gen_get_options. 1653 try: 1654 buffer += ga.sprint_args(__builtin__.arg_obj, indent) 1655 except AttributeError: 1656 pass 1657 1658 if robot_env: 1659 # Get value of global parm_list. 1660 parm_list = BuiltIn().get_variable_value("${parm_list}") 1661 1662 for parm in parm_list: 1663 parm_value = BuiltIn().get_variable_value("${" + parm + "}") 1664 buffer += sprint_varx(parm, parm_value, 0, indent, col1_width) 1665 1666 # Setting global program_pid. 1667 BuiltIn().set_global_variable("${program_pid}", os.getpid()) 1668 1669 if linefeed: 1670 buffer += "\n" 1671 1672 return buffer 1673 1674 1675def sprint_error_report(error_text="\n", 1676 indent=2, 1677 format=None, 1678 stack_frame_ix=None): 1679 r""" 1680 Return a string with a standardized report which includes the caller's 1681 error text, the call stack and the program header. 1682 1683 Description of argument(s): 1684 error_text The error text to be included in the 1685 report. The caller should include any 1686 needed linefeeds. 1687 indent The number of characters to indent each 1688 line of output. 1689 format Long or short format. Long includes 1690 extras like lines of dashes, call stack, 1691 etc. 1692 stack_frame_ix The index of the first stack frame which 1693 is to be shown in the print_call_stack 1694 portion of the error report. 1695 """ 1696 1697 # Process input. 1698 indent = int(indent) 1699 if format is None: 1700 if robot_env: 1701 format = 'short' 1702 else: 1703 format = 'long' 1704 error_text = error_text.rstrip('\n') + '\n' 1705 1706 if format == 'short': 1707 return sprint_error(error_text) 1708 1709 buffer = "" 1710 buffer += sprint_dashes(width=120, char="=") 1711 buffer += sprint_error(error_text) 1712 buffer += "\n" 1713 if not stack_frame_ix: 1714 # Calling sprint_call_stack with stack_frame_ix of 0 causes it to 1715 # show itself and this function in the call stack. This is not 1716 # helpful to a debugger and is therefore clutter. We will adjust the 1717 # stack_frame_ix to hide that information. 1718 stack_frame_ix = 1 1719 caller_func_name = sprint_func_name(1) 1720 if caller_func_name.endswith("print_error_report"): 1721 stack_frame_ix += 1 1722 caller_func_name = sprint_func_name(2) 1723 if caller_func_name.endswith("print_error_report"): 1724 stack_frame_ix += 1 1725 buffer += sprint_call_stack(indent, stack_frame_ix) 1726 buffer += sprint_pgm_header(indent) 1727 buffer += sprint_dashes(width=120, char="=") 1728 1729 return buffer 1730 1731 1732def sprint_issuing(cmd_buf, 1733 test_mode=0): 1734 r""" 1735 Return a line indicating a command that the program is about to execute. 1736 1737 Sample output for a cmd_buf of "ls" 1738 1739 #(CDT) 2016/08/25 17:57:36 - Issuing: ls 1740 1741 Description of argument(s): 1742 cmd_buf The command to be executed by caller. 1743 test_mode With test_mode set, the output will look 1744 like this: 1745 1746 #(CDT) 2016/08/25 17:57:36 - (test_mode) Issuing: ls 1747 1748 """ 1749 1750 buffer = sprint_time() 1751 if test_mode: 1752 buffer += "(test_mode) " 1753 if type(cmd_buf) is list: 1754 # Assume this is a robot command in the form of a list. 1755 cmd_buf = ' '.join([str(element) for element in cmd_buf]) 1756 buffer += "Issuing: " + cmd_buf + "\n" 1757 1758 return buffer 1759 1760 1761def sprint_pgm_footer(): 1762 r""" 1763 Return a standardized footer that programs should print at the end of the 1764 program run. It includes useful information like total run time, etc. 1765 """ 1766 1767 buffer = "\n" + sprint_time() + "Finished running " + pgm_name + ".\n\n" 1768 1769 total_time = time.time() - start_time 1770 total_time_string = "%0.6f" % total_time 1771 1772 buffer += sprint_varx(pgm_name_var_name + "_runtime", total_time_string) 1773 buffer += "\n" 1774 1775 return buffer 1776 1777 1778def sprint(buffer=""): 1779 r""" 1780 Simply return the user's buffer. This function is used by the qprint and 1781 dprint functions defined dynamically below, i.e. it would not normally be 1782 called for general use. 1783 1784 Description of argument(s). 1785 buffer This will be returned to the caller. 1786 """ 1787 1788 try: 1789 return str(buffer) 1790 except UnicodeEncodeError: 1791 return buffer 1792 1793 1794def sprintn(buffer=""): 1795 r""" 1796 Simply return the user's buffer with a line feed. This function is used 1797 by the qprint and dprint functions defined dynamically below, i.e. it 1798 would not normally be called for general use. 1799 1800 Description of argument(s). 1801 buffer This will be returned to the caller. 1802 """ 1803 1804 try: 1805 buffer = str(buffer) + "\n" 1806 except UnicodeEncodeError: 1807 buffer = buffer + "\n" 1808 1809 return buffer 1810 1811 1812def gp_print(buffer, 1813 stream='stdout'): 1814 r""" 1815 Print the buffer using either sys.stdout.write or BuiltIn().log_to_console 1816 depending on whether we are running in a robot environment. 1817 1818 This function is intended for use only by other functions in this module. 1819 1820 Description of argument(s): 1821 buffer The string to be printed. 1822 stream Either "stdout" or "stderr". 1823 """ 1824 1825 if robot_env: 1826 BuiltIn().log_to_console(buffer, stream=stream, no_newline=True) 1827 else: 1828 if stream == "stdout": 1829 sys.stdout.write(buffer) 1830 sys.stdout.flush() 1831 else: 1832 sys.stderr.write(buffer) 1833 sys.stderr.flush() 1834 1835 1836def gp_log(buffer): 1837 r""" 1838 Log the buffer using either python logging or BuiltIn().log depending on 1839 whether we are running in a robot environment. 1840 1841 This function is intended for use only by other functions in this module. 1842 1843 Description of argument(s): 1844 buffer The string to be logged. 1845 """ 1846 1847 if robot_env: 1848 BuiltIn().log(buffer) 1849 else: 1850 logging.warning(buffer) 1851 1852 1853def gp_debug_print(buffer): 1854 r""" 1855 Print with gp_print only if gen_print_debug is set. 1856 1857 This function is intended for use only by other functions in this module. 1858 1859 Description of argument(s): 1860 buffer The string to be printed. 1861 """ 1862 1863 if not gen_print_debug: 1864 return 1865 1866 gp_print(buffer) 1867 1868 1869def get_var_value(var_value=None, 1870 default=1, 1871 var_name=None): 1872 r""" 1873 Return either var_value, the corresponding global value or default. 1874 1875 If var_value is not None, it will simply be returned. 1876 1877 If var_value is None, this function will return the corresponding global 1878 value of the variable in question. 1879 1880 Note: For global values, if we are in a robot environment, 1881 get_variable_value will be used. Otherwise, the __builtin__ version of 1882 the variable is returned (which are set by gen_arg.py functions). 1883 1884 If there is no global value associated with the variable, default is 1885 returned. 1886 1887 This function is useful for other functions in setting default values for 1888 parameters. 1889 1890 Example use: 1891 1892 def my_func(quiet=None): 1893 1894 quiet = int(get_var_value(quiet, 0)) 1895 1896 Example calls to my_func(): 1897 1898 In the following example, the caller is explicitly asking to have quiet be 1899 set to 1. 1900 1901 my_func(quiet=1) 1902 1903 In the following example, quiet will be set to the global value of quiet, 1904 if defined, or to 0 (the default). 1905 1906 my_func() 1907 1908 Description of argument(s): 1909 var_value The value to be returned (if not equal to 1910 None). 1911 default The value that is returned if var_value is 1912 None and there is no corresponding global 1913 value defined. 1914 var_name The name of the variable whose value is to 1915 be returned. Under most circumstances, 1916 this value need not be provided. This 1917 function can figure out the name of the 1918 variable passed as var_value. One 1919 exception to this would be if this 1920 function is called directly from a .robot 1921 file. 1922 """ 1923 1924 if var_value is not None: 1925 return var_value 1926 1927 if var_name is None: 1928 var_name = get_arg_name(None, 1, 2) 1929 1930 if robot_env: 1931 var_value = BuiltIn().get_variable_value("${" + var_name + "}", 1932 default) 1933 else: 1934 var_value = getattr(__builtin__, var_name, default) 1935 1936 return var_value 1937 1938 1939def get_stack_var(var_name, 1940 default="", 1941 init_stack_ix=2): 1942 r""" 1943 Starting with the caller's stack level, search upward in the call stack 1944 for a variable named var_name and return its value. If the variable 1945 cannot be found in the stack, attempt to get the global value. If the 1946 variable still cannot be found, return default. 1947 1948 Example code: 1949 1950 def func12(): 1951 my_loc_var1 = get_stack_var('my_var1', "default value") 1952 1953 def func11(): 1954 my_var1 = 11 1955 func12() 1956 1957 In this example, get_stack_var will find the value of my_var1 in func11's 1958 stack and will therefore return the value 11. Therefore, my_loc_var1 1959 would get set to 11. 1960 1961 Description of argument(s): 1962 var_name The name of the variable to be searched 1963 for. 1964 default The value to return if the the variable 1965 cannot be found. 1966 init_stack_ix The initial stack index from which to 1967 begin the search. 0 would be the index of 1968 this func1tion ("get_stack_var"), 1 would 1969 be the index of the function calling this 1970 function, etc. 1971 """ 1972 1973 work_around_inspect_stack_cwd_failure() 1974 default = get_var_value(var_name=var_name, default=default) 1975 return next((frame[0].f_locals[var_name] 1976 for frame in inspect.stack()[init_stack_ix:] 1977 if var_name in frame[0].f_locals), default) 1978 1979 1980# hidden_text is a list of passwords which are to be replaced with asterisks 1981# by print functions defined in this module. 1982hidden_text = [] 1983# password_regex is created based on the contents of hidden_text. 1984password_regex = "" 1985 1986 1987def register_passwords(*args): 1988 r""" 1989 Register one or more passwords which are to be hidden in output produced 1990 by the print functions in this module. 1991 1992 Note: Blank password values are NOT registered. They are simply ignored. 1993 1994 Description of argument(s): 1995 args One or more password values. If a given 1996 password value is already registered, this 1997 function will simply do nothing. 1998 """ 1999 2000 global hidden_text 2001 global password_regex 2002 2003 for password in args: 2004 if password == "": 2005 break 2006 if password in hidden_text: 2007 break 2008 2009 # Place the password into the hidden_text list. 2010 hidden_text.append(password) 2011 # Create a corresponding password regular expression. Escape regex 2012 # special characters too. 2013 password_regex = '(' +\ 2014 '|'.join([re.escape(x) for x in hidden_text]) + ')' 2015 2016 2017def replace_passwords(buffer): 2018 r""" 2019 Return the buffer but with all registered passwords replaced by a string 2020 of asterisks. 2021 2022 2023 Description of argument(s): 2024 buffer The string to be returned but with 2025 passwords replaced. 2026 """ 2027 2028 global password_regex 2029 2030 if int(os.environ.get("DEBUG_SHOW_PASSWORDS", "0")): 2031 return buffer 2032 2033 if password_regex == "": 2034 # No passwords to replace. 2035 return buffer 2036 2037 return re.sub(password_regex, "********", buffer) 2038 2039 2040def create_print_wrapper_funcs(func_names, 2041 stderr_func_names, 2042 replace_dict, 2043 func_prefix=""): 2044 r""" 2045 Generate code for print wrapper functions and return the generated code as 2046 a string. 2047 2048 To illustrate, suppose there is a "print_foo_bar" function in the 2049 func_names list. 2050 This function will... 2051 - Expect that there is an sprint_foo_bar function already in existence. 2052 - Create a print_foo_bar function which calls sprint_foo_bar and prints 2053 the result. 2054 - Create a qprint_foo_bar function which calls upon sprint_foo_bar only if 2055 global value quiet is 0. 2056 - Create a dprint_foo_bar function which calls upon sprint_foo_bar only if 2057 global value debug is 1. 2058 2059 Also, code will be generated to define aliases for each function as well. 2060 Each alias will be created by replacing "print_" in the function name with 2061 "p" For example, the alias for print_foo_bar will be pfoo_bar. 2062 2063 Description of argument(s): 2064 func_names A list of functions for which print 2065 wrapper function code is to be generated. 2066 stderr_func_names A list of functions whose generated code 2067 should print to stderr rather than to 2068 stdout. 2069 replace_dict Please see the create_func_def_string 2070 function in wrap_utils.py for details on 2071 this parameter. This parameter will be 2072 passed directly to create_func_def_string. 2073 func_prefix Prefix to be pre-pended to the generated 2074 function name. 2075 """ 2076 2077 buffer = "" 2078 2079 for func_name in func_names: 2080 if func_name in stderr_func_names: 2081 replace_dict['output_stream'] = "stderr" 2082 else: 2083 replace_dict['output_stream'] = "stdout" 2084 2085 s_func_name = "s" + func_name 2086 q_func_name = "q" + func_name 2087 d_func_name = "d" + func_name 2088 2089 # We don't want to try to redefine the "print" function, thus the 2090 # following if statement. 2091 if func_name != "print": 2092 func_def = create_func_def_string(s_func_name, 2093 func_prefix + func_name, 2094 print_func_template, 2095 replace_dict) 2096 buffer += func_def 2097 2098 func_def = create_func_def_string(s_func_name, 2099 func_prefix + "q" + func_name, 2100 qprint_func_template, replace_dict) 2101 buffer += func_def 2102 2103 func_def = create_func_def_string(s_func_name, 2104 func_prefix + "d" + func_name, 2105 dprint_func_template, replace_dict) 2106 buffer += func_def 2107 2108 func_def = create_func_def_string(s_func_name, 2109 func_prefix + "l" + func_name, 2110 lprint_func_template, replace_dict) 2111 buffer += func_def 2112 2113 # Create abbreviated aliases (e.g. spvar is an alias for sprint_var). 2114 alias = re.sub("print_", "p", func_name) 2115 alias = re.sub("print", "p", alias) 2116 prefixes = [func_prefix + "", "s", func_prefix + "q", 2117 func_prefix + "d", func_prefix + "l"] 2118 for prefix in prefixes: 2119 if alias == "p": 2120 continue 2121 func_def = prefix + alias + " = " + prefix + func_name 2122 buffer += func_def + "\n" 2123 2124 return buffer 2125 2126 2127# In the following section of code, we will dynamically create print versions 2128# for each of the sprint functions defined above. So, for example, where we 2129# have an sprint_time() function defined above that returns the time to the 2130# caller in a string, we will create a corresponding print_time() function 2131# that will print that string directly to stdout. 2132 2133# It can be complicated to follow what's being created below. Here is an 2134# example of the print_time() function that will be created: 2135 2136# def print_time(buffer=''): 2137# gp_print(replace_passwords(sprint_time(buffer=buffer)), stream='stdout') 2138 2139# For each print function defined below, there will also be a qprint, a 2140# dprint and an lprint version defined (e.g. qprint_time, dprint_time, 2141# lprint_time). 2142 2143# The q version of each print function will only print if the quiet variable 2144# is 0. 2145# The d version of each print function will only print if the debug variable 2146# is 1. 2147# The l version of each print function will print the contents as log data. 2148# For conventional programs, this means use of the logging module. For robot 2149# programs it means use of the BuiltIn().log() function. 2150 2151# Templates for the various print wrapper functions. 2152print_func_template = \ 2153 [ 2154 " <mod_qualifier>gp_print(<mod_qualifier>replace_passwords(" 2155 + "<call_line>), stream='<output_stream>')" 2156 ] 2157 2158qprint_func_template = \ 2159 [ 2160 " quiet = <mod_qualifier>get_stack_var(\"quiet\", 0)", 2161 " if int(quiet): return" 2162 ] + print_func_template 2163 2164dprint_func_template = \ 2165 [ 2166 " debug = <mod_qualifier>get_stack_var(\"debug\", 0)", 2167 " if not int(debug): return" 2168 ] + print_func_template 2169 2170lprint_func_template = \ 2171 [ 2172 " <mod_qualifier>set_last_seconds_ix(<mod_qualifier>" 2173 + "lprint_last_seconds_ix())", 2174 " <mod_qualifier>gp_log(<mod_qualifier>replace_passwords" 2175 + "(<call_line>))", 2176 " <mod_qualifier>set_last_seconds_ix(<mod_qualifier>" 2177 + "standard_print_last_seconds_ix())" 2178 ] 2179 2180replace_dict = {'output_stream': 'stdout', 'mod_qualifier': ''} 2181 2182gp_debug_print("robot_env: " + str(robot_env) + "\n") 2183 2184# func_names contains a list of all print functions which should be created 2185# from their sprint counterparts. 2186func_names = ['print_time', 'print_timen', 'print_error', 'print_varx', 2187 'print_var', 'print_vars', 'print_dashes', 'indent', 2188 'print_call_stack', 'print_func_name', 'print_executing', 2189 'print_pgm_header', 'print_issuing', 'print_pgm_footer', 2190 'print_error_report', 'print', 'printn'] 2191 2192# stderr_func_names is a list of functions whose output should go to stderr 2193# rather than stdout. 2194stderr_func_names = ['print_error', 'print_error_report'] 2195 2196func_defs = create_print_wrapper_funcs(func_names, stderr_func_names, 2197 replace_dict) 2198gp_debug_print(func_defs) 2199exec(func_defs) 2200