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