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 == "": 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 pass 530 if not type_is_dict: 531 try: 532 if type(var_value) is DotDict: 533 type_is_dict = 1 534 except NameError: 535 pass 536 if type_is_dict: 537 for key, value in var_value.iteritems(): 538 ix += 1 539 if ix == length: 540 loc_trailing_char = trailing_char 541 buffer += sprint_varx(var_name + "[" + key + "]", value, hex, 542 loc_col1_indent, loc_col1_width, 543 loc_trailing_char) 544 elif type(var_value) in (list, tuple, set): 545 for key, value in enumerate(var_value): 546 ix += 1 547 if ix == length: 548 loc_trailing_char = trailing_char 549 buffer += sprint_varx(var_name + "[" + str(key) + "]", value, 550 hex, loc_col1_indent, loc_col1_width, 551 loc_trailing_char) 552 elif type(var_value) is argparse.Namespace: 553 for key in var_value.__dict__: 554 ix += 1 555 if ix == length: 556 loc_trailing_char = trailing_char 557 cmd_buf = "buffer += sprint_varx(var_name + \".\" + str(key)" \ 558 + ", var_value." + key + ", hex, loc_col1_indent," \ 559 + " loc_col1_width, loc_trailing_char)" 560 exec(cmd_buf) 561 else: 562 var_type = type(var_value).__name__ 563 func_name = sys._getframe().f_code.co_name 564 var_value = "<" + var_type + " type not supported by " + \ 565 func_name + "()>" 566 value_format = "%s" 567 loc_col1_indent -= 2 568 # Adjust loc_col1_width. 569 loc_col1_width = loc_col1_width - loc_col1_indent 570 format_string = "%" + str(loc_col1_indent) + "s%-" \ 571 + str(loc_col1_width) + "s" + value_format + trailing_char 572 return format_string % ("", var_name + ":", var_value) 573 return buffer 574 575 return "" 576 577############################################################################### 578 579 580############################################################################### 581def sprint_var(*args): 582 583 r""" 584 Figure out the name of the first argument for you and then call 585 sprint_varx with it. Therefore, the following 2 calls are equivalent: 586 sprint_varx("var1", var1) 587 sprint_var(var1) 588 """ 589 590 # Get the name of the first variable passed to this function. 591 stack_frame = 2 592 caller_func_name = sprint_func_name(2) 593 if caller_func_name.endswith("print_var"): 594 stack_frame += 1 595 var_name = get_arg_name(None, 1, stack_frame) 596 return sprint_varx(var_name, *args) 597 598############################################################################### 599 600 601############################################################################### 602def sprint_vars(*args): 603 604 r""" 605 Sprint the values of one or more variables. 606 607 Description of args: 608 args: 609 If the first argument is an integer, it will be interpreted to be the 610 "indent" value. 611 If the second argument is an integer, it will be interpreted to be the 612 "col1_width" value. 613 If the third argument is an integer, it will be interpreted to be the 614 "hex" value. 615 All remaining parms are considered variable names which are to be 616 sprinted. 617 """ 618 619 if len(args) == 0: 620 return 621 622 # Get the name of the first variable passed to this function. 623 stack_frame = 2 624 caller_func_name = sprint_func_name(2) 625 if caller_func_name.endswith("print_vars"): 626 stack_frame += 1 627 628 parm_num = 1 629 630 # Create list from args (which is a tuple) so that it can be modified. 631 args_list = list(args) 632 633 var_name = get_arg_name(None, parm_num, stack_frame) 634 # See if parm 1 is to be interpreted as "indent". 635 try: 636 if type(int(var_name)) is int: 637 indent = int(var_name) 638 args_list.pop(0) 639 parm_num += 1 640 except ValueError: 641 indent = 0 642 643 var_name = get_arg_name(None, parm_num, stack_frame) 644 # See if parm 1 is to be interpreted as "col1_width". 645 try: 646 if type(int(var_name)) is int: 647 loc_col1_width = int(var_name) 648 args_list.pop(0) 649 parm_num += 1 650 except ValueError: 651 loc_col1_width = col1_width 652 653 var_name = get_arg_name(None, parm_num, stack_frame) 654 # See if parm 1 is to be interpreted as "hex". 655 try: 656 if type(int(var_name)) is int: 657 hex = int(var_name) 658 args_list.pop(0) 659 parm_num += 1 660 except ValueError: 661 hex = 0 662 663 buffer = "" 664 for var_value in args_list: 665 var_name = get_arg_name(None, parm_num, stack_frame) 666 buffer += sprint_varx(var_name, var_value, hex, indent, loc_col1_width) 667 parm_num += 1 668 669 return buffer 670 671############################################################################### 672 673 674############################################################################### 675def lprint_varx(var_name, 676 var_value, 677 hex=0, 678 loc_col1_indent=col1_indent, 679 loc_col1_width=col1_width, 680 log_level=getattr(logging, 'INFO')): 681 682 r""" 683 Send sprint_varx output to logging. 684 """ 685 686 logging.log(log_level, sprint_varx(var_name, var_value, hex, 687 loc_col1_indent, loc_col1_width, "")) 688 689############################################################################### 690 691 692############################################################################### 693def lprint_var(*args): 694 695 r""" 696 Figure out the name of the first argument for you and then call 697 lprint_varx with it. Therefore, the following 2 calls are equivalent: 698 lprint_varx("var1", var1) 699 lprint_var(var1) 700 """ 701 702 # Get the name of the first variable passed to this function. 703 stack_frame = 2 704 caller_func_name = sprint_func_name(2) 705 if caller_func_name.endswith("print_var"): 706 stack_frame += 1 707 var_name = get_arg_name(None, 1, stack_frame) 708 lprint_varx(var_name, *args) 709 710############################################################################### 711 712 713############################################################################### 714def sprint_dashes(indent=col1_indent, 715 width=80, 716 line_feed=1, 717 char="-"): 718 719 r""" 720 Return a string of dashes to the caller. 721 722 Description of arguements: 723 indent The number of characters to indent the 724 output. 725 width The width of the string of dashes. 726 line_feed Indicates whether the output should end 727 with a line feed. 728 char The character to be repeated in the output 729 string. 730 """ 731 732 width = int(width) 733 buffer = " "*int(indent) + char*width 734 if line_feed: 735 buffer += "\n" 736 737 return buffer 738 739############################################################################### 740 741 742############################################################################### 743def sindent(text="", 744 indent=0): 745 746 r""" 747 Pre-pend the specified number of characters to the text string (i.e. 748 indent it) and return it. 749 750 Description of arguments: 751 text The string to be indented. 752 indent The number of characters to indent the 753 string. 754 """ 755 756 format_string = "%" + str(indent) + "s%s" 757 buffer = format_string % ("", text) 758 759 return buffer 760 761############################################################################### 762 763 764############################################################################### 765def sprint_call_stack(indent=0, 766 stack_frame_ix=0): 767 768 r""" 769 Return a call stack report for the given point in the program with line 770 numbers, function names and function parameters and arguments. 771 772 Sample output: 773 774 ------------------------------------------------------------------------- 775 Python function call stack 776 777 Line # Function name and arguments 778 ------ ------------------------------------------------------------------ 779 424 sprint_call_stack () 780 4 print_call_stack () 781 31 func1 (last_name = 'walsh', first_name = 'mikey') 782 59 /tmp/scr5.py 783 ------------------------------------------------------------------------- 784 785 Description of arguments: 786 indent The number of characters to indent each 787 line of output. 788 stack_frame_ix The index of the first stack frame which 789 is to be returned. 790 """ 791 792 buffer = "" 793 buffer += sprint_dashes(indent) 794 buffer += sindent("Python function call stack\n\n", indent) 795 buffer += sindent("Line # Function name and arguments\n", indent) 796 buffer += sprint_dashes(indent, 6, 0) + " " + sprint_dashes(0, 73) 797 798 # Grab the current program stack. 799 current_stack = inspect.stack() 800 801 # Process each frame in turn. 802 format_string = "%6s %s\n" 803 ix = 0 804 for stack_frame in current_stack: 805 if ix < stack_frame_ix: 806 ix += 1 807 continue 808 lineno = str(stack_frame[2]) 809 func_name = str(stack_frame[3]) 810 if func_name == "?": 811 # "?" is the name used when code is not in a function. 812 func_name = "(none)" 813 814 if func_name == "<module>": 815 # If the func_name is the "main" program, we simply get the 816 # command line call string. 817 func_and_args = ' '.join(sys.argv) 818 else: 819 # Get the program arguments. 820 arg_vals = inspect.getargvalues(stack_frame[0]) 821 function_parms = arg_vals[0] 822 frame_locals = arg_vals[3] 823 824 args_list = [] 825 for arg_name in function_parms: 826 # Get the arg value from frame locals. 827 arg_value = frame_locals[arg_name] 828 args_list.append(arg_name + " = " + repr(arg_value)) 829 args_str = "(" + ', '.join(map(str, args_list)) + ")" 830 831 # Now we need to print this in a nicely-wrapped way. 832 func_and_args = func_name + " " + args_str 833 834 buffer += sindent(format_string % (lineno, func_and_args), indent) 835 ix += 1 836 837 buffer += sprint_dashes(indent) 838 839 return buffer 840 841############################################################################### 842 843 844############################################################################### 845def sprint_executing(stack_frame_ix=None): 846 847 r""" 848 Print a line indicating what function is executing and with what parameter 849 values. This is useful for debugging. 850 851 Sample output: 852 853 #(CDT) 2016/08/25 17:54:27 - Executing: func1 (x = 1) 854 855 Description of arguments: 856 stack_frame_ix The index of the stack frame whose 857 function info should be returned. If the 858 caller does not specifiy a value, this 859 function will set the value to 1 which is 860 the index of the caller's stack frame. If 861 the caller is the wrapper function 862 "print_executing", this function will bump 863 it up by 1. 864 """ 865 866 # If user wants default stack_frame_ix. 867 if stack_frame_ix is None: 868 func_name = sys._getframe().f_code.co_name 869 caller_func_name = sys._getframe(1).f_code.co_name 870 if caller_func_name.endswith(func_name[1:]): 871 stack_frame_ix = 2 872 else: 873 stack_frame_ix = 1 874 875 stack_frame = inspect.stack()[stack_frame_ix] 876 877 func_name = str(stack_frame[3]) 878 if func_name == "?": 879 # "?" is the name used when code is not in a function. 880 func_name = "(none)" 881 882 if func_name == "<module>": 883 # If the func_name is the "main" program, we simply get the command 884 # line call string. 885 func_and_args = ' '.join(sys.argv) 886 else: 887 # Get the program arguments. 888 arg_vals = inspect.getargvalues(stack_frame[0]) 889 function_parms = arg_vals[0] 890 frame_locals = arg_vals[3] 891 892 args_list = [] 893 for arg_name in function_parms: 894 # Get the arg value from frame locals. 895 arg_value = frame_locals[arg_name] 896 args_list.append(arg_name + " = " + repr(arg_value)) 897 args_str = "(" + ', '.join(map(str, args_list)) + ")" 898 899 # Now we need to print this in a nicely-wrapped way. 900 func_and_args = func_name + " " + args_str 901 902 return sprint_time() + "Executing: " + func_and_args + "\n" 903 904############################################################################### 905 906 907############################################################################### 908def sprint_pgm_header(indent=0, 909 linefeed=1): 910 911 r""" 912 Return a standardized header that programs should print at the beginning 913 of the run. It includes useful information like command line, pid, 914 userid, program parameters, etc. 915 916 Description of arguments: 917 indent The number of characters to indent each 918 line of output. 919 linefeed Indicates whether a line feed be included 920 at the beginning and end of the report. 921 """ 922 923 loc_col1_width = col1_width + indent 924 925 buffer = "" 926 if linefeed: 927 buffer = "\n" 928 929 buffer += sindent(sprint_time() + "Running " + pgm_name + ".\n", indent) 930 buffer += sindent(sprint_time() + "Program parameter values, etc.:\n\n", 931 indent) 932 buffer += sprint_varx("command_line", ' '.join(sys.argv), 0, indent, 933 loc_col1_width) 934 # We want the output to show a customized name for the pid and pgid but 935 # we want it to look like a valid variable name. Therefore, we'll use 936 # pgm_name_var_name which was set when this module was imported. 937 buffer += sprint_varx(pgm_name_var_name + "_pid", os.getpid(), 0, indent, 938 loc_col1_width) 939 buffer += sprint_varx(pgm_name_var_name + "_pgid", os.getpgrp(), 0, indent, 940 loc_col1_width) 941 userid_num = str(os.geteuid()) 942 try: 943 username = os.getlogin() 944 except OSError: 945 if userid_num == "0": 946 username = "root" 947 else: 948 username = "?" 949 buffer += sprint_varx("uid", userid_num + " (" + username + 950 ")", 0, indent, loc_col1_width) 951 buffer += sprint_varx("gid", str(os.getgid()) + " (" + 952 str(grp.getgrgid(os.getgid()).gr_name) + ")", 0, 953 indent, loc_col1_width) 954 buffer += sprint_varx("host_name", socket.gethostname(), 0, indent, 955 loc_col1_width) 956 try: 957 DISPLAY = os.environ['DISPLAY'] 958 except KeyError: 959 DISPLAY = "" 960 buffer += sprint_varx("DISPLAY", DISPLAY, 0, indent, 961 loc_col1_width) 962 # I want to add code to print caller's parms. 963 964 # __builtin__.arg_obj is created by the get_arg module function, 965 # gen_get_options. 966 try: 967 buffer += ga.sprint_args(__builtin__.arg_obj, indent) 968 except AttributeError: 969 pass 970 971 if linefeed: 972 buffer += "\n" 973 974 return buffer 975 976############################################################################### 977 978 979############################################################################### 980def sprint_error_report(error_text="\n", 981 indent=2): 982 983 r""" 984 Return a string with a standardized report which includes the caller's 985 error text, the call stack and the program header. 986 987 Description of args: 988 error_text The error text to be included in the 989 report. The caller should include any 990 needed linefeeds. 991 indent The number of characters to indent each 992 line of output. 993 """ 994 995 buffer = "" 996 buffer += sprint_dashes(width=120, char="=") 997 buffer += sprint_error(error_text) 998 buffer += "\n" 999 # Calling sprint_call_stack with stack_frame_ix of 0 causes it to show 1000 # itself and this function in the call stack. This is not helpful to a 1001 # debugger and is therefore clutter. We will adjust the stack_frame_ix to 1002 # hide that information. 1003 stack_frame_ix = 2 1004 caller_func_name = sprint_func_name(2) 1005 if caller_func_name.endswith("print_error_report"): 1006 stack_frame_ix += 1 1007 buffer += sprint_call_stack(indent, stack_frame_ix) 1008 buffer += sprint_pgm_header(indent) 1009 buffer += sprint_dashes(width=120, char="=") 1010 1011 return buffer 1012 1013############################################################################### 1014 1015 1016############################################################################### 1017def sprint_issuing(cmd_buf, 1018 test_mode=0): 1019 1020 r""" 1021 Return a line indicating a command that the program is about to execute. 1022 1023 Sample output for a cmd_buf of "ls" 1024 1025 #(CDT) 2016/08/25 17:57:36 - Issuing: ls 1026 1027 Description of args: 1028 cmd_buf The command to be executed by caller. 1029 test_mode With test_mode set, your output will look 1030 like this: 1031 1032 #(CDT) 2016/08/25 17:57:36 - (test_mode) Issuing: ls 1033 1034 """ 1035 1036 buffer = sprint_time() 1037 if test_mode: 1038 buffer += "(test_mode) " 1039 buffer += "Issuing: " + cmd_buf + "\n" 1040 1041 return buffer 1042 1043############################################################################### 1044 1045 1046############################################################################### 1047def sprint_pgm_footer(): 1048 1049 r""" 1050 Return a standardized footer that programs should print at the end of the 1051 program run. It includes useful information like total run time, etc. 1052 """ 1053 1054 buffer = "\n" + sprint_time() + "Finished running " + pgm_name + ".\n\n" 1055 1056 total_time = time.time() - start_time 1057 total_time_string = "%0.6f" % total_time 1058 1059 buffer += sprint_varx(pgm_name_var_name + "_runtime", total_time_string) 1060 buffer += "\n" 1061 1062 return buffer 1063 1064############################################################################### 1065 1066 1067############################################################################### 1068def sprint(buffer=""): 1069 1070 r""" 1071 Simply return the user's buffer. This function is used by the qprint and 1072 dprint functions defined dynamically below, i.e. it would not normally be 1073 called for general use. 1074 1075 Description of arguments. 1076 buffer This will be returned to the caller. 1077 """ 1078 1079 return str(buffer) 1080 1081############################################################################### 1082 1083 1084############################################################################### 1085def sprintn(buffer=""): 1086 1087 r""" 1088 Simply return the user's buffer with a line feed. This function is used 1089 by the qprint and dprint functions defined dynamically below, i.e. it 1090 would not normally be called for general use. 1091 1092 Description of arguments. 1093 buffer This will be returned to the caller. 1094 """ 1095 1096 buffer = str(buffer) + "\n" 1097 1098 return buffer 1099 1100############################################################################### 1101 1102 1103############################################################################### 1104# In the following section of code, we will dynamically create print versions 1105# for each of the sprint functions defined above. So, for example, where we 1106# have an sprint_time() function defined above that returns the time to the 1107# caller in a string, we will create a corresponding print_time() function 1108# that will print that string directly to stdout. 1109 1110# It can be complicated to follow what's being creaed by the exec statement 1111# below. Here is an example of the print_time() function that will be created: 1112 1113# def print_time(*args): 1114# s_funcname = "s" + sys._getframe().f_code.co_name 1115# s_func = getattr(sys.modules[__name__], s_funcname) 1116# sys.stdout.write(s_func(*args)) 1117 1118# Here are comments describing the 3 lines in the body of the created function. 1119# Calculate the "s" version of this function name (e.g. if this function name 1120# is print_time, we want s_funcname to be "sprint_time". 1121# Put a reference to the "s" version of this function in s_func. 1122# Call the "s" version of this function passing it all of our arguments. 1123# Write the result to stdout. 1124 1125# func_names contains a list of all print functions which should be created 1126# from their sprint counterparts. 1127func_names = ['print_time', 'print_timen', 'print_error', 'print_varx', 1128 'print_var', 'print_vars', 'print_dashes', 'indent', 1129 'print_call_stack', 'print_func_name', 'print_executing', 1130 'print_pgm_header', 'print_issuing', 'print_pgm_footer', 1131 'print_error_report', 'print', 'printn'] 1132 1133for func_name in func_names: 1134 if func_name == "print": 1135 continue 1136 # Create abbreviated aliases (e.g. spvar is an alias for sprint_var). 1137 alias = re.sub("print_", "p", func_name) 1138 pgm_definition_string = "s" + alias + " = s" + func_name 1139 if gen_print_debug: 1140 print(pgm_definition_string) 1141 exec(pgm_definition_string) 1142 1143for func_name in func_names: 1144 if func_name == "print_error" or func_name == "print_error_report": 1145 output_stream = "stderr" 1146 else: 1147 output_stream = "stdout" 1148 func_def = \ 1149 [ 1150 "def " + func_name + "(*args):", 1151 " s_func_name = \"s\" + sys._getframe().f_code.co_name", 1152 " s_func = getattr(sys.modules[__name__], s_func_name)", 1153 " sys." + output_stream + ".write(s_func(*args))", 1154 " sys." + output_stream + ".flush()" 1155 ] 1156 if func_name != "print": 1157 pgm_definition_string = '\n'.join(func_def) 1158 if gen_print_debug: 1159 print(pgm_definition_string) 1160 exec(pgm_definition_string) 1161 1162 # Now define "q" versions of each print function. 1163 func_def = \ 1164 [ 1165 "def q" + func_name + "(*args):", 1166 " if __builtin__.quiet: return", 1167 " s_func_name = \"s" + func_name + "\"", 1168 " s_func = getattr(sys.modules[__name__], s_func_name)", 1169 " sys." + output_stream + ".write(s_func(*args))", 1170 " sys." + output_stream + ".flush()" 1171 ] 1172 1173 pgm_definition_string = '\n'.join(func_def) 1174 if gen_print_debug: 1175 print(pgm_definition_string) 1176 exec(pgm_definition_string) 1177 1178 # Now define "d" versions of each print function. 1179 func_def = \ 1180 [ 1181 "def d" + func_name + "(*args):", 1182 " if not __builtin__.debug: return", 1183 " s_func_name = \"s" + func_name + "\"", 1184 " s_func = getattr(sys.modules[__name__], s_func_name)", 1185 " sys." + output_stream + ".write(s_func(*args))", 1186 " sys." + output_stream + ".flush()" 1187 ] 1188 1189 pgm_definition_string = '\n'.join(func_def) 1190 if gen_print_debug: 1191 print(pgm_definition_string) 1192 exec(pgm_definition_string) 1193 1194 # Now define "l" versions of each print function. 1195 func_def = \ 1196 [ 1197 "def l" + func_name + "(*args):", 1198 " s_func_name = \"s" + func_name + "\"", 1199 " s_func = getattr(sys.modules[__name__], s_func_name)", 1200 " logging.log(getattr(logging, 'INFO'), s_func(*args))", 1201 ] 1202 1203 if func_name != "print_varx" and func_name != "print_var": 1204 pgm_definition_string = '\n'.join(func_def) 1205 if gen_print_debug: 1206 print(pgm_definition_string) 1207 exec(pgm_definition_string) 1208 1209 if func_name == "print": 1210 continue 1211 1212 # Create abbreviated aliases (e.g. pvar is an alias for print_var). 1213 alias = re.sub("print_", "p", func_name) 1214 pgm_definition_string = alias + " = " + func_name 1215 if gen_print_debug: 1216 print(pgm_definition_string) 1217 exec(pgm_definition_string) 1218 1219 # Create abbreviated aliases (e.g. qpvar is an alias for qprint_var). 1220 alias = re.sub("print_", "p", func_name) 1221 pgm_definition_string = "q" + alias + " = q" + func_name 1222 if gen_print_debug: 1223 print(pgm_definition_string) 1224 exec(pgm_definition_string) 1225 1226 # Create abbreviated aliases (e.g. dpvar is an alias for dprint_var). 1227 alias = re.sub("print_", "p", func_name) 1228 pgm_definition_string = "d" + alias + " = d" + func_name 1229 if gen_print_debug: 1230 print(pgm_definition_string) 1231 exec(pgm_definition_string) 1232 1233 # Create abbreviated aliases (e.g. lpvar is an alias for lprint_var). 1234 alias = re.sub("print_", "p", func_name) 1235 pgm_definition_string = "l" + alias + " = l" + func_name 1236 if gen_print_debug: 1237 print(pgm_definition_string) 1238 exec(pgm_definition_string) 1239 1240############################################################################### 1241