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