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(str(buffer), no_newline=True, stream=stream) 147 148############################################################################### 149 150 151############################################################################### 152def rprintn(buffer="", 153 stream='STDOUT'): 154 155 r""" 156 rprintn stands for "Robot print with linefeed". This keyword will print 157 the user's buffer to the console along with a linefeed. It is basically 158 an abbreviated form of "Log go Console <string> <stream>" 159 160 Description of arguments: 161 buffer The value that is to written to stdout. 162 """ 163 164 BuiltIn().log_to_console(buffer, no_newline=False, stream=stream) 165 166############################################################################### 167 168 169############################################################################### 170def sprint_vars(*args): 171 172 r""" 173 sprint_vars stands for "String Print Vars". This is a robot redefinition 174 of the sprint_vars function in gen_print.py. Given a list of variable 175 names, this keyword will string print each variable name and value such 176 that the value lines up in the same column as messages printed with rptime. 177 178 Description of arguments: 179 args: 180 If the first argument is an integer, it will be interpreted to be the 181 "hex" value. 182 If the second argument is an integer, it will be interpreted to be the 183 "indent" value. 184 If the third argument is an integer, it will be interpreted to be the 185 "col1_width" value. 186 All remaining parms are considered variable names which are to be 187 sprinted. 188 """ 189 190 if len(args) == 0: 191 return 192 193 # Create list from args (which is a tuple) so that it can be modified. 194 args_list = list(args) 195 196 # See if parm 1 is to be interpreted as "hex". 197 try: 198 if type(int(args_list[0])) is int: 199 hex = int(args_list[0]) 200 args_list.pop(0) 201 except ValueError: 202 hex = 0 203 204 # See if parm 2 is to be interpreted as "indent". 205 try: 206 if type(int(args_list[0])) is int: 207 indent = int(args_list[0]) 208 args_list.pop(0) 209 except ValueError: 210 indent = 0 211 212 # See if parm 3 is to be interpreted as "col1_width". 213 try: 214 if type(int(args_list[0])) is int: 215 loc_col1_width = int(args_list[0]) 216 args_list.pop(0) 217 except ValueError: 218 loc_col1_width = gp.col1_width 219 220 buffer = "" 221 for var_name in args_list: 222 var_value = BuiltIn().get_variable_value("${" + str(var_name) + "}") 223 buffer += gp.sprint_varx(var_name, var_value, hex, indent, 224 loc_col1_width) 225 226 return buffer 227 228############################################################################### 229 230 231############################################################################### 232def sprint_pgm_header(indent=0): 233 234 r""" 235 Sprint a standardized header that robot programs should print at the 236 beginning of the run. The header includes useful information like command 237 line, pid, userid, program parameters, etc. Callers need to have declared 238 a global @{parm_list} variable which contains the names of all program 239 parameters. 240 """ 241 242 loc_col1_width = gp.col1_width + indent 243 244 linefeed = 0 245 rprintn() 246 suite_name = BuiltIn().get_variable_value("${suite_name}") 247 248 buffer = "\n" 249 buffer += gp.sindent(gp.sprint_time("Running test suite \"" + 250 suite_name + "\".\n"), 251 indent) 252 buffer += gp.sprint_pgm_header(indent, linefeed) 253 254 # Get value of global parm_list. 255 parm_list = BuiltIn().get_variable_value("${parm_list}") 256 257 buffer += sprint_vars(0, str(indent), str(loc_col1_width), *parm_list) 258 buffer += "\n" 259 260 # Setting global program_pid. 261 BuiltIn().set_global_variable("${program_pid}", os.getpid()) 262 263 return buffer 264 265############################################################################### 266 267 268############################################################################### 269def sprint_error_report(error_text="\n"): 270 271 r""" 272 Print a standardized error report that robot programs should print on 273 failure. The report includes useful information like error text, command 274 line, pid, userid, program parameters, etc. Callers must have declared a 275 @{parm_list} variable which contains the names of all program parameters. 276 """ 277 278 try: 279 error_report_format = int(BuiltIn().get_variable_value( 280 "${error_report_format}")) 281 except TypeError: 282 error_report_format = 0 283 284 # Currently, supported values for error_report_format are: 285 # 0 - Short form 286 # 1 - Long form 287 288 error_text = error_text.rstrip('\n') + '\n' 289 290 if error_report_format == 0: 291 return gp.sprint_error(error_text) 292 293 buffer = "" 294 buffer += gp.sprint_dashes(width=120, char="=") 295 buffer += gp.sprint_error(error_text) 296 297 indent = 2 298 linefeed = 0 299 300 buffer += sprint_pgm_header(indent) 301 302 buffer += gp.sprint_dashes(width=120, char="=") 303 304 return buffer 305 306############################################################################### 307 308 309############################################################################### 310def sprint_issuing_keyword(cmd_buf, 311 test_mode=0): 312 313 r""" 314 Return a line indicating a robot command (i.e. keyword + args) that the 315 program is about to execute. 316 317 For example, for the following robot code... 318 319 @{cmd_buf}= Set Variable Set Environment Variable VAR1 1 320 rdprint_issuing_keyword 321 322 The output would look something like this: 323 324 #(CDT) 2016/10/27 12:04:21 - Issuing: Set Environment Variable VAR1 1 325 326 Description of args: 327 cmd_buf A list containing the keyword and 328 arguments to be run. 329 """ 330 331 buffer = "" 332 cmd_buf_str = ' '.join([str(element) for element in cmd_buf]) 333 buffer += gp.sprint_issuing(cmd_buf_str, int(test_mode)) 334 335 return buffer 336 337############################################################################### 338 339 340############################################################################### 341def sprint_auto_vars(headers=0): 342 343 r""" 344 This keyword will string print all of the Automatic Variables described in 345 the Robot User's Guide using rprint_vars. 346 347 NOTE: Not all automatic variables are guaranteed to exist. 348 349 Description of arguments: 350 headers This indicates that a header and footer 351 should be printed. 352 """ 353 354 buffer = "" 355 if int(headers) == 1: 356 buffer += gp.sprint_dashes() 357 buffer += "Automatic Variables:" 358 359 buffer += \ 360 sprint_vars( 361 "TEST_NAME", "TEST_TAGS", "TEST_DOCUMENTATION", "TEST_STATUS", 362 "TEST_DOCUMENTATION", "TEST_STATUS", "TEST_MESSAGE", 363 "PREV_TEST_NAME", "PREV_TEST_STATUS", "PREV_TEST_MESSAGE", 364 "SUITE_NAME", "SUITE_SOURCE", "SUITE_DOCUMENTATION", 365 "SUITE_METADATA", "SUITE_STATUS", "SUITE_MESSAGE", 366 "KEYWORD_STATUS", "KEYWORD_MESSAGE", "LOG_LEVEL", "OUTPUT_FILE", 367 "LOG_FILE", "REPORT_FILE", "DEBUG_FILE", "OUTPUT_DIR") 368 369 if int(headers) == 1: 370 buffer += gp.sprint_dashes() 371 372 return buffer 373 374############################################################################### 375 376 377############################################################################### 378# In the following section of code, we will dynamically create robot versions 379# of print functions for each of the sprint functions defined in the 380# gen_print.py module. So, for example, where we have an sprint_time() 381# function defined above that returns the time to the caller in a string, we 382# will create a corresponding rprint_time() function that will print that 383# string directly to stdout. 384 385# It can be complicated to follow what's being created by the exec statement 386# below. Here is an example of the rprint_time() function that will be 387# created (as of the time of this writing): 388 389# def rprint_time(*args): 390# s_func = getattr(gp, "sprint_time") 391# BuiltIn().log_to_console(s_func(*args), 392# stream='STDIN', 393# no_newline=True) 394 395# Here are comments describing the lines in the body of the created function. 396# Put a reference to the "s" version of this function in s_func. 397# Call the "s" version of this function passing it all of our arguments. Log 398# the result to the console. 399 400robot_prefix = "r" 401robot_func_names =\ 402 [ 403 'print_error_report', 'print_pgm_header', 404 'print_issuing_keyword', 'print_vars', 'print_auto_vars' 405 ] 406func_names = gp.func_names + robot_func_names 407 408explicit_definitions = ['print', 'printn'] 409 410func_names = list(my_ord_dict.fromkeys(func_names)) 411 412if gen_robot_print_debug: 413 rprintn() 414 BuiltIn().log_to_console(gp.sprint_var(func_names), no_newline=True) 415 rprintn() 416 417for func_name in func_names: 418 419 if func_name not in explicit_definitions: 420 # The print_var function's job is to figure out the name of arg 1 and 421 # then call print_varx. This is not currently supported for robot 422 # programs. Though it IS supported for python modules. 423 if func_name == "print_error" or func_name == "print_error_report": 424 output_stream = "STDERR" 425 else: 426 output_stream = "STDIN" 427 if func_name in robot_func_names: 428 object_name = "__import__(__name__)" 429 else: 430 object_name = "gp" 431 func_def = \ 432 [ 433 "def " + robot_prefix + func_name + "(*args):", 434 " s_func = getattr(" + object_name + ", \"s" + func_name + 435 "\")", 436 " BuiltIn().log_to_console(s_func(*args)," 437 " stream='" + output_stream + "'," 438 " no_newline=True)" 439 ] 440 441 pgm_definition_string = '\n'.join(func_def) 442 if gen_robot_print_debug: 443 rprintn() 444 rprintn(pgm_definition_string) 445 exec(pgm_definition_string) 446 447 # Now define "q" versions of each print function. The q functions only 448 # print if global robot var "quiet" is 0. If the global var quiet is not 449 # defined, it will be treated as though it were "1", i.e. no printing will 450 # be done. 451 func_def = \ 452 [ 453 "def rq" + func_name + "(*args):", 454 " if get_quiet():", 455 " return", 456 " r" + func_name + "(*args)" 457 ] 458 459 pgm_definition_string = '\n'.join(func_def) 460 if gen_robot_print_debug: 461 rprintn(pgm_definition_string) 462 exec(pgm_definition_string) 463 464 # Now define "d" versions of each print function. The d functions only 465 # print if global robot var "debug" is 1. 466 func_def = \ 467 [ 468 "def rd" + func_name + "(*args):", 469 " if not get_debug():", 470 " return", 471 " r" + func_name + "(*args)" 472 ] 473 474 pgm_definition_string = '\n'.join(func_def) 475 if gen_robot_print_debug: 476 rprintn(pgm_definition_string) 477 exec(pgm_definition_string) 478 479 # Create shorter aliases. 480 prefixes = ["", "q", "d"] 481 alias = re.sub("print_", "p", func_name) 482 for prefix2 in prefixes: 483 cmd_buf = robot_prefix + prefix2 + alias + " = " + robot_prefix +\ 484 prefix2 + func_name 485 if gen_robot_print_debug: 486 rprintn(cmd_buf) 487 exec(cmd_buf) 488 489# Define an alias. rpvar is just a special case of rpvars where the args 490# list contains only one element. 491cmd_buf = "rpvar = rpvars" 492if gen_robot_print_debug: 493 rprintn() 494 rprintn(cmd_buf) 495exec(cmd_buf) 496 497############################################################################### 498