blob: fdb25c960ed51abffe04d8f31f96712c706878ff [file] [log] [blame]
Michael Walshde791732016-09-06 14:25:24 -05001#!/usr/bin/env python
2
3r"""
4This module provides many valuable print functions such as sprint_var,
5sprint_time, sprint_error, sprint_call_stack.
6"""
7
8import sys
9import os
10import time
11import inspect
12import re
13import grp
14import socket
15import argparse
Michael Walsh7423c012016-10-04 10:27:21 -050016import __builtin__
17import logging
Michael Walshbec416d2016-11-10 08:54:52 -060018import collections
Michael Walshfd2733c2017-11-13 11:36:20 -060019from wrap_utils import *
Michael Walshbec416d2016-11-10 08:54:52 -060020
Michael Walshbec416d2016-11-10 08:54:52 -060021try:
Michael Walsh2ee77cd2017-03-08 11:50:17 -060022 robot_env = 1
Michael Walshbec416d2016-11-10 08:54:52 -060023 from robot.utils import DotDict
Michael Walsh8e6deb42017-01-27 14:22:41 -060024 from robot.utils import NormalizedDict
Michael Walsh2ee77cd2017-03-08 11:50:17 -060025 from robot.libraries.BuiltIn import BuiltIn
Michael Walshb1500152017-04-12 15:42:43 -050026 # Having access to the robot libraries alone does not indicate that we
27 # are in a robot environment. The following try block should confirm that.
28 try:
29 var_value = BuiltIn().get_variable_value("${SUITE_NAME}", "")
30 except:
31 robot_env = 0
Michael Walshbec416d2016-11-10 08:54:52 -060032except ImportError:
Michael Walsh2ee77cd2017-03-08 11:50:17 -060033 robot_env = 0
Michael Walsh7423c012016-10-04 10:27:21 -050034
35import gen_arg as ga
Michael Walshde791732016-09-06 14:25:24 -050036
37# Setting these variables for use both inside this module and by programs
38# importing this module.
Michael Walshbf605652017-09-01 12:33:26 -050039pgm_file_path = sys.argv[0]
40pgm_name = os.path.basename(pgm_file_path)
41pgm_dir_path = re.sub("/" + pgm_name, "", pgm_file_path) + "/"
Michael Walsh7423c012016-10-04 10:27:21 -050042
Michael Walshde791732016-09-06 14:25:24 -050043
44# Some functions (e.g. sprint_pgm_header) have need of a program name value
45# that looks more like a valid variable name. Therefore, we'll swap odd
46# characters like "." out for underscores.
47pgm_name_var_name = pgm_name.replace(".", "_")
48
49# Initialize global values used as defaults by print_time, print_var, etc.
50col1_indent = 0
51
52# Calculate default column width for print_var functions based on environment
53# variable settings. The objective is to make the variable values line up
54# nicely with the time stamps.
55col1_width = 29
Michael Walshb1500152017-04-12 15:42:43 -050056
57NANOSECONDS = os.environ.get('NANOSECONDS', '1')
58
Michael Walshde791732016-09-06 14:25:24 -050059
60if NANOSECONDS == "1":
61 col1_width = col1_width + 7
62
Michael Walshb1500152017-04-12 15:42:43 -050063SHOW_ELAPSED_TIME = os.environ.get('SHOW_ELAPSED_TIME', '1')
Michael Walshde791732016-09-06 14:25:24 -050064
65if SHOW_ELAPSED_TIME == "1":
66 if NANOSECONDS == "1":
67 col1_width = col1_width + 14
68 else:
69 col1_width = col1_width + 7
70
71# Initialize some time variables used in module functions.
72start_time = time.time()
73sprint_time_last_seconds = start_time
74
Michael Walsh2ee77cd2017-03-08 11:50:17 -060075# The user can set environment variable "GEN_PRINT_DEBUG" to get debug output
76# from this module.
77gen_print_debug = int(os.environ.get('GEN_PRINT_DEBUG', 0))
Michael Walsh7423c012016-10-04 10:27:21 -050078
Michael Walshde791732016-09-06 14:25:24 -050079
Michael Walshde791732016-09-06 14:25:24 -050080def sprint_func_name(stack_frame_ix=None):
81
82 r"""
83 Return the function name associated with the indicated stack frame.
84
85 Description of arguments:
86 stack_frame_ix The index of the stack frame whose
87 function name should be returned. If the
Michael Walsh2ee77cd2017-03-08 11:50:17 -060088 caller does not specify a value, this
Michael Walshde791732016-09-06 14:25:24 -050089 function will set the value to 1 which is
90 the index of the caller's stack frame. If
91 the caller is the wrapper function
92 "print_func_name", this function will bump
93 it up by 1.
94 """
95
96 # If user specified no stack_frame_ix, we'll set it to a proper default
97 # value.
98 if stack_frame_ix is None:
99 func_name = sys._getframe().f_code.co_name
100 caller_func_name = sys._getframe(1).f_code.co_name
101 if func_name[1:] == caller_func_name:
102 stack_frame_ix = 2
103 else:
104 stack_frame_ix = 1
105
106 func_name = sys._getframe(stack_frame_ix).f_code.co_name
107
108 return func_name
109
Michael Walshde791732016-09-06 14:25:24 -0500110
111# get_arg_name is not a print function per se. I have included it in this
112# module because it is used by sprint_var which is found in this module.
Michael Walshde791732016-09-06 14:25:24 -0500113def get_arg_name(var,
114 arg_num=1,
115 stack_frame_ix=1):
116
117 r"""
118 Return the "name" of an argument passed to a function. This could be a
119 literal or a variable name.
120
Michael Walsh2ee77cd2017-03-08 11:50:17 -0600121 Description of arguments:
Michael Walshde791732016-09-06 14:25:24 -0500122 var The variable whose name you want returned.
123 arg_num The arg number (1 through n) whose name
124 you wish to have returned. This value
125 should not exceed the number of arguments
126 allowed by the target function.
127 stack_frame_ix The stack frame index of the target
128 function. This value must be 1 or
129 greater. 1 would indicate get_arg_name's
130 stack frame. 2 would be the caller of
131 get_arg_name's stack frame, etc.
132
133 Example 1:
134
135 my_var = "mike"
136 var_name = get_arg_name(my_var)
137
138 In this example, var_name will receive the value "my_var".
139
140 Example 2:
141
142 def test1(var):
143 # Getting the var name of the first arg to this function, test1.
144 # Note, in this case, it doesn't matter what you pass as the first arg
145 # to get_arg_name since it is the caller's variable name that matters.
146 dummy = 1
147 arg_num = 1
148 stack_frame = 2
149 var_name = get_arg_name(dummy, arg_num, stack_frame)
150
151 # Mainline...
152
153 another_var = "whatever"
154 test1(another_var)
155
156 In this example, var_name will be set to "another_var".
157
158 """
159
160 # Note: I wish to avoid recursion so I refrain from calling any function
161 # that calls this function (i.e. sprint_var, valid_value, etc.).
162
Michael Walsh23e7f492017-01-10 11:34:47 -0600163 # The user can set environment variable "GET_ARG_NAME_DEBUG" to get debug
164 # output from this function.
165 local_debug = int(os.environ.get('GET_ARG_NAME_DEBUG', 0))
166 # In addition to GET_ARG_NAME_DEBUG, the user can set environment
167 # variable "GET_ARG_NAME_SHOW_SOURCE" to have this function include source
168 # code in the debug output.
169 local_debug_show_source = int(
170 os.environ.get('GET_ARG_NAME_SHOW_SOURCE', 0))
Michael Walshde791732016-09-06 14:25:24 -0500171
172 if arg_num < 1:
173 print_error("Programmer error - Variable \"arg_num\" has an invalid" +
174 " value of \"" + str(arg_num) + "\". The value must be" +
175 " an integer that is greater than 0.\n")
176 # What is the best way to handle errors? Raise exception? I'll
177 # revisit later.
178 return
179 if stack_frame_ix < 1:
180 print_error("Programmer error - Variable \"stack_frame_ix\" has an" +
181 " invalid value of \"" + str(stack_frame_ix) + "\". The" +
182 " value must be an integer that is greater than or equal" +
183 " to 1.\n")
184 return
185
186 if local_debug:
187 debug_indent = 2
Michael Walsh23e7f492017-01-10 11:34:47 -0600188 print("")
189 print_dashes(0, 120)
Michael Walshde791732016-09-06 14:25:24 -0500190 print(sprint_func_name() + "() parms:")
191 print_varx("var", var, 0, debug_indent)
192 print_varx("arg_num", arg_num, 0, debug_indent)
193 print_varx("stack_frame_ix", stack_frame_ix, 0, debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600194 print("")
195 print_call_stack(debug_indent, 2)
Michael Walshde791732016-09-06 14:25:24 -0500196
Michael Walsh23e7f492017-01-10 11:34:47 -0600197 for count in range(0, 2):
198 try:
199 frame, filename, cur_line_no, function_name, lines, index = \
200 inspect.stack()[stack_frame_ix]
201 except IndexError:
202 print_error("Programmer error - The caller has asked for" +
203 " information about the stack frame at index \"" +
204 str(stack_frame_ix) + "\". However, the stack" +
205 " only contains " + str(len(inspect.stack())) +
206 " entries. Therefore the stack frame index is out" +
207 " of range.\n")
208 return
209 if filename != "<string>":
210 break
211 # filename of "<string>" may mean that the function in question was
212 # defined dynamically and therefore its code stack is inaccessible.
213 # This may happen with functions like "rqprint_var". In this case,
214 # we'll increment the stack_frame_ix and try again.
215 stack_frame_ix += 1
216 if local_debug:
217 print("Adjusted stack_frame_ix...")
218 print_varx("stack_frame_ix", stack_frame_ix, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500219
220 called_func_name = sprint_func_name(stack_frame_ix)
Michael Walsh23e7f492017-01-10 11:34:47 -0600221
222 module = inspect.getmodule(frame)
223
224 # Though I would expect inspect.getsourcelines(frame) to get all module
225 # source lines if the frame is "<module>", it doesn't do that. Therefore,
226 # for this special case, I will do inspect.getsourcelines(module).
227 if function_name == "<module>":
228 source_lines, source_line_num =\
229 inspect.getsourcelines(module)
230 line_ix = cur_line_no - source_line_num - 1
231 else:
232 source_lines, source_line_num =\
233 inspect.getsourcelines(frame)
234 line_ix = cur_line_no - source_line_num
235
236 if local_debug:
237 print("\n Variables retrieved from inspect.stack() function:")
238 print_varx("frame", frame, 0, debug_indent + 2)
239 print_varx("filename", filename, 0, debug_indent + 2)
240 print_varx("cur_line_no", cur_line_no, 0, debug_indent + 2)
241 print_varx("function_name", function_name, 0, debug_indent + 2)
242 print_varx("lines", lines, 0, debug_indent + 2)
243 print_varx("index", index, 0, debug_indent + 2)
244 print_varx("source_line_num", source_line_num, 0, debug_indent)
245 print_varx("line_ix", line_ix, 0, debug_indent)
246 if local_debug_show_source:
247 print_varx("source_lines", source_lines, 0, debug_indent)
248 print_varx("called_func_name", called_func_name, 0, debug_indent)
249
250 # Get a list of all functions defined for the module. Note that this
251 # doesn't work consistently when _run_exitfuncs is at the top of the stack
252 # (i.e. if we're running an exit function). I've coded a work-around
253 # below for this deficiency.
254 all_functions = inspect.getmembers(module, inspect.isfunction)
255
256 # Get called_func_id by searching for our function in the list of all
257 # functions.
258 called_func_id = None
259 for func_name, function in all_functions:
260 if func_name == called_func_name:
261 called_func_id = id(function)
262 break
263 # NOTE: The only time I've found that called_func_id can't be found is
264 # when we're running from an exit function.
265
266 # Look for other functions in module with matching id.
267 aliases = set([called_func_name])
268 for func_name, function in all_functions:
269 if func_name == called_func_name:
270 continue
271 func_id = id(function)
272 if func_id == called_func_id:
273 aliases.add(func_name)
274
275 # In most cases, my general purpose code above will find all aliases.
276 # However, for the odd case (i.e. running from exit function), I've added
277 # code to handle pvar, qpvar, dpvar, etc. aliases explicitly since they
278 # are defined in this module and used frequently.
279 # pvar is an alias for print_var.
280 aliases.add(re.sub("print_var", "pvar", called_func_name))
281
282 func_regex = ".*(" + '|'.join(aliases) + ")[ ]*\("
283
284 # Search backward through source lines looking for the calling function
285 # name.
286 found = False
287 for start_line_ix in range(line_ix, 0, -1):
288 # Skip comment lines.
289 if re.match(r"[ ]*#", source_lines[start_line_ix]):
290 continue
291 if re.match(func_regex, source_lines[start_line_ix]):
292 found = True
293 break
294 if not found:
295 print_error("Programmer error - Could not find the source line with" +
296 " a reference to function \"" + called_func_name + "\".\n")
297 return
298
Michael Walsh82acf002017-05-04 14:33:05 -0500299 # Search forward through the source lines looking for a line whose
300 # indentation is the same or less than the start line. The end of our
301 # composite line should be the line preceding that line.
Michael Walsh23e7f492017-01-10 11:34:47 -0600302 start_indent = len(source_lines[start_line_ix]) -\
303 len(source_lines[start_line_ix].lstrip(' '))
304 end_line_ix = line_ix
305 for end_line_ix in range(line_ix + 1, len(source_lines)):
306 if source_lines[end_line_ix].strip() == "":
307 continue
308 line_indent = len(source_lines[end_line_ix]) -\
309 len(source_lines[end_line_ix].lstrip(' '))
Michael Walsh82acf002017-05-04 14:33:05 -0500310 if line_indent <= start_indent:
Michael Walsh23e7f492017-01-10 11:34:47 -0600311 end_line_ix -= 1
312 break
313
314 # Join the start line through the end line into a composite line.
315 composite_line = ''.join(map(str.strip,
316 source_lines[start_line_ix:end_line_ix + 1]))
Michael Walsh7423c012016-10-04 10:27:21 -0500317
Michael Walshbec416d2016-11-10 08:54:52 -0600318 # arg_list_etc = re.sub(".*" + called_func_name, "", composite_line)
319 arg_list_etc = "(" + re.sub(func_regex, "", composite_line)
Michael Walshde791732016-09-06 14:25:24 -0500320 if local_debug:
Michael Walsh23e7f492017-01-10 11:34:47 -0600321 print_varx("aliases", aliases, 0, debug_indent)
Michael Walshbec416d2016-11-10 08:54:52 -0600322 print_varx("func_regex", func_regex, 0, debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600323 print_varx("start_line_ix", start_line_ix, 0, debug_indent)
324 print_varx("end_line_ix", end_line_ix, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500325 print_varx("composite_line", composite_line, 0, debug_indent)
326 print_varx("arg_list_etc", arg_list_etc, 0, debug_indent)
327
328 # Parse arg list...
329 # Initialize...
330 nest_level = -1
331 arg_ix = 0
Michael Walsh7423c012016-10-04 10:27:21 -0500332 args_list = [""]
Michael Walshde791732016-09-06 14:25:24 -0500333 for ix in range(0, len(arg_list_etc)):
334 char = arg_list_etc[ix]
335 # Set the nest_level based on whether we've encounted a parenthesis.
336 if char == "(":
337 nest_level += 1
338 if nest_level == 0:
339 continue
340 elif char == ")":
341 nest_level -= 1
342 if nest_level < 0:
343 break
344
345 # If we reach a comma at base nest level, we are done processing an
Michael Walsh7423c012016-10-04 10:27:21 -0500346 # argument so we increment arg_ix and initialize a new args_list entry.
Michael Walshde791732016-09-06 14:25:24 -0500347 if char == "," and nest_level == 0:
348 arg_ix += 1
Michael Walsh7423c012016-10-04 10:27:21 -0500349 args_list.append("")
Michael Walshde791732016-09-06 14:25:24 -0500350 continue
351
Michael Walsh7423c012016-10-04 10:27:21 -0500352 # For any other character, we append it it to the current arg list
Michael Walshde791732016-09-06 14:25:24 -0500353 # entry.
Michael Walsh7423c012016-10-04 10:27:21 -0500354 args_list[arg_ix] += char
Michael Walshde791732016-09-06 14:25:24 -0500355
356 # Trim whitespace from each list entry.
Michael Walsh7423c012016-10-04 10:27:21 -0500357 args_list = [arg.strip() for arg in args_list]
Michael Walshde791732016-09-06 14:25:24 -0500358
Michael Walsh7423c012016-10-04 10:27:21 -0500359 if arg_num > len(args_list):
Michael Walshde791732016-09-06 14:25:24 -0500360 print_error("Programmer error - The caller has asked for the name of" +
361 " argument number \"" + str(arg_num) + "\" but there " +
Michael Walsh7423c012016-10-04 10:27:21 -0500362 "were only \"" + str(len(args_list)) + "\" args used:\n" +
363 sprint_varx("args_list", args_list))
Michael Walshde791732016-09-06 14:25:24 -0500364 return
365
Michael Walsh7423c012016-10-04 10:27:21 -0500366 argument = args_list[arg_num - 1]
Michael Walshde791732016-09-06 14:25:24 -0500367
368 if local_debug:
Michael Walsh7423c012016-10-04 10:27:21 -0500369 print_varx("args_list", args_list, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500370 print_varx("argument", argument, 0, debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600371 print_dashes(0, 120)
Michael Walshde791732016-09-06 14:25:24 -0500372
373 return argument
374
Michael Walshde791732016-09-06 14:25:24 -0500375
Michael Walshde791732016-09-06 14:25:24 -0500376def sprint_time(buffer=""):
377
378 r"""
379 Return the time in the following format.
380
381 Example:
382
383 The following python code...
384
385 sys.stdout.write(sprint_time())
386 sys.stdout.write("Hi.\n")
387
388 Will result in the following type of output:
389
390 #(CDT) 2016/07/08 15:25:35 - Hi.
391
392 Example:
393
394 The following python code...
395
396 sys.stdout.write(sprint_time("Hi.\n"))
397
398 Will result in the following type of output:
399
400 #(CDT) 2016/08/03 17:12:05 - Hi.
401
402 The following environment variables will affect the formatting as
403 described:
404 NANOSECONDS This will cause the time stamps to be
405 precise to the microsecond (Yes, it
406 probably should have been named
407 MICROSECONDS but the convention was set
408 long ago so we're sticking with it).
409 Example of the output when environment
410 variable NANOSECONDS=1.
411
412 #(CDT) 2016/08/03 17:16:25.510469 - Hi.
413
414 SHOW_ELAPSED_TIME This will cause the elapsed time to be
415 included in the output. This is the
416 amount of time that has elapsed since the
417 last time this function was called. The
418 precision of the elapsed time field is
419 also affected by the value of the
420 NANOSECONDS environment variable. Example
421 of the output when environment variable
422 NANOSECONDS=0 and SHOW_ELAPSED_TIME=1.
423
424 #(CDT) 2016/08/03 17:17:40 - 0 - Hi.
425
426 Example of the output when environment variable NANOSECONDS=1 and
427 SHOW_ELAPSED_TIME=1.
428
429 #(CDT) 2016/08/03 17:18:47.317339 - 0.000046 - Hi.
430
431 Description of arguments.
432 buffer This will be appended to the formatted
433 time string.
434 """
435
436 global NANOSECONDS
437 global SHOW_ELAPSED_TIME
438 global sprint_time_last_seconds
439
440 seconds = time.time()
441 loc_time = time.localtime(seconds)
442 nanoseconds = "%0.6f" % seconds
443 pos = nanoseconds.find(".")
444 nanoseconds = nanoseconds[pos:]
445
446 time_string = time.strftime("#(%Z) %Y/%m/%d %H:%M:%S", loc_time)
447 if NANOSECONDS == "1":
448 time_string = time_string + nanoseconds
449
450 if SHOW_ELAPSED_TIME == "1":
451 cur_time_seconds = seconds
452 math_string = "%9.9f" % cur_time_seconds + " - " + "%9.9f" % \
453 sprint_time_last_seconds
454 elapsed_seconds = eval(math_string)
455 if NANOSECONDS == "1":
456 elapsed_seconds = "%11.6f" % elapsed_seconds
457 else:
458 elapsed_seconds = "%4i" % elapsed_seconds
459 sprint_time_last_seconds = cur_time_seconds
460 time_string = time_string + " - " + elapsed_seconds
461
462 return time_string + " - " + buffer
463
Michael Walshde791732016-09-06 14:25:24 -0500464
Michael Walshde791732016-09-06 14:25:24 -0500465def sprint_timen(buffer=""):
466
467 r"""
468 Append a line feed to the buffer, pass it to sprint_time and return the
469 result.
470 """
471
472 return sprint_time(buffer + "\n")
473
Michael Walshde791732016-09-06 14:25:24 -0500474
Michael Walshde791732016-09-06 14:25:24 -0500475def sprint_error(buffer=""):
476
477 r"""
478 Return a standardized error string. This includes:
479 - A time stamp
480 - The "**ERROR**" string
481 - The caller's buffer string.
482
483 Example:
484
485 The following python code...
486
487 print(sprint_error("Oops.\n"))
488
489 Will result in the following type of output:
490
491 #(CDT) 2016/08/03 17:12:05 - **ERROR** Oops.
492
493 Description of arguments.
494 buffer This will be appended to the formatted
495 error string.
496 """
497
498 return sprint_time() + "**ERROR** " + buffer
499
Michael Walshde791732016-09-06 14:25:24 -0500500
Michael Walshde791732016-09-06 14:25:24 -0500501def sprint_varx(var_name,
502 var_value,
503 hex=0,
504 loc_col1_indent=col1_indent,
Michael Walsh7423c012016-10-04 10:27:21 -0500505 loc_col1_width=col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500506 trailing_char="\n",
507 key_list=None):
Michael Walshde791732016-09-06 14:25:24 -0500508
509 r"""
510 Print the var name/value passed to it. If the caller lets loc_col1_width
511 default, the printing lines up nicely with output generated by the
512 print_time functions.
513
514 Note that the sprint_var function (defined below) can be used to call this
515 function so that the programmer does not need to pass the var_name.
516 sprint_var will figure out the var_name. The sprint_var function is the
517 one that would normally be used by the general user.
518
519 For example, the following python code:
520
521 first_name = "Mike"
522 print_time("Doing this...\n")
523 print_varx("first_name", first_name)
524 print_time("Doing that...\n")
525
526 Will generate output like this:
527
528 #(CDT) 2016/08/10 17:34:42.847374 - 0.001285 - Doing this...
529 first_name: Mike
530 #(CDT) 2016/08/10 17:34:42.847510 - 0.000136 - Doing that...
531
532 This function recognizes several complex types of data such as dict, list
533 or tuple.
534
535 For example, the following python code:
536
537 my_dict = dict(one=1, two=2, three=3)
538 print_var(my_dict)
539
540 Will generate the following output:
541
542 my_dict:
543 my_dict[three]: 3
544 my_dict[two]: 2
545 my_dict[one]: 1
546
547 Description of arguments.
548 var_name The name of the variable to be printed.
549 var_value The value of the variable to be printed.
550 hex This indicates that the value should be
551 printed in hex format. It is the user's
552 responsibility to ensure that a var_value
Michael Walshbec416d2016-11-10 08:54:52 -0600553 contains a valid hex number. For string
554 var_values, this will be interpreted as
555 show_blanks which means that blank values
Michael Walshd995cb02017-02-07 14:46:01 -0600556 will be printed as "<blank>". For dict
557 var_values, this will be interpreted as
558 terse format where keys are not repeated
559 in the output.
Michael Walshde791732016-09-06 14:25:24 -0500560 loc_col1_indent The number of spaces to indent the output.
561 loc_col1_width The width of the output column containing
562 the variable name. The default value of
563 this is adjusted so that the var_value
564 lines up with text printed via the
565 print_time function.
Michael Walsh7423c012016-10-04 10:27:21 -0500566 trailing_char The character to be used at the end of the
567 returned string. The default value is a
568 line feed.
Michael Walshd2869032018-03-22 16:12:11 -0500569 key_list A list of which dictionary keys should be
570 printed. All others keys will be skipped.
571 Each value in key_list will be regarded
572 as a regular expression and it will be
573 regarded as anchored to the beginning and
574 ends of the dictionary key being
575 referenced. For example if key_list is
576 ["one", "two"], the resulting regex used
577 will be "^one|two$", i.e. only keys "one"
578 and "two" from the var_value dictionary
579 will be printed. As another example, if
580 the caller were to specify a key_list of
581 ["one.*"], then only dictionary keys whose
582 names begin with "one" will be printed.
583 Note: This argument pertains only to
584 var_values which are dictionaries.
Michael Walsh7423c012016-10-04 10:27:21 -0500585 """
Michael Walshde791732016-09-06 14:25:24 -0500586
587 # Determine the type
588 if type(var_value) in (int, float, bool, str, unicode) \
589 or var_value is None:
590 # The data type is simple in the sense that it has no subordinate
591 # parts.
Michael Walsh7423c012016-10-04 10:27:21 -0500592 # Adjust loc_col1_width.
593 loc_col1_width = loc_col1_width - loc_col1_indent
Michael Walshde791732016-09-06 14:25:24 -0500594 # See if the user wants the output in hex format.
595 if hex:
Michael Walsh18176322016-11-15 15:11:21 -0600596 if type(var_value) not in (int, long):
Michael Walshbec416d2016-11-10 08:54:52 -0600597 value_format = "%s"
Michael Walsh2795edc2016-12-13 16:00:33 -0600598 if var_value == "":
Michael Walshbec416d2016-11-10 08:54:52 -0600599 var_value = "<blank>"
600 else:
601 value_format = "0x%08x"
Michael Walshde791732016-09-06 14:25:24 -0500602 else:
603 value_format = "%s"
604 format_string = "%" + str(loc_col1_indent) + "s%-" \
Michael Walsh7423c012016-10-04 10:27:21 -0500605 + str(loc_col1_width) + "s" + value_format + trailing_char
Michael Walsh3383e652017-09-01 17:10:59 -0500606 if value_format == "0x%08x":
607 return format_string % ("", str(var_name) + ":",
608 var_value & 0xffffffff)
609 else:
610 return format_string % ("", str(var_name) + ":", var_value)
Michael Walsh20a87ab2017-06-30 17:00:30 -0500611 elif type(var_value) is type:
612 return sprint_varx(var_name, str(var_value).split("'")[1], hex,
Michael Walshd2869032018-03-22 16:12:11 -0500613 loc_col1_indent, loc_col1_width, trailing_char,
614 key_list)
Michael Walshde791732016-09-06 14:25:24 -0500615 else:
616 # The data type is complex in the sense that it has subordinate parts.
617 format_string = "%" + str(loc_col1_indent) + "s%s\n"
618 buffer = format_string % ("", var_name + ":")
619 loc_col1_indent += 2
Michael Walsh7423c012016-10-04 10:27:21 -0500620 try:
621 length = len(var_value)
622 except TypeError:
Michael Walsh23e7f492017-01-10 11:34:47 -0600623 length = 0
Michael Walsh7423c012016-10-04 10:27:21 -0500624 ix = 0
625 loc_trailing_char = "\n"
Michael Walshbec416d2016-11-10 08:54:52 -0600626 type_is_dict = 0
Michael Walsh23e7f492017-01-10 11:34:47 -0600627 if type(var_value) is dict:
628 type_is_dict = 1
Michael Walsh8e6deb42017-01-27 14:22:41 -0600629 try:
630 if type(var_value) is collections.OrderedDict:
631 type_is_dict = 1
632 except AttributeError:
633 pass
634 try:
635 if type(var_value) is DotDict:
636 type_is_dict = 1
637 except NameError:
638 pass
639 try:
640 if type(var_value) is NormalizedDict:
641 type_is_dict = 1
642 except NameError:
643 pass
Michael Walshbec416d2016-11-10 08:54:52 -0600644 if type_is_dict:
Michael Walshde791732016-09-06 14:25:24 -0500645 for key, value in var_value.iteritems():
Michael Walshd2869032018-03-22 16:12:11 -0500646 if key_list is not None:
647 key_list_regex = "^" + "|".join(key_list) + "$"
648 if not re.match(key_list_regex, key):
649 continue
Michael Walsh7423c012016-10-04 10:27:21 -0500650 ix += 1
651 if ix == length:
652 loc_trailing_char = trailing_char
Michael Walshd995cb02017-02-07 14:46:01 -0600653 if hex:
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600654 # Since hex is being used as a format type, we want it
655 # turned off when processing integer dictionary values so
656 # it is not interpreted as a hex indicator.
657 loc_hex = not (type(value) is int)
Michael Walshf7b8a002017-08-29 10:38:39 -0500658 buffer += sprint_varx("[" + key + "]", value,
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600659 loc_hex, loc_col1_indent,
660 loc_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500661 loc_trailing_char,
662 key_list)
Michael Walshd995cb02017-02-07 14:46:01 -0600663 else:
664 buffer += sprint_varx(var_name + "[" + key + "]", value,
665 hex, loc_col1_indent, loc_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500666 loc_trailing_char, key_list)
Michael Walsh7423c012016-10-04 10:27:21 -0500667 elif type(var_value) in (list, tuple, set):
Michael Walshde791732016-09-06 14:25:24 -0500668 for key, value in enumerate(var_value):
Michael Walsh7423c012016-10-04 10:27:21 -0500669 ix += 1
670 if ix == length:
671 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500672 buffer += sprint_varx(var_name + "[" + str(key) + "]", value,
Michael Walsh7423c012016-10-04 10:27:21 -0500673 hex, loc_col1_indent, loc_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500674 loc_trailing_char, key_list)
Michael Walshde791732016-09-06 14:25:24 -0500675 elif type(var_value) is argparse.Namespace:
676 for key in var_value.__dict__:
Michael Walsh7423c012016-10-04 10:27:21 -0500677 ix += 1
678 if ix == length:
679 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500680 cmd_buf = "buffer += sprint_varx(var_name + \".\" + str(key)" \
Michael Walsh7423c012016-10-04 10:27:21 -0500681 + ", var_value." + key + ", hex, loc_col1_indent," \
Michael Walshd2869032018-03-22 16:12:11 -0500682 + " loc_col1_width, loc_trailing_char, key_list)"
Michael Walshde791732016-09-06 14:25:24 -0500683 exec(cmd_buf)
684 else:
685 var_type = type(var_value).__name__
686 func_name = sys._getframe().f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -0500687 var_value = "<" + var_type + " type not supported by " + \
688 func_name + "()>"
Michael Walshde791732016-09-06 14:25:24 -0500689 value_format = "%s"
690 loc_col1_indent -= 2
Michael Walsh7423c012016-10-04 10:27:21 -0500691 # Adjust loc_col1_width.
692 loc_col1_width = loc_col1_width - loc_col1_indent
Michael Walshde791732016-09-06 14:25:24 -0500693 format_string = "%" + str(loc_col1_indent) + "s%-" \
Michael Walsh7423c012016-10-04 10:27:21 -0500694 + str(loc_col1_width) + "s" + value_format + trailing_char
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600695 return format_string % ("", str(var_name) + ":", var_value)
Michael Walsh23e7f492017-01-10 11:34:47 -0600696
Michael Walshde791732016-09-06 14:25:24 -0500697 return buffer
698
699 return ""
700
Michael Walshde791732016-09-06 14:25:24 -0500701
Michael Walshfd2733c2017-11-13 11:36:20 -0600702def sprint_var(var_value,
703 hex=0,
704 loc_col1_indent=col1_indent,
705 loc_col1_width=col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500706 trailing_char="\n",
707 key_list=None):
Michael Walshde791732016-09-06 14:25:24 -0500708
709 r"""
710 Figure out the name of the first argument for you and then call
711 sprint_varx with it. Therefore, the following 2 calls are equivalent:
712 sprint_varx("var1", var1)
713 sprint_var(var1)
714 """
715
716 # Get the name of the first variable passed to this function.
717 stack_frame = 2
Michael Walsh7423c012016-10-04 10:27:21 -0500718 caller_func_name = sprint_func_name(2)
719 if caller_func_name.endswith("print_var"):
Michael Walshde791732016-09-06 14:25:24 -0500720 stack_frame += 1
721 var_name = get_arg_name(None, 1, stack_frame)
Michael Walshfd2733c2017-11-13 11:36:20 -0600722 return sprint_varx(var_name, var_value=var_value, hex=hex,
723 loc_col1_indent=loc_col1_indent,
724 loc_col1_width=loc_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500725 trailing_char=trailing_char,
726 key_list=key_list)
Michael Walshde791732016-09-06 14:25:24 -0500727
728
Michael Walsh18176322016-11-15 15:11:21 -0600729def sprint_vars(*args):
730
731 r"""
732 Sprint the values of one or more variables.
733
734 Description of args:
735 args:
736 If the first argument is an integer, it will be interpreted to be the
737 "indent" value.
738 If the second argument is an integer, it will be interpreted to be the
739 "col1_width" value.
740 If the third argument is an integer, it will be interpreted to be the
741 "hex" value.
742 All remaining parms are considered variable names which are to be
743 sprinted.
744 """
745
746 if len(args) == 0:
747 return
748
749 # Get the name of the first variable passed to this function.
750 stack_frame = 2
751 caller_func_name = sprint_func_name(2)
752 if caller_func_name.endswith("print_vars"):
753 stack_frame += 1
754
755 parm_num = 1
756
757 # Create list from args (which is a tuple) so that it can be modified.
758 args_list = list(args)
759
760 var_name = get_arg_name(None, parm_num, stack_frame)
761 # See if parm 1 is to be interpreted as "indent".
762 try:
763 if type(int(var_name)) is int:
764 indent = int(var_name)
765 args_list.pop(0)
766 parm_num += 1
767 except ValueError:
768 indent = 0
769
770 var_name = get_arg_name(None, parm_num, stack_frame)
771 # See if parm 1 is to be interpreted as "col1_width".
772 try:
773 if type(int(var_name)) is int:
774 loc_col1_width = int(var_name)
775 args_list.pop(0)
776 parm_num += 1
777 except ValueError:
778 loc_col1_width = col1_width
779
780 var_name = get_arg_name(None, parm_num, stack_frame)
781 # See if parm 1 is to be interpreted as "hex".
782 try:
783 if type(int(var_name)) is int:
784 hex = int(var_name)
785 args_list.pop(0)
786 parm_num += 1
787 except ValueError:
788 hex = 0
789
790 buffer = ""
791 for var_value in args_list:
792 var_name = get_arg_name(None, parm_num, stack_frame)
793 buffer += sprint_varx(var_name, var_value, hex, indent, loc_col1_width)
794 parm_num += 1
795
796 return buffer
797
Michael Walsh18176322016-11-15 15:11:21 -0600798
Michael Walsh7423c012016-10-04 10:27:21 -0500799def sprint_dashes(indent=col1_indent,
800 width=80,
801 line_feed=1,
802 char="-"):
Michael Walshde791732016-09-06 14:25:24 -0500803
804 r"""
805 Return a string of dashes to the caller.
806
Michael Walsh2ee77cd2017-03-08 11:50:17 -0600807 Description of arguments:
Michael Walshde791732016-09-06 14:25:24 -0500808 indent The number of characters to indent the
809 output.
810 width The width of the string of dashes.
811 line_feed Indicates whether the output should end
812 with a line feed.
Michael Walsh7423c012016-10-04 10:27:21 -0500813 char The character to be repeated in the output
814 string.
Michael Walshde791732016-09-06 14:25:24 -0500815 """
816
Michael Walsh7423c012016-10-04 10:27:21 -0500817 width = int(width)
Michael Walsh23e7f492017-01-10 11:34:47 -0600818 buffer = " " * int(indent) + char * width
Michael Walshde791732016-09-06 14:25:24 -0500819 if line_feed:
820 buffer += "\n"
821
822 return buffer
823
Michael Walshde791732016-09-06 14:25:24 -0500824
Michael Walsh7423c012016-10-04 10:27:21 -0500825def sindent(text="",
826 indent=0):
827
828 r"""
829 Pre-pend the specified number of characters to the text string (i.e.
830 indent it) and return it.
831
832 Description of arguments:
833 text The string to be indented.
834 indent The number of characters to indent the
835 string.
836 """
837
838 format_string = "%" + str(indent) + "s%s"
839 buffer = format_string % ("", text)
840
841 return buffer
842
Michael Walsh7423c012016-10-04 10:27:21 -0500843
Michael Walsh7423c012016-10-04 10:27:21 -0500844def sprint_call_stack(indent=0,
845 stack_frame_ix=0):
Michael Walshde791732016-09-06 14:25:24 -0500846
847 r"""
848 Return a call stack report for the given point in the program with line
849 numbers, function names and function parameters and arguments.
850
851 Sample output:
852
853 -------------------------------------------------------------------------
854 Python function call stack
855
856 Line # Function name and arguments
857 ------ ------------------------------------------------------------------
858 424 sprint_call_stack ()
859 4 print_call_stack ()
860 31 func1 (last_name = 'walsh', first_name = 'mikey')
861 59 /tmp/scr5.py
862 -------------------------------------------------------------------------
863
864 Description of arguments:
865 indent The number of characters to indent each
866 line of output.
867 stack_frame_ix The index of the first stack frame which
868 is to be returned.
869 """
870
871 buffer = ""
Michael Walsh7423c012016-10-04 10:27:21 -0500872 buffer += sprint_dashes(indent)
873 buffer += sindent("Python function call stack\n\n", indent)
874 buffer += sindent("Line # Function name and arguments\n", indent)
875 buffer += sprint_dashes(indent, 6, 0) + " " + sprint_dashes(0, 73)
Michael Walshde791732016-09-06 14:25:24 -0500876
877 # Grab the current program stack.
878 current_stack = inspect.stack()
879
880 # Process each frame in turn.
881 format_string = "%6s %s\n"
Michael Walsh7423c012016-10-04 10:27:21 -0500882 ix = 0
Michael Walshde791732016-09-06 14:25:24 -0500883 for stack_frame in current_stack:
Michael Walsh7423c012016-10-04 10:27:21 -0500884 if ix < stack_frame_ix:
885 ix += 1
886 continue
Michael Walsh23e7f492017-01-10 11:34:47 -0600887 # I want the line number shown to be the line where you find the line
888 # shown.
889 try:
890 line_num = str(current_stack[ix + 1][2])
891 except IndexError:
892 line_num = ""
Michael Walshde791732016-09-06 14:25:24 -0500893 func_name = str(stack_frame[3])
894 if func_name == "?":
895 # "?" is the name used when code is not in a function.
896 func_name = "(none)"
897
898 if func_name == "<module>":
Michael Walsh7423c012016-10-04 10:27:21 -0500899 # If the func_name is the "main" program, we simply get the
900 # command line call string.
Michael Walshde791732016-09-06 14:25:24 -0500901 func_and_args = ' '.join(sys.argv)
902 else:
903 # Get the program arguments.
904 arg_vals = inspect.getargvalues(stack_frame[0])
905 function_parms = arg_vals[0]
906 frame_locals = arg_vals[3]
907
Michael Walsh7423c012016-10-04 10:27:21 -0500908 args_list = []
Michael Walshde791732016-09-06 14:25:24 -0500909 for arg_name in function_parms:
910 # Get the arg value from frame locals.
911 arg_value = frame_locals[arg_name]
Michael Walsh7423c012016-10-04 10:27:21 -0500912 args_list.append(arg_name + " = " + repr(arg_value))
913 args_str = "(" + ', '.join(map(str, args_list)) + ")"
Michael Walshde791732016-09-06 14:25:24 -0500914
915 # Now we need to print this in a nicely-wrapped way.
916 func_and_args = func_name + " " + args_str
917
Michael Walsh23e7f492017-01-10 11:34:47 -0600918 buffer += sindent(format_string % (line_num, func_and_args), indent)
Michael Walsh7423c012016-10-04 10:27:21 -0500919 ix += 1
Michael Walshde791732016-09-06 14:25:24 -0500920
Michael Walsh7423c012016-10-04 10:27:21 -0500921 buffer += sprint_dashes(indent)
Michael Walshde791732016-09-06 14:25:24 -0500922
923 return buffer
924
Michael Walshde791732016-09-06 14:25:24 -0500925
Michael Walshde791732016-09-06 14:25:24 -0500926def sprint_executing(stack_frame_ix=None):
927
928 r"""
929 Print a line indicating what function is executing and with what parameter
930 values. This is useful for debugging.
931
932 Sample output:
933
934 #(CDT) 2016/08/25 17:54:27 - Executing: func1 (x = 1)
935
936 Description of arguments:
937 stack_frame_ix The index of the stack frame whose
938 function info should be returned. If the
Michael Walsh2ee77cd2017-03-08 11:50:17 -0600939 caller does not specify a value, this
Michael Walshde791732016-09-06 14:25:24 -0500940 function will set the value to 1 which is
941 the index of the caller's stack frame. If
942 the caller is the wrapper function
943 "print_executing", this function will bump
944 it up by 1.
945 """
946
947 # If user wants default stack_frame_ix.
948 if stack_frame_ix is None:
949 func_name = sys._getframe().f_code.co_name
950 caller_func_name = sys._getframe(1).f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -0500951 if caller_func_name.endswith(func_name[1:]):
Michael Walshde791732016-09-06 14:25:24 -0500952 stack_frame_ix = 2
953 else:
954 stack_frame_ix = 1
955
956 stack_frame = inspect.stack()[stack_frame_ix]
957
958 func_name = str(stack_frame[3])
959 if func_name == "?":
960 # "?" is the name used when code is not in a function.
961 func_name = "(none)"
962
963 if func_name == "<module>":
964 # If the func_name is the "main" program, we simply get the command
965 # line call string.
966 func_and_args = ' '.join(sys.argv)
967 else:
968 # Get the program arguments.
969 arg_vals = inspect.getargvalues(stack_frame[0])
970 function_parms = arg_vals[0]
971 frame_locals = arg_vals[3]
972
Michael Walsh7423c012016-10-04 10:27:21 -0500973 args_list = []
Michael Walshde791732016-09-06 14:25:24 -0500974 for arg_name in function_parms:
975 # Get the arg value from frame locals.
976 arg_value = frame_locals[arg_name]
Michael Walsh7423c012016-10-04 10:27:21 -0500977 args_list.append(arg_name + " = " + repr(arg_value))
978 args_str = "(" + ', '.join(map(str, args_list)) + ")"
Michael Walshde791732016-09-06 14:25:24 -0500979
980 # Now we need to print this in a nicely-wrapped way.
981 func_and_args = func_name + " " + args_str
982
983 return sprint_time() + "Executing: " + func_and_args + "\n"
984
Michael Walshde791732016-09-06 14:25:24 -0500985
Michael Walshbec416d2016-11-10 08:54:52 -0600986def sprint_pgm_header(indent=0,
987 linefeed=1):
Michael Walshde791732016-09-06 14:25:24 -0500988
989 r"""
990 Return a standardized header that programs should print at the beginning
991 of the run. It includes useful information like command line, pid,
992 userid, program parameters, etc.
993
Michael Walsh7423c012016-10-04 10:27:21 -0500994 Description of arguments:
995 indent The number of characters to indent each
996 line of output.
Michael Walshbec416d2016-11-10 08:54:52 -0600997 linefeed Indicates whether a line feed be included
998 at the beginning and end of the report.
Michael Walshde791732016-09-06 14:25:24 -0500999 """
1000
Michael Walshbec416d2016-11-10 08:54:52 -06001001 loc_col1_width = col1_width + indent
1002
1003 buffer = ""
1004 if linefeed:
1005 buffer = "\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001006
Michael Walshdb6e68a2017-05-23 17:55:31 -05001007 if robot_env:
1008 suite_name = BuiltIn().get_variable_value("${suite_name}")
1009 buffer += sindent(sprint_time("Running test suite \"" + suite_name +
1010 "\".\n"), indent)
1011
Michael Walsh7423c012016-10-04 10:27:21 -05001012 buffer += sindent(sprint_time() + "Running " + pgm_name + ".\n", indent)
1013 buffer += sindent(sprint_time() + "Program parameter values, etc.:\n\n",
1014 indent)
Michael Walshbec416d2016-11-10 08:54:52 -06001015 buffer += sprint_varx("command_line", ' '.join(sys.argv), 0, indent,
1016 loc_col1_width)
Michael Walsh7423c012016-10-04 10:27:21 -05001017 # We want the output to show a customized name for the pid and pgid but
1018 # we want it to look like a valid variable name. Therefore, we'll use
Michael Walshde791732016-09-06 14:25:24 -05001019 # pgm_name_var_name which was set when this module was imported.
Michael Walshbec416d2016-11-10 08:54:52 -06001020 buffer += sprint_varx(pgm_name_var_name + "_pid", os.getpid(), 0, indent,
1021 loc_col1_width)
1022 buffer += sprint_varx(pgm_name_var_name + "_pgid", os.getpgrp(), 0, indent,
1023 loc_col1_width)
Michael Walsh86de0d22016-12-05 10:13:15 -06001024 userid_num = str(os.geteuid())
1025 try:
1026 username = os.getlogin()
1027 except OSError:
1028 if userid_num == "0":
1029 username = "root"
1030 else:
1031 username = "?"
1032 buffer += sprint_varx("uid", userid_num + " (" + username +
Michael Walshbec416d2016-11-10 08:54:52 -06001033 ")", 0, indent, loc_col1_width)
Michael Walshde791732016-09-06 14:25:24 -05001034 buffer += sprint_varx("gid", str(os.getgid()) + " (" +
Michael Walsh7423c012016-10-04 10:27:21 -05001035 str(grp.getgrgid(os.getgid()).gr_name) + ")", 0,
Michael Walshbec416d2016-11-10 08:54:52 -06001036 indent, loc_col1_width)
1037 buffer += sprint_varx("host_name", socket.gethostname(), 0, indent,
1038 loc_col1_width)
Michael Walsh86de0d22016-12-05 10:13:15 -06001039 try:
1040 DISPLAY = os.environ['DISPLAY']
1041 except KeyError:
1042 DISPLAY = ""
1043 buffer += sprint_varx("DISPLAY", DISPLAY, 0, indent,
Michael Walshbec416d2016-11-10 08:54:52 -06001044 loc_col1_width)
Michael Walshde791732016-09-06 14:25:24 -05001045 # I want to add code to print caller's parms.
1046
Michael Walsh7423c012016-10-04 10:27:21 -05001047 # __builtin__.arg_obj is created by the get_arg module function,
1048 # gen_get_options.
1049 try:
1050 buffer += ga.sprint_args(__builtin__.arg_obj, indent)
1051 except AttributeError:
1052 pass
1053
Michael Walshdb6e68a2017-05-23 17:55:31 -05001054 if robot_env:
1055 # Get value of global parm_list.
1056 parm_list = BuiltIn().get_variable_value("${parm_list}")
1057
1058 for parm in parm_list:
1059 parm_value = BuiltIn().get_variable_value("${" + parm + "}")
1060 buffer += sprint_varx(parm, parm_value, 0, indent, loc_col1_width)
1061
1062 # Setting global program_pid.
1063 BuiltIn().set_global_variable("${program_pid}", os.getpid())
1064
Michael Walshbec416d2016-11-10 08:54:52 -06001065 if linefeed:
1066 buffer += "\n"
Michael Walshde791732016-09-06 14:25:24 -05001067
1068 return buffer
1069
Michael Walshde791732016-09-06 14:25:24 -05001070
Michael Walsh7423c012016-10-04 10:27:21 -05001071def sprint_error_report(error_text="\n",
Michael Walshdb6e68a2017-05-23 17:55:31 -05001072 indent=2,
1073 format=None):
Michael Walsh7423c012016-10-04 10:27:21 -05001074
1075 r"""
1076 Return a string with a standardized report which includes the caller's
1077 error text, the call stack and the program header.
1078
1079 Description of args:
1080 error_text The error text to be included in the
1081 report. The caller should include any
1082 needed linefeeds.
1083 indent The number of characters to indent each
1084 line of output.
Michael Walshdb6e68a2017-05-23 17:55:31 -05001085 format Long or short format. Long includes
1086 extras like lines of dashes, call stack,
1087 etc.
Michael Walsh7423c012016-10-04 10:27:21 -05001088 """
1089
Michael Walshdb6e68a2017-05-23 17:55:31 -05001090 # Process input.
1091 indent = int(indent)
1092 if format is None:
1093 if robot_env:
1094 format = 'short'
1095 else:
1096 format = 'long'
1097 error_text = error_text.rstrip('\n') + '\n'
1098
1099 if format == 'short':
1100 return sprint_error(error_text)
1101
Michael Walsh7423c012016-10-04 10:27:21 -05001102 buffer = ""
1103 buffer += sprint_dashes(width=120, char="=")
1104 buffer += sprint_error(error_text)
1105 buffer += "\n"
1106 # Calling sprint_call_stack with stack_frame_ix of 0 causes it to show
1107 # itself and this function in the call stack. This is not helpful to a
1108 # debugger and is therefore clutter. We will adjust the stack_frame_ix to
1109 # hide that information.
Michael Walsh9c75f672017-09-12 17:11:35 -05001110 stack_frame_ix = 1
Michael Walsh7423c012016-10-04 10:27:21 -05001111 caller_func_name = sprint_func_name(2)
1112 if caller_func_name.endswith("print_error_report"):
1113 stack_frame_ix += 1
Michael Walshdb6e68a2017-05-23 17:55:31 -05001114 if not robot_env:
1115 buffer += sprint_call_stack(indent, stack_frame_ix)
Michael Walsh7423c012016-10-04 10:27:21 -05001116 buffer += sprint_pgm_header(indent)
1117 buffer += sprint_dashes(width=120, char="=")
1118
1119 return buffer
1120
Michael Walsh7423c012016-10-04 10:27:21 -05001121
Michael Walsh18176322016-11-15 15:11:21 -06001122def sprint_issuing(cmd_buf,
1123 test_mode=0):
Michael Walshde791732016-09-06 14:25:24 -05001124
1125 r"""
1126 Return a line indicating a command that the program is about to execute.
1127
1128 Sample output for a cmd_buf of "ls"
1129
1130 #(CDT) 2016/08/25 17:57:36 - Issuing: ls
Michael Walshbec416d2016-11-10 08:54:52 -06001131
Michael Walshde791732016-09-06 14:25:24 -05001132 Description of args:
1133 cmd_buf The command to be executed by caller.
Michael Walshbec416d2016-11-10 08:54:52 -06001134 test_mode With test_mode set, your output will look
1135 like this:
1136
1137 #(CDT) 2016/08/25 17:57:36 - (test_mode) Issuing: ls
1138
Michael Walshde791732016-09-06 14:25:24 -05001139 """
1140
Michael Walshbec416d2016-11-10 08:54:52 -06001141 buffer = sprint_time()
1142 if test_mode:
1143 buffer += "(test_mode) "
1144 buffer += "Issuing: " + cmd_buf + "\n"
Michael Walshde791732016-09-06 14:25:24 -05001145
1146 return buffer
1147
Michael Walshde791732016-09-06 14:25:24 -05001148
Michael Walshde791732016-09-06 14:25:24 -05001149def sprint_pgm_footer():
1150
1151 r"""
1152 Return a standardized footer that programs should print at the end of the
1153 program run. It includes useful information like total run time, etc.
1154 """
1155
1156 buffer = "\n" + sprint_time() + "Finished running " + pgm_name + ".\n\n"
1157
1158 total_time = time.time() - start_time
1159 total_time_string = "%0.6f" % total_time
1160
Michael Walsh7423c012016-10-04 10:27:21 -05001161 buffer += sprint_varx(pgm_name_var_name + "_runtime", total_time_string)
Michael Walshbec416d2016-11-10 08:54:52 -06001162 buffer += "\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001163
1164 return buffer
1165
Michael Walsh7423c012016-10-04 10:27:21 -05001166
Michael Walsh7423c012016-10-04 10:27:21 -05001167def sprint(buffer=""):
1168
1169 r"""
1170 Simply return the user's buffer. This function is used by the qprint and
1171 dprint functions defined dynamically below, i.e. it would not normally be
1172 called for general use.
1173
1174 Description of arguments.
1175 buffer This will be returned to the caller.
1176 """
Michael Walshde791732016-09-06 14:25:24 -05001177
Michael Walsh95e45102018-02-09 12:44:43 -06001178 try:
1179 return str(buffer)
1180 except UnicodeEncodeError:
1181 return buffer
Michael Walshbec416d2016-11-10 08:54:52 -06001182
Michael Walshbec416d2016-11-10 08:54:52 -06001183
Michael Walshbec416d2016-11-10 08:54:52 -06001184def sprintn(buffer=""):
1185
1186 r"""
1187 Simply return the user's buffer with a line feed. This function is used
1188 by the qprint and dprint functions defined dynamically below, i.e. it
1189 would not normally be called for general use.
1190
1191 Description of arguments.
1192 buffer This will be returned to the caller.
1193 """
1194
Michael Walsh95e45102018-02-09 12:44:43 -06001195 try:
1196 buffer = str(buffer) + "\n"
1197 except UnicodeEncodeError:
1198 buffer = buffer + "\n"
Michael Walshbec416d2016-11-10 08:54:52 -06001199
Michael Walshde791732016-09-06 14:25:24 -05001200 return buffer
1201
Michael Walsh168eb0f2017-12-01 15:35:32 -06001202
Michael Walshfd2733c2017-11-13 11:36:20 -06001203def gp_print(buffer,
1204 stream='stdout'):
1205
1206 r"""
1207 Print the buffer using either sys.stdout.write or BuiltIn().log_to_console
1208 depending on whether we are running in a robot environment.
1209
1210 This function is intended for use only by other functions in this module.
1211
1212 Description of arguments:
1213 buffer The string to be printed.
1214 stream Either "stdout" or "stderr".
1215 """
1216
1217 if robot_env:
1218 BuiltIn().log_to_console(buffer, stream=stream, no_newline=True)
1219 else:
1220 if stream == "stdout":
1221 sys.stdout.write(buffer)
1222 sys.stdout.flush()
1223 else:
1224 sys.stderr.write(buffer)
1225 sys.stderr.flush()
Michael Walshde791732016-09-06 14:25:24 -05001226
1227
Michael Walsh168eb0f2017-12-01 15:35:32 -06001228def gp_log(buffer):
1229
1230 r"""
1231 Log the buffer using either python logging or BuiltIn().log depending on
1232 whether we are running in a robot environment.
1233
1234 This function is intended for use only by other functions in this module.
1235
1236 Description of arguments:
1237 buffer The string to be logged.
1238 """
1239
1240 if robot_env:
1241 BuiltIn().log(buffer)
1242 else:
1243 logging.warning(buffer)
1244
1245
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001246def gp_debug_print(buffer):
1247
1248 r"""
Michael Walshfd2733c2017-11-13 11:36:20 -06001249 Print with gp_print only if gen_print_debug is set.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001250
1251 This function is intended for use only by other functions in this module.
1252
1253 Description of arguments:
1254 buffer The string to be printed.
1255 """
1256
1257 if not gen_print_debug:
1258 return
1259
Michael Walshfd2733c2017-11-13 11:36:20 -06001260 gp_print(buffer)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001261
1262
Michael Walshb1500152017-04-12 15:42:43 -05001263def get_var_value(var_value=None,
1264 default=1,
1265 var_name=None):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001266
1267 r"""
Michael Walshb1500152017-04-12 15:42:43 -05001268 Return either var_value, the corresponding global value or default.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001269
Michael Walshb1500152017-04-12 15:42:43 -05001270 If var_value is not None, it will simply be returned.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001271
Michael Walshb1500152017-04-12 15:42:43 -05001272 If var_value is None, this function will return the corresponding global
1273 value of the variable in question.
1274
1275 Note: For global values, if we are in a robot environment,
1276 get_variable_value will be used. Otherwise, the __builtin__ version of
1277 the variable is returned (which are set by gen_arg.py functions).
1278
1279 If there is no global value associated with the variable, default is
1280 returned.
1281
1282 This function is useful for other functions in setting default values for
1283 parameters.
1284
1285 Example use:
1286
1287 def my_func(quiet=None):
1288
1289 quiet = int(get_var_value(quiet, 0))
1290
1291 Example calls to my_func():
1292
1293 In the following example, the caller is explicitly asking to have quiet be
1294 set to 1.
1295
1296 my_func(quiet=1)
1297
1298 In the following example, quiet will be set to the global value of quiet,
1299 if defined, or to 0 (the default).
1300
1301 my_func()
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001302
1303 Description of arguments:
Michael Walshb1500152017-04-12 15:42:43 -05001304 var_value The value to be returned (if not equal to
1305 None).
1306 default The value that is returned if var_value is
1307 None and there is no corresponding global
1308 value defined.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001309 var_name The name of the variable whose value is to
Michael Walshb1500152017-04-12 15:42:43 -05001310 be returned. Under most circumstances,
1311 this value need not be provided. This
1312 function can figure out the name of the
1313 variable passed as var_value. One
1314 exception to this would be if this
1315 function is called directly from a .robot
1316 file.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001317 """
1318
Michael Walshb1500152017-04-12 15:42:43 -05001319 if var_value is not None:
1320 return var_value
1321
1322 if var_name is None:
1323 var_name = get_arg_name(None, 1, 2)
1324
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001325 if robot_env:
Michael Walshc6537442017-06-06 15:33:52 -05001326 var_value = BuiltIn().get_variable_value("${" + var_name + "}",
1327 default)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001328 else:
1329 var_value = getattr(__builtin__, var_name, default)
1330
1331 return var_value
1332
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001333
Michael Walsh82acf002017-05-04 14:33:05 -05001334# hidden_text is a list of passwords which are to be replaced with asterisks
1335# by print functions defined in this module.
1336hidden_text = []
1337# password_regex is created based on the contents of hidden_text.
1338password_regex = ""
1339
1340
Michael Walsh82acf002017-05-04 14:33:05 -05001341def register_passwords(*args):
1342
1343 r"""
1344 Register one or more passwords which are to be hidden in output produced
1345 by the print functions in this module.
1346
1347 Note: Blank password values are NOT registered. They are simply ignored.
1348
1349 Description of argument(s):
1350 args One or more password values. If a given
1351 password value is already registered, this
1352 function will simply do nothing.
1353 """
1354
1355 global hidden_text
1356 global password_regex
1357
1358 for password in args:
1359 if password == "":
1360 break
1361 if password in hidden_text:
1362 break
1363
1364 # Place the password into the hidden_text list.
1365 hidden_text.append(password)
1366 # Create a corresponding password regular expression. Escape regex
1367 # special characters too.
1368 password_regex = '(' +\
1369 '|'.join([re.escape(x) for x in hidden_text]) + ')'
1370
Michael Walsh82acf002017-05-04 14:33:05 -05001371
Michael Walsh82acf002017-05-04 14:33:05 -05001372def replace_passwords(buffer):
1373
1374 r"""
1375 Return the buffer but with all registered passwords replaced by a string
1376 of asterisks.
1377
1378
1379 Description of argument(s):
1380 buffer The string to be returned but with
1381 passwords replaced.
1382 """
1383
1384 global password_regex
1385
1386 if int(os.environ.get("DEBUG_SHOW_PASSWORDS", "0")):
1387 return buffer
1388
1389 if password_regex == "":
1390 # No passwords to replace.
1391 return buffer
1392
1393 return re.sub(password_regex, "********", buffer)
1394
Michael Walshfd2733c2017-11-13 11:36:20 -06001395
1396def create_print_wrapper_funcs(func_names,
1397 stderr_func_names,
1398 replace_dict):
1399
1400 r"""
1401 Generate code for print wrapper functions and return the generated code as
1402 a string.
1403
1404 To illustrate, suppose there is a "print_foo_bar" function in the
1405 func_names list.
1406 This function will...
1407 - Expect that there is an sprint_foo_bar function already in existence.
1408 - Create a print_foo_bar function which calls sprint_foo_bar and prints
1409 the result.
1410 - Create a qprint_foo_bar function which calls upon sprint_foo_bar only if
1411 global value quiet is 0.
1412 - Create a dprint_foo_bar function which calls upon sprint_foo_bar only if
1413 global value debug is 1.
1414
1415 Also, code will be generated to define aliases for each function as well.
1416 Each alias will be created by replacing "print_" in the function name with
1417 "p" For example, the alias for print_foo_bar will be pfoo_bar.
1418
1419 Description of argument(s):
1420 func_names A list of functions for which print
1421 wrapper function code is to be generated.
1422 stderr_func_names A list of functions whose generated code
1423 should print to stderr rather than to
1424 stdout.
1425 replace_dict Please see the create_func_def_string
1426 function in wrap_utils.py for details on
1427 this parameter. This parameter will be
1428 passed directly to create_func_def_string.
1429 """
1430
1431 buffer = ""
1432
1433 for func_name in func_names:
1434 if func_name in stderr_func_names:
1435 replace_dict['output_stream'] = "stderr"
1436 else:
1437 replace_dict['output_stream'] = "stdout"
1438
1439 s_func_name = "s" + func_name
1440 q_func_name = "q" + func_name
1441 d_func_name = "d" + func_name
1442
1443 # We don't want to try to redefine the "print" function, thus the
1444 # following if statement.
1445 if func_name != "print":
1446 func_def = create_func_def_string(s_func_name, func_name,
1447 print_func_template,
1448 replace_dict)
1449 buffer += func_def
1450
1451 func_def = create_func_def_string(s_func_name, "q" + func_name,
1452 qprint_func_template, replace_dict)
1453 buffer += func_def
1454
1455 func_def = create_func_def_string(s_func_name, "d" + func_name,
1456 dprint_func_template, replace_dict)
1457 buffer += func_def
1458
Michael Walsh168eb0f2017-12-01 15:35:32 -06001459 func_def = create_func_def_string(s_func_name, "l" + func_name,
1460 lprint_func_template, replace_dict)
1461 buffer += func_def
1462
Michael Walshfd2733c2017-11-13 11:36:20 -06001463 # Create abbreviated aliases (e.g. spvar is an alias for sprint_var).
1464 alias = re.sub("print_", "p", func_name)
1465 alias = re.sub("print", "p", alias)
Michael Walsh168eb0f2017-12-01 15:35:32 -06001466 prefixes = ["", "s", "q", "d", "l"]
Michael Walshfd2733c2017-11-13 11:36:20 -06001467 for prefix in prefixes:
1468 if alias == "p":
1469 continue
1470 func_def = prefix + alias + " = " + prefix + func_name
1471 buffer += func_def + "\n"
1472
1473 return buffer
Michael Walsh82acf002017-05-04 14:33:05 -05001474
1475
Michael Walshde791732016-09-06 14:25:24 -05001476# In the following section of code, we will dynamically create print versions
1477# for each of the sprint functions defined above. So, for example, where we
1478# have an sprint_time() function defined above that returns the time to the
Michael Walsh7423c012016-10-04 10:27:21 -05001479# caller in a string, we will create a corresponding print_time() function
1480# that will print that string directly to stdout.
Michael Walshde791732016-09-06 14:25:24 -05001481
Michael Walshfd2733c2017-11-13 11:36:20 -06001482# It can be complicated to follow what's being created by below. Here is an
1483# example of the print_time() function that will be created:
Michael Walshde791732016-09-06 14:25:24 -05001484
Michael Walshfd2733c2017-11-13 11:36:20 -06001485# def print_time(buffer=''):
1486# sys.stdout.write(replace_passwords(sprint_time(buffer=buffer)))
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001487# sys.stdout.flush()
Michael Walshde791732016-09-06 14:25:24 -05001488
Michael Walshfd2733c2017-11-13 11:36:20 -06001489# Templates for the various print wrapper functions.
1490print_func_template = \
1491 [
Michael Walsh81c02342018-01-05 15:43:28 -06001492 " <mod_qualifier>gp_print(<mod_qualifier>replace_passwords(" +
1493 "<call_line>), stream='<output_stream>')"
Michael Walshfd2733c2017-11-13 11:36:20 -06001494 ]
1495
1496qprint_func_template = \
1497 [
Michael Walsh81c02342018-01-05 15:43:28 -06001498 " if int(<mod_qualifier>get_var_value(None, 0, \"quiet\")): return"
Michael Walshfd2733c2017-11-13 11:36:20 -06001499 ] + print_func_template
1500
1501dprint_func_template = \
1502 [
Michael Walsh81c02342018-01-05 15:43:28 -06001503 " if not int(<mod_qualifier>get_var_value(None, 0, \"debug\")):" +
Michael Walshfd2733c2017-11-13 11:36:20 -06001504 " return"
1505 ] + print_func_template
1506
Michael Walsh168eb0f2017-12-01 15:35:32 -06001507lprint_func_template = \
1508 [
Michael Walsh81c02342018-01-05 15:43:28 -06001509 " gp_log(<mod_qualifier>replace_passwords(<call_line>))"
Michael Walsh168eb0f2017-12-01 15:35:32 -06001510 ]
1511
Michael Walsh81c02342018-01-05 15:43:28 -06001512replace_dict = {'output_stream': 'stdout', 'mod_qualifier': ''}
Michael Walshfd2733c2017-11-13 11:36:20 -06001513
1514
1515gp_debug_print("robot_env: " + str(robot_env))
Michael Walshde791732016-09-06 14:25:24 -05001516
1517# func_names contains a list of all print functions which should be created
1518# from their sprint counterparts.
1519func_names = ['print_time', 'print_timen', 'print_error', 'print_varx',
Michael Walsh18176322016-11-15 15:11:21 -06001520 'print_var', 'print_vars', 'print_dashes', 'indent',
1521 'print_call_stack', 'print_func_name', 'print_executing',
1522 'print_pgm_header', 'print_issuing', 'print_pgm_footer',
1523 'print_error_report', 'print', 'printn']
Michael Walshde791732016-09-06 14:25:24 -05001524
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001525# stderr_func_names is a list of functions whose output should go to stderr
1526# rather than stdout.
1527stderr_func_names = ['print_error', 'print_error_report']
Michael Walshde791732016-09-06 14:25:24 -05001528
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001529
Michael Walshfd2733c2017-11-13 11:36:20 -06001530func_defs = create_print_wrapper_funcs(func_names, stderr_func_names,
1531 replace_dict)
1532gp_debug_print(func_defs)
1533exec(func_defs)