1#!/usr/bin/env python 2 3r""" 4This module provides many valuable print functions such as sprint_var, 5sprint_time, sprint_error, sprint_call_stack. 6""" 7 8import sys 9import os 10import time 11import inspect 12import re 13import grp 14import socket 15import argparse 16import __builtin__ 17import logging 18import collections 19 20try: 21 from robot.utils import DotDict 22except ImportError: 23 pass 24 25import gen_arg as ga 26 27# Setting these variables for use both inside this module and by programs 28# importing this module. 29pgm_dir_path = sys.argv[0] 30pgm_name = os.path.basename(pgm_dir_path) 31pgm_dir_name = re.sub("/" + pgm_name, "", pgm_dir_path) + "/" 32 33 34# Some functions (e.g. sprint_pgm_header) have need of a program name value 35# that looks more like a valid variable name. Therefore, we'll swap odd 36# characters like "." out for underscores. 37pgm_name_var_name = pgm_name.replace(".", "_") 38 39# Initialize global values used as defaults by print_time, print_var, etc. 40col1_indent = 0 41 42# Calculate default column width for print_var functions based on environment 43# variable settings. The objective is to make the variable values line up 44# nicely with the time stamps. 45col1_width = 29 46if 'NANOSECONDS' in os.environ: 47 NANOSECONDS = os.environ['NANOSECONDS'] 48else: 49 NANOSECONDS = 0 50 51if NANOSECONDS == "1": 52 col1_width = col1_width + 7 53 54if 'SHOW_ELAPSED_TIME' in os.environ: 55 SHOW_ELAPSED_TIME = os.environ['SHOW_ELAPSED_TIME'] 56else: 57 SHOW_ELAPSED_TIME = 0 58 59if SHOW_ELAPSED_TIME == "1": 60 if NANOSECONDS == "1": 61 col1_width = col1_width + 14 62 else: 63 col1_width = col1_width + 7 64 65# Initialize some time variables used in module functions. 66start_time = time.time() 67sprint_time_last_seconds = start_time 68 69try: 70 # The user can set environment variable "GEN_PRINT_DEBUG" to get debug 71 # output from this module. 72 gen_print_debug = int(os.environ['GEN_PRINT_DEBUG']) 73except KeyError: 74 gen_print_debug = 0 75 76 77############################################################################### 78def sprint_func_name(stack_frame_ix=None): 79 80 r""" 81 Return the function name associated with the indicated stack frame. 82 83 Description of arguments: 84 stack_frame_ix The index of the stack frame whose 85 function name should be returned. If the 86 caller does not specifiy a value, this 87 function will set the value to 1 which is 88 the index of the caller's stack frame. If 89 the caller is the wrapper function 90 "print_func_name", this function will bump 91 it up by 1. 92 """ 93 94 # If user specified no stack_frame_ix, we'll set it to a proper default 95 # value. 96 if stack_frame_ix is None: 97 func_name = sys._getframe().f_code.co_name 98 caller_func_name = sys._getframe(1).f_code.co_name 99 if func_name[1:] == caller_func_name: 100 stack_frame_ix = 2 101 else: 102 stack_frame_ix = 1 103 104 func_name = sys._getframe(stack_frame_ix).f_code.co_name 105 106 return func_name 107 108############################################################################### 109 110 111# get_arg_name is not a print function per se. I have included it in this 112# module because it is used by sprint_var which is found in this module. 113############################################################################### 114def get_arg_name(var, 115 arg_num=1, 116 stack_frame_ix=1): 117 118 r""" 119 Return the "name" of an argument passed to a function. This could be a 120 literal or a variable name. 121 122 Description of arguements: 123 var The variable whose name you want returned. 124 arg_num The arg number (1 through n) whose name 125 you wish to have returned. This value 126 should not exceed the number of arguments 127 allowed by the target function. 128 stack_frame_ix The stack frame index of the target 129 function. This value must be 1 or 130 greater. 1 would indicate get_arg_name's 131 stack frame. 2 would be the caller of 132 get_arg_name's stack frame, etc. 133 134 Example 1: 135 136 my_var = "mike" 137 var_name = get_arg_name(my_var) 138 139 In this example, var_name will receive the value "my_var". 140 141 Example 2: 142 143 def test1(var): 144 # Getting the var name of the first arg to this function, test1. 145 # Note, in this case, it doesn't matter what you pass as the first arg 146 # to get_arg_name since it is the caller's variable name that matters. 147 dummy = 1 148 arg_num = 1 149 stack_frame = 2 150 var_name = get_arg_name(dummy, arg_num, stack_frame) 151 152 # Mainline... 153 154 another_var = "whatever" 155 test1(another_var) 156 157 In this example, var_name will be set to "another_var". 158 159 """ 160 161 # Note: I wish to avoid recursion so I refrain from calling any function 162 # that calls this function (i.e. sprint_var, valid_value, etc.). 163 164 try: 165 # The user can set environment variable "GET_ARG_NAME_DEBUG" to get 166 # debug output from this function. 167 local_debug = os.environ['GET_ARG_NAME_DEBUG'] 168 except KeyError: 169 local_debug = 0 170 171 if arg_num < 1: 172 print_error("Programmer error - Variable \"arg_num\" has an invalid" + 173 " value of \"" + str(arg_num) + "\". The value must be" + 174 " an integer that is greater than 0.\n") 175 # What is the best way to handle errors? Raise exception? I'll 176 # revisit later. 177 return 178 if stack_frame_ix < 1: 179 print_error("Programmer error - Variable \"stack_frame_ix\" has an" + 180 " invalid value of \"" + str(stack_frame_ix) + "\". The" + 181 " value must be an integer that is greater than or equal" + 182 " to 1.\n") 183 return 184 185 if local_debug: 186 debug_indent = 2 187 print(sprint_func_name() + "() parms:") 188 print_varx("var", var, 0, debug_indent) 189 print_varx("arg_num", arg_num, 0, debug_indent) 190 print_varx("stack_frame_ix", stack_frame_ix, 0, debug_indent) 191 192 try: 193 frame, filename, cur_line_no, function_name, lines, index = \ 194 inspect.stack()[stack_frame_ix] 195 except IndexError: 196 print_error("Programmer error - The caller has asked for information" + 197 " about the stack frame at index \"" + 198 str(stack_frame_ix) + "\". However, the stack only" + 199 " contains " + str(len(inspect.stack())) + " entries." + 200 " Therefore the stack frame index is out of range.\n") 201 return 202 203 if local_debug: 204 print("\nVariables retrieved from inspect.stack() function:") 205 print_varx("frame", frame, 0, debug_indent) 206 print_varx("filename", filename, 0, debug_indent) 207 print_varx("cur_line_no", cur_line_no, 0, debug_indent) 208 print_varx("function_name", function_name, 0, debug_indent) 209 print_varx("lines", lines, 0, debug_indent) 210 print_varx("index", index, 0, debug_indent) 211 212 composite_line = lines[0].strip() 213 214 called_func_name = sprint_func_name(stack_frame_ix) 215 # Needed to add a right anchor to func_regex for cases like this where 216 # there is an arg whose name is a substring of the function name. So the 217 # function name needs to be bounded on the right by zero or more spaces 218 # and a left parenthesis. 219 # if not valid_value(whatever, valid_values=["one", "two"]): 220 func_regex = ".*" + called_func_name + "[ ]*\(" 221 # if not re.match(r".*" + called_func_name, composite_line): 222 if not re.match(func_regex, composite_line): 223 # The called function name was not found in the composite line. The 224 # caller may be using a function alias. 225 # I added code to handle pvar, qpvar, dpvar, etc. aliases. 226 # pvar is an alias for print_var. However, when it is used, 227 # sprint_func_name() returns the non-alias version, i.e. "print_var". 228 # Adjusting for that here. 229 alias = re.sub("print_var", "pvar", called_func_name) 230 if local_debug: 231 print_varx("alias", alias, 0, debug_indent) 232 called_func_name = alias 233 func_regex = ".*" + called_func_name + "[ ]*\(" 234 235 # arg_list_etc = re.sub(".*" + called_func_name, "", composite_line) 236 arg_list_etc = "(" + re.sub(func_regex, "", composite_line) 237 if local_debug: 238 print_varx("func_regex", func_regex, 0, debug_indent) 239 print_varx("called_func_name", called_func_name, 0, debug_indent) 240 print_varx("composite_line", composite_line, 0, debug_indent) 241 print_varx("arg_list_etc", arg_list_etc, 0, debug_indent) 242 243 # Parse arg list... 244 # Initialize... 245 nest_level = -1 246 arg_ix = 0 247 args_list = [""] 248 for ix in range(0, len(arg_list_etc)): 249 char = arg_list_etc[ix] 250 # Set the nest_level based on whether we've encounted a parenthesis. 251 if char == "(": 252 nest_level += 1 253 if nest_level == 0: 254 continue 255 elif char == ")": 256 nest_level -= 1 257 if nest_level < 0: 258 break 259 260 # If we reach a comma at base nest level, we are done processing an 261 # argument so we increment arg_ix and initialize a new args_list entry. 262 if char == "," and nest_level == 0: 263 arg_ix += 1 264 args_list.append("") 265 continue 266 267 # For any other character, we append it it to the current arg list 268 # entry. 269 args_list[arg_ix] += char 270 271 # Trim whitespace from each list entry. 272 args_list = [arg.strip() for arg in args_list] 273 274 if arg_num > len(args_list): 275 print_error("Programmer error - The caller has asked for the name of" + 276 " argument number \"" + str(arg_num) + "\" but there " + 277 "were only \"" + str(len(args_list)) + "\" args used:\n" + 278 sprint_varx("args_list", args_list)) 279 return 280 281 argument = args_list[arg_num - 1] 282 283 if local_debug: 284 print_varx("args_list", args_list, 0, debug_indent) 285 print_varx("argument", argument, 0, debug_indent) 286 287 return argument 288 289############################################################################### 290 291 292############################################################################### 293def sprint_time(buffer=""): 294 295 r""" 296 Return the time in the following format. 297 298 Example: 299 300 The following python code... 301 302 sys.stdout.write(sprint_time()) 303 sys.stdout.write("Hi.\n") 304 305 Will result in the following type of output: 306 307 #(CDT) 2016/07/08 15:25:35 - Hi. 308 309 Example: 310 311 The following python code... 312 313 sys.stdout.write(sprint_time("Hi.\n")) 314 315 Will result in the following type of output: 316 317 #(CDT) 2016/08/03 17:12:05 - Hi. 318 319 The following environment variables will affect the formatting as 320 described: 321 NANOSECONDS This will cause the time stamps to be 322 precise to the microsecond (Yes, it 323 probably should have been named 324 MICROSECONDS but the convention was set 325 long ago so we're sticking with it). 326 Example of the output when environment 327 variable NANOSECONDS=1. 328 329 #(CDT) 2016/08/03 17:16:25.510469 - Hi. 330 331 SHOW_ELAPSED_TIME This will cause the elapsed time to be 332 included in the output. This is the 333 amount of time that has elapsed since the 334 last time this function was called. The 335 precision of the elapsed time field is 336 also affected by the value of the 337 NANOSECONDS environment variable. Example 338 of the output when environment variable 339 NANOSECONDS=0 and SHOW_ELAPSED_TIME=1. 340 341 #(CDT) 2016/08/03 17:17:40 - 0 - Hi. 342 343 Example of the output when environment variable NANOSECONDS=1 and 344 SHOW_ELAPSED_TIME=1. 345 346 #(CDT) 2016/08/03 17:18:47.317339 - 0.000046 - Hi. 347 348 Description of arguments. 349 buffer This will be appended to the formatted 350 time string. 351 """ 352 353 global NANOSECONDS 354 global SHOW_ELAPSED_TIME 355 global sprint_time_last_seconds 356 357 seconds = time.time() 358 loc_time = time.localtime(seconds) 359 nanoseconds = "%0.6f" % seconds 360 pos = nanoseconds.find(".") 361 nanoseconds = nanoseconds[pos:] 362 363 time_string = time.strftime("#(%Z) %Y/%m/%d %H:%M:%S", loc_time) 364 if NANOSECONDS == "1": 365 time_string = time_string + nanoseconds 366 367 if SHOW_ELAPSED_TIME == "1": 368 cur_time_seconds = seconds 369 math_string = "%9.9f" % cur_time_seconds + " - " + "%9.9f" % \ 370 sprint_time_last_seconds 371 elapsed_seconds = eval(math_string) 372 if NANOSECONDS == "1": 373 elapsed_seconds = "%11.6f" % elapsed_seconds 374 else: 375 elapsed_seconds = "%4i" % elapsed_seconds 376 sprint_time_last_seconds = cur_time_seconds 377 time_string = time_string + " - " + elapsed_seconds 378 379 return time_string + " - " + buffer 380 381############################################################################### 382 383 384############################################################################### 385def sprint_timen(buffer=""): 386 387 r""" 388 Append a line feed to the buffer, pass it to sprint_time and return the 389 result. 390 """ 391 392 return sprint_time(buffer + "\n") 393 394############################################################################### 395 396 397############################################################################### 398def sprint_error(buffer=""): 399 400 r""" 401 Return a standardized error string. This includes: 402 - A time stamp 403 - The "**ERROR**" string 404 - The caller's buffer string. 405 406 Example: 407 408 The following python code... 409 410 print(sprint_error("Oops.\n")) 411 412 Will result in the following type of output: 413 414 #(CDT) 2016/08/03 17:12:05 - **ERROR** Oops. 415 416 Description of arguments. 417 buffer This will be appended to the formatted 418 error string. 419 """ 420 421 return sprint_time() + "**ERROR** " + buffer 422 423############################################################################### 424 425 426############################################################################### 427def sprint_varx(var_name, 428 var_value, 429 hex=0, 430 loc_col1_indent=col1_indent, 431 loc_col1_width=col1_width, 432 trailing_char="\n"): 433 434 r""" 435 Print the var name/value passed to it. If the caller lets loc_col1_width 436 default, the printing lines up nicely with output generated by the 437 print_time functions. 438 439 Note that the sprint_var function (defined below) can be used to call this 440 function so that the programmer does not need to pass the var_name. 441 sprint_var will figure out the var_name. The sprint_var function is the 442 one that would normally be used by the general user. 443 444 For example, the following python code: 445 446 first_name = "Mike" 447 print_time("Doing this...\n") 448 print_varx("first_name", first_name) 449 print_time("Doing that...\n") 450 451 Will generate output like this: 452 453 #(CDT) 2016/08/10 17:34:42.847374 - 0.001285 - Doing this... 454 first_name: Mike 455 #(CDT) 2016/08/10 17:34:42.847510 - 0.000136 - Doing that... 456 457 This function recognizes several complex types of data such as dict, list 458 or tuple. 459 460 For example, the following python code: 461 462 my_dict = dict(one=1, two=2, three=3) 463 print_var(my_dict) 464 465 Will generate the following output: 466 467 my_dict: 468 my_dict[three]: 3 469 my_dict[two]: 2 470 my_dict[one]: 1 471 472 Description of arguments. 473 var_name The name of the variable to be printed. 474 var_value The value of the variable to be printed. 475 hex This indicates that the value should be 476 printed in hex format. It is the user's 477 responsibility to ensure that a var_value 478 contains a valid hex number. For string 479 var_values, this will be interpreted as 480 show_blanks which means that blank values 481 will be printed as "<blank>". 482 loc_col1_indent The number of spaces to indent the output. 483 loc_col1_width The width of the output column containing 484 the variable name. The default value of 485 this is adjusted so that the var_value 486 lines up with text printed via the 487 print_time function. 488 trailing_char The character to be used at the end of the 489 returned string. The default value is a 490 line feed. 491 """ 492 493 # Determine the type 494 if type(var_value) in (int, float, bool, str, unicode) \ 495 or var_value is None: 496 # The data type is simple in the sense that it has no subordinate 497 # parts. 498 # Adjust loc_col1_width. 499 loc_col1_width = loc_col1_width - loc_col1_indent 500 # See if the user wants the output in hex format. 501 if hex: 502 if type(var_value) not in (int, long): 503 value_format = "%s" 504 if var_value is "": 505 var_value = "<blank>" 506 else: 507 value_format = "0x%08x" 508 else: 509 value_format = "%s" 510 format_string = "%" + str(loc_col1_indent) + "s%-" \ 511 + str(loc_col1_width) + "s" + value_format + trailing_char 512 return format_string % ("", var_name + ":", var_value) 513 else: 514 # The data type is complex in the sense that it has subordinate parts. 515 format_string = "%" + str(loc_col1_indent) + "s%s\n" 516 buffer = format_string % ("", var_name + ":") 517 loc_col1_indent += 2 518 try: 519 length = len(var_value) 520 except TypeError: 521 pass 522 ix = 0 523 loc_trailing_char = "\n" 524 type_is_dict = 0 525 try: 526 if type(var_value) in (dict, collections.OrderedDict): 527 type_is_dict = 1 528 except AttributeError: 529 try: 530 if type(var_value) is DotDict: 531 type_is_dict = 1 532 except NameError: 533 pass 534 if type_is_dict: 535 for key, value in var_value.iteritems(): 536 ix += 1 537 if ix == length: 538 loc_trailing_char = trailing_char 539 buffer += sprint_varx(var_name + "[" + key + "]", value, hex, 540 loc_col1_indent, loc_col1_width, 541 loc_trailing_char) 542 elif type(var_value) in (list, tuple, set): 543 for key, value in enumerate(var_value): 544 ix += 1 545 if ix == length: 546 loc_trailing_char = trailing_char 547 buffer += sprint_varx(var_name + "[" + str(key) + "]", value, 548 hex, loc_col1_indent, loc_col1_width, 549 loc_trailing_char) 550 elif type(var_value) is argparse.Namespace: 551 for key in var_value.__dict__: 552 ix += 1 553 if ix == length: 554 loc_trailing_char = trailing_char 555 cmd_buf = "buffer += sprint_varx(var_name + \".\" + str(key)" \ 556 + ", var_value." + key + ", hex, loc_col1_indent," \ 557 + " loc_col1_width, loc_trailing_char)" 558 exec(cmd_buf) 559 else: 560 var_type = type(var_value).__name__ 561 func_name = sys._getframe().f_code.co_name 562 var_value = "<" + var_type + " type not supported by " + \ 563 func_name + "()>" 564 value_format = "%s" 565 loc_col1_indent -= 2 566 # Adjust loc_col1_width. 567 loc_col1_width = loc_col1_width - loc_col1_indent 568 format_string = "%" + str(loc_col1_indent) + "s%-" \ 569 + str(loc_col1_width) + "s" + value_format + trailing_char 570 return format_string % ("", var_name + ":", var_value) 571 return buffer 572 573 return "" 574 575############################################################################### 576 577 578############################################################################### 579def sprint_var(*args): 580 581 r""" 582 Figure out the name of the first argument for you and then call 583 sprint_varx with it. Therefore, the following 2 calls are equivalent: 584 sprint_varx("var1", var1) 585 sprint_var(var1) 586 """ 587 588 # Get the name of the first variable passed to this function. 589 stack_frame = 2 590 caller_func_name = sprint_func_name(2) 591 if caller_func_name.endswith("print_var"): 592 stack_frame += 1 593 var_name = get_arg_name(None, 1, stack_frame) 594 return sprint_varx(var_name, *args) 595 596############################################################################### 597 598 599############################################################################### 600def sprint_vars(*args): 601 602 r""" 603 Sprint the values of one or more variables. 604 605 Description of args: 606 args: 607 If the first argument is an integer, it will be interpreted to be the 608 "indent" value. 609 If the second argument is an integer, it will be interpreted to be the 610 "col1_width" value. 611 If the third argument is an integer, it will be interpreted to be the 612 "hex" value. 613 All remaining parms are considered variable names which are to be 614 sprinted. 615 """ 616 617 if len(args) == 0: 618 return 619 620 # Get the name of the first variable passed to this function. 621 stack_frame = 2 622 caller_func_name = sprint_func_name(2) 623 if caller_func_name.endswith("print_vars"): 624 stack_frame += 1 625 626 parm_num = 1 627 628 # Create list from args (which is a tuple) so that it can be modified. 629 args_list = list(args) 630 631 var_name = get_arg_name(None, parm_num, stack_frame) 632 # See if parm 1 is to be interpreted as "indent". 633 try: 634 if type(int(var_name)) is int: 635 indent = int(var_name) 636 args_list.pop(0) 637 parm_num += 1 638 except ValueError: 639 indent = 0 640 641 var_name = get_arg_name(None, parm_num, stack_frame) 642 # See if parm 1 is to be interpreted as "col1_width". 643 try: 644 if type(int(var_name)) is int: 645 loc_col1_width = int(var_name) 646 args_list.pop(0) 647 parm_num += 1 648 except ValueError: 649 loc_col1_width = col1_width 650 651 var_name = get_arg_name(None, parm_num, stack_frame) 652 # See if parm 1 is to be interpreted as "hex". 653 try: 654 if type(int(var_name)) is int: 655 hex = int(var_name) 656 args_list.pop(0) 657 parm_num += 1 658 except ValueError: 659 hex = 0 660 661 buffer = "" 662 for var_value in args_list: 663 var_name = get_arg_name(None, parm_num, stack_frame) 664 buffer += sprint_varx(var_name, var_value, hex, indent, loc_col1_width) 665 parm_num += 1 666 667 return buffer 668 669############################################################################### 670 671 672############################################################################### 673def lprint_varx(var_name, 674 var_value, 675 hex=0, 676 loc_col1_indent=col1_indent, 677 loc_col1_width=col1_width, 678 log_level=getattr(logging, 'INFO')): 679 680 r""" 681 Send sprint_varx output to logging. 682 """ 683 684 logging.log(log_level, sprint_varx(var_name, var_value, hex, 685 loc_col1_indent, loc_col1_width, "")) 686 687############################################################################### 688 689 690############################################################################### 691def lprint_var(*args): 692 693 r""" 694 Figure out the name of the first argument for you and then call 695 lprint_varx with it. Therefore, the following 2 calls are equivalent: 696 lprint_varx("var1", var1) 697 lprint_var(var1) 698 """ 699 700 # Get the name of the first variable passed to this function. 701 stack_frame = 2 702 caller_func_name = sprint_func_name(2) 703 if caller_func_name.endswith("print_var"): 704 stack_frame += 1 705 var_name = get_arg_name(None, 1, stack_frame) 706 lprint_varx(var_name, *args) 707 708############################################################################### 709 710 711############################################################################### 712def sprint_dashes(indent=col1_indent, 713 width=80, 714 line_feed=1, 715 char="-"): 716 717 r""" 718 Return a string of dashes to the caller. 719 720 Description of arguements: 721 indent The number of characters to indent the 722 output. 723 width The width of the string of dashes. 724 line_feed Indicates whether the output should end 725 with a line feed. 726 char The character to be repeated in the output 727 string. 728 """ 729 730 width = int(width) 731 buffer = " "*int(indent) + char*width 732 if line_feed: 733 buffer += "\n" 734 735 return buffer 736 737############################################################################### 738 739 740############################################################################### 741def sindent(text="", 742 indent=0): 743 744 r""" 745 Pre-pend the specified number of characters to the text string (i.e. 746 indent it) and return it. 747 748 Description of arguments: 749 text The string to be indented. 750 indent The number of characters to indent the 751 string. 752 """ 753 754 format_string = "%" + str(indent) + "s%s" 755 buffer = format_string % ("", text) 756 757 return buffer 758 759############################################################################### 760 761 762############################################################################### 763def sprint_call_stack(indent=0, 764 stack_frame_ix=0): 765 766 r""" 767 Return a call stack report for the given point in the program with line 768 numbers, function names and function parameters and arguments. 769 770 Sample output: 771 772 ------------------------------------------------------------------------- 773 Python function call stack 774 775 Line # Function name and arguments 776 ------ ------------------------------------------------------------------ 777 424 sprint_call_stack () 778 4 print_call_stack () 779 31 func1 (last_name = 'walsh', first_name = 'mikey') 780 59 /tmp/scr5.py 781 ------------------------------------------------------------------------- 782 783 Description of arguments: 784 indent The number of characters to indent each 785 line of output. 786 stack_frame_ix The index of the first stack frame which 787 is to be returned. 788 """ 789 790 buffer = "" 791 buffer += sprint_dashes(indent) 792 buffer += sindent("Python function call stack\n\n", indent) 793 buffer += sindent("Line # Function name and arguments\n", indent) 794 buffer += sprint_dashes(indent, 6, 0) + " " + sprint_dashes(0, 73) 795 796 # Grab the current program stack. 797 current_stack = inspect.stack() 798 799 # Process each frame in turn. 800 format_string = "%6s %s\n" 801 ix = 0 802 for stack_frame in current_stack: 803 if ix < stack_frame_ix: 804 ix += 1 805 continue 806 lineno = str(stack_frame[2]) 807 func_name = str(stack_frame[3]) 808 if func_name == "?": 809 # "?" is the name used when code is not in a function. 810 func_name = "(none)" 811 812 if func_name == "<module>": 813 # If the func_name is the "main" program, we simply get the 814 # command line call string. 815 func_and_args = ' '.join(sys.argv) 816 else: 817 # Get the program arguments. 818 arg_vals = inspect.getargvalues(stack_frame[0]) 819 function_parms = arg_vals[0] 820 frame_locals = arg_vals[3] 821 822 args_list = [] 823 for arg_name in function_parms: 824 # Get the arg value from frame locals. 825 arg_value = frame_locals[arg_name] 826 args_list.append(arg_name + " = " + repr(arg_value)) 827 args_str = "(" + ', '.join(map(str, args_list)) + ")" 828 829 # Now we need to print this in a nicely-wrapped way. 830 func_and_args = func_name + " " + args_str 831 832 buffer += sindent(format_string % (lineno, func_and_args), indent) 833 ix += 1 834 835 buffer += sprint_dashes(indent) 836 837 return buffer 838 839############################################################################### 840 841 842############################################################################### 843def sprint_executing(stack_frame_ix=None): 844 845 r""" 846 Print a line indicating what function is executing and with what parameter 847 values. This is useful for debugging. 848 849 Sample output: 850 851 #(CDT) 2016/08/25 17:54:27 - Executing: func1 (x = 1) 852 853 Description of arguments: 854 stack_frame_ix The index of the stack frame whose 855 function info should be returned. If the 856 caller does not specifiy a value, this 857 function will set the value to 1 which is 858 the index of the caller's stack frame. If 859 the caller is the wrapper function 860 "print_executing", this function will bump 861 it up by 1. 862 """ 863 864 # If user wants default stack_frame_ix. 865 if stack_frame_ix is None: 866 func_name = sys._getframe().f_code.co_name 867 caller_func_name = sys._getframe(1).f_code.co_name 868 if caller_func_name.endswith(func_name[1:]): 869 stack_frame_ix = 2 870 else: 871 stack_frame_ix = 1 872 873 stack_frame = inspect.stack()[stack_frame_ix] 874 875 func_name = str(stack_frame[3]) 876 if func_name == "?": 877 # "?" is the name used when code is not in a function. 878 func_name = "(none)" 879 880 if func_name == "<module>": 881 # If the func_name is the "main" program, we simply get the command 882 # line call string. 883 func_and_args = ' '.join(sys.argv) 884 else: 885 # Get the program arguments. 886 arg_vals = inspect.getargvalues(stack_frame[0]) 887 function_parms = arg_vals[0] 888 frame_locals = arg_vals[3] 889 890 args_list = [] 891 for arg_name in function_parms: 892 # Get the arg value from frame locals. 893 arg_value = frame_locals[arg_name] 894 args_list.append(arg_name + " = " + repr(arg_value)) 895 args_str = "(" + ', '.join(map(str, args_list)) + ")" 896 897 # Now we need to print this in a nicely-wrapped way. 898 func_and_args = func_name + " " + args_str 899 900 return sprint_time() + "Executing: " + func_and_args + "\n" 901 902############################################################################### 903 904 905############################################################################### 906def sprint_pgm_header(indent=0, 907 linefeed=1): 908 909 r""" 910 Return a standardized header that programs should print at the beginning 911 of the run. It includes useful information like command line, pid, 912 userid, program parameters, etc. 913 914 Description of arguments: 915 indent The number of characters to indent each 916 line of output. 917 linefeed Indicates whether a line feed be included 918 at the beginning and end of the report. 919 """ 920 921 loc_col1_width = col1_width + indent 922 923 buffer = "" 924 if linefeed: 925 buffer = "\n" 926 927 buffer += sindent(sprint_time() + "Running " + pgm_name + ".\n", indent) 928 buffer += sindent(sprint_time() + "Program parameter values, etc.:\n\n", 929 indent) 930 buffer += sprint_varx("command_line", ' '.join(sys.argv), 0, indent, 931 loc_col1_width) 932 # We want the output to show a customized name for the pid and pgid but 933 # we want it to look like a valid variable name. Therefore, we'll use 934 # pgm_name_var_name which was set when this module was imported. 935 buffer += sprint_varx(pgm_name_var_name + "_pid", os.getpid(), 0, indent, 936 loc_col1_width) 937 buffer += sprint_varx(pgm_name_var_name + "_pgid", os.getpgrp(), 0, indent, 938 loc_col1_width) 939 buffer += sprint_varx("uid", str(os.geteuid()) + " (" + os.getlogin() + 940 ")", 0, indent, loc_col1_width) 941 buffer += sprint_varx("gid", str(os.getgid()) + " (" + 942 str(grp.getgrgid(os.getgid()).gr_name) + ")", 0, 943 indent, loc_col1_width) 944 buffer += sprint_varx("host_name", socket.gethostname(), 0, indent, 945 loc_col1_width) 946 buffer += sprint_varx("DISPLAY", os.environ['DISPLAY'], 0, indent, 947 loc_col1_width) 948 # I want to add code to print caller's parms. 949 950 # __builtin__.arg_obj is created by the get_arg module function, 951 # gen_get_options. 952 try: 953 buffer += ga.sprint_args(__builtin__.arg_obj, indent) 954 except AttributeError: 955 pass 956 957 if linefeed: 958 buffer += "\n" 959 960 return buffer 961 962############################################################################### 963 964 965############################################################################### 966def sprint_error_report(error_text="\n", 967 indent=2): 968 969 r""" 970 Return a string with a standardized report which includes the caller's 971 error text, the call stack and the program header. 972 973 Description of args: 974 error_text The error text to be included in the 975 report. The caller should include any 976 needed linefeeds. 977 indent The number of characters to indent each 978 line of output. 979 """ 980 981 buffer = "" 982 buffer += sprint_dashes(width=120, char="=") 983 buffer += sprint_error(error_text) 984 buffer += "\n" 985 # Calling sprint_call_stack with stack_frame_ix of 0 causes it to show 986 # itself and this function in the call stack. This is not helpful to a 987 # debugger and is therefore clutter. We will adjust the stack_frame_ix to 988 # hide that information. 989 stack_frame_ix = 2 990 caller_func_name = sprint_func_name(2) 991 if caller_func_name.endswith("print_error_report"): 992 stack_frame_ix += 1 993 buffer += sprint_call_stack(indent, stack_frame_ix) 994 buffer += sprint_pgm_header(indent) 995 buffer += sprint_dashes(width=120, char="=") 996 997 return buffer 998 999############################################################################### 1000 1001 1002############################################################################### 1003def sprint_issuing(cmd_buf, 1004 test_mode=0): 1005 1006 r""" 1007 Return a line indicating a command that the program is about to execute. 1008 1009 Sample output for a cmd_buf of "ls" 1010 1011 #(CDT) 2016/08/25 17:57:36 - Issuing: ls 1012 1013 Description of args: 1014 cmd_buf The command to be executed by caller. 1015 test_mode With test_mode set, your output will look 1016 like this: 1017 1018 #(CDT) 2016/08/25 17:57:36 - (test_mode) Issuing: ls 1019 1020 """ 1021 1022 buffer = sprint_time() 1023 if test_mode: 1024 buffer += "(test_mode) " 1025 buffer += "Issuing: " + cmd_buf + "\n" 1026 1027 return buffer 1028 1029############################################################################### 1030 1031 1032############################################################################### 1033def sprint_pgm_footer(): 1034 1035 r""" 1036 Return a standardized footer that programs should print at the end of the 1037 program run. It includes useful information like total run time, etc. 1038 """ 1039 1040 buffer = "\n" + sprint_time() + "Finished running " + pgm_name + ".\n\n" 1041 1042 total_time = time.time() - start_time 1043 total_time_string = "%0.6f" % total_time 1044 1045 buffer += sprint_varx(pgm_name_var_name + "_runtime", total_time_string) 1046 buffer += "\n" 1047 1048 return buffer 1049 1050############################################################################### 1051 1052 1053############################################################################### 1054def sprint(buffer=""): 1055 1056 r""" 1057 Simply return the user's buffer. This function is used by the qprint and 1058 dprint functions defined dynamically below, i.e. it would not normally be 1059 called for general use. 1060 1061 Description of arguments. 1062 buffer This will be returned to the caller. 1063 """ 1064 1065 return str(buffer) 1066 1067############################################################################### 1068 1069 1070############################################################################### 1071def sprintn(buffer=""): 1072 1073 r""" 1074 Simply return the user's buffer with a line feed. This function is used 1075 by the qprint and dprint functions defined dynamically below, i.e. it 1076 would not normally be called for general use. 1077 1078 Description of arguments. 1079 buffer This will be returned to the caller. 1080 """ 1081 1082 buffer = str(buffer) + "\n" 1083 1084 return buffer 1085 1086############################################################################### 1087 1088 1089############################################################################### 1090# In the following section of code, we will dynamically create print versions 1091# for each of the sprint functions defined above. So, for example, where we 1092# have an sprint_time() function defined above that returns the time to the 1093# caller in a string, we will create a corresponding print_time() function 1094# that will print that string directly to stdout. 1095 1096# It can be complicated to follow what's being creaed by the exec statement 1097# below. Here is an example of the print_time() function that will be created: 1098 1099# def print_time(*args): 1100# s_funcname = "s" + sys._getframe().f_code.co_name 1101# s_func = getattr(sys.modules[__name__], s_funcname) 1102# sys.stdout.write(s_func(*args)) 1103 1104# Here are comments describing the 3 lines in the body of the created function. 1105# Calculate the "s" version of this function name (e.g. if this function name 1106# is print_time, we want s_funcname to be "sprint_time". 1107# Put a reference to the "s" version of this function in s_func. 1108# Call the "s" version of this function passing it all of our arguments. 1109# Write the result to stdout. 1110 1111# func_names contains a list of all print functions which should be created 1112# from their sprint counterparts. 1113func_names = ['print_time', 'print_timen', 'print_error', 'print_varx', 1114 'print_var', 'print_vars', 'print_dashes', 'indent', 1115 'print_call_stack', 'print_func_name', 'print_executing', 1116 'print_pgm_header', 'print_issuing', 'print_pgm_footer', 1117 'print_error_report', 'print', 'printn'] 1118 1119for func_name in func_names: 1120 if func_name == "print": 1121 continue 1122 # Create abbreviated aliases (e.g. spvar is an alias for sprint_var). 1123 alias = re.sub("print_", "p", func_name) 1124 pgm_definition_string = "s" + alias + " = s" + func_name 1125 if gen_print_debug: 1126 print(pgm_definition_string) 1127 exec(pgm_definition_string) 1128 1129for func_name in func_names: 1130 if func_name == "print_error" or func_name == "print_error_report": 1131 output_stream = "stderr" 1132 else: 1133 output_stream = "stdout" 1134 func_def = \ 1135 [ 1136 "def " + func_name + "(*args):", 1137 " s_func_name = \"s\" + sys._getframe().f_code.co_name", 1138 " s_func = getattr(sys.modules[__name__], s_func_name)", 1139 " sys." + output_stream + ".write(s_func(*args))", 1140 " sys." + output_stream + ".flush()" 1141 ] 1142 if func_name != "print": 1143 pgm_definition_string = '\n'.join(func_def) 1144 if gen_print_debug: 1145 print(pgm_definition_string) 1146 exec(pgm_definition_string) 1147 1148 # Now define "q" versions of each print function. 1149 func_def = \ 1150 [ 1151 "def q" + func_name + "(*args):", 1152 " if __builtin__.quiet: return", 1153 " s_func_name = \"s" + func_name + "\"", 1154 " s_func = getattr(sys.modules[__name__], s_func_name)", 1155 " sys." + output_stream + ".write(s_func(*args))", 1156 " sys." + output_stream + ".flush()" 1157 ] 1158 1159 pgm_definition_string = '\n'.join(func_def) 1160 if gen_print_debug: 1161 print(pgm_definition_string) 1162 exec(pgm_definition_string) 1163 1164 # Now define "d" versions of each print function. 1165 func_def = \ 1166 [ 1167 "def d" + func_name + "(*args):", 1168 " if not __builtin__.debug: return", 1169 " s_func_name = \"s" + func_name + "\"", 1170 " s_func = getattr(sys.modules[__name__], s_func_name)", 1171 " sys." + output_stream + ".write(s_func(*args))", 1172 " sys." + output_stream + ".flush()" 1173 ] 1174 1175 pgm_definition_string = '\n'.join(func_def) 1176 if gen_print_debug: 1177 print(pgm_definition_string) 1178 exec(pgm_definition_string) 1179 1180 # Now define "l" versions of each print function. 1181 func_def = \ 1182 [ 1183 "def l" + func_name + "(*args):", 1184 " s_func_name = \"s" + func_name + "\"", 1185 " s_func = getattr(sys.modules[__name__], s_func_name)", 1186 " logging.log(getattr(logging, 'INFO'), s_func(*args))", 1187 ] 1188 1189 if func_name != "print_varx" and func_name != "print_var": 1190 pgm_definition_string = '\n'.join(func_def) 1191 if gen_print_debug: 1192 print(pgm_definition_string) 1193 exec(pgm_definition_string) 1194 1195 if func_name == "print": 1196 continue 1197 1198 # Create abbreviated aliases (e.g. pvar is an alias for print_var). 1199 alias = re.sub("print_", "p", func_name) 1200 pgm_definition_string = alias + " = " + func_name 1201 if gen_print_debug: 1202 print(pgm_definition_string) 1203 exec(pgm_definition_string) 1204 1205 # Create abbreviated aliases (e.g. qpvar is an alias for qprint_var). 1206 alias = re.sub("print_", "p", func_name) 1207 pgm_definition_string = "q" + alias + " = q" + func_name 1208 if gen_print_debug: 1209 print(pgm_definition_string) 1210 exec(pgm_definition_string) 1211 1212 # Create abbreviated aliases (e.g. dpvar is an alias for dprint_var). 1213 alias = re.sub("print_", "p", func_name) 1214 pgm_definition_string = "d" + alias + " = d" + func_name 1215 if gen_print_debug: 1216 print(pgm_definition_string) 1217 exec(pgm_definition_string) 1218 1219 # Create abbreviated aliases (e.g. lpvar is an alias for lprint_var). 1220 alias = re.sub("print_", "p", func_name) 1221 pgm_definition_string = "l" + alias + " = l" + func_name 1222 if gen_print_debug: 1223 print(pgm_definition_string) 1224 exec(pgm_definition_string) 1225 1226############################################################################### 1227