1#!/usr/bin/env python 2 3r""" 4This module provides many valuable print functions such as sprint_var, 5sprint_time, sprint_error, sprint_call_stack. 6""" 7 8import sys 9import os 10import time 11import inspect 12import re 13import grp 14import socket 15import argparse 16import __builtin__ 17import logging 18import collections 19from wrap_utils import * 20 21try: 22 robot_env = 1 23 from robot.utils import DotDict 24 from robot.utils import NormalizedDict 25 from robot.libraries.BuiltIn import BuiltIn 26 # Having access to the robot libraries alone does not indicate that we 27 # are in a robot environment. The following try block should confirm that. 28 try: 29 var_value = BuiltIn().get_variable_value("${SUITE_NAME}", "") 30 except: 31 robot_env = 0 32except ImportError: 33 robot_env = 0 34 35import gen_arg as ga 36 37# Setting these variables for use both inside this module and by programs 38# importing this module. 39pgm_file_path = sys.argv[0] 40pgm_name = os.path.basename(pgm_file_path) 41pgm_dir_path = os.path.normpath(re.sub("/" + pgm_name, "", pgm_file_path)) +\ 42 os.path.sep 43 44 45# Some functions (e.g. sprint_pgm_header) have need of a program name value 46# that looks more like a valid variable name. Therefore, we'll swap odd 47# characters like "." out for underscores. 48pgm_name_var_name = pgm_name.replace(".", "_") 49 50# Initialize global values used as defaults by print_time, print_var, etc. 51col1_indent = 0 52 53# Calculate default column width for print_var functions based on environment 54# variable settings. The objective is to make the variable values line up 55# nicely with the time stamps. 56col1_width = 29 57 58NANOSECONDS = os.environ.get('NANOSECONDS', '1') 59 60 61if NANOSECONDS == "1": 62 col1_width = col1_width + 7 63 64SHOW_ELAPSED_TIME = os.environ.get('SHOW_ELAPSED_TIME', '1') 65 66if SHOW_ELAPSED_TIME == "1": 67 if NANOSECONDS == "1": 68 col1_width = col1_width + 14 69 else: 70 col1_width = col1_width + 7 71 72# Initialize some time variables used in module functions. 73start_time = time.time() 74sprint_time_last_seconds = start_time 75 76# The user can set environment variable "GEN_PRINT_DEBUG" to get debug output 77# from this module. 78gen_print_debug = int(os.environ.get('GEN_PRINT_DEBUG', 0)) 79 80 81def sprint_func_name(stack_frame_ix=None): 82 r""" 83 Return the function name associated with the indicated stack frame. 84 85 Description of arguments: 86 stack_frame_ix The index of the stack frame whose 87 function name should be returned. If the 88 caller does not specify a value, this 89 function will set the value to 1 which is 90 the index of the caller's stack frame. If 91 the caller is the wrapper function 92 "print_func_name", this function will bump 93 it up by 1. 94 """ 95 96 # If user specified no stack_frame_ix, we'll set it to a proper default 97 # value. 98 if stack_frame_ix is None: 99 func_name = sys._getframe().f_code.co_name 100 caller_func_name = sys._getframe(1).f_code.co_name 101 if func_name[1:] == caller_func_name: 102 stack_frame_ix = 2 103 else: 104 stack_frame_ix = 1 105 106 func_name = sys._getframe(stack_frame_ix).f_code.co_name 107 108 return func_name 109 110 111def get_line_indent(line): 112 r""" 113 Return the number of spaces at the beginning of the line. 114 """ 115 116 return len(line) - len(line.lstrip(' ')) 117 118 119# get_arg_name is not a print function per se. I have included it in this 120# module because it is used by sprint_var which is found in this module. 121def get_arg_name(var, 122 arg_num=1, 123 stack_frame_ix=1): 124 r""" 125 Return the "name" of an argument passed to a function. This could be a 126 literal or a variable name. 127 128 Description of arguments: 129 var The variable whose name you want returned. 130 arg_num The arg number whose name is to be 131 returned. To illustrate how arg_num is 132 processed, suppose that a programmer codes 133 this line: "rc, outbuf = my_func(var1, 134 var2)" and suppose that my_func has this 135 line of code: "result = gp.get_arg_name(0, 136 arg_num, 2)". If arg_num is positive, the 137 indicated argument is returned. For 138 example, if arg_num is 1, "var1" would be 139 returned, If arg_num is 2, "var2" would be 140 returned. If arg_num exceeds the number 141 of arguments, get_arg_name will simply 142 return a complete list of the arguments. 143 If arg_num is 0, get_arg_name will return 144 the name of the target function as 145 specified in the calling line ("my_func" 146 in this case). To clarify, if the caller 147 of the target function uses an alias 148 function name, the alias name would be 149 returned. If arg_num is negative, an 150 lvalue variable name is returned. 151 Continuing with the given example, if 152 arg_num is -2 the 2nd parm to the left of 153 the "=" ("rc" in this case) should be 154 returned. If arg_num is -1, the 1st parm 155 to the left of the "=" ("out_buf" in this 156 case) should be returned. If arg_num is 157 less than -2, an entire dictionary is 158 returned. The keys to the dictionary for 159 this example would be -2 and -1. 160 stack_frame_ix The stack frame index of the target 161 function. This value must be 1 or 162 greater. 1 would indicate get_arg_name's 163 stack frame. 2 would be the caller of 164 get_arg_name's stack frame, etc. 165 166 Example 1: 167 168 my_var = "mike" 169 var_name = get_arg_name(my_var) 170 171 In this example, var_name will receive the value "my_var". 172 173 Example 2: 174 175 def test1(var): 176 # Getting the var name of the first arg to this function, test1. 177 # Note, in this case, it doesn't matter what you pass as the first arg 178 # to get_arg_name since it is the caller's variable name that matters. 179 dummy = 1 180 arg_num = 1 181 stack_frame = 2 182 var_name = get_arg_name(dummy, arg_num, stack_frame) 183 184 # Mainline... 185 186 another_var = "whatever" 187 test1(another_var) 188 189 In this example, var_name will be set to "another_var". 190 191 """ 192 193 # Note: I wish to avoid recursion so I refrain from calling any function 194 # that calls this function (i.e. sprint_var, valid_value, etc.). 195 196 # The user can set environment variable "GET_ARG_NAME_DEBUG" to get debug 197 # output from this function. 198 local_debug = int(os.environ.get('GET_ARG_NAME_DEBUG', 0)) 199 # In addition to GET_ARG_NAME_DEBUG, the user can set environment 200 # variable "GET_ARG_NAME_SHOW_SOURCE" to have this function include source 201 # code in the debug output. 202 local_debug_show_source = int( 203 os.environ.get('GET_ARG_NAME_SHOW_SOURCE', 0)) 204 205 if stack_frame_ix < 1: 206 print_error("Programmer error - Variable \"stack_frame_ix\" has an" + 207 " invalid value of \"" + str(stack_frame_ix) + "\". The" + 208 " value must be an integer that is greater than or equal" + 209 " to 1.\n") 210 return 211 212 if local_debug: 213 debug_indent = 2 214 print("") 215 print_dashes(0, 120) 216 print(sprint_func_name() + "() parms:") 217 print_varx("var", var, 0, debug_indent) 218 print_varx("arg_num", arg_num, 0, debug_indent) 219 print_varx("stack_frame_ix", stack_frame_ix, 0, debug_indent) 220 print("") 221 print_call_stack(debug_indent, 2) 222 223 for count in range(0, 2): 224 try: 225 frame, filename, cur_line_no, function_name, lines, index = \ 226 inspect.stack()[stack_frame_ix] 227 except IndexError: 228 print_error("Programmer error - The caller has asked for" + 229 " information about the stack frame at index \"" + 230 str(stack_frame_ix) + "\". However, the stack" + 231 " only contains " + str(len(inspect.stack())) + 232 " entries. Therefore the stack frame index is out" + 233 " of range.\n") 234 return 235 if filename != "<string>": 236 break 237 # filename of "<string>" may mean that the function in question was 238 # defined dynamically and therefore its code stack is inaccessible. 239 # This may happen with functions like "rqprint_var". In this case, 240 # we'll increment the stack_frame_ix and try again. 241 stack_frame_ix += 1 242 if local_debug: 243 print("Adjusted stack_frame_ix...") 244 print_varx("stack_frame_ix", stack_frame_ix, 0, debug_indent) 245 246 real_called_func_name = sprint_func_name(stack_frame_ix) 247 248 module = inspect.getmodule(frame) 249 250 # Though I would expect inspect.getsourcelines(frame) to get all module 251 # source lines if the frame is "<module>", it doesn't do that. Therefore, 252 # for this special case, I will do inspect.getsourcelines(module). 253 if function_name == "<module>": 254 source_lines, source_line_num =\ 255 inspect.getsourcelines(module) 256 line_ix = cur_line_no - source_line_num - 1 257 else: 258 source_lines, source_line_num =\ 259 inspect.getsourcelines(frame) 260 line_ix = cur_line_no - source_line_num 261 262 if local_debug: 263 print("\n Variables retrieved from inspect.stack() function:") 264 print_varx("frame", frame, 0, debug_indent + 2) 265 print_varx("filename", filename, 0, debug_indent + 2) 266 print_varx("cur_line_no", cur_line_no, 0, debug_indent + 2) 267 print_varx("function_name", function_name, 0, debug_indent + 2) 268 print_varx("lines", lines, 0, debug_indent + 2) 269 print_varx("index", index, 0, debug_indent + 2) 270 print_varx("source_line_num", source_line_num, 0, debug_indent) 271 print_varx("line_ix", line_ix, 0, debug_indent) 272 if local_debug_show_source: 273 print_varx("source_lines", source_lines, 0, debug_indent) 274 print_varx("real_called_func_name", real_called_func_name, 0, 275 debug_indent) 276 277 # Get a list of all functions defined for the module. Note that this 278 # doesn't work consistently when _run_exitfuncs is at the top of the stack 279 # (i.e. if we're running an exit function). I've coded a work-around 280 # below for this deficiency. 281 all_functions = inspect.getmembers(module, inspect.isfunction) 282 283 # Get called_func_id by searching for our function in the list of all 284 # functions. 285 called_func_id = None 286 for func_name, function in all_functions: 287 if func_name == real_called_func_name: 288 called_func_id = id(function) 289 break 290 # NOTE: The only time I've found that called_func_id can't be found is 291 # when we're running from an exit function. 292 293 # Look for other functions in module with matching id. 294 aliases = set([real_called_func_name]) 295 for func_name, function in all_functions: 296 if func_name == real_called_func_name: 297 continue 298 func_id = id(function) 299 if func_id == called_func_id: 300 aliases.add(func_name) 301 302 # In most cases, my general purpose code above will find all aliases. 303 # However, for the odd case (i.e. running from exit function), I've added 304 # code to handle pvar, qpvar, dpvar, etc. aliases explicitly since they 305 # are defined in this module and used frequently. 306 # pvar is an alias for print_var. 307 aliases.add(re.sub("print_var", "pvar", real_called_func_name)) 308 309 func_name_regex = "(" + '|'.join(aliases) + ")" 310 pre_args_regex = ".*" + func_name_regex + "[ ]*\(" 311 312 # Search backward through source lines looking for the calling function 313 # name. 314 found = False 315 for start_line_ix in range(line_ix, 0, -1): 316 # Skip comment lines. 317 if re.match(r"[ ]*#", source_lines[start_line_ix]): 318 continue 319 if re.match(pre_args_regex, source_lines[start_line_ix]): 320 found = True 321 break 322 if not found: 323 print_error("Programmer error - Could not find the source line with" + 324 " a reference to function \"" + real_called_func_name + 325 "\".\n") 326 return 327 328 # Search forward through the source lines looking for a line whose 329 # indentation is the same or less than the start line. The end of our 330 # composite line should be the line preceding that line. 331 start_indent = get_line_indent(source_lines[start_line_ix]) 332 end_line_ix = line_ix 333 for end_line_ix in range(line_ix + 1, len(source_lines)): 334 if source_lines[end_line_ix].strip() == "": 335 continue 336 line_indent = get_line_indent(source_lines[end_line_ix]) 337 if line_indent <= start_indent: 338 end_line_ix -= 1 339 break 340 if start_line_ix != 0: 341 # Check to see whether the start line is a continuation of the prior 342 # line? 343 line_indent = get_line_indent(source_lines[start_line_ix - 1]) 344 if line_indent < start_indent: 345 start_line_ix -= 1 346 # Remove the backslash (continuation char). 347 source_lines[start_line_ix] = re.sub(r"[ ]*\\([\r\n]$)", 348 " \\1", 349 source_lines[start_line_ix]) 350 351 # Join the start line through the end line into a composite line. 352 composite_line = ''.join(map(str.strip, 353 source_lines[start_line_ix:end_line_ix + 1])) 354 # Insert one space after first "=" if there isn't one already. 355 composite_line = re.sub("=[ ]*([^ ])", "= \\1", composite_line, 1) 356 357 lvalue_regex = "[ ]+=[ ]*" + func_name_regex + ".*" 358 lvalue_string = re.sub(lvalue_regex, "", composite_line) 359 lvalues_list = map(str.strip, lvalue_string.split(",")) 360 lvalues = collections.OrderedDict() 361 ix = len(lvalues_list) * -1 362 for lvalue in lvalues_list: 363 lvalues[ix] = lvalue 364 ix += 1 365 called_func_name = re.sub("(.*=)?[ ]+" + func_name_regex + 366 "[ ]*\(.*", "\\2", 367 composite_line) 368 arg_list_etc = "(" + re.sub(pre_args_regex, "", composite_line) 369 if local_debug: 370 print_varx("aliases", aliases, 0, debug_indent) 371 print_varx("pre_args_regex", pre_args_regex, 0, debug_indent) 372 print_varx("start_line_ix", start_line_ix, 0, debug_indent) 373 print_varx("end_line_ix", end_line_ix, 0, debug_indent) 374 print_varx("composite_line", composite_line, 0, debug_indent) 375 print_varx("lvalue_regex", lvalue_regex, 0, debug_indent) 376 print_varx("lvalue_string", lvalue_string, 0, debug_indent) 377 print_varx("lvalues", lvalues, 0, debug_indent) 378 print_varx("called_func_name", called_func_name, 0, debug_indent) 379 print_varx("arg_list_etc", arg_list_etc, 0, debug_indent) 380 381 # Parse arg list... 382 # Initialize... 383 nest_level = -1 384 arg_ix = 0 385 args_list = [""] 386 for ix in range(0, len(arg_list_etc)): 387 char = arg_list_etc[ix] 388 # Set the nest_level based on whether we've encounted a parenthesis. 389 if char == "(": 390 nest_level += 1 391 if nest_level == 0: 392 continue 393 elif char == ")": 394 nest_level -= 1 395 if nest_level < 0: 396 break 397 398 # If we reach a comma at base nest level, we are done processing an 399 # argument so we increment arg_ix and initialize a new args_list entry. 400 if char == "," and nest_level == 0: 401 arg_ix += 1 402 args_list.append("") 403 continue 404 405 # For any other character, we append it it to the current arg list 406 # entry. 407 args_list[arg_ix] += char 408 409 # Trim whitespace from each list entry. 410 args_list = [arg.strip() for arg in args_list] 411 412 if arg_num < 0: 413 if abs(arg_num) > len(lvalues): 414 argument = lvalues 415 else: 416 argument = lvalues[arg_num] 417 elif arg_num == 0: 418 argument = called_func_name 419 else: 420 if arg_num > len(args_list): 421 argument = args_list 422 else: 423 argument = args_list[arg_num - 1] 424 425 if local_debug: 426 print_varx("args_list", args_list, 0, debug_indent) 427 print_varx("argument", argument, 0, debug_indent) 428 print_dashes(0, 120) 429 430 return argument 431 432 433def sprint_time(buffer=""): 434 r""" 435 Return the time in the following format. 436 437 Example: 438 439 The following python code... 440 441 sys.stdout.write(sprint_time()) 442 sys.stdout.write("Hi.\n") 443 444 Will result in the following type of output: 445 446 #(CDT) 2016/07/08 15:25:35 - Hi. 447 448 Example: 449 450 The following python code... 451 452 sys.stdout.write(sprint_time("Hi.\n")) 453 454 Will result in the following type of output: 455 456 #(CDT) 2016/08/03 17:12:05 - Hi. 457 458 The following environment variables will affect the formatting as 459 described: 460 NANOSECONDS This will cause the time stamps to be 461 precise to the microsecond (Yes, it 462 probably should have been named 463 MICROSECONDS but the convention was set 464 long ago so we're sticking with it). 465 Example of the output when environment 466 variable NANOSECONDS=1. 467 468 #(CDT) 2016/08/03 17:16:25.510469 - Hi. 469 470 SHOW_ELAPSED_TIME This will cause the elapsed time to be 471 included in the output. This is the 472 amount of time that has elapsed since the 473 last time this function was called. The 474 precision of the elapsed time field is 475 also affected by the value of the 476 NANOSECONDS environment variable. Example 477 of the output when environment variable 478 NANOSECONDS=0 and SHOW_ELAPSED_TIME=1. 479 480 #(CDT) 2016/08/03 17:17:40 - 0 - Hi. 481 482 Example of the output when environment variable NANOSECONDS=1 and 483 SHOW_ELAPSED_TIME=1. 484 485 #(CDT) 2016/08/03 17:18:47.317339 - 0.000046 - Hi. 486 487 Description of arguments. 488 buffer This will be appended to the formatted 489 time string. 490 """ 491 492 global NANOSECONDS 493 global SHOW_ELAPSED_TIME 494 global sprint_time_last_seconds 495 496 seconds = time.time() 497 loc_time = time.localtime(seconds) 498 nanoseconds = "%0.6f" % seconds 499 pos = nanoseconds.find(".") 500 nanoseconds = nanoseconds[pos:] 501 502 time_string = time.strftime("#(%Z) %Y/%m/%d %H:%M:%S", loc_time) 503 if NANOSECONDS == "1": 504 time_string = time_string + nanoseconds 505 506 if SHOW_ELAPSED_TIME == "1": 507 cur_time_seconds = seconds 508 math_string = "%9.9f" % cur_time_seconds + " - " + "%9.9f" % \ 509 sprint_time_last_seconds 510 elapsed_seconds = eval(math_string) 511 if NANOSECONDS == "1": 512 elapsed_seconds = "%11.6f" % elapsed_seconds 513 else: 514 elapsed_seconds = "%4i" % elapsed_seconds 515 sprint_time_last_seconds = cur_time_seconds 516 time_string = time_string + " - " + elapsed_seconds 517 518 return time_string + " - " + buffer 519 520 521def sprint_timen(buffer=""): 522 r""" 523 Append a line feed to the buffer, pass it to sprint_time and return the 524 result. 525 """ 526 527 return sprint_time(buffer + "\n") 528 529 530def sprint_error(buffer=""): 531 r""" 532 Return a standardized error string. This includes: 533 - A time stamp 534 - The "**ERROR**" string 535 - The caller's buffer string. 536 537 Example: 538 539 The following python code... 540 541 print(sprint_error("Oops.\n")) 542 543 Will result in the following type of output: 544 545 #(CDT) 2016/08/03 17:12:05 - **ERROR** Oops. 546 547 Description of arguments. 548 buffer This will be appended to the formatted 549 error string. 550 """ 551 552 return sprint_time() + "**ERROR** " + buffer 553 554 555def sprint_varx(var_name, 556 var_value, 557 hex=0, 558 loc_col1_indent=col1_indent, 559 loc_col1_width=col1_width, 560 trailing_char="\n", 561 key_list=None): 562 r""" 563 Print the var name/value passed to it. If the caller lets loc_col1_width 564 default, the printing lines up nicely with output generated by the 565 print_time functions. 566 567 Note that the sprint_var function (defined below) can be used to call this 568 function so that the programmer does not need to pass the var_name. 569 sprint_var will figure out the var_name. The sprint_var function is the 570 one that would normally be used by the general user. 571 572 For example, the following python code: 573 574 first_name = "Mike" 575 print_time("Doing this...\n") 576 print_varx("first_name", first_name) 577 print_time("Doing that...\n") 578 579 Will generate output like this: 580 581 #(CDT) 2016/08/10 17:34:42.847374 - 0.001285 - Doing this... 582 first_name: Mike 583 #(CDT) 2016/08/10 17:34:42.847510 - 0.000136 - Doing that... 584 585 This function recognizes several complex types of data such as dict, list 586 or tuple. 587 588 For example, the following python code: 589 590 my_dict = dict(one=1, two=2, three=3) 591 print_var(my_dict) 592 593 Will generate the following output: 594 595 my_dict: 596 my_dict[three]: 3 597 my_dict[two]: 2 598 my_dict[one]: 1 599 600 Description of arguments. 601 var_name The name of the variable to be printed. 602 var_value The value of the variable to be printed. 603 hex This indicates that the value should be 604 printed in hex format. It is the user's 605 responsibility to ensure that a var_value 606 contains a valid hex number. For string 607 var_values, this will be interpreted as 608 show_blanks which means that blank values 609 will be printed as "<blank>". For dict 610 var_values, this will be interpreted as 611 terse format where keys are not repeated 612 in the output. 613 loc_col1_indent The number of spaces to indent the output. 614 loc_col1_width The width of the output column containing 615 the variable name. The default value of 616 this is adjusted so that the var_value 617 lines up with text printed via the 618 print_time function. 619 trailing_char The character to be used at the end of the 620 returned string. The default value is a 621 line feed. 622 key_list A list of which dictionary keys should be 623 printed. All others keys will be skipped. 624 Each value in key_list will be regarded 625 as a regular expression and it will be 626 regarded as anchored to the beginning and 627 ends of the dictionary key being 628 referenced. For example if key_list is 629 ["one", "two"], the resulting regex used 630 will be "^one|two$", i.e. only keys "one" 631 and "two" from the var_value dictionary 632 will be printed. As another example, if 633 the caller were to specify a key_list of 634 ["one.*"], then only dictionary keys whose 635 names begin with "one" will be printed. 636 Note: This argument pertains only to 637 var_values which are dictionaries. 638 """ 639 640 # Determine the type 641 if type(var_value) in (int, long, float, bool, str, unicode) \ 642 or var_value is None: 643 # The data type is simple in the sense that it has no subordinate 644 # parts. 645 # Adjust loc_col1_width. 646 loc_col1_width = loc_col1_width - loc_col1_indent 647 # See if the user wants the output in hex format. 648 if hex: 649 if type(var_value) not in (int, long): 650 value_format = "%s" 651 if var_value == "": 652 var_value = "<blank>" 653 else: 654 if type(var_value) is long or var_value >= 0x100000000: 655 value_format = "0x%16x" 656 else: 657 value_format = "0x%08x" 658 else: 659 value_format = "%s" 660 format_string = "%" + str(loc_col1_indent) + "s%-" \ 661 + str(loc_col1_width) + "s" + value_format + trailing_char 662 if value_format == "0x%08x": 663 return format_string % ("", str(var_name) + ":", 664 var_value & 0xffffffff) 665 else: 666 return format_string % ("", str(var_name) + ":", var_value) 667 elif type(var_value) is type: 668 return sprint_varx(var_name, str(var_value).split("'")[1], hex, 669 loc_col1_indent, loc_col1_width, trailing_char, 670 key_list) 671 else: 672 # The data type is complex in the sense that it has subordinate parts. 673 format_string = "%" + str(loc_col1_indent) + "s%s\n" 674 buffer = format_string % ("", var_name + ":") 675 loc_col1_indent += 2 676 try: 677 length = len(var_value) 678 except TypeError: 679 length = 0 680 ix = 0 681 loc_trailing_char = "\n" 682 type_is_dict = 0 683 if type(var_value) is dict: 684 type_is_dict = 1 685 try: 686 if type(var_value) is collections.OrderedDict: 687 type_is_dict = 1 688 except AttributeError: 689 pass 690 try: 691 if type(var_value) is DotDict: 692 type_is_dict = 1 693 except NameError: 694 pass 695 try: 696 if type(var_value) is NormalizedDict: 697 type_is_dict = 1 698 except NameError: 699 pass 700 if type_is_dict: 701 for key, value in var_value.iteritems(): 702 if key_list is not None: 703 key_list_regex = "^" + "|".join(key_list) + "$" 704 if not re.match(key_list_regex, key): 705 continue 706 ix += 1 707 if ix == length: 708 loc_trailing_char = trailing_char 709 if hex: 710 # Since hex is being used as a format type, we want it 711 # turned off when processing integer dictionary values so 712 # it is not interpreted as a hex indicator. 713 loc_hex = not (type(value) is int) 714 buffer += sprint_varx("[" + key + "]", value, 715 loc_hex, loc_col1_indent, 716 loc_col1_width, 717 loc_trailing_char, 718 key_list) 719 else: 720 buffer += sprint_varx(var_name + "[" + str(key) + "]", 721 value, hex, loc_col1_indent, 722 loc_col1_width, loc_trailing_char, 723 key_list) 724 elif type(var_value) in (list, tuple, set): 725 for key, value in enumerate(var_value): 726 ix += 1 727 if ix == length: 728 loc_trailing_char = trailing_char 729 buffer += sprint_varx(var_name + "[" + str(key) + "]", value, 730 hex, loc_col1_indent, loc_col1_width, 731 loc_trailing_char, key_list) 732 elif type(var_value) is argparse.Namespace: 733 for key in var_value.__dict__: 734 ix += 1 735 if ix == length: 736 loc_trailing_char = trailing_char 737 cmd_buf = "buffer += sprint_varx(var_name + \".\" + str(key)" \ 738 + ", var_value." + key + ", hex, loc_col1_indent," \ 739 + " loc_col1_width, loc_trailing_char, key_list)" 740 exec(cmd_buf) 741 else: 742 var_type = type(var_value).__name__ 743 func_name = sys._getframe().f_code.co_name 744 var_value = "<" + var_type + " type not supported by " + \ 745 func_name + "()>" 746 value_format = "%s" 747 loc_col1_indent -= 2 748 # Adjust loc_col1_width. 749 loc_col1_width = loc_col1_width - loc_col1_indent 750 format_string = "%" + str(loc_col1_indent) + "s%-" \ 751 + str(loc_col1_width) + "s" + value_format + trailing_char 752 return format_string % ("", str(var_name) + ":", var_value) 753 754 return buffer 755 756 return "" 757 758 759def sprint_var(var_value, 760 hex=0, 761 loc_col1_indent=col1_indent, 762 loc_col1_width=col1_width, 763 trailing_char="\n", 764 key_list=None): 765 r""" 766 Figure out the name of the first argument for you and then call 767 sprint_varx with it. Therefore, the following 2 calls are equivalent: 768 sprint_varx("var1", var1) 769 sprint_var(var1) 770 """ 771 772 # Get the name of the first variable passed to this function. 773 stack_frame = 2 774 caller_func_name = sprint_func_name(2) 775 if caller_func_name.endswith("print_var"): 776 stack_frame += 1 777 var_name = get_arg_name(None, 1, stack_frame) 778 return sprint_varx(var_name, var_value=var_value, hex=hex, 779 loc_col1_indent=loc_col1_indent, 780 loc_col1_width=loc_col1_width, 781 trailing_char=trailing_char, 782 key_list=key_list) 783 784 785def sprint_vars(*args): 786 r""" 787 Sprint the values of one or more variables. 788 789 Description of args: 790 args: 791 If the first argument is an integer, it will be interpreted to be the 792 "indent" value. 793 If the second argument is an integer, it will be interpreted to be the 794 "col1_width" value. 795 If the third argument is an integer, it will be interpreted to be the 796 "hex" value. 797 All remaining parms are considered variable names which are to be 798 sprinted. 799 """ 800 801 if len(args) == 0: 802 return 803 804 # Get the name of the first variable passed to this function. 805 stack_frame = 2 806 caller_func_name = sprint_func_name(2) 807 if caller_func_name.endswith("print_vars"): 808 stack_frame += 1 809 810 parm_num = 1 811 812 # Create list from args (which is a tuple) so that it can be modified. 813 args_list = list(args) 814 815 var_name = get_arg_name(None, parm_num, stack_frame) 816 # See if parm 1 is to be interpreted as "indent". 817 try: 818 if type(int(var_name)) is int: 819 indent = int(var_name) 820 args_list.pop(0) 821 parm_num += 1 822 except ValueError: 823 indent = 0 824 825 var_name = get_arg_name(None, parm_num, stack_frame) 826 # See if parm 1 is to be interpreted as "col1_width". 827 try: 828 if type(int(var_name)) is int: 829 loc_col1_width = int(var_name) 830 args_list.pop(0) 831 parm_num += 1 832 except ValueError: 833 loc_col1_width = col1_width 834 835 var_name = get_arg_name(None, parm_num, stack_frame) 836 # See if parm 1 is to be interpreted as "hex". 837 try: 838 if type(int(var_name)) is int: 839 hex = int(var_name) 840 args_list.pop(0) 841 parm_num += 1 842 except ValueError: 843 hex = 0 844 845 buffer = "" 846 for var_value in args_list: 847 var_name = get_arg_name(None, parm_num, stack_frame) 848 buffer += sprint_varx(var_name, var_value, hex, indent, loc_col1_width) 849 parm_num += 1 850 851 return buffer 852 853 854def sprint_dashes(indent=col1_indent, 855 width=80, 856 line_feed=1, 857 char="-"): 858 r""" 859 Return a string of dashes to the caller. 860 861 Description of arguments: 862 indent The number of characters to indent the 863 output. 864 width The width of the string of dashes. 865 line_feed Indicates whether the output should end 866 with a line feed. 867 char The character to be repeated in the output 868 string. 869 """ 870 871 width = int(width) 872 buffer = " " * int(indent) + char * width 873 if line_feed: 874 buffer += "\n" 875 876 return buffer 877 878 879def sindent(text="", 880 indent=0): 881 r""" 882 Pre-pend the specified number of characters to the text string (i.e. 883 indent it) and return it. 884 885 Description of arguments: 886 text The string to be indented. 887 indent The number of characters to indent the 888 string. 889 """ 890 891 format_string = "%" + str(indent) + "s%s" 892 buffer = format_string % ("", text) 893 894 return buffer 895 896 897def sprint_call_stack(indent=0, 898 stack_frame_ix=0): 899 r""" 900 Return a call stack report for the given point in the program with line 901 numbers, function names and function parameters and arguments. 902 903 Sample output: 904 905 ------------------------------------------------------------------------- 906 Python function call stack 907 908 Line # Function name and arguments 909 ------ ------------------------------------------------------------------ 910 424 sprint_call_stack () 911 4 print_call_stack () 912 31 func1 (last_name = 'walsh', first_name = 'mikey') 913 59 /tmp/scr5.py 914 ------------------------------------------------------------------------- 915 916 Description of arguments: 917 indent The number of characters to indent each 918 line of output. 919 stack_frame_ix The index of the first stack frame which 920 is to be returned. 921 """ 922 923 buffer = "" 924 buffer += sprint_dashes(indent) 925 buffer += sindent("Python function call stack\n\n", indent) 926 buffer += sindent("Line # Function name and arguments\n", indent) 927 buffer += sprint_dashes(indent, 6, 0) + " " + sprint_dashes(0, 73) 928 929 # Grab the current program stack. 930 current_stack = inspect.stack() 931 932 # Process each frame in turn. 933 format_string = "%6s %s\n" 934 ix = 0 935 for stack_frame in current_stack: 936 if ix < stack_frame_ix: 937 ix += 1 938 continue 939 # I want the line number shown to be the line where you find the line 940 # shown. 941 try: 942 line_num = str(current_stack[ix + 1][2]) 943 except IndexError: 944 line_num = "" 945 func_name = str(stack_frame[3]) 946 if func_name == "?": 947 # "?" is the name used when code is not in a function. 948 func_name = "(none)" 949 950 if func_name == "<module>": 951 # If the func_name is the "main" program, we simply get the 952 # command line call string. 953 func_and_args = ' '.join(sys.argv) 954 else: 955 # Get the program arguments. 956 arg_vals = inspect.getargvalues(stack_frame[0]) 957 function_parms = arg_vals[0] 958 frame_locals = arg_vals[3] 959 960 args_list = [] 961 for arg_name in function_parms: 962 # Get the arg value from frame locals. 963 arg_value = frame_locals[arg_name] 964 args_list.append(arg_name + " = " + repr(arg_value)) 965 args_str = "(" + ', '.join(map(str, args_list)) + ")" 966 967 # Now we need to print this in a nicely-wrapped way. 968 func_and_args = func_name + " " + args_str 969 970 buffer += sindent(format_string % (line_num, func_and_args), indent) 971 ix += 1 972 973 buffer += sprint_dashes(indent) 974 975 return buffer 976 977 978def sprint_executing(stack_frame_ix=None): 979 r""" 980 Print a line indicating what function is executing and with what parameter 981 values. This is useful for debugging. 982 983 Sample output: 984 985 #(CDT) 2016/08/25 17:54:27 - Executing: func1 (x = 1) 986 987 Description of arguments: 988 stack_frame_ix The index of the stack frame whose 989 function info should be returned. If the 990 caller does not specify a value, this 991 function will set the value to 1 which is 992 the index of the caller's stack frame. If 993 the caller is the wrapper function 994 "print_executing", this function will bump 995 it up by 1. 996 """ 997 998 # If user wants default stack_frame_ix. 999 if stack_frame_ix is None: 1000 func_name = sys._getframe().f_code.co_name 1001 caller_func_name = sys._getframe(1).f_code.co_name 1002 if caller_func_name.endswith(func_name[1:]): 1003 stack_frame_ix = 2 1004 else: 1005 stack_frame_ix = 1 1006 1007 stack_frame = inspect.stack()[stack_frame_ix] 1008 1009 func_name = str(stack_frame[3]) 1010 if func_name == "?": 1011 # "?" is the name used when code is not in a function. 1012 func_name = "(none)" 1013 1014 if func_name == "<module>": 1015 # If the func_name is the "main" program, we simply get the command 1016 # line call string. 1017 func_and_args = ' '.join(sys.argv) 1018 else: 1019 # Get the program arguments. 1020 arg_vals = inspect.getargvalues(stack_frame[0]) 1021 function_parms = arg_vals[0] 1022 frame_locals = arg_vals[3] 1023 1024 args_list = [] 1025 for arg_name in function_parms: 1026 # Get the arg value from frame locals. 1027 arg_value = frame_locals[arg_name] 1028 args_list.append(arg_name + " = " + repr(arg_value)) 1029 args_str = "(" + ', '.join(map(str, args_list)) + ")" 1030 1031 # Now we need to print this in a nicely-wrapped way. 1032 func_and_args = func_name + " " + args_str 1033 1034 return sprint_time() + "Executing: " + func_and_args + "\n" 1035 1036 1037def sprint_pgm_header(indent=0, 1038 linefeed=1): 1039 r""" 1040 Return a standardized header that programs should print at the beginning 1041 of the run. It includes useful information like command line, pid, 1042 userid, program parameters, etc. 1043 1044 Description of arguments: 1045 indent The number of characters to indent each 1046 line of output. 1047 linefeed Indicates whether a line feed be included 1048 at the beginning and end of the report. 1049 """ 1050 1051 loc_col1_width = col1_width + indent 1052 1053 buffer = "" 1054 if linefeed: 1055 buffer = "\n" 1056 1057 if robot_env: 1058 suite_name = BuiltIn().get_variable_value("${suite_name}") 1059 buffer += sindent(sprint_time("Running test suite \"" + suite_name + 1060 "\".\n"), indent) 1061 1062 buffer += sindent(sprint_time() + "Running " + pgm_name + ".\n", indent) 1063 buffer += sindent(sprint_time() + "Program parameter values, etc.:\n\n", 1064 indent) 1065 buffer += sprint_varx("command_line", ' '.join(sys.argv), 0, indent, 1066 loc_col1_width) 1067 # We want the output to show a customized name for the pid and pgid but 1068 # we want it to look like a valid variable name. Therefore, we'll use 1069 # pgm_name_var_name which was set when this module was imported. 1070 buffer += sprint_varx(pgm_name_var_name + "_pid", os.getpid(), 0, indent, 1071 loc_col1_width) 1072 buffer += sprint_varx(pgm_name_var_name + "_pgid", os.getpgrp(), 0, indent, 1073 loc_col1_width) 1074 userid_num = str(os.geteuid()) 1075 try: 1076 username = os.getlogin() 1077 except OSError: 1078 if userid_num == "0": 1079 username = "root" 1080 else: 1081 username = "?" 1082 buffer += sprint_varx("uid", userid_num + " (" + username + 1083 ")", 0, indent, loc_col1_width) 1084 buffer += sprint_varx("gid", str(os.getgid()) + " (" + 1085 str(grp.getgrgid(os.getgid()).gr_name) + ")", 0, 1086 indent, loc_col1_width) 1087 buffer += sprint_varx("host_name", socket.gethostname(), 0, indent, 1088 loc_col1_width) 1089 try: 1090 DISPLAY = os.environ['DISPLAY'] 1091 except KeyError: 1092 DISPLAY = "" 1093 buffer += sprint_varx("DISPLAY", DISPLAY, 0, indent, 1094 loc_col1_width) 1095 # I want to add code to print caller's parms. 1096 1097 # __builtin__.arg_obj is created by the get_arg module function, 1098 # gen_get_options. 1099 try: 1100 buffer += ga.sprint_args(__builtin__.arg_obj, indent) 1101 except AttributeError: 1102 pass 1103 1104 if robot_env: 1105 # Get value of global parm_list. 1106 parm_list = BuiltIn().get_variable_value("${parm_list}") 1107 1108 for parm in parm_list: 1109 parm_value = BuiltIn().get_variable_value("${" + parm + "}") 1110 buffer += sprint_varx(parm, parm_value, 0, indent, loc_col1_width) 1111 1112 # Setting global program_pid. 1113 BuiltIn().set_global_variable("${program_pid}", os.getpid()) 1114 1115 if linefeed: 1116 buffer += "\n" 1117 1118 return buffer 1119 1120 1121def sprint_error_report(error_text="\n", 1122 indent=2, 1123 format=None): 1124 r""" 1125 Return a string with a standardized report which includes the caller's 1126 error text, the call stack and the program header. 1127 1128 Description of args: 1129 error_text The error text to be included in the 1130 report. The caller should include any 1131 needed linefeeds. 1132 indent The number of characters to indent each 1133 line of output. 1134 format Long or short format. Long includes 1135 extras like lines of dashes, call stack, 1136 etc. 1137 """ 1138 1139 # Process input. 1140 indent = int(indent) 1141 if format is None: 1142 if robot_env: 1143 format = 'short' 1144 else: 1145 format = 'long' 1146 error_text = error_text.rstrip('\n') + '\n' 1147 1148 if format == 'short': 1149 return sprint_error(error_text) 1150 1151 buffer = "" 1152 buffer += sprint_dashes(width=120, char="=") 1153 buffer += sprint_error(error_text) 1154 buffer += "\n" 1155 # Calling sprint_call_stack with stack_frame_ix of 0 causes it to show 1156 # itself and this function in the call stack. This is not helpful to a 1157 # debugger and is therefore clutter. We will adjust the stack_frame_ix to 1158 # hide that information. 1159 stack_frame_ix = 1 1160 caller_func_name = sprint_func_name(2) 1161 if caller_func_name.endswith("print_error_report"): 1162 stack_frame_ix += 1 1163 if not robot_env: 1164 buffer += sprint_call_stack(indent, stack_frame_ix) 1165 buffer += sprint_pgm_header(indent) 1166 buffer += sprint_dashes(width=120, char="=") 1167 1168 return buffer 1169 1170 1171def sprint_issuing(cmd_buf, 1172 test_mode=0): 1173 r""" 1174 Return a line indicating a command that the program is about to execute. 1175 1176 Sample output for a cmd_buf of "ls" 1177 1178 #(CDT) 2016/08/25 17:57:36 - Issuing: ls 1179 1180 Description of args: 1181 cmd_buf The command to be executed by caller. 1182 test_mode With test_mode set, your output will look 1183 like this: 1184 1185 #(CDT) 2016/08/25 17:57:36 - (test_mode) Issuing: ls 1186 1187 """ 1188 1189 buffer = sprint_time() 1190 if test_mode: 1191 buffer += "(test_mode) " 1192 buffer += "Issuing: " + cmd_buf + "\n" 1193 1194 return buffer 1195 1196 1197def sprint_pgm_footer(): 1198 r""" 1199 Return a standardized footer that programs should print at the end of the 1200 program run. It includes useful information like total run time, etc. 1201 """ 1202 1203 buffer = "\n" + sprint_time() + "Finished running " + pgm_name + ".\n\n" 1204 1205 total_time = time.time() - start_time 1206 total_time_string = "%0.6f" % total_time 1207 1208 buffer += sprint_varx(pgm_name_var_name + "_runtime", total_time_string) 1209 buffer += "\n" 1210 1211 return buffer 1212 1213 1214def sprint(buffer=""): 1215 r""" 1216 Simply return the user's buffer. This function is used by the qprint and 1217 dprint functions defined dynamically below, i.e. it would not normally be 1218 called for general use. 1219 1220 Description of arguments. 1221 buffer This will be returned to the caller. 1222 """ 1223 1224 try: 1225 return str(buffer) 1226 except UnicodeEncodeError: 1227 return buffer 1228 1229 1230def sprintn(buffer=""): 1231 r""" 1232 Simply return the user's buffer with a line feed. This function is used 1233 by the qprint and dprint functions defined dynamically below, i.e. it 1234 would not normally be called for general use. 1235 1236 Description of arguments. 1237 buffer This will be returned to the caller. 1238 """ 1239 1240 try: 1241 buffer = str(buffer) + "\n" 1242 except UnicodeEncodeError: 1243 buffer = buffer + "\n" 1244 1245 return buffer 1246 1247 1248def gp_print(buffer, 1249 stream='stdout'): 1250 r""" 1251 Print the buffer using either sys.stdout.write or BuiltIn().log_to_console 1252 depending on whether we are running in a robot environment. 1253 1254 This function is intended for use only by other functions in this module. 1255 1256 Description of arguments: 1257 buffer The string to be printed. 1258 stream Either "stdout" or "stderr". 1259 """ 1260 1261 if robot_env: 1262 BuiltIn().log_to_console(buffer, stream=stream, no_newline=True) 1263 else: 1264 if stream == "stdout": 1265 sys.stdout.write(buffer) 1266 sys.stdout.flush() 1267 else: 1268 sys.stderr.write(buffer) 1269 sys.stderr.flush() 1270 1271 1272def gp_log(buffer): 1273 r""" 1274 Log the buffer using either python logging or BuiltIn().log depending on 1275 whether we are running in a robot environment. 1276 1277 This function is intended for use only by other functions in this module. 1278 1279 Description of arguments: 1280 buffer The string to be logged. 1281 """ 1282 1283 if robot_env: 1284 BuiltIn().log(buffer) 1285 else: 1286 logging.warning(buffer) 1287 1288 1289def gp_debug_print(buffer): 1290 r""" 1291 Print with gp_print only if gen_print_debug is set. 1292 1293 This function is intended for use only by other functions in this module. 1294 1295 Description of arguments: 1296 buffer The string to be printed. 1297 """ 1298 1299 if not gen_print_debug: 1300 return 1301 1302 gp_print(buffer) 1303 1304 1305def get_var_value(var_value=None, 1306 default=1, 1307 var_name=None): 1308 r""" 1309 Return either var_value, the corresponding global value or default. 1310 1311 If var_value is not None, it will simply be returned. 1312 1313 If var_value is None, this function will return the corresponding global 1314 value of the variable in question. 1315 1316 Note: For global values, if we are in a robot environment, 1317 get_variable_value will be used. Otherwise, the __builtin__ version of 1318 the variable is returned (which are set by gen_arg.py functions). 1319 1320 If there is no global value associated with the variable, default is 1321 returned. 1322 1323 This function is useful for other functions in setting default values for 1324 parameters. 1325 1326 Example use: 1327 1328 def my_func(quiet=None): 1329 1330 quiet = int(get_var_value(quiet, 0)) 1331 1332 Example calls to my_func(): 1333 1334 In the following example, the caller is explicitly asking to have quiet be 1335 set to 1. 1336 1337 my_func(quiet=1) 1338 1339 In the following example, quiet will be set to the global value of quiet, 1340 if defined, or to 0 (the default). 1341 1342 my_func() 1343 1344 Description of arguments: 1345 var_value The value to be returned (if not equal to 1346 None). 1347 default The value that is returned if var_value is 1348 None and there is no corresponding global 1349 value defined. 1350 var_name The name of the variable whose value is to 1351 be returned. Under most circumstances, 1352 this value need not be provided. This 1353 function can figure out the name of the 1354 variable passed as var_value. One 1355 exception to this would be if this 1356 function is called directly from a .robot 1357 file. 1358 """ 1359 1360 if var_value is not None: 1361 return var_value 1362 1363 if var_name is None: 1364 var_name = get_arg_name(None, 1, 2) 1365 1366 if robot_env: 1367 var_value = BuiltIn().get_variable_value("${" + var_name + "}", 1368 default) 1369 else: 1370 var_value = getattr(__builtin__, var_name, default) 1371 1372 return var_value 1373 1374 1375def get_stack_var(var_name, 1376 default="", 1377 init_stack_ix=2): 1378 1379 r""" 1380 Starting with the caller's stack level, search upward in the call stack, 1381 for a variable named var_name and return its value. If the variable 1382 cannot be found, return default. 1383 1384 Example code: 1385 1386 def func12(): 1387 my_loc_var1 = get_stack_var('my_var1', "default value") 1388 1389 def func11(): 1390 my_var1 = 11 1391 func12() 1392 1393 In this example, get_stack_var will find the value of my_var1 in func11's 1394 stack and will therefore return the value 11. Therefore, my_loc_var1 1395 would get set to 11. 1396 1397 Description of argument(s): 1398 var_name The name of the variable to be searched 1399 for. 1400 default The value to return if the the variable 1401 cannot be found. 1402 init_stack_ix The initial stack index from which to 1403 begin the search. 0 would be the index of 1404 this func1tion ("get_stack_var"), 1 would 1405 be the index of the function calling this 1406 function, etc. 1407 """ 1408 1409 return next((frame[0].f_locals[var_name] 1410 for frame in inspect.stack()[init_stack_ix:] 1411 if var_name in frame[0].f_locals), default) 1412 1413 1414# hidden_text is a list of passwords which are to be replaced with asterisks 1415# by print functions defined in this module. 1416hidden_text = [] 1417# password_regex is created based on the contents of hidden_text. 1418password_regex = "" 1419 1420 1421def register_passwords(*args): 1422 r""" 1423 Register one or more passwords which are to be hidden in output produced 1424 by the print functions in this module. 1425 1426 Note: Blank password values are NOT registered. They are simply ignored. 1427 1428 Description of argument(s): 1429 args One or more password values. If a given 1430 password value is already registered, this 1431 function will simply do nothing. 1432 """ 1433 1434 global hidden_text 1435 global password_regex 1436 1437 for password in args: 1438 if password == "": 1439 break 1440 if password in hidden_text: 1441 break 1442 1443 # Place the password into the hidden_text list. 1444 hidden_text.append(password) 1445 # Create a corresponding password regular expression. Escape regex 1446 # special characters too. 1447 password_regex = '(' +\ 1448 '|'.join([re.escape(x) for x in hidden_text]) + ')' 1449 1450 1451def replace_passwords(buffer): 1452 r""" 1453 Return the buffer but with all registered passwords replaced by a string 1454 of asterisks. 1455 1456 1457 Description of argument(s): 1458 buffer The string to be returned but with 1459 passwords replaced. 1460 """ 1461 1462 global password_regex 1463 1464 if int(os.environ.get("DEBUG_SHOW_PASSWORDS", "0")): 1465 return buffer 1466 1467 if password_regex == "": 1468 # No passwords to replace. 1469 return buffer 1470 1471 return re.sub(password_regex, "********", buffer) 1472 1473 1474def create_print_wrapper_funcs(func_names, 1475 stderr_func_names, 1476 replace_dict): 1477 r""" 1478 Generate code for print wrapper functions and return the generated code as 1479 a string. 1480 1481 To illustrate, suppose there is a "print_foo_bar" function in the 1482 func_names list. 1483 This function will... 1484 - Expect that there is an sprint_foo_bar function already in existence. 1485 - Create a print_foo_bar function which calls sprint_foo_bar and prints 1486 the result. 1487 - Create a qprint_foo_bar function which calls upon sprint_foo_bar only if 1488 global value quiet is 0. 1489 - Create a dprint_foo_bar function which calls upon sprint_foo_bar only if 1490 global value debug is 1. 1491 1492 Also, code will be generated to define aliases for each function as well. 1493 Each alias will be created by replacing "print_" in the function name with 1494 "p" For example, the alias for print_foo_bar will be pfoo_bar. 1495 1496 Description of argument(s): 1497 func_names A list of functions for which print 1498 wrapper function code is to be generated. 1499 stderr_func_names A list of functions whose generated code 1500 should print to stderr rather than to 1501 stdout. 1502 replace_dict Please see the create_func_def_string 1503 function in wrap_utils.py for details on 1504 this parameter. This parameter will be 1505 passed directly to create_func_def_string. 1506 """ 1507 1508 buffer = "" 1509 1510 for func_name in func_names: 1511 if func_name in stderr_func_names: 1512 replace_dict['output_stream'] = "stderr" 1513 else: 1514 replace_dict['output_stream'] = "stdout" 1515 1516 s_func_name = "s" + func_name 1517 q_func_name = "q" + func_name 1518 d_func_name = "d" + func_name 1519 1520 # We don't want to try to redefine the "print" function, thus the 1521 # following if statement. 1522 if func_name != "print": 1523 func_def = create_func_def_string(s_func_name, func_name, 1524 print_func_template, 1525 replace_dict) 1526 buffer += func_def 1527 1528 func_def = create_func_def_string(s_func_name, "q" + func_name, 1529 qprint_func_template, replace_dict) 1530 buffer += func_def 1531 1532 func_def = create_func_def_string(s_func_name, "d" + func_name, 1533 dprint_func_template, replace_dict) 1534 buffer += func_def 1535 1536 func_def = create_func_def_string(s_func_name, "l" + func_name, 1537 lprint_func_template, replace_dict) 1538 buffer += func_def 1539 1540 # Create abbreviated aliases (e.g. spvar is an alias for sprint_var). 1541 alias = re.sub("print_", "p", func_name) 1542 alias = re.sub("print", "p", alias) 1543 prefixes = ["", "s", "q", "d", "l"] 1544 for prefix in prefixes: 1545 if alias == "p": 1546 continue 1547 func_def = prefix + alias + " = " + prefix + func_name 1548 buffer += func_def + "\n" 1549 1550 return buffer 1551 1552 1553# In the following section of code, we will dynamically create print versions 1554# for each of the sprint functions defined above. So, for example, where we 1555# have an sprint_time() function defined above that returns the time to the 1556# caller in a string, we will create a corresponding print_time() function 1557# that will print that string directly to stdout. 1558 1559# It can be complicated to follow what's being created by below. Here is an 1560# example of the print_time() function that will be created: 1561 1562# def print_time(buffer=''): 1563# sys.stdout.write(replace_passwords(sprint_time(buffer=buffer))) 1564# sys.stdout.flush() 1565 1566# Templates for the various print wrapper functions. 1567print_func_template = \ 1568 [ 1569 " <mod_qualifier>gp_print(<mod_qualifier>replace_passwords(" + 1570 "<call_line>), stream='<output_stream>')" 1571 ] 1572 1573qprint_func_template = \ 1574 [ 1575 " if int(<mod_qualifier>get_var_value(None, 0, \"quiet\")): return" 1576 ] + print_func_template 1577 1578dprint_func_template = \ 1579 [ 1580 " if not int(<mod_qualifier>get_var_value(None, 0, \"debug\")):" + 1581 " return" 1582 ] + print_func_template 1583 1584lprint_func_template = \ 1585 [ 1586 " gp_log(<mod_qualifier>replace_passwords(<call_line>))" 1587 ] 1588 1589replace_dict = {'output_stream': 'stdout', 'mod_qualifier': ''} 1590 1591 1592gp_debug_print("robot_env: " + str(robot_env)) 1593 1594# func_names contains a list of all print functions which should be created 1595# from their sprint counterparts. 1596func_names = ['print_time', 'print_timen', 'print_error', 'print_varx', 1597 'print_var', 'print_vars', 'print_dashes', 'indent', 1598 'print_call_stack', 'print_func_name', 'print_executing', 1599 'print_pgm_header', 'print_issuing', 'print_pgm_footer', 1600 'print_error_report', 'print', 'printn'] 1601 1602# stderr_func_names is a list of functions whose output should go to stderr 1603# rather than stdout. 1604stderr_func_names = ['print_error', 'print_error_report'] 1605 1606 1607func_defs = create_print_wrapper_funcs(func_names, stderr_func_names, 1608 replace_dict) 1609gp_debug_print(func_defs) 1610exec(func_defs) 1611