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 format_string = "%" + str(indent) + "s%s\n" 1245 buffer = format_string % ("", loc_var_name + ":") 1246 if fmt & show_type(): 1247 buffer = buffer.replace("\n", " " + type_str + "\n") 1248 indent += 2 1249 try: 1250 length = len(var_value) 1251 except TypeError: 1252 length = 0 1253 ix = 0 1254 loc_trailing_char = "\n" 1255 if is_dict(var_value): 1256 if type(child_fmt) is list: 1257 child_quote_keys = (child_fmt[0] & quote_keys()) 1258 else: 1259 child_quote_keys = (child_fmt & quote_keys()) 1260 for key, value in var_value.items(): 1261 if key_list is not None: 1262 key_list_regex = "^" + "|".join(key_list) + "$" 1263 if not re.match(key_list_regex, key): 1264 continue 1265 ix += 1 1266 if ix == length: 1267 loc_trailing_char = trailing_char 1268 if child_quote_keys: 1269 key = "'" + key + "'" 1270 key = "[" + str(key) + "]" 1271 buffer += sprint_varx(var_name + key, value, child_fmt, indent, 1272 col1_width, loc_trailing_char, key_list, 1273 delim) 1274 elif type(var_value) in (list, tuple, set): 1275 for key, value in enumerate(var_value): 1276 ix += 1 1277 if ix == length: 1278 loc_trailing_char = trailing_char 1279 key = "[" + str(key) + "]" 1280 buffer += sprint_varx(var_name + key, value, child_fmt, indent, 1281 col1_width, loc_trailing_char, key_list, 1282 delim) 1283 elif isinstance(var_value, argparse.Namespace): 1284 for key in var_value.__dict__: 1285 ix += 1 1286 if ix == length: 1287 loc_trailing_char = trailing_char 1288 cmd_buf = "buffer += sprint_varx(var_name + \".\" + str(key)" \ 1289 + ", var_value." + key + ", child_fmt, indent," \ 1290 + " col1_width, loc_trailing_char, key_list," \ 1291 + " delim)" 1292 exec(cmd_buf) 1293 else: 1294 var_type = type(var_value).__name__ 1295 func_name = sys._getframe().f_code.co_name 1296 var_value = "<" + var_type + " type not supported by " + \ 1297 func_name + "()>" 1298 value_format = "%s" 1299 indent -= 2 1300 # Adjust col1_width. 1301 col1_width = col1_width - indent 1302 format_string = "%" + str(indent) + "s%-" \ 1303 + str(col1_width) + "s" + value_format + trailing_char 1304 return format_string % ("", str(var_name) + ":", var_value) 1305 1306 return buffer 1307 1308 return "" 1309 1310 1311def sprint_var(*args, **kwargs): 1312 r""" 1313 Figure out the name of the first argument for the caller and then call 1314 sprint_varx with it. Therefore, the following 2 calls are equivalent: 1315 sprint_varx("var1", var1) 1316 sprint_var(var1) 1317 1318 See sprint_varx for description of arguments. 1319 """ 1320 1321 stack_frame = 2 1322 caller_func_name = sprint_func_name(2) 1323 if caller_func_name.endswith("print_var"): 1324 stack_frame += 1 1325 # Get the name of the first variable passed to this function. 1326 var_name = get_arg_name(None, 1, stack_frame) 1327 return sprint_varx(var_name, *args, **kwargs) 1328 1329 1330def sprint_vars(*args, **kwargs): 1331 r""" 1332 Sprint the values of one or more variables. 1333 1334 Description of argument(s): 1335 args The variable values which are to be 1336 printed. 1337 kwargs See sprint_varx (above) for description of 1338 additional arguments. 1339 """ 1340 1341 stack_frame = 2 1342 caller_func_name = sprint_func_name(2) 1343 if caller_func_name.endswith("print_vars"): 1344 stack_frame += 1 1345 1346 buffer = "" 1347 arg_num = 1 1348 for var_value in args: 1349 var_name = get_arg_name(None, arg_num, stack_frame) 1350 buffer += sprint_varx(var_name, var_value, **kwargs) 1351 arg_num += 1 1352 1353 return buffer 1354 1355 1356def sprint_dashes(indent=dft_indent, 1357 width=80, 1358 line_feed=1, 1359 char="-"): 1360 r""" 1361 Return a string of dashes to the caller. 1362 1363 Description of argument(s): 1364 indent The number of characters to indent the 1365 output. 1366 width The width of the string of dashes. 1367 line_feed Indicates whether the output should end 1368 with a line feed. 1369 char The character to be repeated in the output 1370 string. 1371 """ 1372 1373 width = int(width) 1374 buffer = " " * int(indent) + char * width 1375 if line_feed: 1376 buffer += "\n" 1377 1378 return buffer 1379 1380 1381def sindent(text="", 1382 indent=0): 1383 r""" 1384 Pre-pend the specified number of characters to the text string (i.e. 1385 indent it) and return it. 1386 1387 Description of argument(s): 1388 text The string to be indented. 1389 indent The number of characters to indent the 1390 string. 1391 """ 1392 1393 format_string = "%" + str(indent) + "s%s" 1394 buffer = format_string % ("", text) 1395 1396 return buffer 1397 1398 1399func_line_style_std = None 1400func_line_style_short = 1 1401 1402 1403def sprint_func_line(stack_frame, style=None): 1404 r""" 1405 For the given stack_frame, return a formatted string containing the 1406 function name and all its arguments. 1407 1408 Example: 1409 1410 func1(last_name = 'walsh', first_name = 'mikey') 1411 1412 Description of argument(s): 1413 stack_frame A stack frame (such as is returned by 1414 inspect.stack()). 1415 style Indicates the style or formatting of the 1416 result string. Acceptable values are 1417 shown above. 1418 1419 Description of styles: 1420 func_line_style_std The standard formatting. 1421 func_line_style_short 1) The self parm (associated with methods) 1422 will be dropped. 2) The args and kwargs 1423 values will be treated as special. In 1424 both cases the arg name ('args' or 1425 'kwargs') will be dropped and only the 1426 values will be shown. 1427 """ 1428 1429 func_name = str(stack_frame[3]) 1430 if func_name == "?": 1431 # "?" is the name used when code is not in a function. 1432 func_name = "(none)" 1433 1434 if func_name == "<module>": 1435 # If the func_name is the "main" program, we simply get the command 1436 # line call string. 1437 func_and_args = ' '.join(sys.argv) 1438 else: 1439 # Get the program arguments. 1440 (args, varargs, keywords, locals) =\ 1441 inspect.getargvalues(stack_frame[0]) 1442 1443 args_list = [] 1444 for arg_name in filter(None, args + [varargs, keywords]): 1445 # Get the arg value from frame locals. 1446 arg_value = locals[arg_name] 1447 if arg_name == 'self': 1448 if style == func_line_style_short: 1449 continue 1450 # Manipulations to improve output for class methods. 1451 func_name = arg_value.__class__.__name__ + "." + func_name 1452 args_list.append(arg_name + " = <self>") 1453 elif (style == func_line_style_short 1454 and arg_name == 'args' 1455 and type(arg_value) in (list, tuple)): 1456 if len(arg_value) == 0: 1457 continue 1458 args_list.append(repr(', '.join(arg_value))) 1459 elif (style == func_line_style_short 1460 and arg_name == 'kwargs' 1461 and type(arg_value) is dict): 1462 for key, value in arg_value.items(): 1463 args_list.append(key + "=" + repr(value)) 1464 else: 1465 args_list.append(arg_name + " = " + repr(arg_value)) 1466 args_str = "(" + ', '.join(map(str, args_list)) + ")" 1467 1468 # Now we need to print this in a nicely-wrapped way. 1469 func_and_args = func_name + args_str 1470 1471 return func_and_args 1472 1473 1474def sprint_call_stack(indent=0, 1475 stack_frame_ix=0, 1476 style=None): 1477 r""" 1478 Return a call stack report for the given point in the program with line 1479 numbers, function names and function parameters and arguments. 1480 1481 Sample output: 1482 1483 ------------------------------------------------------------------------- 1484 Python function call stack 1485 1486 Line # Function name and arguments 1487 ------ ------------------------------------------------------------------ 1488 424 sprint_call_stack() 1489 4 print_call_stack() 1490 31 func1(last_name = 'walsh', first_name = 'mikey') 1491 59 /tmp/scr5.py 1492 ------------------------------------------------------------------------- 1493 1494 Description of argument(s): 1495 indent The number of characters to indent each 1496 line of output. 1497 stack_frame_ix The index of the first stack frame which 1498 is to be returned. 1499 style See the sprint_line_func prolog above for 1500 details. 1501 """ 1502 1503 buffer = "" 1504 buffer += sprint_dashes(indent) 1505 buffer += sindent("Python function call stack\n\n", indent) 1506 buffer += sindent("Line # Function name and arguments\n", indent) 1507 buffer += sprint_dashes(indent, 6, 0) + " " + sprint_dashes(0, 73) 1508 1509 # Grab the current program stack. 1510 work_around_inspect_stack_cwd_failure() 1511 current_stack = inspect.stack() 1512 1513 # Process each frame in turn. 1514 format_string = "%6s %s\n" 1515 ix = 0 1516 for stack_frame in current_stack: 1517 if ix < stack_frame_ix: 1518 ix += 1 1519 continue 1520 # Make the line number shown to be the line where one finds the line 1521 # shown. 1522 try: 1523 line_num = str(current_stack[ix + 1][2]) 1524 except IndexError: 1525 line_num = "" 1526 func_and_args = sprint_func_line(stack_frame, style=style) 1527 1528 buffer += sindent(format_string % (line_num, func_and_args), indent) 1529 ix += 1 1530 1531 buffer += sprint_dashes(indent) 1532 1533 return buffer 1534 1535 1536def sprint_executing(stack_frame_ix=None, style=None): 1537 r""" 1538 Print a line indicating what function is executing and with what parameter 1539 values. This is useful for debugging. 1540 1541 Sample output: 1542 1543 #(CDT) 2016/08/25 17:54:27 - Executing: func1(x = 1) 1544 1545 Description of argument(s): 1546 stack_frame_ix The index of the stack frame whose 1547 function info should be returned. If the 1548 caller does not specify a value, this 1549 function will set the value to 1 which is 1550 the index of the caller's stack frame. If 1551 the caller is the wrapper function 1552 "print_executing", this function will bump 1553 it up by 1. 1554 style See the sprint_line_func prolog above for 1555 details. 1556 """ 1557 1558 # If user wants default stack_frame_ix. 1559 if stack_frame_ix is None: 1560 func_name = sys._getframe().f_code.co_name 1561 caller_func_name = sys._getframe(1).f_code.co_name 1562 if caller_func_name.endswith(func_name[1:]): 1563 stack_frame_ix = 2 1564 else: 1565 stack_frame_ix = 1 1566 1567 work_around_inspect_stack_cwd_failure() 1568 stack_frame = inspect.stack()[stack_frame_ix] 1569 1570 func_and_args = sprint_func_line(stack_frame, style) 1571 1572 return sprint_time() + "Executing: " + func_and_args + "\n" 1573 1574 1575def sprint_pgm_header(indent=0, 1576 linefeed=1): 1577 r""" 1578 Return a standardized header that programs should print at the beginning 1579 of the run. It includes useful information like command line, pid, 1580 userid, program parameters, etc. 1581 1582 Description of argument(s): 1583 indent The number of characters to indent each 1584 line of output. 1585 linefeed Indicates whether a line feed be included 1586 at the beginning and end of the report. 1587 """ 1588 1589 col1_width = dft_col1_width + indent 1590 1591 buffer = "" 1592 if linefeed: 1593 buffer = "\n" 1594 1595 if robot_env: 1596 suite_name = BuiltIn().get_variable_value("${suite_name}") 1597 buffer += sindent(sprint_time("Running test suite \"" + suite_name 1598 + "\".\n"), indent) 1599 1600 buffer += sindent(sprint_time() + "Running " + pgm_name + ".\n", indent) 1601 buffer += sindent(sprint_time() + "Program parameter values, etc.:\n\n", 1602 indent) 1603 buffer += sprint_varx("command_line", ' '.join(sys.argv), 0, indent, 1604 col1_width) 1605 # We want the output to show a customized name for the pid and pgid but 1606 # we want it to look like a valid variable name. Therefore, we'll use 1607 # pgm_name_var_name which was set when this module was imported. 1608 buffer += sprint_varx(pgm_name_var_name + "_pid", os.getpid(), 0, indent, 1609 col1_width) 1610 buffer += sprint_varx(pgm_name_var_name + "_pgid", os.getpgrp(), 0, indent, 1611 col1_width) 1612 userid_num = str(os.geteuid()) 1613 try: 1614 username = os.getlogin() 1615 except OSError: 1616 if userid_num == "0": 1617 username = "root" 1618 else: 1619 username = "?" 1620 buffer += sprint_varx("uid", userid_num + " (" + username 1621 + ")", 0, indent, col1_width) 1622 buffer += sprint_varx("gid", str(os.getgid()) + " (" 1623 + str(grp.getgrgid(os.getgid()).gr_name) + ")", 0, 1624 indent, col1_width) 1625 buffer += sprint_varx("host_name", socket.gethostname(), 0, indent, 1626 col1_width) 1627 try: 1628 DISPLAY = os.environ['DISPLAY'] 1629 except KeyError: 1630 DISPLAY = "" 1631 buffer += sprint_var(DISPLAY, 0, indent, col1_width) 1632 PYTHON_VERSION = os.environ.get('PYTHON_VERSION', None) 1633 if PYTHON_VERSION is not None: 1634 buffer += sprint_var(PYTHON_VERSION, 0, indent, col1_width) 1635 PYTHON_PGM_PATH = os.environ.get('PYTHON_PGM_PATH', None) 1636 if PYTHON_PGM_PATH is not None: 1637 buffer += sprint_var(PYTHON_PGM_PATH, 0, indent, col1_width) 1638 python_version = sys.version.replace("\n", "") 1639 buffer += sprint_var(python_version, 0, indent, col1_width) 1640 ROBOT_VERSION = os.environ.get('ROBOT_VERSION', None) 1641 if ROBOT_VERSION is not None: 1642 buffer += sprint_var(ROBOT_VERSION, 0, indent, col1_width) 1643 ROBOT_PGM_PATH = os.environ.get('ROBOT_PGM_PATH', None) 1644 if ROBOT_PGM_PATH is not None: 1645 buffer += sprint_var(ROBOT_PGM_PATH, 0, indent, col1_width) 1646 1647 # TODO: Add code to print caller's parms. 1648 1649 # __builtin__.arg_obj is created by the get_arg module function, 1650 # gen_get_options. 1651 try: 1652 buffer += ga.sprint_args(__builtin__.arg_obj, indent) 1653 except AttributeError: 1654 pass 1655 1656 if robot_env: 1657 # Get value of global parm_list. 1658 parm_list = BuiltIn().get_variable_value("${parm_list}") 1659 1660 for parm in parm_list: 1661 parm_value = BuiltIn().get_variable_value("${" + parm + "}") 1662 buffer += sprint_varx(parm, parm_value, 0, indent, col1_width) 1663 1664 # Setting global program_pid. 1665 BuiltIn().set_global_variable("${program_pid}", os.getpid()) 1666 1667 if linefeed: 1668 buffer += "\n" 1669 1670 return buffer 1671 1672 1673def sprint_error_report(error_text="\n", 1674 indent=2, 1675 format=None, 1676 stack_frame_ix=None): 1677 r""" 1678 Return a string with a standardized report which includes the caller's 1679 error text, the call stack and the program header. 1680 1681 Description of argument(s): 1682 error_text The error text to be included in the 1683 report. The caller should include any 1684 needed linefeeds. 1685 indent The number of characters to indent each 1686 line of output. 1687 format Long or short format. Long includes 1688 extras like lines of dashes, call stack, 1689 etc. 1690 stack_frame_ix The index of the first stack frame which 1691 is to be shown in the print_call_stack 1692 portion of the error report. 1693 """ 1694 1695 # Process input. 1696 indent = int(indent) 1697 if format is None: 1698 if robot_env: 1699 format = 'short' 1700 else: 1701 format = 'long' 1702 error_text = error_text.rstrip('\n') + '\n' 1703 1704 if format == 'short': 1705 return sprint_error(error_text) 1706 1707 buffer = "" 1708 buffer += sprint_dashes(width=120, char="=") 1709 buffer += sprint_error(error_text) 1710 buffer += "\n" 1711 if not stack_frame_ix: 1712 # Calling sprint_call_stack with stack_frame_ix of 0 causes it to 1713 # show itself and this function in the call stack. This is not 1714 # helpful to a debugger and is therefore clutter. We will adjust the 1715 # stack_frame_ix to hide that information. 1716 stack_frame_ix = 1 1717 caller_func_name = sprint_func_name(1) 1718 if caller_func_name.endswith("print_error_report"): 1719 stack_frame_ix += 1 1720 caller_func_name = sprint_func_name(2) 1721 if caller_func_name.endswith("print_error_report"): 1722 stack_frame_ix += 1 1723 buffer += sprint_call_stack(indent, stack_frame_ix) 1724 buffer += sprint_pgm_header(indent) 1725 buffer += sprint_dashes(width=120, char="=") 1726 1727 return buffer 1728 1729 1730def sprint_issuing(cmd_buf, 1731 test_mode=0): 1732 r""" 1733 Return a line indicating a command that the program is about to execute. 1734 1735 Sample output for a cmd_buf of "ls" 1736 1737 #(CDT) 2016/08/25 17:57:36 - Issuing: ls 1738 1739 Description of argument(s): 1740 cmd_buf The command to be executed by caller. 1741 test_mode With test_mode set, the output will look 1742 like this: 1743 1744 #(CDT) 2016/08/25 17:57:36 - (test_mode) Issuing: ls 1745 1746 """ 1747 1748 buffer = sprint_time() 1749 if test_mode: 1750 buffer += "(test_mode) " 1751 if type(cmd_buf) is list: 1752 # Assume this is a robot command in the form of a list. 1753 cmd_buf = ' '.join([str(element) for element in cmd_buf]) 1754 buffer += "Issuing: " + cmd_buf + "\n" 1755 1756 return buffer 1757 1758 1759def sprint_pgm_footer(): 1760 r""" 1761 Return a standardized footer that programs should print at the end of the 1762 program run. It includes useful information like total run time, etc. 1763 """ 1764 1765 buffer = "\n" + sprint_time() + "Finished running " + pgm_name + ".\n\n" 1766 1767 total_time = time.time() - start_time 1768 total_time_string = "%0.6f" % total_time 1769 1770 buffer += sprint_varx(pgm_name_var_name + "_runtime", total_time_string) 1771 buffer += "\n" 1772 1773 return buffer 1774 1775 1776def sprint(buffer=""): 1777 r""" 1778 Simply return the user's buffer. This function is used by the qprint and 1779 dprint functions defined dynamically below, i.e. it would not normally be 1780 called for general use. 1781 1782 Description of argument(s). 1783 buffer This will be returned to the caller. 1784 """ 1785 1786 try: 1787 return str(buffer) 1788 except UnicodeEncodeError: 1789 return buffer 1790 1791 1792def sprintn(buffer=""): 1793 r""" 1794 Simply return the user's buffer with a line feed. This function is used 1795 by the qprint and dprint functions defined dynamically below, i.e. it 1796 would not normally be called for general use. 1797 1798 Description of argument(s). 1799 buffer This will be returned to the caller. 1800 """ 1801 1802 try: 1803 buffer = str(buffer) + "\n" 1804 except UnicodeEncodeError: 1805 buffer = buffer + "\n" 1806 1807 return buffer 1808 1809 1810def gp_print(buffer, 1811 stream='stdout'): 1812 r""" 1813 Print the buffer using either sys.stdout.write or BuiltIn().log_to_console 1814 depending on whether we are running in a robot environment. 1815 1816 This function is intended for use only by other functions in this module. 1817 1818 Description of argument(s): 1819 buffer The string to be printed. 1820 stream Either "stdout" or "stderr". 1821 """ 1822 1823 if robot_env: 1824 BuiltIn().log_to_console(buffer, stream=stream, no_newline=True) 1825 else: 1826 if stream == "stdout": 1827 sys.stdout.write(buffer) 1828 sys.stdout.flush() 1829 else: 1830 sys.stderr.write(buffer) 1831 sys.stderr.flush() 1832 1833 1834def gp_log(buffer): 1835 r""" 1836 Log the buffer using either python logging or BuiltIn().log depending on 1837 whether we are running in a robot environment. 1838 1839 This function is intended for use only by other functions in this module. 1840 1841 Description of argument(s): 1842 buffer The string to be logged. 1843 """ 1844 1845 if robot_env: 1846 BuiltIn().log(buffer) 1847 else: 1848 logging.warning(buffer) 1849 1850 1851def gp_debug_print(buffer): 1852 r""" 1853 Print with gp_print only if gen_print_debug is set. 1854 1855 This function is intended for use only by other functions in this module. 1856 1857 Description of argument(s): 1858 buffer The string to be printed. 1859 """ 1860 1861 if not gen_print_debug: 1862 return 1863 1864 gp_print(buffer) 1865 1866 1867def get_var_value(var_value=None, 1868 default=1, 1869 var_name=None): 1870 r""" 1871 Return either var_value, the corresponding global value or default. 1872 1873 If var_value is not None, it will simply be returned. 1874 1875 If var_value is None, this function will return the corresponding global 1876 value of the variable in question. 1877 1878 Note: For global values, if we are in a robot environment, 1879 get_variable_value will be used. Otherwise, the __builtin__ version of 1880 the variable is returned (which are set by gen_arg.py functions). 1881 1882 If there is no global value associated with the variable, default is 1883 returned. 1884 1885 This function is useful for other functions in setting default values for 1886 parameters. 1887 1888 Example use: 1889 1890 def my_func(quiet=None): 1891 1892 quiet = int(get_var_value(quiet, 0)) 1893 1894 Example calls to my_func(): 1895 1896 In the following example, the caller is explicitly asking to have quiet be 1897 set to 1. 1898 1899 my_func(quiet=1) 1900 1901 In the following example, quiet will be set to the global value of quiet, 1902 if defined, or to 0 (the default). 1903 1904 my_func() 1905 1906 Description of argument(s): 1907 var_value The value to be returned (if not equal to 1908 None). 1909 default The value that is returned if var_value is 1910 None and there is no corresponding global 1911 value defined. 1912 var_name The name of the variable whose value is to 1913 be returned. Under most circumstances, 1914 this value need not be provided. This 1915 function can figure out the name of the 1916 variable passed as var_value. One 1917 exception to this would be if this 1918 function is called directly from a .robot 1919 file. 1920 """ 1921 1922 if var_value is not None: 1923 return var_value 1924 1925 if var_name is None: 1926 var_name = get_arg_name(None, 1, 2) 1927 1928 if robot_env: 1929 var_value = BuiltIn().get_variable_value("${" + var_name + "}", 1930 default) 1931 else: 1932 var_value = getattr(__builtin__, var_name, default) 1933 1934 return var_value 1935 1936 1937def get_stack_var(var_name, 1938 default="", 1939 init_stack_ix=2): 1940 r""" 1941 Starting with the caller's stack level, search upward in the call stack 1942 for a variable named var_name and return its value. If the variable 1943 cannot be found in the stack, attempt to get the global value. If the 1944 variable still cannot be found, return default. 1945 1946 Example code: 1947 1948 def func12(): 1949 my_loc_var1 = get_stack_var('my_var1', "default value") 1950 1951 def func11(): 1952 my_var1 = 11 1953 func12() 1954 1955 In this example, get_stack_var will find the value of my_var1 in func11's 1956 stack and will therefore return the value 11. Therefore, my_loc_var1 1957 would get set to 11. 1958 1959 Description of argument(s): 1960 var_name The name of the variable to be searched 1961 for. 1962 default The value to return if the the variable 1963 cannot be found. 1964 init_stack_ix The initial stack index from which to 1965 begin the search. 0 would be the index of 1966 this func1tion ("get_stack_var"), 1 would 1967 be the index of the function calling this 1968 function, etc. 1969 """ 1970 1971 work_around_inspect_stack_cwd_failure() 1972 default = get_var_value(var_name=var_name, default=default) 1973 return next((frame[0].f_locals[var_name] 1974 for frame in inspect.stack()[init_stack_ix:] 1975 if var_name in frame[0].f_locals), default) 1976 1977 1978# hidden_text is a list of passwords which are to be replaced with asterisks 1979# by print functions defined in this module. 1980hidden_text = [] 1981# password_regex is created based on the contents of hidden_text. 1982password_regex = "" 1983 1984 1985def register_passwords(*args): 1986 r""" 1987 Register one or more passwords which are to be hidden in output produced 1988 by the print functions in this module. 1989 1990 Note: Blank password values are NOT registered. They are simply ignored. 1991 1992 Description of argument(s): 1993 args One or more password values. If a given 1994 password value is already registered, this 1995 function will simply do nothing. 1996 """ 1997 1998 global hidden_text 1999 global password_regex 2000 2001 for password in args: 2002 if password == "": 2003 break 2004 if password in hidden_text: 2005 break 2006 2007 # Place the password into the hidden_text list. 2008 hidden_text.append(password) 2009 # Create a corresponding password regular expression. Escape regex 2010 # special characters too. 2011 password_regex = '(' +\ 2012 '|'.join([re.escape(x) for x in hidden_text]) + ')' 2013 2014 2015def replace_passwords(buffer): 2016 r""" 2017 Return the buffer but with all registered passwords replaced by a string 2018 of asterisks. 2019 2020 2021 Description of argument(s): 2022 buffer The string to be returned but with 2023 passwords replaced. 2024 """ 2025 2026 global password_regex 2027 2028 if int(os.environ.get("DEBUG_SHOW_PASSWORDS", "0")): 2029 return buffer 2030 2031 if password_regex == "": 2032 # No passwords to replace. 2033 return buffer 2034 2035 return re.sub(password_regex, "********", buffer) 2036 2037 2038def create_print_wrapper_funcs(func_names, 2039 stderr_func_names, 2040 replace_dict, 2041 func_prefix=""): 2042 r""" 2043 Generate code for print wrapper functions and return the generated code as 2044 a string. 2045 2046 To illustrate, suppose there is a "print_foo_bar" function in the 2047 func_names list. 2048 This function will... 2049 - Expect that there is an sprint_foo_bar function already in existence. 2050 - Create a print_foo_bar function which calls sprint_foo_bar and prints 2051 the result. 2052 - Create a qprint_foo_bar function which calls upon sprint_foo_bar only if 2053 global value quiet is 0. 2054 - Create a dprint_foo_bar function which calls upon sprint_foo_bar only if 2055 global value debug is 1. 2056 2057 Also, code will be generated to define aliases for each function as well. 2058 Each alias will be created by replacing "print_" in the function name with 2059 "p" For example, the alias for print_foo_bar will be pfoo_bar. 2060 2061 Description of argument(s): 2062 func_names A list of functions for which print 2063 wrapper function code is to be generated. 2064 stderr_func_names A list of functions whose generated code 2065 should print to stderr rather than to 2066 stdout. 2067 replace_dict Please see the create_func_def_string 2068 function in wrap_utils.py for details on 2069 this parameter. This parameter will be 2070 passed directly to create_func_def_string. 2071 func_prefix Prefix to be pre-pended to the generated 2072 function name. 2073 """ 2074 2075 buffer = "" 2076 2077 for func_name in func_names: 2078 if func_name in stderr_func_names: 2079 replace_dict['output_stream'] = "stderr" 2080 else: 2081 replace_dict['output_stream'] = "stdout" 2082 2083 s_func_name = "s" + func_name 2084 q_func_name = "q" + func_name 2085 d_func_name = "d" + func_name 2086 2087 # We don't want to try to redefine the "print" function, thus the 2088 # following if statement. 2089 if func_name != "print": 2090 func_def = create_func_def_string(s_func_name, 2091 func_prefix + func_name, 2092 print_func_template, 2093 replace_dict) 2094 buffer += func_def 2095 2096 func_def = create_func_def_string(s_func_name, 2097 func_prefix + "q" + func_name, 2098 qprint_func_template, replace_dict) 2099 buffer += func_def 2100 2101 func_def = create_func_def_string(s_func_name, 2102 func_prefix + "d" + func_name, 2103 dprint_func_template, replace_dict) 2104 buffer += func_def 2105 2106 func_def = create_func_def_string(s_func_name, 2107 func_prefix + "l" + func_name, 2108 lprint_func_template, replace_dict) 2109 buffer += func_def 2110 2111 # Create abbreviated aliases (e.g. spvar is an alias for sprint_var). 2112 alias = re.sub("print_", "p", func_name) 2113 alias = re.sub("print", "p", alias) 2114 prefixes = [func_prefix + "", "s", func_prefix + "q", 2115 func_prefix + "d", func_prefix + "l"] 2116 for prefix in prefixes: 2117 if alias == "p": 2118 continue 2119 func_def = prefix + alias + " = " + prefix + func_name 2120 buffer += func_def + "\n" 2121 2122 return buffer 2123 2124 2125# In the following section of code, we will dynamically create print versions 2126# for each of the sprint functions defined above. So, for example, where we 2127# have an sprint_time() function defined above that returns the time to the 2128# caller in a string, we will create a corresponding print_time() function 2129# that will print that string directly to stdout. 2130 2131# It can be complicated to follow what's being created below. Here is an 2132# example of the print_time() function that will be created: 2133 2134# def print_time(buffer=''): 2135# gp_print(replace_passwords(sprint_time(buffer=buffer)), stream='stdout') 2136 2137# For each print function defined below, there will also be a qprint, a 2138# dprint and an lprint version defined (e.g. qprint_time, dprint_time, 2139# lprint_time). 2140 2141# The q version of each print function will only print if the quiet variable 2142# is 0. 2143# The d version of each print function will only print if the debug variable 2144# is 1. 2145# The l version of each print function will print the contents as log data. 2146# For conventional programs, this means use of the logging module. For robot 2147# programs it means use of the BuiltIn().log() function. 2148 2149# Templates for the various print wrapper functions. 2150print_func_template = \ 2151 [ 2152 " <mod_qualifier>gp_print(<mod_qualifier>replace_passwords(" 2153 + "<call_line>), stream='<output_stream>')" 2154 ] 2155 2156qprint_func_template = \ 2157 [ 2158 " quiet = <mod_qualifier>get_stack_var(\"quiet\", 0)", 2159 " if int(quiet): return" 2160 ] + print_func_template 2161 2162dprint_func_template = \ 2163 [ 2164 " debug = <mod_qualifier>get_stack_var(\"debug\", 0)", 2165 " if not int(debug): return" 2166 ] + print_func_template 2167 2168lprint_func_template = \ 2169 [ 2170 " <mod_qualifier>set_last_seconds_ix(<mod_qualifier>" 2171 + "lprint_last_seconds_ix())", 2172 " <mod_qualifier>gp_log(<mod_qualifier>replace_passwords" 2173 + "(<call_line>))", 2174 " <mod_qualifier>set_last_seconds_ix(<mod_qualifier>" 2175 + "standard_print_last_seconds_ix())" 2176 ] 2177 2178replace_dict = {'output_stream': 'stdout', 'mod_qualifier': ''} 2179 2180gp_debug_print("robot_env: " + str(robot_env) + "\n") 2181 2182# func_names contains a list of all print functions which should be created 2183# from their sprint counterparts. 2184func_names = ['print_time', 'print_timen', 'print_error', 'print_varx', 2185 'print_var', 'print_vars', 'print_dashes', 'indent', 2186 'print_call_stack', 'print_func_name', 'print_executing', 2187 'print_pgm_header', 'print_issuing', 'print_pgm_footer', 2188 'print_error_report', 'print', 'printn'] 2189 2190# stderr_func_names is a list of functions whose output should go to stderr 2191# rather than stdout. 2192stderr_func_names = ['print_error', 'print_error_report'] 2193 2194func_defs = create_print_wrapper_funcs(func_names, stderr_func_names, 2195 replace_dict) 2196gp_debug_print(func_defs) 2197exec(func_defs) 2198