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