1#!/usr/bin/env python 2 3r""" 4This module provides many print functions such as sprint_var, sprint_time, sprint_error, sprint_call_stack. 5""" 6 7import sys 8import os 9import time 10import inspect 11import re 12import grp 13import socket 14import argparse 15import copy 16try: 17 import __builtin__ 18except ImportError: 19 import builtins as __builtin__ 20import logging 21import collections 22from wrap_utils import * 23 24try: 25 robot_env = 1 26 from robot.utils import DotDict 27 from robot.utils import NormalizedDict 28 from robot.libraries.BuiltIn import BuiltIn 29 # Having access to the robot libraries alone does not indicate that we are in a robot environment. The 30 # following try block should confirm that. 31 try: 32 var_value = BuiltIn().get_variable_value("${SUITE_NAME}", "") 33 except BaseException: 34 robot_env = 0 35except ImportError: 36 robot_env = 0 37 38import gen_arg as ga 39 40# Setting these variables for use both inside this module and by programs importing this module. 41pgm_file_path = sys.argv[0] 42pgm_name = os.path.basename(pgm_file_path) 43pgm_dir_path = os.path.normpath(re.sub("/" + pgm_name, "", pgm_file_path)) +\ 44 os.path.sep 45 46 47# Some functions (e.g. sprint_pgm_header) have need of a program name value that looks more like a valid 48# variable name. Therefore, we'll swap odd characters like "." out for underscores. 49pgm_name_var_name = pgm_name.replace(".", "_") 50 51# Initialize global values used as defaults by print_time, print_var, etc. 52dft_indent = 0 53 54# Calculate default column width for print_var functions based on environment variable settings. The 55# objective is to make the variable values line up nicely with the time stamps. 56dft_col1_width = 29 57 58NANOSECONDS = os.environ.get('NANOSECONDS', '1') 59 60if NANOSECONDS == "1": 61 dft_col1_width = dft_col1_width + 7 62 63SHOW_ELAPSED_TIME = os.environ.get('SHOW_ELAPSED_TIME', '1') 64 65if SHOW_ELAPSED_TIME == "1": 66 if NANOSECONDS == "1": 67 dft_col1_width = dft_col1_width + 14 68 else: 69 dft_col1_width = dft_col1_width + 7 70 71# Initialize some time variables used in module functions. 72start_time = time.time() 73# sprint_time_last_seconds is used to calculate elapsed seconds. 74sprint_time_last_seconds = [start_time, start_time] 75# Define global index for the sprint_time_last_seconds list. 76last_seconds_ix = 0 77 78 79def set_last_seconds_ix(ix): 80 r""" 81 Set the "last_seconds_ix" module variable to the index value. 82 83 Description of argument(s): 84 ix The index value to be set into the module global last_seconds_ix variable. 85 """ 86 global last_seconds_ix 87 last_seconds_ix = ix 88 89 90# Since output from the lprint_ functions goes to a different location than the output from the print_ 91# functions (e.g. a file vs. the console), sprint_time_last_seconds has been created as a list rather than a 92# simple integer so that it can store multiple sprint_time_last_seconds values. Standard print_ functions 93# defined in this file will use sprint_time_last_seconds[0] and the lprint_ functions will use 94# sprint_time_last_seconds[1]. 95def standard_print_last_seconds_ix(): 96 r""" 97 Return the standard print last_seconds index value to the caller. 98 """ 99 return 0 100 101 102def lprint_last_seconds_ix(): 103 r""" 104 Return lprint last_seconds index value to the caller. 105 """ 106 return 1 107 108 109# The user can set environment variable "GEN_PRINT_DEBUG" to get debug output from this module. 110gen_print_debug = int(os.environ.get('GEN_PRINT_DEBUG', 0)) 111 112 113def sprint_func_name(stack_frame_ix=None): 114 r""" 115 Return the function name associated with the indicated stack frame. 116 117 Description of argument(s): 118 stack_frame_ix The index of the stack frame whose function name should be returned. If 119 the caller does not specify a value, this function will set the value to 120 1 which is the index of the caller's stack frame. If the caller is the 121 wrapper function "print_func_name", this function will bump it up by 1. 122 """ 123 124 # If user specified no stack_frame_ix, we'll set it to a proper default value. 125 if stack_frame_ix is None: 126 func_name = sys._getframe().f_code.co_name 127 caller_func_name = sys._getframe(1).f_code.co_name 128 if func_name[1:] == caller_func_name: 129 stack_frame_ix = 2 130 else: 131 stack_frame_ix = 1 132 133 func_name = sys._getframe(stack_frame_ix).f_code.co_name 134 135 return func_name 136 137 138def work_around_inspect_stack_cwd_failure(): 139 r""" 140 Work around the inspect.stack() getcwd() failure by making "/tmp" the current working directory. 141 142 NOTES: If the current working directory has been deleted, inspect.stack() will fail with "OSError: [Errno 143 2] No such file or directory" because it tries to do a getcwd(). 144 145 This function will try to prevent this failure by detecting the scenario in advance and making "/tmp" the 146 current working directory. 147 """ 148 try: 149 os.getcwd() 150 except OSError: 151 os.chdir("/tmp") 152 153 154def get_line_indent(line): 155 r""" 156 Return the number of spaces at the beginning of the line. 157 """ 158 159 return len(line) - len(line.lstrip(' ')) 160 161 162# get_arg_name is not a print function per se. It has been included in this module because it is used by 163# sprint_var which is defined in this module. 164def get_arg_name(var, 165 arg_num=1, 166 stack_frame_ix=1): 167 r""" 168 Return the "name" of an argument passed to a function. This could be a literal or a variable name. 169 170 Description of argument(s): 171 var The variable whose name is to be returned. 172 arg_num The arg number whose name is to be returned. To illustrate how arg_num 173 is processed, suppose that a programmer codes this line: "rc, outbuf = 174 my_func(var1, var2)" and suppose that my_func has this line of code: 175 "result = gp.get_arg_name(0, arg_num, 2)". If arg_num is positive, the 176 indicated argument is returned. For example, if arg_num is 1, "var1" 177 would be returned, If arg_num is 2, "var2" would be returned. If arg_num 178 exceeds the number of arguments, get_arg_name will simply return a 179 complete list of the arguments. If arg_num is 0, get_arg_name will 180 return the name of the target function as specified in the calling line 181 ("my_func" in this case). To clarify, if the caller of the target 182 function uses an alias function name, the alias name would be returned. 183 If arg_num is negative, an lvalue variable name is returned. Continuing 184 with the given example, if arg_num is -2 the 2nd parm to the left of the 185 "=" ("rc" in this case) should be returned. If arg_num is -1, the 1st 186 parm to the left of the "=" ("out_buf" in this case) should be returned. 187 If arg_num is less than -2, an entire dictionary is returned. The keys 188 to the dictionary for this example would be -2 and -1. 189 stack_frame_ix The stack frame index of the target function. This value must be 1 or 190 greater. 1 would indicate get_arg_name's stack frame. 2 would be the 191 caller of get_arg_name's stack frame, etc. 192 193 Example 1: 194 195 my_var = "mike" 196 var_name = get_arg_name(my_var) 197 198 In this example, var_name will receive the value "my_var". 199 200 Example 2: 201 202 def test1(var): 203 # Getting the var name of the first arg to this function, test1. Note, in this case, it doesn't 204 # matter what is passed as the first arg to get_arg_name since it is the caller's variable name that 205 # matters. 206 dummy = 1 207 arg_num = 1 208 stack_frame = 2 209 var_name = get_arg_name(dummy, arg_num, stack_frame) 210 211 # Mainline... 212 213 another_var = "whatever" 214 test1(another_var) 215 216 In this example, var_name will be set to "another_var". 217 218 """ 219 220 # Note: To avoid infinite recursion, avoid calling any function that calls this function (e.g. 221 # sprint_var, valid_value, etc.). 222 223 # The user can set environment variable "GET_ARG_NAME_DEBUG" to get debug output from this function. 224 local_debug = int(os.environ.get('GET_ARG_NAME_DEBUG', 0)) 225 # In addition to GET_ARG_NAME_DEBUG, the user can set environment variable "GET_ARG_NAME_SHOW_SOURCE" to 226 # have this function include source code in the debug output. 227 local_debug_show_source = int( 228 os.environ.get('GET_ARG_NAME_SHOW_SOURCE', 0)) 229 230 if stack_frame_ix < 1: 231 print_error("Programmer error - Variable \"stack_frame_ix\" has an" 232 + " invalid value of \"" + str(stack_frame_ix) + "\". The" 233 + " value must be an integer that is greater than or equal" 234 + " to 1.\n") 235 return 236 237 if local_debug: 238 debug_indent = 2 239 print("") 240 print_dashes(0, 120) 241 print(sprint_func_name() + "() parms:") 242 print_varx("var", var, indent=debug_indent) 243 print_varx("arg_num", arg_num, indent=debug_indent) 244 print_varx("stack_frame_ix", stack_frame_ix, indent=debug_indent) 245 print("") 246 print_call_stack(debug_indent, 2) 247 248 work_around_inspect_stack_cwd_failure() 249 for count in range(0, 2): 250 try: 251 frame, filename, cur_line_no, function_name, lines, index = \ 252 inspect.stack()[stack_frame_ix] 253 except IndexError: 254 print_error("Programmer error - The caller has asked for" 255 + " information about the stack frame at index \"" 256 + str(stack_frame_ix) + "\". However, the stack" 257 + " only contains " + str(len(inspect.stack())) 258 + " entries. Therefore the stack frame index is out" 259 + " of range.\n") 260 return 261 if filename != "<string>": 262 break 263 # filename of "<string>" may mean that the function in question was defined dynamically and 264 # therefore its code stack is inaccessible. This may happen with functions like "rqprint_var". In 265 # this case, we'll increment the stack_frame_ix and try again. 266 stack_frame_ix += 1 267 if local_debug: 268 print("Adjusted stack_frame_ix...") 269 print_varx("stack_frame_ix", stack_frame_ix, indent=debug_indent) 270 271 real_called_func_name = sprint_func_name(stack_frame_ix) 272 273 module = inspect.getmodule(frame) 274 275 # Though one would expect inspect.getsourcelines(frame) to get all module source lines if the frame is 276 # "<module>", it doesn't do that. Therefore, for this special case, do inspect.getsourcelines(module). 277 if function_name == "<module>": 278 source_lines, source_line_num =\ 279 inspect.getsourcelines(module) 280 line_ix = cur_line_no - source_line_num - 1 281 else: 282 source_lines, source_line_num =\ 283 inspect.getsourcelines(frame) 284 line_ix = cur_line_no - source_line_num 285 286 if local_debug: 287 print("\n Variables retrieved from inspect.stack() function:") 288 print_varx("frame", frame, indent=debug_indent + 2) 289 print_varx("filename", filename, indent=debug_indent + 2) 290 print_varx("cur_line_no", cur_line_no, indent=debug_indent + 2) 291 print_varx("function_name", function_name, indent=debug_indent + 2) 292 print_varx("lines", lines, indent=debug_indent + 2) 293 print_varx("index", index, indent=debug_indent + 2) 294 print_varx("source_line_num", source_line_num, indent=debug_indent) 295 print_varx("line_ix", line_ix, indent=debug_indent) 296 if local_debug_show_source: 297 print_varx("source_lines", source_lines, indent=debug_indent) 298 print_varx("real_called_func_name", real_called_func_name, 299 indent=debug_indent) 300 301 # Get a list of all functions defined for the module. Note that this doesn't work consistently when 302 # _run_exitfuncs is at the top of the stack (i.e. if we're running an exit function). I've coded a 303 # work-around below for this deficiency. 304 all_functions = inspect.getmembers(module, inspect.isfunction) 305 306 # Get called_func_id by searching for our function in the list of all functions. 307 called_func_id = None 308 for func_name, function in all_functions: 309 if func_name == real_called_func_name: 310 called_func_id = id(function) 311 break 312 # NOTE: The only time I've found that called_func_id can't be found is when we're running from an exit 313 # function. 314 315 # Look for other functions in module with matching id. 316 aliases = set([real_called_func_name]) 317 for func_name, function in all_functions: 318 if func_name == real_called_func_name: 319 continue 320 func_id = id(function) 321 if func_id == called_func_id: 322 aliases.add(func_name) 323 324 # In most cases, my general purpose code above will find all aliases. However, for the odd case (i.e. 325 # running from exit function), I've added code to handle pvar, qpvar, dpvar, etc. aliases explicitly 326 # since they are defined in this module and used frequently. 327 # pvar is an alias for print_var. 328 aliases.add(re.sub("print_var", "pvar", real_called_func_name)) 329 330 # The call to the function could be encased in a recast (e.g. int(func_name())). 331 recast_regex = "([^ ]+\\([ ]*)?" 332 import_name_regex = "([a-zA-Z0-9_]+\\.)?" 333 func_name_regex = recast_regex + import_name_regex + "(" +\ 334 '|'.join(aliases) + ")" 335 pre_args_regex = ".*" + func_name_regex + "[ ]*\\(" 336 337 # Search backward through source lines looking for the calling function name. 338 found = False 339 for start_line_ix in range(line_ix, 0, -1): 340 # Skip comment lines. 341 if re.match(r"[ ]*#", source_lines[start_line_ix]): 342 continue 343 if re.match(pre_args_regex, source_lines[start_line_ix]): 344 found = True 345 break 346 if not found: 347 print_error("Programmer error - Could not find the source line with" 348 + " a reference to function \"" + real_called_func_name 349 + "\".\n") 350 return 351 352 # Search forward through the source lines looking for a line whose indentation is the same or less than 353 # the start line. The end of our composite line should be the line preceding that line. 354 start_indent = get_line_indent(source_lines[start_line_ix]) 355 end_line_ix = line_ix 356 for end_line_ix in range(line_ix + 1, len(source_lines)): 357 if source_lines[end_line_ix].strip() == "": 358 continue 359 line_indent = get_line_indent(source_lines[end_line_ix]) 360 if line_indent <= start_indent: 361 end_line_ix -= 1 362 break 363 if start_line_ix != 0: 364 # Check to see whether the start line is a continuation of the prior line. 365 prior_line = source_lines[start_line_ix - 1] 366 prior_line_stripped = re.sub(r"[ ]*\\([\r\n]$)", " \\1", prior_line) 367 prior_line_indent = get_line_indent(prior_line) 368 if prior_line != prior_line_stripped and\ 369 prior_line_indent < start_indent: 370 start_line_ix -= 1 371 # Remove the backslash (continuation char) from prior line. 372 source_lines[start_line_ix] = prior_line_stripped 373 374 # Join the start line through the end line into a composite line. 375 composite_line = ''.join(map(str.strip, 376 source_lines[start_line_ix:end_line_ix + 1])) 377 # Insert one space after first "=" if there isn't one already. 378 composite_line = re.sub("=[ ]*([^ ])", "= \\1", composite_line, 1) 379 380 lvalue_regex = "[ ]*=[ ]+" + func_name_regex + ".*" 381 lvalue_string = re.sub(lvalue_regex, "", composite_line) 382 if lvalue_string == composite_line: 383 # i.e. the regex did not match so there are no lvalues. 384 lvalue_string = "" 385 lvalues_list = list(filter(None, map(str.strip, lvalue_string.split(",")))) 386 try: 387 lvalues = collections.OrderedDict() 388 except AttributeError: 389 # A non-ordered dict doesn't look as nice when printed but it will do. 390 lvalues = {} 391 ix = len(lvalues_list) * -1 392 for lvalue in lvalues_list: 393 lvalues[ix] = lvalue 394 ix += 1 395 lvalue_prefix_regex = "(.*=[ ]+)?" 396 called_func_name_regex = lvalue_prefix_regex + func_name_regex +\ 397 "[ ]*\\(.*" 398 called_func_name = re.sub(called_func_name_regex, "\\4", composite_line) 399 arg_list_etc = "(" + re.sub(pre_args_regex, "", composite_line) 400 if local_debug: 401 print_varx("aliases", aliases, indent=debug_indent) 402 print_varx("import_name_regex", import_name_regex, indent=debug_indent) 403 print_varx("func_name_regex", func_name_regex, indent=debug_indent) 404 print_varx("pre_args_regex", pre_args_regex, indent=debug_indent) 405 print_varx("start_line_ix", start_line_ix, indent=debug_indent) 406 print_varx("end_line_ix", end_line_ix, indent=debug_indent) 407 print_varx("composite_line", composite_line, indent=debug_indent) 408 print_varx("lvalue_regex", lvalue_regex, indent=debug_indent) 409 print_varx("lvalue_string", lvalue_string, indent=debug_indent) 410 print_varx("lvalues", lvalues, indent=debug_indent) 411 print_varx("called_func_name_regex", called_func_name_regex, 412 indent=debug_indent) 413 print_varx("called_func_name", called_func_name, indent=debug_indent) 414 print_varx("arg_list_etc", arg_list_etc, indent=debug_indent) 415 416 # Parse arg list... 417 # Initialize... 418 nest_level = -1 419 arg_ix = 0 420 args_list = [""] 421 for ix in range(0, len(arg_list_etc)): 422 char = arg_list_etc[ix] 423 # Set the nest_level based on whether we've encounted a parenthesis. 424 if char == "(": 425 nest_level += 1 426 if nest_level == 0: 427 continue 428 elif char == ")": 429 nest_level -= 1 430 if nest_level < 0: 431 break 432 433 # If we reach a comma at base nest level, we are done processing an argument so we increment arg_ix 434 # and initialize a new args_list entry. 435 if char == "," and nest_level == 0: 436 arg_ix += 1 437 args_list.append("") 438 continue 439 440 # For any other character, we append it it to the current arg list entry. 441 args_list[arg_ix] += char 442 443 # Trim whitespace from each list entry. 444 args_list = [arg.strip() for arg in args_list] 445 446 if arg_num < 0: 447 if abs(arg_num) > len(lvalues): 448 argument = lvalues 449 else: 450 argument = lvalues[arg_num] 451 elif arg_num == 0: 452 argument = called_func_name 453 else: 454 if arg_num > len(args_list): 455 argument = args_list 456 else: 457 argument = args_list[arg_num - 1] 458 459 if local_debug: 460 print_varx("args_list", args_list, indent=debug_indent) 461 print_varx("argument", argument, indent=debug_indent) 462 print_dashes(0, 120) 463 464 return argument 465 466 467def sprint_time(buffer=""): 468 r""" 469 Return the time in the following format. 470 471 Example: 472 473 The following python code... 474 475 sys.stdout.write(sprint_time()) 476 sys.stdout.write("Hi.\n") 477 478 Will result in the following type of output: 479 480 #(CDT) 2016/07/08 15:25:35 - Hi. 481 482 Example: 483 484 The following python code... 485 486 sys.stdout.write(sprint_time("Hi.\n")) 487 488 Will result in the following type of output: 489 490 #(CDT) 2016/08/03 17:12:05 - Hi. 491 492 The following environment variables will affect the formatting as described: 493 NANOSECONDS This will cause the time stamps to be precise to the microsecond (Yes, it 494 probably should have been named MICROSECONDS but the convention was set 495 long ago so we're sticking with it). Example of the output when 496 environment variable NANOSECONDS=1. 497 498 #(CDT) 2016/08/03 17:16:25.510469 - Hi. 499 500 SHOW_ELAPSED_TIME This will cause the elapsed time to be included in the output. This is 501 the amount of time that has elapsed since the last time this function was 502 called. The precision of the elapsed time field is also affected by the 503 value of the NANOSECONDS environment variable. Example of the output 504 when environment variable NANOSECONDS=0 and SHOW_ELAPSED_TIME=1. 505 506 #(CDT) 2016/08/03 17:17:40 - 0 - Hi. 507 508 Example of the output when environment variable NANOSECONDS=1 and SHOW_ELAPSED_TIME=1. 509 510 #(CDT) 2016/08/03 17:18:47.317339 - 0.000046 - Hi. 511 512 Description of argument(s). 513 buffer This will be appended to the formatted time string. 514 """ 515 516 global NANOSECONDS 517 global SHOW_ELAPSED_TIME 518 global sprint_time_last_seconds 519 global last_seconds_ix 520 521 seconds = time.time() 522 loc_time = time.localtime(seconds) 523 nanoseconds = "%0.6f" % seconds 524 pos = nanoseconds.find(".") 525 nanoseconds = nanoseconds[pos:] 526 527 time_string = time.strftime("#(%Z) %Y/%m/%d %H:%M:%S", loc_time) 528 if NANOSECONDS == "1": 529 time_string = time_string + nanoseconds 530 531 if SHOW_ELAPSED_TIME == "1": 532 cur_time_seconds = seconds 533 math_string = "%9.9f" % cur_time_seconds + " - " + "%9.9f" % \ 534 sprint_time_last_seconds[last_seconds_ix] 535 elapsed_seconds = eval(math_string) 536 if NANOSECONDS == "1": 537 elapsed_seconds = "%11.6f" % elapsed_seconds 538 else: 539 elapsed_seconds = "%4i" % elapsed_seconds 540 sprint_time_last_seconds[last_seconds_ix] = cur_time_seconds 541 time_string = time_string + " - " + elapsed_seconds 542 543 return time_string + " - " + buffer 544 545 546def sprint_timen(buffer=""): 547 r""" 548 Append a line feed to the buffer, pass it to sprint_time and return the result. 549 """ 550 551 return sprint_time(buffer + "\n") 552 553 554def sprint_error(buffer=""): 555 r""" 556 Return a standardized error string. This includes: 557 - A time stamp 558 - The "**ERROR**" string 559 - The caller's buffer string. 560 561 Example: 562 563 The following python code... 564 565 print(sprint_error("Oops.\n")) 566 567 Will result in the following type of output: 568 569 #(CDT) 2016/08/03 17:12:05 - **ERROR** Oops. 570 571 Description of argument(s). 572 buffer This will be appended to the formatted error string. 573 """ 574 575 return sprint_time() + "**ERROR** " + buffer 576 577 578# Implement "constants" with functions. 579def digit_length_in_bits(): 580 r""" 581 Return the digit length in bits. 582 """ 583 584 return 4 585 586 587def word_length_in_digits(): 588 r""" 589 Return the word length in digits. 590 """ 591 592 return 8 593 594 595def bit_length(number): 596 r""" 597 Return the bit length of the number. 598 599 Description of argument(s): 600 number The number to be analyzed. 601 """ 602 603 if number < 0: 604 # Convert negative numbers to positive and subtract one. The following example illustrates the 605 # reason for this: 606 # Consider a single nibble whose signed values can range from -8 to 7 (0x8 to 0x7). A value of 0x7 607 # equals 0b0111. Therefore, its length in bits is 3. Since the negative bit (i.e. 0b1000) is not 608 # set, the value 7 clearly will fit in one nibble. With -8 = 0x8 = 0b1000, one has the smallest 609 # negative value that will fit. Note that it requires 3 bits of 0. So by converting a number value 610 # of -8 to a working_number of 7, this function can accurately calculate the number of bits and 611 # therefore nibbles required to represent the number in print. 612 working_number = abs(number) - 1 613 else: 614 working_number = number 615 616 # Handle the special case of the number 0. 617 if working_number == 0: 618 return 0 619 620 return len(bin(working_number)) - 2 621 622 623def get_req_num_hex_digits(number): 624 r""" 625 Return the required number of hex digits required to display the given number. 626 627 The returned value will always be rounded up to the nearest multiple of 8. 628 629 Description of argument(s): 630 number The number to be analyzed. 631 """ 632 633 if number < 0: 634 # Convert negative numbers to positive and subtract one. The following example illustrates the 635 # reason for this: 636 # Consider a single nibble whose signed values can range from -8 to 7 (0x8 to 0x7). A value of 0x7 637 # equals 0b0111. Therefore, its length in bits is 3. Since the negative bit (i.e. 0b1000) is not 638 # set, the value 7 clearly will fit in one nibble. With -8 = 0x8 = 0b1000, one has the smallest 639 # negative value that will fit. Note that it requires 3 bits of 0. So by converting a number value 640 # of -8 to a working_number of 7, this function can accurately calculate the number of bits and 641 # therefore nibbles required to represent the number in print. 642 working_number = abs(number) - 1 643 else: 644 working_number = number 645 646 # Handle the special case of the number 0. 647 if working_number == 0: 648 return word_length_in_digits() 649 650 num_length_in_bits = bit_length(working_number) 651 num_hex_digits, remainder = divmod(num_length_in_bits, 652 digit_length_in_bits()) 653 if remainder > 0: 654 # Example: the number 7 requires 3 bits. The divmod above produces, 0 with remainder of 3. So 655 # because we have a remainder, we increment num_hex_digits from 0 to 1. 656 num_hex_digits += 1 657 658 # Check to see whether the negative bit is set. This is the left-most bit in the highest order digit. 659 negative_mask = 2 ** (num_hex_digits * 4 - 1) 660 if working_number & negative_mask: 661 # If a number that is intended to be positive has its negative bit on, an additional digit will be 662 # required to represent it correctly in print. 663 num_hex_digits += 1 664 665 num_words, remainder = divmod(num_hex_digits, word_length_in_digits()) 666 if remainder > 0 or num_words == 0: 667 num_words += 1 668 669 # Round up to the next word length in digits. 670 return num_words * word_length_in_digits() 671 672 673def dft_num_hex_digits(): 674 r""" 675 Return the default number of hex digits to be used to represent a hex number in print. 676 677 The value returned is a function of sys.maxsize. 678 """ 679 680 global _gen_print_dft_num_hex_digits_ 681 try: 682 return _gen_print_dft_num_hex_digits_ 683 except NameError: 684 _gen_print_dft_num_hex_digits_ = get_req_num_hex_digits(sys.maxsize) 685 return _gen_print_dft_num_hex_digits_ 686 687 688# Create constant functions to describe various types of dictionaries. 689def dict_type(): 690 return 1 691 692 693def ordered_dict_type(): 694 return 2 695 696 697def dot_dict_type(): 698 return 3 699 700 701def normalized_dict_type(): 702 return 4 703 704 705def proxy_dict_type(): 706 return 5 707 708 709def is_dict(var_value): 710 r""" 711 Return non-zero if var_value is a type of dictionary and 0 if it is not. 712 713 The specific non-zero value returned will indicate what type of dictionary var_value is (see constant 714 functions above). 715 716 Description of argument(s): 717 var_value The object to be analyzed to determine whether it is a dictionary and if 718 so, what type of dictionary. 719 """ 720 721 if isinstance(var_value, dict): 722 return dict_type() 723 try: 724 if isinstance(var_value, collections.OrderedDict): 725 return ordered_dict_type() 726 except AttributeError: 727 pass 728 try: 729 if isinstance(var_value, DotDict): 730 return dot_dict_type() 731 except NameError: 732 pass 733 try: 734 if isinstance(var_value, NormalizedDict): 735 return normalized_dict_type() 736 except NameError: 737 pass 738 try: 739 if str(type(var_value)).split("'")[1] == "dictproxy": 740 return proxy_dict_type() 741 except NameError: 742 pass 743 return 0 744 745 746def get_int_types(): 747 r""" 748 Return a tuple consisting of the valid integer data types for the system and version of python being run. 749 750 Example: 751 (int, long) 752 """ 753 754 try: 755 int_types = (int, long) 756 except NameError: 757 int_types = (int,) 758 return int_types 759 760 761def get_string_types(): 762 r""" 763 Return a tuple consisting of the valid string data types for the system and version of python being run. 764 765 Example: 766 (str, unicode) 767 """ 768 769 try: 770 string_types = (str, unicode) 771 except NameError: 772 string_types = (bytes, str) 773 return string_types 774 775 776def valid_fmts(): 777 r""" 778 Return a list of the valid formats that can be specified for the fmt argument of the sprint_varx function 779 (defined below). 780 """ 781 782 return [ 783 'hexa', 784 'octal', 785 'binary', 786 'blank', 787 'verbose', 788 'quote_keys', 789 'show_type', 790 'strip_brackets', 791 'no_header', 792 'quote_values'] 793 794 795def create_fmt_definition(): 796 r""" 797 Create a string consisting of function-definition code that can be executed to create constant fmt 798 definition functions. 799 800 These functions can be used by callers of sprint_var/sprint_varx to set the fmt argument correctly. 801 802 Likewise, the sprint_varx function will use these generated functions to correctly interpret the fmt 803 argument. 804 805 Example output from this function: 806 807 def hexa(): 808 return 0x00000001 809 def octal_fmt(): 810 return 0x00000002 811 etc. 812 """ 813 814 buffer = "" 815 bits = 0x00000001 816 for fmt_name in valid_fmts(): 817 buffer += "def " + fmt_name + "():\n" 818 buffer += " return " + "0x%08x" % bits + "\n" 819 bits = bits << 1 820 return buffer 821 822 823# Dynamically create fmt definitions (for use with the fmt argument of sprint_varx function): 824exec(create_fmt_definition()) 825 826 827def terse(): 828 r""" 829 Constant function to return fmt value of 0. 830 831 Now that sprint_varx defaults to printing in terse format, the terse option is deprecated. This function 832 is here for backward compatibility. 833 834 Once the repo has been purged of the use of terse, this function can be removed. 835 """ 836 837 return 0 838 839 840def list_pop(a_list, index=0, default=None): 841 r""" 842 Pop the list entry indicated by the index and return the entry. If no such entry exists, return default. 843 844 Note that the list passed to this function will be modified. 845 846 Description of argument(s): 847 a_list The list from which an entry is to be popped. 848 index The index indicating which entry is to be popped. 849 default The value to be returned if there is no entry at the given index location. 850 """ 851 try: 852 return a_list.pop(index) 853 except IndexError: 854 return default 855 856 857def parse_fmt(fmt): 858 r""" 859 Parse the fmt argument and return a tuple consisting of a format and a child format. 860 861 This function was written for use by the sprint_varx function defined in this module. 862 863 When sprint_varx is processing a multi-level object such as a list or dictionary (which in turn may 864 contain other lists or dictionaries), it will use the fmt value to dictate the print formatting of the 865 current level and the child_fmt value to dictate the print formatting of subordinate levels. Consider 866 the following example: 867 868 python code example: 869 870 ord_dict = \ 871 collections.OrderedDict([ 872 ('one', 1), 873 ('two', 2), 874 ('sub', 875 collections.OrderedDict([ 876 ('three', 3), ('four', 4)]))]) 877 878 print_var(ord_dict) 879 880 This would generate the following output: 881 882 ord_dict: 883 [one]: 1 884 [two]: 2 885 [sub]: 886 [three]: 3 887 [four]: 4 888 889 The first level in this example is the line that simply says "ord_dict". The second level is comprised 890 of the dictionary entries with the keys 'one', 'two' and 'sub'. The third level is comprised of the last 891 2 lines (i.e. printed values 3 and 4). 892 893 Given the data structure shown above, the programmer could code the following where fmt is a simple 894 integer value set by calling the verbose() function. 895 896 print_var(ord_dict, fmt=verbose()) 897 898 The output would look like this: 899 900 ord_dict: 901 ord_dict[one]: 1 902 ord_dict[two]: 2 903 ord_dict[sub]: 904 ord_dict[sub][three]: 3 905 ord_dict[sub][four]: 4 906 907 Note the verbose format where the name of the object ("ord_dict") is repeated on every line. 908 909 If the programmer wishes to get more granular with the fmt argument, he/she can specify it as a list 910 where each entry corresponds to a level of the object being printed. The last such list entry governs 911 the print formatting of all subordinate parts of the given object. 912 913 Look at each of the following code examples and their corresponding output. See how the show_type() 914 formatting affects the printing depending on which position it occupies in the fmt list argument: 915 916 print_var(ord_dict, fmt=[show_type()]) 917 918 ord_dict: <collections.OrderedDict> 919 ord_dict[one]: 1 <int> 920 ord_dict[two]: 2 <int> 921 ord_dict[sub]: <collections.OrderedDict> 922 ord_dict[sub][three]: 3 <int> 923 ord_dict[sub][four]: 4 <int> 924 925 print_var(ord_dict, fmt=[0, show_type()]) 926 927 ord_dict: 928 ord_dict[one]: 1 <int> 929 ord_dict[two]: 2 <int> 930 ord_dict[sub]: <collections.OrderedDict> 931 ord_dict[sub][three]: 3 <int> 932 ord_dict[sub][four]: 4 <int> 933 934 print_var(ord_dict, fmt=[0, 0, show_type()]) 935 936 ord_dict: 937 ord_dict[one]: 1 938 ord_dict[two]: 2 939 ord_dict[sub]: 940 ord_dict[sub][three]: 3 <int> 941 ord_dict[sub][four]: 4 <int> 942 943 Description of argument(s): 944 fmt The format argument such as is passed to sprint_varx. This argument may 945 be an integer or a list of integers. See the prolog of sprint_varx for 946 more details. 947 """ 948 949 # Make a deep copy of the fmt argument in order to avoid modifying the caller's fmt value when it is a 950 # list. 951 fmt = copy.deepcopy(fmt) 952 try: 953 # Assume fmt is a list. Pop the first element from the list. 954 first_element = list_pop(fmt, index=0, default=0) 955 # Return the first list element along with either 1) the remainder of the fmt list if not null or 2) 956 # another copy of the first element. 957 return first_element, fmt if len(fmt) else first_element 958 except AttributeError: 959 # fmt is not a list so treat it as a simple integer value. 960 return fmt, fmt 961 962 963def sprint_varx(var_name, 964 var_value, 965 fmt=0, 966 indent=dft_indent, 967 col1_width=dft_col1_width, 968 trailing_char="\n", 969 key_list=None, 970 delim=":"): 971 r""" 972 Print the var name/value passed to it. If the caller lets col1_width default, the printing lines up 973 nicely with output generated by the print_time functions. 974 975 Note that the sprint_var function (defined below) can be used to call this function so that the 976 programmer does not need to pass the var_name. sprint_var will figure out the var_name. The sprint_var 977 function is the one that would normally be used by the general user. 978 979 For example, the following python code: 980 981 first_name = "Mike" 982 print_time("Doing this...\n") 983 print_varx("first_name", first_name) 984 print_time("Doing that...\n") 985 986 Will generate output like this: 987 988 #(CDT) 2016/08/10 17:34:42.847374 - 0.001285 - Doing this... 989 first_name: Mike 990 #(CDT) 2016/08/10 17:34:42.847510 - 0.000136 - Doing that... 991 992 This function recognizes several complex types of data such as dict, list or tuple. 993 994 For example, the following python code: 995 996 my_dict = dict(one=1, two=2, three=3) 997 print_var(my_dict) 998 999 Will generate the following output: 1000 1001 my_dict: 1002 my_dict[three]: 3 1003 my_dict[two]: 2 1004 my_dict[one]: 1 1005 1006 Description of argument(s). 1007 var_name The name of the variable to be printed. 1008 var_value The value of the variable to be printed. 1009 fmt A bit map to dictate the format of the output. For printing multi-level 1010 objects like lists and dictionaries, this argument may also be a list of 1011 bit maps. The first list element pertains to the highest level of 1012 output, the second element pertains to the 2nd level of output, etc. The 1013 last element in the list pertains to all subordinate levels. The bits 1014 can be set using the dynamically created functionhs above. Example: 1015 sprint_varx("var1", var1, fmt=verbose()). Note that these values can be 1016 OR'ed together: print_var(var1, hexa() | verbose()). If the caller ORs 1017 mutually exclusive bits (hexa() | octal()), behavior is not guaranteed. 1018 The following features are supported: 1019 hexa Print all integer values in hexadecimal format. 1020 octal Print all integer values in octal format. 1021 binary Print all integer values in binary format. 1022 blank For blank string values, print "<blank>" instead of an actual blank. 1023 verbose For structured values like dictionaries, lists, etc. repeat the name of 1024 the variable on each line to the right of the key or subscript value. 1025 Example: print "my_dict[key1]" instead of just "[key1]". 1026 quote_keys Quote dictionary keys in the output. Example: my_dict['key1'] instead of 1027 my_dict[key1]. 1028 show_type Show the type of the data in angled brackets just to the right of the 1029 data. 1030 strip_brackets Strip the brackets from the variable name portion of the output. This is 1031 applicable when printing complex objects like lists or dictionaries. 1032 no_header For complex objects like dictionaries, do not include a header line. 1033 This necessarily means that the member lines will be indented 2 1034 characters less than they otherwise would have been. 1035 quote_values Quote the values printed. 1036 indent The number of spaces to indent the output. 1037 col1_width The width of the output column containing the variable name. The default 1038 value of this is adjusted so that the var_value lines up with text 1039 printed via the print_time function. 1040 trailing_char The character to be used at the end of the returned string. The default 1041 value is a line feed. 1042 key_list A list of which dictionary keys should be printed. All others keys will 1043 be skipped. Each value in key_list will be regarded as a regular 1044 expression and it will be regarded as anchored to the beginning and ends 1045 of the dictionary key being referenced. For example if key_list is 1046 ["one", "two"], the resulting regex used will be "^one|two$", i.e. only 1047 keys "one" and "two" from the var_value dictionary will be printed. As 1048 another example, if the caller were to specify a key_list of ["one.*"], 1049 then only dictionary keys whose names begin with "one" will be printed. 1050 Note: This argument pertains only to var_values which are dictionaries. 1051 delim The value to be used to delimit the variable name from the variable value 1052 in the output. 1053 """ 1054 1055 fmt, child_fmt = parse_fmt(fmt) 1056 1057 if fmt & show_type(): 1058 type_str = "<" + str(type(var_value)).split("'")[1] + ">" 1059 # Compose object type categories. 1060 int_types = get_int_types() 1061 string_types = get_string_types() 1062 simple_types = int_types + string_types + (float, bool, type, type(None)) 1063 # Determine the type. 1064 if type(var_value) in simple_types: 1065 # The data type is simple in the sense that it has no subordinate parts. 1066 # Adjust col1_width. 1067 col1_width = col1_width - indent 1068 # Set default value for value_format. 1069 value_format = "%s" 1070 # Process format requests. 1071 if type(var_value) in int_types: 1072 # Process format values pertaining to int types. 1073 if fmt & hexa(): 1074 num_hex_digits = max(dft_num_hex_digits(), 1075 get_req_num_hex_digits(var_value)) 1076 # Convert a negative number to its positive twos complement for proper printing. For 1077 # example, instead of printing -1 as "0x-000000000000001" it will be printed as 1078 # "0xffffffffffffffff". 1079 var_value = var_value & (2 ** (num_hex_digits * 4) - 1) 1080 value_format = "0x%0" + str(num_hex_digits) + "x" 1081 elif fmt & octal(): 1082 value_format = "0o%016o" 1083 elif fmt & binary(): 1084 num_digits, remainder = \ 1085 divmod(max(bit_length(var_value), 1), 8) 1086 num_digits *= 8 1087 if remainder: 1088 num_digits += 8 1089 num_digits += 2 1090 value_format = '#0' + str(num_digits) + 'b' 1091 var_value = format(var_value, value_format) 1092 value_format = "%s" 1093 elif type(var_value) in string_types: 1094 # Process format values pertaining to string types. 1095 if fmt & blank() and var_value == "": 1096 value_format = "%s" 1097 var_value = "<blank>" 1098 elif type(var_value) is type: 1099 var_value = str(var_value).split("'")[1] 1100 format_string = "%" + str(indent) + "s%-" + str(col1_width) + "s" \ 1101 + value_format 1102 if fmt & show_type(): 1103 if var_value != "": 1104 format_string += " " 1105 format_string += type_str 1106 format_string += trailing_char 1107 if fmt & quote_values(): 1108 var_value = "'" + var_value + "'" 1109 if not (fmt & verbose()): 1110 # Strip everything leading up to the first left square brace. 1111 var_name = re.sub(r".*\[", "[", var_name) 1112 if (fmt & strip_brackets()): 1113 var_name = re.sub(r"[\[\]]", "", var_name) 1114 if value_format == "0x%08x": 1115 return format_string % ("", str(var_name) + delim, 1116 var_value & 0xffffffff) 1117 else: 1118 return format_string % ("", str(var_name) + delim, var_value) 1119 else: 1120 # The data type is complex in the sense that it has subordinate parts. 1121 if (fmt & no_header()): 1122 buffer = "" 1123 else: 1124 # Create header line. 1125 if not (fmt & verbose()): 1126 # Strip everything leading up to the first square brace. 1127 loc_var_name = re.sub(r".*\[", "[", var_name) 1128 else: 1129 loc_var_name = var_name 1130 if (fmt & strip_brackets()): 1131 loc_var_name = re.sub(r"[\[\]]", "", loc_var_name) 1132 format_string = "%" + str(indent) + "s%s\n" 1133 buffer = format_string % ("", loc_var_name + ":") 1134 if fmt & show_type(): 1135 buffer = buffer.replace("\n", " " + type_str + "\n") 1136 indent += 2 1137 try: 1138 length = len(var_value) 1139 except TypeError: 1140 length = 0 1141 ix = 0 1142 loc_trailing_char = "\n" 1143 if is_dict(var_value): 1144 if type(child_fmt) is list: 1145 child_quote_keys = (child_fmt[0] & quote_keys()) 1146 else: 1147 child_quote_keys = (child_fmt & quote_keys()) 1148 for key, value in var_value.items(): 1149 if key_list is not None: 1150 key_list_regex = "^" + "|".join(key_list) + "$" 1151 if not re.match(key_list_regex, key): 1152 continue 1153 ix += 1 1154 if ix == length: 1155 loc_trailing_char = trailing_char 1156 if child_quote_keys: 1157 key = "'" + key + "'" 1158 key = "[" + str(key) + "]" 1159 buffer += sprint_varx(var_name + key, value, child_fmt, indent, 1160 col1_width, loc_trailing_char, key_list, 1161 delim) 1162 elif type(var_value) in (list, tuple, set): 1163 for key, value in enumerate(var_value): 1164 ix += 1 1165 if ix == length: 1166 loc_trailing_char = trailing_char 1167 key = "[" + str(key) + "]" 1168 buffer += sprint_varx(var_name + key, value, child_fmt, indent, 1169 col1_width, loc_trailing_char, key_list, 1170 delim) 1171 elif isinstance(var_value, argparse.Namespace): 1172 for key in var_value.__dict__: 1173 ix += 1 1174 if ix == length: 1175 loc_trailing_char = trailing_char 1176 cmd_buf = "buffer += sprint_varx(var_name + \".\" + str(key)" \ 1177 + ", var_value." + key + ", child_fmt, indent," \ 1178 + " col1_width, loc_trailing_char, key_list," \ 1179 + " delim)" 1180 exec(cmd_buf) 1181 else: 1182 var_type = type(var_value).__name__ 1183 func_name = sys._getframe().f_code.co_name 1184 var_value = "<" + var_type + " type not supported by " + \ 1185 func_name + "()>" 1186 value_format = "%s" 1187 indent -= 2 1188 # Adjust col1_width. 1189 col1_width = col1_width - indent 1190 format_string = "%" + str(indent) + "s%-" \ 1191 + str(col1_width) + "s" + value_format + trailing_char 1192 return format_string % ("", str(var_name) + ":", var_value) 1193 1194 return buffer 1195 1196 return "" 1197 1198 1199def sprint_var(*args, **kwargs): 1200 r""" 1201 Figure out the name of the first argument for the caller and then call sprint_varx with it. Therefore, 1202 the following 2 calls are equivalent: 1203 sprint_varx("var1", var1) 1204 sprint_var(var1) 1205 1206 See sprint_varx for description of arguments. 1207 """ 1208 1209 stack_frame = 2 1210 caller_func_name = sprint_func_name(2) 1211 if caller_func_name.endswith("print_var"): 1212 stack_frame += 1 1213 # Get the name of the first variable passed to this function. 1214 var_name = get_arg_name(None, 1, stack_frame) 1215 return sprint_varx(var_name, *args, **kwargs) 1216 1217 1218def sprint_vars(*args, **kwargs): 1219 r""" 1220 Sprint the values of one or more variables. 1221 1222 Description of argument(s): 1223 args The variable values which are to be printed. 1224 kwargs See sprint_varx (above) for description of additional arguments. 1225 """ 1226 1227 stack_frame = 2 1228 caller_func_name = sprint_func_name(2) 1229 if caller_func_name.endswith("print_vars"): 1230 stack_frame += 1 1231 1232 buffer = "" 1233 arg_num = 1 1234 for var_value in args: 1235 var_name = get_arg_name(None, arg_num, stack_frame) 1236 buffer += sprint_varx(var_name, var_value, **kwargs) 1237 arg_num += 1 1238 1239 return buffer 1240 1241 1242def sprint_dashes(indent=dft_indent, 1243 width=80, 1244 line_feed=1, 1245 char="-"): 1246 r""" 1247 Return a string of dashes to the caller. 1248 1249 Description of argument(s): 1250 indent The number of characters to indent the output. 1251 width The width of the string of dashes. 1252 line_feed Indicates whether the output should end with a line feed. 1253 char The character to be repeated in the output string. 1254 """ 1255 1256 width = int(width) 1257 buffer = " " * int(indent) + char * width 1258 if line_feed: 1259 buffer += "\n" 1260 1261 return buffer 1262 1263 1264def sindent(text="", 1265 indent=0): 1266 r""" 1267 Pre-pend the specified number of characters to the text string (i.e. indent it) and return it. 1268 1269 Description of argument(s): 1270 text The string to be indented. 1271 indent The number of characters to indent the string. 1272 """ 1273 1274 format_string = "%" + str(indent) + "s%s" 1275 buffer = format_string % ("", text) 1276 1277 return buffer 1278 1279 1280func_line_style_std = None 1281func_line_style_short = 1 1282 1283 1284def sprint_func_line(stack_frame, style=None, max_width=160): 1285 r""" 1286 For the given stack_frame, return a formatted string containing the function name and all its arguments. 1287 1288 Example: 1289 1290 func1(last_name = 'walsh', first_name = 'mikey') 1291 1292 Description of argument(s): 1293 stack_frame A stack frame (such as is returned by inspect.stack()). 1294 style Indicates the style or formatting of the result string. Acceptable 1295 values are shown above. 1296 max_width The max width of the result. If it exceeds this length, it will be 1297 truncated on the right. 1298 1299 Description of styles: 1300 func_line_style_std The standard formatting. 1301 func_line_style_short 1) The self parm (associated with methods) will be dropped. 2) The args 1302 and kwargs values will be treated as special. In both cases the arg name 1303 ('args' or 'kwargs') will be dropped and only the values will be shown. 1304 """ 1305 1306 func_name = str(stack_frame[3]) 1307 if func_name == "?": 1308 # "?" is the name used when code is not in a function. 1309 func_name = "(none)" 1310 1311 if func_name == "<module>": 1312 # If the func_name is the "main" program, we simply get the command line call string. 1313 func_and_args = ' '.join(sys.argv) 1314 else: 1315 # Get the program arguments. 1316 (args, varargs, keywords, locals) =\ 1317 inspect.getargvalues(stack_frame[0]) 1318 1319 args_list = [] 1320 for arg_name in filter(None, args + [varargs, keywords]): 1321 # Get the arg value from frame locals. 1322 arg_value = locals[arg_name] 1323 if arg_name == 'self': 1324 if style == func_line_style_short: 1325 continue 1326 # Manipulations to improve output for class methods. 1327 func_name = arg_value.__class__.__name__ + "." + func_name 1328 args_list.append(arg_name + " = <self>") 1329 elif (style == func_line_style_short 1330 and arg_name == 'args' 1331 and type(arg_value) in (list, tuple)): 1332 if len(arg_value) == 0: 1333 continue 1334 args_list.append(repr(', '.join(arg_value))) 1335 elif (style == func_line_style_short 1336 and arg_name == 'kwargs' 1337 and type(arg_value) is dict): 1338 for key, value in arg_value.items(): 1339 args_list.append(key + "=" + repr(value)) 1340 else: 1341 args_list.append(arg_name + " = " + repr(arg_value)) 1342 args_str = "(" + ', '.join(map(str, args_list)) + ")" 1343 1344 # Now we need to print this in a nicely-wrapped way. 1345 func_and_args = func_name + args_str 1346 1347 if len(func_and_args) > max_width: 1348 func_and_args = func_and_args[0:max_width] + "..." 1349 return func_and_args 1350 1351 1352def sprint_call_stack(indent=0, 1353 stack_frame_ix=0, 1354 style=None): 1355 r""" 1356 Return a call stack report for the given point in the program with line numbers, function names and 1357 function parameters and arguments. 1358 1359 Sample output: 1360 1361 ------------------------------------------------------------------------- 1362 Python function call stack 1363 1364 Line # Function name and arguments 1365 ------ ------------------------------------------------------------------ 1366 424 sprint_call_stack() 1367 4 print_call_stack() 1368 31 func1(last_name = 'walsh', first_name = 'mikey') 1369 59 /tmp/scr5.py 1370 ------------------------------------------------------------------------- 1371 1372 Description of argument(s): 1373 indent The number of characters to indent each line of output. 1374 stack_frame_ix The index of the first stack frame which is to be returned. 1375 style See the sprint_line_func prolog above for details. 1376 """ 1377 1378 buffer = "" 1379 buffer += sprint_dashes(indent) 1380 buffer += sindent("Python function call stack\n\n", indent) 1381 buffer += sindent("Line # Function name and arguments\n", indent) 1382 buffer += sprint_dashes(indent, 6, 0) + " " + sprint_dashes(0, 73) 1383 1384 # Grab the current program stack. 1385 work_around_inspect_stack_cwd_failure() 1386 current_stack = inspect.stack() 1387 1388 # Process each frame in turn. 1389 format_string = "%6s %s\n" 1390 ix = 0 1391 for stack_frame in current_stack: 1392 if ix < stack_frame_ix: 1393 ix += 1 1394 continue 1395 # Make the line number shown to be the line where one finds the line shown. 1396 try: 1397 line_num = str(current_stack[ix + 1][2]) 1398 except IndexError: 1399 line_num = "" 1400 func_and_args = sprint_func_line(stack_frame, style=style) 1401 1402 buffer += sindent(format_string % (line_num, func_and_args), indent) 1403 ix += 1 1404 1405 buffer += sprint_dashes(indent) 1406 1407 return buffer 1408 1409 1410def sprint_executing(stack_frame_ix=None, style=None, max_width=None): 1411 r""" 1412 Print a line indicating what function is executing and with what parameter values. This is useful for 1413 debugging. 1414 1415 Sample output: 1416 1417 #(CDT) 2016/08/25 17:54:27 - Executing: func1(x = 1) 1418 1419 Description of argument(s): 1420 stack_frame_ix The index of the stack frame whose function info should be returned. If 1421 the caller does not specify a value, this function will set the value to 1422 1 which is the index of the caller's stack frame. If the caller is the 1423 wrapper function "print_executing", this function will bump it up by 1. 1424 style See the sprint_line_func prolog above for details. 1425 max_width See the sprint_line_func prolog above for details. 1426 """ 1427 1428 # If user wants default stack_frame_ix. 1429 if stack_frame_ix is None: 1430 func_name = sys._getframe().f_code.co_name 1431 caller_func_name = sys._getframe(1).f_code.co_name 1432 if caller_func_name.endswith(func_name[1:]): 1433 stack_frame_ix = 2 1434 else: 1435 stack_frame_ix = 1 1436 1437 work_around_inspect_stack_cwd_failure() 1438 stack_frame = inspect.stack()[stack_frame_ix] 1439 1440 if max_width is None: 1441 max_width = 160 - (dft_col1_width + 11) 1442 func_and_args = sprint_func_line(stack_frame, style, max_width=max_width) 1443 1444 return sprint_time() + "Executing: " + func_and_args + "\n" 1445 1446 1447def sprint_pgm_header(indent=0, 1448 linefeed=1): 1449 r""" 1450 Return a standardized header that programs should print at the beginning of the run. It includes useful 1451 information like command line, pid, userid, program parameters, etc. 1452 1453 Description of argument(s): 1454 indent The number of characters to indent each line of output. 1455 linefeed Indicates whether a line feed be included at the beginning and end of the 1456 report. 1457 """ 1458 1459 col1_width = dft_col1_width + indent 1460 1461 buffer = "" 1462 if linefeed: 1463 buffer = "\n" 1464 1465 if robot_env: 1466 suite_name = BuiltIn().get_variable_value("${suite_name}") 1467 buffer += sindent(sprint_time("Running test suite \"" + suite_name 1468 + "\".\n"), indent) 1469 1470 buffer += sindent(sprint_time() + "Running " + pgm_name + ".\n", indent) 1471 buffer += sindent(sprint_time() + "Program parameter values, etc.:\n\n", 1472 indent) 1473 buffer += sprint_varx("command_line", ' '.join(sys.argv), 0, indent, 1474 col1_width) 1475 # We want the output to show a customized name for the pid and pgid but we want it to look like a valid 1476 # variable name. Therefore, we'll use pgm_name_var_name which was set when this module was imported. 1477 buffer += sprint_varx(pgm_name_var_name + "_pid", os.getpid(), 0, indent, 1478 col1_width) 1479 buffer += sprint_varx(pgm_name_var_name + "_pgid", os.getpgrp(), 0, indent, 1480 col1_width) 1481 userid_num = str(os.geteuid()) 1482 try: 1483 username = os.getlogin() 1484 except OSError: 1485 if userid_num == "0": 1486 username = "root" 1487 else: 1488 username = "?" 1489 buffer += sprint_varx("uid", userid_num + " (" + username 1490 + ")", 0, indent, col1_width) 1491 buffer += sprint_varx("gid", str(os.getgid()) + " (" 1492 + str(grp.getgrgid(os.getgid()).gr_name) + ")", 0, 1493 indent, col1_width) 1494 buffer += sprint_varx("host_name", socket.gethostname(), 0, indent, 1495 col1_width) 1496 try: 1497 DISPLAY = os.environ['DISPLAY'] 1498 except KeyError: 1499 DISPLAY = "" 1500 buffer += sprint_var(DISPLAY, 0, indent, col1_width) 1501 PYTHON_VERSION = os.environ.get('PYTHON_VERSION', None) 1502 if PYTHON_VERSION is not None: 1503 buffer += sprint_var(PYTHON_VERSION, 0, indent, col1_width) 1504 PYTHON_PGM_PATH = os.environ.get('PYTHON_PGM_PATH', None) 1505 if PYTHON_PGM_PATH is not None: 1506 buffer += sprint_var(PYTHON_PGM_PATH, 0, indent, col1_width) 1507 python_version = sys.version.replace("\n", "") 1508 buffer += sprint_var(python_version, 0, indent, col1_width) 1509 ROBOT_VERSION = os.environ.get('ROBOT_VERSION', None) 1510 if ROBOT_VERSION is not None: 1511 buffer += sprint_var(ROBOT_VERSION, 0, indent, col1_width) 1512 ROBOT_PGM_PATH = os.environ.get('ROBOT_PGM_PATH', None) 1513 if ROBOT_PGM_PATH is not None: 1514 buffer += sprint_var(ROBOT_PGM_PATH, 0, indent, col1_width) 1515 1516 # TODO: Add code to print caller's parms. 1517 1518 # __builtin__.arg_obj is created by the get_arg module function, gen_get_options. 1519 try: 1520 buffer += ga.sprint_args(__builtin__.arg_obj, indent) 1521 except AttributeError: 1522 pass 1523 1524 if robot_env: 1525 # Get value of global parm_list. 1526 parm_list = BuiltIn().get_variable_value("${parm_list}") 1527 1528 for parm in parm_list: 1529 parm_value = BuiltIn().get_variable_value("${" + parm + "}") 1530 buffer += sprint_varx(parm, parm_value, 0, indent, col1_width) 1531 1532 # Setting global program_pid. 1533 BuiltIn().set_global_variable("${program_pid}", os.getpid()) 1534 1535 if linefeed: 1536 buffer += "\n" 1537 1538 return buffer 1539 1540 1541def sprint_error_report(error_text="\n", 1542 indent=2, 1543 format=None, 1544 stack_frame_ix=None): 1545 r""" 1546 Return a string with a standardized report which includes the caller's error text, the call stack and the 1547 program header. 1548 1549 Description of argument(s): 1550 error_text The error text to be included in the report. The caller should include 1551 any needed linefeeds. 1552 indent The number of characters to indent each line of output. 1553 format Long or short format. Long includes extras like lines of dashes, call 1554 stack, etc. 1555 stack_frame_ix The index of the first stack frame which is to be shown in the 1556 print_call_stack portion of the error report. 1557 """ 1558 1559 # Process input. 1560 indent = int(indent) 1561 if format is None: 1562 if robot_env: 1563 format = 'short' 1564 else: 1565 format = 'long' 1566 error_text = error_text.rstrip('\n') + '\n' 1567 1568 if format == 'short': 1569 return sprint_error(error_text) 1570 1571 buffer = "" 1572 buffer += sprint_dashes(width=120, char="=") 1573 buffer += sprint_error(error_text) 1574 buffer += "\n" 1575 if not stack_frame_ix: 1576 # Calling sprint_call_stack with stack_frame_ix of 0 causes it to show itself and this function in 1577 # the call stack. This is not helpful to a debugger and is therefore clutter. We will adjust the 1578 # stack_frame_ix to hide that information. 1579 stack_frame_ix = 1 1580 caller_func_name = sprint_func_name(1) 1581 if caller_func_name.endswith("print_error_report"): 1582 stack_frame_ix += 1 1583 caller_func_name = sprint_func_name(2) 1584 if caller_func_name.endswith("print_error_report"): 1585 stack_frame_ix += 1 1586 buffer += sprint_call_stack(indent, stack_frame_ix) 1587 buffer += sprint_pgm_header(indent) 1588 buffer += sprint_dashes(width=120, char="=") 1589 1590 return buffer 1591 1592 1593def sprint_issuing(cmd_buf, 1594 test_mode=0): 1595 r""" 1596 Return a line indicating a command that the program is about to execute. 1597 1598 Sample output for a cmd_buf of "ls" 1599 1600 #(CDT) 2016/08/25 17:57:36 - Issuing: ls 1601 1602 Description of argument(s): 1603 cmd_buf The command to be executed by caller. 1604 test_mode With test_mode set, the output will look like this: 1605 1606 #(CDT) 2016/08/25 17:57:36 - (test_mode) Issuing: ls 1607 1608 """ 1609 1610 buffer = sprint_time() 1611 if test_mode: 1612 buffer += "(test_mode) " 1613 if type(cmd_buf) is list: 1614 # Assume this is a robot command in the form of a list. 1615 cmd_buf = ' '.join([str(element) for element in cmd_buf]) 1616 buffer += "Issuing: " + cmd_buf + "\n" 1617 1618 return buffer 1619 1620 1621def sprint_pgm_footer(): 1622 r""" 1623 Return a standardized footer that programs should print at the end of the program run. It includes 1624 useful information like total run time, etc. 1625 """ 1626 1627 buffer = "\n" + sprint_time() + "Finished running " + pgm_name + ".\n\n" 1628 1629 total_time = time.time() - start_time 1630 total_time_string = "%0.6f" % total_time 1631 1632 buffer += sprint_varx(pgm_name_var_name + "_runtime", total_time_string) 1633 buffer += "\n" 1634 1635 return buffer 1636 1637 1638def sprint_file(file_path): 1639 r""" 1640 Return the file data as a string. 1641 1642 Description of argument(s): 1643 file_path The path to a file (e.g. "/tmp/file1"). 1644 """ 1645 1646 with open(file_path, 'r') as file: 1647 buffer = file.read() 1648 return buffer 1649 1650 1651def sprint(buffer=""): 1652 r""" 1653 Simply return the user's buffer. This function is used by the qprint and dprint functions defined 1654 dynamically below, i.e. it would not normally be called for general use. 1655 1656 Description of argument(s). 1657 buffer This will be returned to the caller. 1658 """ 1659 1660 try: 1661 return str(buffer) 1662 except UnicodeEncodeError: 1663 return buffer 1664 1665 1666def sprintn(buffer=""): 1667 r""" 1668 Simply return the user's buffer with a line feed. This function is used by the qprint and dprint 1669 functions defined dynamically below, i.e. it would not normally be called for general use. 1670 1671 Description of argument(s). 1672 buffer This will be returned to the caller. 1673 """ 1674 1675 try: 1676 buffer = str(buffer) + "\n" 1677 except UnicodeEncodeError: 1678 buffer = buffer + "\n" 1679 1680 return buffer 1681 1682 1683def gp_print(buffer, 1684 stream='stdout'): 1685 r""" 1686 Print the buffer using either sys.stdout.write or BuiltIn().log_to_console depending on whether we are 1687 running in a robot environment. 1688 1689 This function is intended for use only by other functions in this module. 1690 1691 Description of argument(s): 1692 buffer The string to be printed. 1693 stream Either "stdout" or "stderr". 1694 """ 1695 1696 if robot_env: 1697 BuiltIn().log_to_console(buffer, stream=stream, no_newline=True) 1698 else: 1699 if stream == "stdout": 1700 sys.stdout.write(buffer) 1701 sys.stdout.flush() 1702 else: 1703 sys.stderr.write(buffer) 1704 sys.stderr.flush() 1705 1706 1707def gp_log(buffer): 1708 r""" 1709 Log the buffer using either python logging or BuiltIn().log depending on whether we are running in a 1710 robot environment. 1711 1712 This function is intended for use only by other functions in this module. 1713 1714 Description of argument(s): 1715 buffer The string to be logged. 1716 """ 1717 1718 if robot_env: 1719 BuiltIn().log(buffer) 1720 else: 1721 logging.warning(buffer) 1722 1723 1724def gp_debug_print(buffer): 1725 r""" 1726 Print with gp_print only if gen_print_debug is set. 1727 1728 This function is intended for use only by other functions in this module. 1729 1730 Description of argument(s): 1731 buffer The string to be printed. 1732 """ 1733 1734 if not gen_print_debug: 1735 return 1736 1737 gp_print(buffer) 1738 1739 1740def get_var_value(var_value=None, 1741 default=1, 1742 var_name=None): 1743 r""" 1744 Return either var_value, the corresponding global value or default. 1745 1746 If var_value is not None, it will simply be returned. 1747 1748 If var_value is None, this function will return the corresponding global value of the variable in 1749 question. 1750 1751 Note: For global values, if we are in a robot environment, get_variable_value will be used. Otherwise, 1752 the __builtin__ version of the variable is returned (which are set by gen_arg.py functions). 1753 1754 If there is no global value associated with the variable, default is returned. 1755 1756 This function is useful for other functions in setting default values for parameters. 1757 1758 Example use: 1759 1760 def my_func(quiet=None): 1761 1762 quiet = int(get_var_value(quiet, 0)) 1763 1764 Example calls to my_func(): 1765 1766 In the following example, the caller is explicitly asking to have quiet be set to 1. 1767 1768 my_func(quiet=1) 1769 1770 In the following example, quiet will be set to the global value of quiet, if defined, or to 0 (the 1771 default). 1772 1773 my_func() 1774 1775 Description of argument(s): 1776 var_value The value to be returned (if not equal to None). 1777 default The value that is returned if var_value is None and there is no 1778 corresponding global value defined. 1779 var_name The name of the variable whose value is to be returned. Under most 1780 circumstances, this value need not be provided. This function can figure 1781 out the name of the variable passed as var_value. One exception to this 1782 would be if this function is called directly from a .robot file. 1783 """ 1784 1785 if var_value is not None: 1786 return var_value 1787 1788 if var_name is None: 1789 var_name = get_arg_name(None, 1, 2) 1790 1791 if robot_env: 1792 var_value = BuiltIn().get_variable_value("${" + var_name + "}", 1793 default) 1794 else: 1795 var_value = getattr(__builtin__, var_name, default) 1796 1797 return var_value 1798 1799 1800def get_stack_var(var_name, 1801 default="", 1802 init_stack_ix=2): 1803 r""" 1804 Starting with the caller's stack level, search upward in the call stack for a variable named var_name and 1805 return its value. If the variable cannot be found in the stack, attempt to get the global value. If the 1806 variable still cannot be found, return default. 1807 1808 Example code: 1809 1810 def func12(): 1811 my_loc_var1 = get_stack_var('my_var1', "default value") 1812 1813 def func11(): 1814 my_var1 = 11 1815 func12() 1816 1817 In this example, get_stack_var will find the value of my_var1 in func11's stack and will therefore return 1818 the value 11. Therefore, my_loc_var1 would get set to 11. 1819 1820 Description of argument(s): 1821 var_name The name of the variable to be searched for. 1822 default The value to return if the the variable cannot be found. 1823 init_stack_ix The initial stack index from which to begin the search. 0 would be the 1824 index of this func1tion ("get_stack_var"), 1 would be the index of the 1825 function calling this function, etc. 1826 """ 1827 1828 work_around_inspect_stack_cwd_failure() 1829 default = get_var_value(var_name=var_name, default=default) 1830 return next((frame[0].f_locals[var_name] 1831 for frame in inspect.stack()[init_stack_ix:] 1832 if var_name in frame[0].f_locals), default) 1833 1834 1835# hidden_text is a list of passwords which are to be replaced with asterisks by print functions defined in 1836# this module. 1837hidden_text = [] 1838# password_regex is created based on the contents of hidden_text. 1839password_regex = "" 1840 1841 1842def register_passwords(*args): 1843 r""" 1844 Register one or more passwords which are to be hidden in output produced by the print functions in this 1845 module. 1846 1847 Note: Blank password values are NOT registered. They are simply ignored. 1848 1849 Description of argument(s): 1850 args One or more password values. If a given password value is already 1851 registered, this function will simply do nothing. 1852 """ 1853 1854 global hidden_text 1855 global password_regex 1856 1857 for password in args: 1858 if password == "": 1859 break 1860 if password in hidden_text: 1861 break 1862 1863 # Place the password into the hidden_text list. 1864 hidden_text.append(password) 1865 # Create a corresponding password regular expression. Escape regex special characters too. 1866 password_regex = '(' +\ 1867 '|'.join([re.escape(x) for x in hidden_text]) + ')' 1868 1869 1870def replace_passwords(buffer): 1871 r""" 1872 Return the buffer but with all registered passwords replaced by a string of asterisks. 1873 1874 1875 Description of argument(s): 1876 buffer The string to be returned but with passwords replaced. 1877 """ 1878 1879 global password_regex 1880 1881 if int(os.environ.get("DEBUG_SHOW_PASSWORDS", "0")): 1882 return buffer 1883 1884 if password_regex == "": 1885 # No passwords to replace. 1886 return buffer 1887 1888 return re.sub(password_regex, "********", buffer) 1889 1890 1891def create_print_wrapper_funcs(func_names, 1892 stderr_func_names, 1893 replace_dict, 1894 func_prefix=""): 1895 r""" 1896 Generate code for print wrapper functions and return the generated code as a string. 1897 1898 To illustrate, suppose there is a "print_foo_bar" function in the func_names list. 1899 This function will... 1900 - Expect that there is an sprint_foo_bar function already in existence. 1901 - Create a print_foo_bar function which calls sprint_foo_bar and prints the result. 1902 - Create a qprint_foo_bar function which calls upon sprint_foo_bar only if global value quiet is 0. 1903 - Create a dprint_foo_bar function which calls upon sprint_foo_bar only if global value debug is 1. 1904 1905 Also, code will be generated to define aliases for each function as well. Each alias will be created by 1906 replacing "print_" in the function name with "p" For example, the alias for print_foo_bar will be 1907 pfoo_bar. 1908 1909 Description of argument(s): 1910 func_names A list of functions for which print wrapper function code is to be 1911 generated. 1912 stderr_func_names A list of functions whose generated code should print to stderr rather 1913 than to stdout. 1914 replace_dict Please see the create_func_def_string function in wrap_utils.py for 1915 details on this parameter. This parameter will be passed directly to 1916 create_func_def_string. 1917 func_prefix Prefix to be pre-pended to the generated function name. 1918 """ 1919 1920 buffer = "" 1921 1922 for func_name in func_names: 1923 if func_name in stderr_func_names: 1924 replace_dict['output_stream'] = "stderr" 1925 else: 1926 replace_dict['output_stream'] = "stdout" 1927 1928 s_func_name = "s" + func_name 1929 q_func_name = "q" + func_name 1930 d_func_name = "d" + func_name 1931 1932 # We don't want to try to redefine the "print" function, thus the following if statement. 1933 if func_name != "print": 1934 func_def = create_func_def_string(s_func_name, 1935 func_prefix + func_name, 1936 print_func_template, 1937 replace_dict) 1938 buffer += func_def 1939 1940 func_def = create_func_def_string(s_func_name, 1941 func_prefix + "q" + func_name, 1942 qprint_func_template, replace_dict) 1943 buffer += func_def 1944 1945 func_def = create_func_def_string(s_func_name, 1946 func_prefix + "d" + func_name, 1947 dprint_func_template, replace_dict) 1948 buffer += func_def 1949 1950 func_def = create_func_def_string(s_func_name, 1951 func_prefix + "l" + func_name, 1952 lprint_func_template, replace_dict) 1953 buffer += func_def 1954 1955 # Create abbreviated aliases (e.g. spvar is an alias for sprint_var). 1956 alias = re.sub("print_", "p", func_name) 1957 alias = re.sub("print", "p", alias) 1958 prefixes = [func_prefix + "", "s", func_prefix + "q", 1959 func_prefix + "d", func_prefix + "l"] 1960 for prefix in prefixes: 1961 if alias == "p": 1962 continue 1963 func_def = prefix + alias + " = " + prefix + func_name 1964 buffer += func_def + "\n" 1965 1966 return buffer 1967 1968 1969# In the following section of code, we will dynamically create print versions for each of the sprint 1970# functions defined above. So, for example, where we have an sprint_time() function defined above that 1971# returns the time to the caller in a string, we will create a corresponding print_time() function that will 1972# print that string directly to stdout. 1973 1974# It can be complicated to follow what's being created below. Here is an example of the print_time() 1975# function that will be created: 1976 1977# def print_time(buffer=''): 1978# gp_print(replace_passwords(sprint_time(buffer=buffer)), stream='stdout') 1979 1980# For each print function defined below, there will also be a qprint, a dprint and an lprint version defined 1981# (e.g. qprint_time, dprint_time, lprint_time). 1982 1983# The q version of each print function will only print if the quiet variable is 0. 1984# The d version of each print function will only print if the debug variable is 1. 1985# The l version of each print function will print the contents as log data. For conventional programs, this 1986# means use of the logging module. For robot programs it means use of the BuiltIn().log() function. 1987 1988# Templates for the various print wrapper functions. 1989print_func_template = \ 1990 [ 1991 " <mod_qualifier>gp_print(<mod_qualifier>replace_passwords(" 1992 + "<call_line>), stream='<output_stream>')" 1993 ] 1994 1995qprint_func_template = \ 1996 [ 1997 " quiet = <mod_qualifier>get_stack_var(\"quiet\", 0)", 1998 " if int(quiet): return" 1999 ] + print_func_template 2000 2001dprint_func_template = \ 2002 [ 2003 " debug = <mod_qualifier>get_stack_var(\"debug\", 0)", 2004 " if not int(debug): return" 2005 ] + print_func_template 2006 2007lprint_func_template = \ 2008 [ 2009 " <mod_qualifier>set_last_seconds_ix(<mod_qualifier>" 2010 + "lprint_last_seconds_ix())", 2011 " <mod_qualifier>gp_log(<mod_qualifier>replace_passwords" 2012 + "(<call_line>))", 2013 " <mod_qualifier>set_last_seconds_ix(<mod_qualifier>" 2014 + "standard_print_last_seconds_ix())" 2015 ] 2016 2017replace_dict = {'output_stream': 'stdout', 'mod_qualifier': ''} 2018 2019gp_debug_print("robot_env: " + str(robot_env) + "\n") 2020 2021# func_names contains a list of all print functions which should be created from their sprint counterparts. 2022func_names = ['print_time', 'print_timen', 'print_error', 'print_varx', 2023 'print_var', 'print_vars', 'print_dashes', 'indent', 2024 'print_call_stack', 'print_func_name', 'print_executing', 2025 'print_pgm_header', 'print_issuing', 'print_pgm_footer', 2026 'print_file', 'print_error_report', 'print', 'printn'] 2027 2028# stderr_func_names is a list of functions whose output should go to stderr rather than stdout. 2029stderr_func_names = ['print_error', 'print_error_report'] 2030 2031func_defs = create_print_wrapper_funcs(func_names, stderr_func_names, 2032 replace_dict) 2033gp_debug_print(func_defs) 2034exec(func_defs) 2035