1#!/usr/bin/env python 2 3r""" 4This file contains functions useful for printing to stdout from robot programs. 5""" 6 7import sys 8import re 9import os 10 11try: 12 from robot.utils import DotDict as my_ord_dict 13except ImportError: 14 from collections import OrderedDict as my_ord_dict 15 16import gen_print as gp 17 18from robot.libraries.BuiltIn import BuiltIn 19from robot.api import logger 20 21try: 22 # The user can set environment variable "GEN_ROBOT_PRINT_DEBUG" to get 23 # debug output from this module. 24 gen_robot_print_debug = int(os.environ['GEN_ROBOT_PRINT_DEBUG']) 25except KeyError: 26 gen_robot_print_debug = 0 27 28 29############################################################################### 30def get_quiet_default(var_value, 31 default=0): 32 33 r""" 34 If var_value is not None, return it. Otherwise, return the global 35 variable of the same name, if it exists. If not, return default. 36 37 This is meant for use by functions needing help assigning dynamic default 38 values to their parameters. Example: 39 40 def func1(parm1=None): 41 42 parm1 = global_default(parm1, 0) 43 44 Description of arguments: 45 var_value The value being evaluated. 46 default The value to be returned if var_value is 47 None AND the global 48 variable of the same name does not exist. 49 """ 50 51 var_name = gp.get_arg_name(0, 1, stack_frame_ix=2) 52 53 return dft(var_value, get_mod_global(var_name, 0)) 54 55############################################################################### 56 57 58############################################################################### 59def set_quiet_default(quiet=None, 60 default=0): 61 62 r""" 63 Return a default value for the quiet variable based on its current value, 64 the value of global ${QUIET} and default. 65 66 Description of Arguments: 67 quiet If this is set already, no default value 68 is chosen. Otherwise, it will be set to 69 either the global ${QUIET} robot variable 70 or to default (below). 71 default The default value to be used if global 72 ${QUIET} does not exist. 73 """ 74 75 if quiet is None: 76 # Set default quiet value. 77 try: 78 quiet = int(BuiltIn().get_variable_value("${quiet}")) 79 except TypeError: 80 quiet = int(default) 81 quiet = int(quiet) 82 83 return quiet 84 85############################################################################### 86 87 88############################################################################### 89def get_quiet(default=1): 90 91 r""" 92 Get the value of robot variable "quiet" and return it. If "quiet" is not 93 defined, the "default" value is returned. 94 95 Description of arguments: 96 default The value that is returned if robot 97 variable "quiet" is not defined. 98 """ 99 100 try: 101 quiet = int(BuiltIn().get_variable_value("${quiet}")) 102 except TypeError: 103 quiet = default 104 105 return quiet 106 107############################################################################### 108 109 110############################################################################### 111def get_debug(default=0): 112 113 r""" 114 Get the value of robot variable "debug" and return it. If "debug" is not 115 defined, the "default" value is returned. 116 117 Description of arguments: 118 default The value that is returned if robot 119 variable "debug" is not defined. 120 """ 121 122 try: 123 debug = int(BuiltIn().get_variable_value("${debug}")) 124 except TypeError: 125 debug = default 126 127 return debug 128 129############################################################################### 130 131 132############################################################################### 133def rprint(buffer="", 134 stream="STDOUT"): 135 136 r""" 137 rprint stands for "Robot Print". This keyword will print the user's 138 buffer to the console. This keyword does not write a linefeed. It is the 139 responsibility of the caller to include a line feed if desired. This 140 keyword is essentially an alias for "Log to Console <string> <stream>". 141 142 Description of arguments: 143 buffer The value that is to written to stdout. 144 """ 145 146 BuiltIn().log_to_console(gp.replace_passwords(str(buffer)), 147 no_newline=True, stream=stream) 148 149############################################################################### 150 151 152############################################################################### 153def rprintn(buffer="", 154 stream='STDOUT'): 155 156 r""" 157 rprintn stands for "Robot print with linefeed". This keyword will print 158 the user's buffer to the console along with a linefeed. It is basically 159 an abbreviated form of "Log go Console <string> <stream>" 160 161 Description of arguments: 162 buffer The value that is to written to stdout. 163 """ 164 165 BuiltIn().log_to_console(gp.replace_passwords(buffer), no_newline=False, 166 stream=stream) 167 168############################################################################### 169 170 171############################################################################### 172def sprint_vars(*args): 173 174 r""" 175 sprint_vars stands for "String Print Vars". This is a robot redefinition 176 of the sprint_vars function in gen_print.py. Given a list of variable 177 names, this keyword will string print each variable name and value such 178 that the value lines up in the same column as messages printed with rptime. 179 180 Description of arguments: 181 args: 182 If the first argument is an integer, it will be interpreted to be the 183 "hex" value. 184 If the second argument is an integer, it will be interpreted to be the 185 "indent" value. 186 If the third argument is an integer, it will be interpreted to be the 187 "col1_width" value. 188 All remaining parms are considered variable names which are to be 189 sprinted. 190 """ 191 192 if len(args) == 0: 193 return 194 195 # Create list from args (which is a tuple) so that it can be modified. 196 args_list = list(args) 197 198 # See if parm 1 is to be interpreted as "hex". 199 try: 200 if type(int(args_list[0])) is int: 201 hex = int(args_list[0]) 202 args_list.pop(0) 203 except ValueError: 204 hex = 0 205 206 # See if parm 2 is to be interpreted as "indent". 207 try: 208 if type(int(args_list[0])) is int: 209 indent = int(args_list[0]) 210 args_list.pop(0) 211 except ValueError: 212 indent = 0 213 214 # See if parm 3 is to be interpreted as "col1_width". 215 try: 216 if type(int(args_list[0])) is int: 217 loc_col1_width = int(args_list[0]) 218 args_list.pop(0) 219 except ValueError: 220 loc_col1_width = gp.col1_width 221 222 buffer = "" 223 for var_name in args_list: 224 var_value = BuiltIn().get_variable_value("${" + str(var_name) + "}") 225 buffer += gp.sprint_varx(var_name, var_value, hex, indent, 226 loc_col1_width) 227 228 return buffer 229 230############################################################################### 231 232 233############################################################################### 234def sprint_pgm_header(indent=0): 235 236 r""" 237 Sprint a standardized header that robot programs should print at the 238 beginning of the run. The header includes useful information like command 239 line, pid, userid, program parameters, etc. Callers need to have declared 240 a global @{parm_list} variable which contains the names of all program 241 parameters. 242 """ 243 244 loc_col1_width = gp.col1_width + indent 245 246 linefeed = 0 247 rprintn() 248 suite_name = BuiltIn().get_variable_value("${suite_name}") 249 250 buffer = "\n" 251 buffer += gp.sindent(gp.sprint_time("Running test suite \"" + 252 suite_name + "\".\n"), 253 indent) 254 buffer += gp.sprint_pgm_header(indent, linefeed) 255 256 # Get value of global parm_list. 257 parm_list = BuiltIn().get_variable_value("${parm_list}") 258 259 buffer += sprint_vars(0, str(indent), str(loc_col1_width), *parm_list) 260 buffer += "\n" 261 262 # Setting global program_pid. 263 BuiltIn().set_global_variable("${program_pid}", os.getpid()) 264 265 return buffer 266 267############################################################################### 268 269 270############################################################################### 271def sprint_error_report(error_text="\n"): 272 273 r""" 274 Print a standardized error report that robot programs should print on 275 failure. The report includes useful information like error text, command 276 line, pid, userid, program parameters, etc. Callers must have declared a 277 @{parm_list} variable which contains the names of all program parameters. 278 """ 279 280 try: 281 error_report_format = int(BuiltIn().get_variable_value( 282 "${error_report_format}")) 283 except TypeError: 284 error_report_format = 0 285 286 # Currently, supported values for error_report_format are: 287 # 0 - Short form 288 # 1 - Long form 289 290 error_text = error_text.rstrip('\n') + '\n' 291 292 if error_report_format == 0: 293 return gp.sprint_error(error_text) 294 295 buffer = "" 296 buffer += gp.sprint_dashes(width=120, char="=") 297 buffer += gp.sprint_error(error_text) 298 299 indent = 2 300 linefeed = 0 301 302 buffer += sprint_pgm_header(indent) 303 304 buffer += gp.sprint_dashes(width=120, char="=") 305 306 return buffer 307 308############################################################################### 309 310 311############################################################################### 312def sprint_issuing_keyword(cmd_buf, 313 test_mode=0): 314 315 r""" 316 Return a line indicating a robot command (i.e. keyword + args) that the 317 program is about to execute. 318 319 For example, for the following robot code... 320 321 @{cmd_buf}= Set Variable Set Environment Variable VAR1 1 322 rdprint_issuing_keyword 323 324 The output would look something like this: 325 326 #(CDT) 2016/10/27 12:04:21 - Issuing: Set Environment Variable VAR1 1 327 328 Description of args: 329 cmd_buf A list containing the keyword and 330 arguments to be run. 331 """ 332 333 buffer = "" 334 cmd_buf_str = ' '.join([str(element) for element in cmd_buf]) 335 buffer += gp.sprint_issuing(cmd_buf_str, int(test_mode)) 336 337 return buffer 338 339############################################################################### 340 341 342############################################################################### 343def sprint_auto_vars(headers=0): 344 345 r""" 346 This keyword will string print all of the Automatic Variables described in 347 the Robot User's Guide using rprint_vars. 348 349 NOTE: Not all automatic variables are guaranteed to exist. 350 351 Description of arguments: 352 headers This indicates that a header and footer 353 should be printed. 354 """ 355 356 buffer = "" 357 if int(headers) == 1: 358 buffer += gp.sprint_dashes() 359 buffer += "Automatic Variables:" 360 361 buffer += \ 362 sprint_vars( 363 "TEST_NAME", "TEST_TAGS", "TEST_DOCUMENTATION", "TEST_STATUS", 364 "TEST_DOCUMENTATION", "TEST_STATUS", "TEST_MESSAGE", 365 "PREV_TEST_NAME", "PREV_TEST_STATUS", "PREV_TEST_MESSAGE", 366 "SUITE_NAME", "SUITE_SOURCE", "SUITE_DOCUMENTATION", 367 "SUITE_METADATA", "SUITE_STATUS", "SUITE_MESSAGE", 368 "KEYWORD_STATUS", "KEYWORD_MESSAGE", "LOG_LEVEL", "OUTPUT_FILE", 369 "LOG_FILE", "REPORT_FILE", "DEBUG_FILE", "OUTPUT_DIR") 370 371 if int(headers) == 1: 372 buffer += gp.sprint_dashes() 373 374 return buffer 375 376############################################################################### 377 378 379############################################################################### 380# In the following section of code, we will dynamically create robot versions 381# of print functions for each of the sprint functions defined in the 382# gen_print.py module. So, for example, where we have an sprint_time() 383# function defined above that returns the time to the caller in a string, we 384# will create a corresponding rprint_time() function that will print that 385# string directly to stdout. 386 387# It can be complicated to follow what's being created by the exec statement 388# below. Here is an example of the rprint_time() function that will be 389# created (as of the time of this writing): 390 391# def rprint_time(*args): 392# s_func = getattr(gp, "sprint_time") 393# BuiltIn().log_to_console(s_func(*args), 394# stream='STDIN', 395# no_newline=True) 396 397# Here are comments describing the lines in the body of the created function. 398# Put a reference to the "s" version of this function in s_func. 399# Call the "s" version of this function passing it all of our arguments. Log 400# the result to the console. 401 402robot_prefix = "r" 403robot_func_names =\ 404 [ 405 'print_error_report', 'print_pgm_header', 406 'print_issuing_keyword', 'print_vars', 'print_auto_vars' 407 ] 408func_names = gp.func_names + robot_func_names 409 410explicit_definitions = ['print', 'printn'] 411 412func_names = list(my_ord_dict.fromkeys(func_names)) 413 414if gen_robot_print_debug: 415 rprintn() 416 BuiltIn().log_to_console(gp.sprint_var(func_names), no_newline=True) 417 rprintn() 418 419for func_name in func_names: 420 421 if func_name not in explicit_definitions: 422 # The print_var function's job is to figure out the name of arg 1 and 423 # then call print_varx. This is not currently supported for robot 424 # programs. Though it IS supported for python modules. 425 if func_name == "print_error" or func_name == "print_error_report": 426 output_stream = "STDERR" 427 else: 428 output_stream = "STDIN" 429 if func_name in robot_func_names: 430 object_name = "__import__(__name__)" 431 else: 432 object_name = "gp" 433 func_def = \ 434 [ 435 "def " + robot_prefix + func_name + "(*args):", 436 " s_func = getattr(" + object_name + ", \"s" + func_name + 437 "\")", 438 " BuiltIn().log_to_console" + 439 "(gp.replace_passwords(s_func(*args))," 440 " stream='" + output_stream + "'," 441 " no_newline=True)" 442 ] 443 444 pgm_definition_string = '\n'.join(func_def) 445 if gen_robot_print_debug: 446 rprintn() 447 rprintn(pgm_definition_string) 448 exec(pgm_definition_string) 449 450 # Now define "q" versions of each print function. The q functions only 451 # print if global robot var "quiet" is 0. If the global var quiet is not 452 # defined, it will be treated as though it were "1", i.e. no printing will 453 # be done. 454 func_def = \ 455 [ 456 "def rq" + func_name + "(*args):", 457 " if get_quiet():", 458 " return", 459 " r" + func_name + "(*args)" 460 ] 461 462 pgm_definition_string = '\n'.join(func_def) 463 if gen_robot_print_debug: 464 rprintn(pgm_definition_string) 465 exec(pgm_definition_string) 466 467 # Now define "d" versions of each print function. The d functions only 468 # print if global robot var "debug" is 1. 469 func_def = \ 470 [ 471 "def rd" + func_name + "(*args):", 472 " if not get_debug():", 473 " return", 474 " r" + func_name + "(*args)" 475 ] 476 477 pgm_definition_string = '\n'.join(func_def) 478 if gen_robot_print_debug: 479 rprintn(pgm_definition_string) 480 exec(pgm_definition_string) 481 482 # Create shorter aliases. 483 prefixes = ["", "q", "d"] 484 alias = re.sub("print_", "p", func_name) 485 for prefix2 in prefixes: 486 cmd_buf = robot_prefix + prefix2 + alias + " = " + robot_prefix +\ 487 prefix2 + func_name 488 if gen_robot_print_debug: 489 rprintn(cmd_buf) 490 exec(cmd_buf) 491 492# Define an alias. rpvar is just a special case of rpvars where the args 493# list contains only one element. 494cmd_buf = "rpvar = rpvars" 495if gen_robot_print_debug: 496 rprintn() 497 rprintn(cmd_buf) 498exec(cmd_buf) 499 500############################################################################### 501