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