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