blob: ab7fdc63983990e4e960fbb04cb2bad8e7a38814 [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):
Michael Walshde791732016-09-06 14:25:24 -050081 r"""
82 Return the function name associated with the indicated stack frame.
83
84 Description of arguments:
85 stack_frame_ix The index of the stack frame whose
86 function name should be returned. If the
Michael Walsh2ee77cd2017-03-08 11:50:17 -060087 caller does not specify a value, this
Michael Walshde791732016-09-06 14:25:24 -050088 function will set the value to 1 which is
89 the index of the caller's stack frame. If
90 the caller is the wrapper function
91 "print_func_name", this function will bump
92 it up by 1.
93 """
94
95 # If user specified no stack_frame_ix, we'll set it to a proper default
96 # value.
97 if stack_frame_ix is None:
98 func_name = sys._getframe().f_code.co_name
99 caller_func_name = sys._getframe(1).f_code.co_name
100 if func_name[1:] == caller_func_name:
101 stack_frame_ix = 2
102 else:
103 stack_frame_ix = 1
104
105 func_name = sys._getframe(stack_frame_ix).f_code.co_name
106
107 return func_name
108
Michael Walshde791732016-09-06 14:25:24 -0500109
110# get_arg_name is not a print function per se. I have included it in this
111# module because it is used by sprint_var which is found in this module.
Michael Walshde791732016-09-06 14:25:24 -0500112def get_arg_name(var,
113 arg_num=1,
114 stack_frame_ix=1):
Michael Walshde791732016-09-06 14:25:24 -0500115 r"""
116 Return the "name" of an argument passed to a function. This could be a
117 literal or a variable name.
118
Michael Walsh2ee77cd2017-03-08 11:50:17 -0600119 Description of arguments:
Michael Walshde791732016-09-06 14:25:24 -0500120 var The variable whose name you want returned.
121 arg_num The arg number (1 through n) whose name
122 you wish to have returned. This value
123 should not exceed the number of arguments
124 allowed by the target function.
125 stack_frame_ix The stack frame index of the target
126 function. This value must be 1 or
127 greater. 1 would indicate get_arg_name's
128 stack frame. 2 would be the caller of
129 get_arg_name's stack frame, etc.
130
131 Example 1:
132
133 my_var = "mike"
134 var_name = get_arg_name(my_var)
135
136 In this example, var_name will receive the value "my_var".
137
138 Example 2:
139
140 def test1(var):
141 # Getting the var name of the first arg to this function, test1.
142 # Note, in this case, it doesn't matter what you pass as the first arg
143 # to get_arg_name since it is the caller's variable name that matters.
144 dummy = 1
145 arg_num = 1
146 stack_frame = 2
147 var_name = get_arg_name(dummy, arg_num, stack_frame)
148
149 # Mainline...
150
151 another_var = "whatever"
152 test1(another_var)
153
154 In this example, var_name will be set to "another_var".
155
156 """
157
158 # Note: I wish to avoid recursion so I refrain from calling any function
159 # that calls this function (i.e. sprint_var, valid_value, etc.).
160
Michael Walsh23e7f492017-01-10 11:34:47 -0600161 # The user can set environment variable "GET_ARG_NAME_DEBUG" to get debug
162 # output from this function.
163 local_debug = int(os.environ.get('GET_ARG_NAME_DEBUG', 0))
164 # In addition to GET_ARG_NAME_DEBUG, the user can set environment
165 # variable "GET_ARG_NAME_SHOW_SOURCE" to have this function include source
166 # code in the debug output.
167 local_debug_show_source = int(
168 os.environ.get('GET_ARG_NAME_SHOW_SOURCE', 0))
Michael Walshde791732016-09-06 14:25:24 -0500169
170 if arg_num < 1:
171 print_error("Programmer error - Variable \"arg_num\" has an invalid" +
172 " value of \"" + str(arg_num) + "\". The value must be" +
173 " an integer that is greater than 0.\n")
174 # What is the best way to handle errors? Raise exception? I'll
175 # revisit later.
176 return
177 if stack_frame_ix < 1:
178 print_error("Programmer error - Variable \"stack_frame_ix\" has an" +
179 " invalid value of \"" + str(stack_frame_ix) + "\". The" +
180 " value must be an integer that is greater than or equal" +
181 " to 1.\n")
182 return
183
184 if local_debug:
185 debug_indent = 2
Michael Walsh23e7f492017-01-10 11:34:47 -0600186 print("")
187 print_dashes(0, 120)
Michael Walshde791732016-09-06 14:25:24 -0500188 print(sprint_func_name() + "() parms:")
189 print_varx("var", var, 0, debug_indent)
190 print_varx("arg_num", arg_num, 0, debug_indent)
191 print_varx("stack_frame_ix", stack_frame_ix, 0, debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600192 print("")
193 print_call_stack(debug_indent, 2)
Michael Walshde791732016-09-06 14:25:24 -0500194
Michael Walsh23e7f492017-01-10 11:34:47 -0600195 for count in range(0, 2):
196 try:
197 frame, filename, cur_line_no, function_name, lines, index = \
198 inspect.stack()[stack_frame_ix]
199 except IndexError:
200 print_error("Programmer error - The caller has asked for" +
201 " information about the stack frame at index \"" +
202 str(stack_frame_ix) + "\". However, the stack" +
203 " only contains " + str(len(inspect.stack())) +
204 " entries. Therefore the stack frame index is out" +
205 " of range.\n")
206 return
207 if filename != "<string>":
208 break
209 # filename of "<string>" may mean that the function in question was
210 # defined dynamically and therefore its code stack is inaccessible.
211 # This may happen with functions like "rqprint_var". In this case,
212 # we'll increment the stack_frame_ix and try again.
213 stack_frame_ix += 1
214 if local_debug:
215 print("Adjusted stack_frame_ix...")
216 print_varx("stack_frame_ix", stack_frame_ix, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500217
218 called_func_name = sprint_func_name(stack_frame_ix)
Michael Walsh23e7f492017-01-10 11:34:47 -0600219
220 module = inspect.getmodule(frame)
221
222 # Though I would expect inspect.getsourcelines(frame) to get all module
223 # source lines if the frame is "<module>", it doesn't do that. Therefore,
224 # for this special case, I will do inspect.getsourcelines(module).
225 if function_name == "<module>":
226 source_lines, source_line_num =\
227 inspect.getsourcelines(module)
228 line_ix = cur_line_no - source_line_num - 1
229 else:
230 source_lines, source_line_num =\
231 inspect.getsourcelines(frame)
232 line_ix = cur_line_no - source_line_num
233
234 if local_debug:
235 print("\n Variables retrieved from inspect.stack() function:")
236 print_varx("frame", frame, 0, debug_indent + 2)
237 print_varx("filename", filename, 0, debug_indent + 2)
238 print_varx("cur_line_no", cur_line_no, 0, debug_indent + 2)
239 print_varx("function_name", function_name, 0, debug_indent + 2)
240 print_varx("lines", lines, 0, debug_indent + 2)
241 print_varx("index", index, 0, debug_indent + 2)
242 print_varx("source_line_num", source_line_num, 0, debug_indent)
243 print_varx("line_ix", line_ix, 0, debug_indent)
244 if local_debug_show_source:
245 print_varx("source_lines", source_lines, 0, debug_indent)
246 print_varx("called_func_name", called_func_name, 0, debug_indent)
247
248 # Get a list of all functions defined for the module. Note that this
249 # doesn't work consistently when _run_exitfuncs is at the top of the stack
250 # (i.e. if we're running an exit function). I've coded a work-around
251 # below for this deficiency.
252 all_functions = inspect.getmembers(module, inspect.isfunction)
253
254 # Get called_func_id by searching for our function in the list of all
255 # functions.
256 called_func_id = None
257 for func_name, function in all_functions:
258 if func_name == called_func_name:
259 called_func_id = id(function)
260 break
261 # NOTE: The only time I've found that called_func_id can't be found is
262 # when we're running from an exit function.
263
264 # Look for other functions in module with matching id.
265 aliases = set([called_func_name])
266 for func_name, function in all_functions:
267 if func_name == called_func_name:
268 continue
269 func_id = id(function)
270 if func_id == called_func_id:
271 aliases.add(func_name)
272
273 # In most cases, my general purpose code above will find all aliases.
274 # However, for the odd case (i.e. running from exit function), I've added
275 # code to handle pvar, qpvar, dpvar, etc. aliases explicitly since they
276 # are defined in this module and used frequently.
277 # pvar is an alias for print_var.
278 aliases.add(re.sub("print_var", "pvar", called_func_name))
279
280 func_regex = ".*(" + '|'.join(aliases) + ")[ ]*\("
281
282 # Search backward through source lines looking for the calling function
283 # name.
284 found = False
285 for start_line_ix in range(line_ix, 0, -1):
286 # Skip comment lines.
287 if re.match(r"[ ]*#", source_lines[start_line_ix]):
288 continue
289 if re.match(func_regex, source_lines[start_line_ix]):
290 found = True
291 break
292 if not found:
293 print_error("Programmer error - Could not find the source line with" +
294 " a reference to function \"" + called_func_name + "\".\n")
295 return
296
Michael Walsh82acf002017-05-04 14:33:05 -0500297 # Search forward through the source lines looking for a line whose
298 # indentation is the same or less than the start line. The end of our
299 # composite line should be the line preceding that line.
Michael Walsh23e7f492017-01-10 11:34:47 -0600300 start_indent = len(source_lines[start_line_ix]) -\
301 len(source_lines[start_line_ix].lstrip(' '))
302 end_line_ix = line_ix
303 for end_line_ix in range(line_ix + 1, len(source_lines)):
304 if source_lines[end_line_ix].strip() == "":
305 continue
306 line_indent = len(source_lines[end_line_ix]) -\
307 len(source_lines[end_line_ix].lstrip(' '))
Michael Walsh82acf002017-05-04 14:33:05 -0500308 if line_indent <= start_indent:
Michael Walsh23e7f492017-01-10 11:34:47 -0600309 end_line_ix -= 1
310 break
311
312 # Join the start line through the end line into a composite line.
313 composite_line = ''.join(map(str.strip,
Gunnar Mills096cd562018-03-26 10:19:12 -0500314 source_lines[start_line_ix:end_line_ix + 1]))
Michael Walsh7423c012016-10-04 10:27:21 -0500315
Michael Walshbec416d2016-11-10 08:54:52 -0600316 # arg_list_etc = re.sub(".*" + called_func_name, "", composite_line)
317 arg_list_etc = "(" + re.sub(func_regex, "", composite_line)
Michael Walshde791732016-09-06 14:25:24 -0500318 if local_debug:
Michael Walsh23e7f492017-01-10 11:34:47 -0600319 print_varx("aliases", aliases, 0, debug_indent)
Michael Walshbec416d2016-11-10 08:54:52 -0600320 print_varx("func_regex", func_regex, 0, debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600321 print_varx("start_line_ix", start_line_ix, 0, debug_indent)
322 print_varx("end_line_ix", end_line_ix, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500323 print_varx("composite_line", composite_line, 0, debug_indent)
324 print_varx("arg_list_etc", arg_list_etc, 0, debug_indent)
325
326 # Parse arg list...
327 # Initialize...
328 nest_level = -1
329 arg_ix = 0
Michael Walsh7423c012016-10-04 10:27:21 -0500330 args_list = [""]
Michael Walshde791732016-09-06 14:25:24 -0500331 for ix in range(0, len(arg_list_etc)):
332 char = arg_list_etc[ix]
333 # Set the nest_level based on whether we've encounted a parenthesis.
334 if char == "(":
335 nest_level += 1
336 if nest_level == 0:
337 continue
338 elif char == ")":
339 nest_level -= 1
340 if nest_level < 0:
341 break
342
343 # If we reach a comma at base nest level, we are done processing an
Michael Walsh7423c012016-10-04 10:27:21 -0500344 # argument so we increment arg_ix and initialize a new args_list entry.
Michael Walshde791732016-09-06 14:25:24 -0500345 if char == "," and nest_level == 0:
346 arg_ix += 1
Michael Walsh7423c012016-10-04 10:27:21 -0500347 args_list.append("")
Michael Walshde791732016-09-06 14:25:24 -0500348 continue
349
Michael Walsh7423c012016-10-04 10:27:21 -0500350 # For any other character, we append it it to the current arg list
Michael Walshde791732016-09-06 14:25:24 -0500351 # entry.
Michael Walsh7423c012016-10-04 10:27:21 -0500352 args_list[arg_ix] += char
Michael Walshde791732016-09-06 14:25:24 -0500353
354 # Trim whitespace from each list entry.
Michael Walsh7423c012016-10-04 10:27:21 -0500355 args_list = [arg.strip() for arg in args_list]
Michael Walshde791732016-09-06 14:25:24 -0500356
Michael Walsh7423c012016-10-04 10:27:21 -0500357 if arg_num > len(args_list):
Michael Walshde791732016-09-06 14:25:24 -0500358 print_error("Programmer error - The caller has asked for the name of" +
359 " argument number \"" + str(arg_num) + "\" but there " +
Michael Walsh7423c012016-10-04 10:27:21 -0500360 "were only \"" + str(len(args_list)) + "\" args used:\n" +
361 sprint_varx("args_list", args_list))
Michael Walshde791732016-09-06 14:25:24 -0500362 return
363
Michael Walsh7423c012016-10-04 10:27:21 -0500364 argument = args_list[arg_num - 1]
Michael Walshde791732016-09-06 14:25:24 -0500365
366 if local_debug:
Michael Walsh7423c012016-10-04 10:27:21 -0500367 print_varx("args_list", args_list, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500368 print_varx("argument", argument, 0, debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600369 print_dashes(0, 120)
Michael Walshde791732016-09-06 14:25:24 -0500370
371 return argument
372
Michael Walshde791732016-09-06 14:25:24 -0500373
Michael Walshde791732016-09-06 14:25:24 -0500374def sprint_time(buffer=""):
Michael Walshde791732016-09-06 14:25:24 -0500375 r"""
376 Return the time in the following format.
377
378 Example:
379
380 The following python code...
381
382 sys.stdout.write(sprint_time())
383 sys.stdout.write("Hi.\n")
384
385 Will result in the following type of output:
386
387 #(CDT) 2016/07/08 15:25:35 - Hi.
388
389 Example:
390
391 The following python code...
392
393 sys.stdout.write(sprint_time("Hi.\n"))
394
395 Will result in the following type of output:
396
397 #(CDT) 2016/08/03 17:12:05 - Hi.
398
399 The following environment variables will affect the formatting as
400 described:
401 NANOSECONDS This will cause the time stamps to be
402 precise to the microsecond (Yes, it
403 probably should have been named
404 MICROSECONDS but the convention was set
405 long ago so we're sticking with it).
406 Example of the output when environment
407 variable NANOSECONDS=1.
408
409 #(CDT) 2016/08/03 17:16:25.510469 - Hi.
410
411 SHOW_ELAPSED_TIME This will cause the elapsed time to be
412 included in the output. This is the
413 amount of time that has elapsed since the
414 last time this function was called. The
415 precision of the elapsed time field is
416 also affected by the value of the
417 NANOSECONDS environment variable. Example
418 of the output when environment variable
419 NANOSECONDS=0 and SHOW_ELAPSED_TIME=1.
420
421 #(CDT) 2016/08/03 17:17:40 - 0 - Hi.
422
423 Example of the output when environment variable NANOSECONDS=1 and
424 SHOW_ELAPSED_TIME=1.
425
426 #(CDT) 2016/08/03 17:18:47.317339 - 0.000046 - Hi.
427
428 Description of arguments.
429 buffer This will be appended to the formatted
430 time string.
431 """
432
433 global NANOSECONDS
434 global SHOW_ELAPSED_TIME
435 global sprint_time_last_seconds
436
437 seconds = time.time()
438 loc_time = time.localtime(seconds)
439 nanoseconds = "%0.6f" % seconds
440 pos = nanoseconds.find(".")
441 nanoseconds = nanoseconds[pos:]
442
443 time_string = time.strftime("#(%Z) %Y/%m/%d %H:%M:%S", loc_time)
444 if NANOSECONDS == "1":
445 time_string = time_string + nanoseconds
446
447 if SHOW_ELAPSED_TIME == "1":
448 cur_time_seconds = seconds
449 math_string = "%9.9f" % cur_time_seconds + " - " + "%9.9f" % \
450 sprint_time_last_seconds
451 elapsed_seconds = eval(math_string)
452 if NANOSECONDS == "1":
453 elapsed_seconds = "%11.6f" % elapsed_seconds
454 else:
455 elapsed_seconds = "%4i" % elapsed_seconds
456 sprint_time_last_seconds = cur_time_seconds
457 time_string = time_string + " - " + elapsed_seconds
458
459 return time_string + " - " + buffer
460
Michael Walshde791732016-09-06 14:25:24 -0500461
Michael Walshde791732016-09-06 14:25:24 -0500462def sprint_timen(buffer=""):
Michael Walshde791732016-09-06 14:25:24 -0500463 r"""
464 Append a line feed to the buffer, pass it to sprint_time and return the
465 result.
466 """
467
468 return sprint_time(buffer + "\n")
469
Michael Walshde791732016-09-06 14:25:24 -0500470
Michael Walshde791732016-09-06 14:25:24 -0500471def sprint_error(buffer=""):
Michael Walshde791732016-09-06 14:25:24 -0500472 r"""
473 Return a standardized error string. This includes:
474 - A time stamp
475 - The "**ERROR**" string
476 - The caller's buffer string.
477
478 Example:
479
480 The following python code...
481
482 print(sprint_error("Oops.\n"))
483
484 Will result in the following type of output:
485
486 #(CDT) 2016/08/03 17:12:05 - **ERROR** Oops.
487
488 Description of arguments.
489 buffer This will be appended to the formatted
490 error string.
491 """
492
493 return sprint_time() + "**ERROR** " + buffer
494
Michael Walshde791732016-09-06 14:25:24 -0500495
Michael Walshde791732016-09-06 14:25:24 -0500496def sprint_varx(var_name,
497 var_value,
498 hex=0,
499 loc_col1_indent=col1_indent,
Michael Walsh7423c012016-10-04 10:27:21 -0500500 loc_col1_width=col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500501 trailing_char="\n",
502 key_list=None):
Michael Walshde791732016-09-06 14:25:24 -0500503 r"""
504 Print the var name/value passed to it. If the caller lets loc_col1_width
505 default, the printing lines up nicely with output generated by the
506 print_time functions.
507
508 Note that the sprint_var function (defined below) can be used to call this
509 function so that the programmer does not need to pass the var_name.
510 sprint_var will figure out the var_name. The sprint_var function is the
511 one that would normally be used by the general user.
512
513 For example, the following python code:
514
515 first_name = "Mike"
516 print_time("Doing this...\n")
517 print_varx("first_name", first_name)
518 print_time("Doing that...\n")
519
520 Will generate output like this:
521
522 #(CDT) 2016/08/10 17:34:42.847374 - 0.001285 - Doing this...
523 first_name: Mike
524 #(CDT) 2016/08/10 17:34:42.847510 - 0.000136 - Doing that...
525
526 This function recognizes several complex types of data such as dict, list
527 or tuple.
528
529 For example, the following python code:
530
531 my_dict = dict(one=1, two=2, three=3)
532 print_var(my_dict)
533
534 Will generate the following output:
535
536 my_dict:
537 my_dict[three]: 3
538 my_dict[two]: 2
539 my_dict[one]: 1
540
541 Description of arguments.
542 var_name The name of the variable to be printed.
543 var_value The value of the variable to be printed.
544 hex This indicates that the value should be
545 printed in hex format. It is the user's
546 responsibility to ensure that a var_value
Michael Walshbec416d2016-11-10 08:54:52 -0600547 contains a valid hex number. For string
548 var_values, this will be interpreted as
549 show_blanks which means that blank values
Michael Walshd995cb02017-02-07 14:46:01 -0600550 will be printed as "<blank>". For dict
551 var_values, this will be interpreted as
552 terse format where keys are not repeated
553 in the output.
Michael Walshde791732016-09-06 14:25:24 -0500554 loc_col1_indent The number of spaces to indent the output.
555 loc_col1_width The width of the output column containing
556 the variable name. The default value of
557 this is adjusted so that the var_value
558 lines up with text printed via the
559 print_time function.
Michael Walsh7423c012016-10-04 10:27:21 -0500560 trailing_char The character to be used at the end of the
561 returned string. The default value is a
562 line feed.
Michael Walshd2869032018-03-22 16:12:11 -0500563 key_list A list of which dictionary keys should be
564 printed. All others keys will be skipped.
565 Each value in key_list will be regarded
566 as a regular expression and it will be
567 regarded as anchored to the beginning and
568 ends of the dictionary key being
569 referenced. For example if key_list is
570 ["one", "two"], the resulting regex used
571 will be "^one|two$", i.e. only keys "one"
572 and "two" from the var_value dictionary
573 will be printed. As another example, if
574 the caller were to specify a key_list of
575 ["one.*"], then only dictionary keys whose
576 names begin with "one" will be printed.
577 Note: This argument pertains only to
578 var_values which are dictionaries.
Michael Walsh7423c012016-10-04 10:27:21 -0500579 """
Michael Walshde791732016-09-06 14:25:24 -0500580
581 # Determine the type
582 if type(var_value) in (int, float, bool, str, unicode) \
583 or var_value is None:
584 # The data type is simple in the sense that it has no subordinate
585 # parts.
Michael Walsh7423c012016-10-04 10:27:21 -0500586 # Adjust loc_col1_width.
587 loc_col1_width = loc_col1_width - loc_col1_indent
Michael Walshde791732016-09-06 14:25:24 -0500588 # See if the user wants the output in hex format.
589 if hex:
Michael Walsh18176322016-11-15 15:11:21 -0600590 if type(var_value) not in (int, long):
Michael Walshbec416d2016-11-10 08:54:52 -0600591 value_format = "%s"
Michael Walsh2795edc2016-12-13 16:00:33 -0600592 if var_value == "":
Michael Walshbec416d2016-11-10 08:54:52 -0600593 var_value = "<blank>"
594 else:
595 value_format = "0x%08x"
Michael Walshde791732016-09-06 14:25:24 -0500596 else:
597 value_format = "%s"
598 format_string = "%" + str(loc_col1_indent) + "s%-" \
Michael Walsh7423c012016-10-04 10:27:21 -0500599 + str(loc_col1_width) + "s" + value_format + trailing_char
Michael Walsh3383e652017-09-01 17:10:59 -0500600 if value_format == "0x%08x":
601 return format_string % ("", str(var_name) + ":",
602 var_value & 0xffffffff)
603 else:
604 return format_string % ("", str(var_name) + ":", var_value)
Michael Walsh20a87ab2017-06-30 17:00:30 -0500605 elif type(var_value) is type:
606 return sprint_varx(var_name, str(var_value).split("'")[1], hex,
Michael Walshd2869032018-03-22 16:12:11 -0500607 loc_col1_indent, loc_col1_width, trailing_char,
608 key_list)
Michael Walshde791732016-09-06 14:25:24 -0500609 else:
610 # The data type is complex in the sense that it has subordinate parts.
611 format_string = "%" + str(loc_col1_indent) + "s%s\n"
612 buffer = format_string % ("", var_name + ":")
613 loc_col1_indent += 2
Michael Walsh7423c012016-10-04 10:27:21 -0500614 try:
615 length = len(var_value)
616 except TypeError:
Michael Walsh23e7f492017-01-10 11:34:47 -0600617 length = 0
Michael Walsh7423c012016-10-04 10:27:21 -0500618 ix = 0
619 loc_trailing_char = "\n"
Michael Walshbec416d2016-11-10 08:54:52 -0600620 type_is_dict = 0
Michael Walsh23e7f492017-01-10 11:34:47 -0600621 if type(var_value) is dict:
622 type_is_dict = 1
Michael Walsh8e6deb42017-01-27 14:22:41 -0600623 try:
624 if type(var_value) is collections.OrderedDict:
625 type_is_dict = 1
626 except AttributeError:
627 pass
628 try:
629 if type(var_value) is DotDict:
630 type_is_dict = 1
631 except NameError:
632 pass
633 try:
634 if type(var_value) is NormalizedDict:
635 type_is_dict = 1
636 except NameError:
637 pass
Michael Walshbec416d2016-11-10 08:54:52 -0600638 if type_is_dict:
Michael Walshde791732016-09-06 14:25:24 -0500639 for key, value in var_value.iteritems():
Michael Walshd2869032018-03-22 16:12:11 -0500640 if key_list is not None:
641 key_list_regex = "^" + "|".join(key_list) + "$"
642 if not re.match(key_list_regex, key):
643 continue
Michael Walsh7423c012016-10-04 10:27:21 -0500644 ix += 1
645 if ix == length:
646 loc_trailing_char = trailing_char
Michael Walshd995cb02017-02-07 14:46:01 -0600647 if hex:
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600648 # Since hex is being used as a format type, we want it
649 # turned off when processing integer dictionary values so
650 # it is not interpreted as a hex indicator.
651 loc_hex = not (type(value) is int)
Michael Walshf7b8a002017-08-29 10:38:39 -0500652 buffer += sprint_varx("[" + key + "]", value,
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600653 loc_hex, loc_col1_indent,
654 loc_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500655 loc_trailing_char,
656 key_list)
Michael Walshd995cb02017-02-07 14:46:01 -0600657 else:
658 buffer += sprint_varx(var_name + "[" + key + "]", value,
659 hex, loc_col1_indent, loc_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500660 loc_trailing_char, key_list)
Michael Walsh7423c012016-10-04 10:27:21 -0500661 elif type(var_value) in (list, tuple, set):
Michael Walshde791732016-09-06 14:25:24 -0500662 for key, value in enumerate(var_value):
Michael Walsh7423c012016-10-04 10:27:21 -0500663 ix += 1
664 if ix == length:
665 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500666 buffer += sprint_varx(var_name + "[" + str(key) + "]", value,
Michael Walsh7423c012016-10-04 10:27:21 -0500667 hex, loc_col1_indent, loc_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500668 loc_trailing_char, key_list)
Michael Walshde791732016-09-06 14:25:24 -0500669 elif type(var_value) is argparse.Namespace:
670 for key in var_value.__dict__:
Michael Walsh7423c012016-10-04 10:27:21 -0500671 ix += 1
672 if ix == length:
673 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500674 cmd_buf = "buffer += sprint_varx(var_name + \".\" + str(key)" \
Michael Walsh7423c012016-10-04 10:27:21 -0500675 + ", var_value." + key + ", hex, loc_col1_indent," \
Michael Walshd2869032018-03-22 16:12:11 -0500676 + " loc_col1_width, loc_trailing_char, key_list)"
Michael Walshde791732016-09-06 14:25:24 -0500677 exec(cmd_buf)
678 else:
679 var_type = type(var_value).__name__
680 func_name = sys._getframe().f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -0500681 var_value = "<" + var_type + " type not supported by " + \
682 func_name + "()>"
Michael Walshde791732016-09-06 14:25:24 -0500683 value_format = "%s"
684 loc_col1_indent -= 2
Michael Walsh7423c012016-10-04 10:27:21 -0500685 # Adjust loc_col1_width.
686 loc_col1_width = loc_col1_width - loc_col1_indent
Michael Walshde791732016-09-06 14:25:24 -0500687 format_string = "%" + str(loc_col1_indent) + "s%-" \
Michael Walsh7423c012016-10-04 10:27:21 -0500688 + str(loc_col1_width) + "s" + value_format + trailing_char
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600689 return format_string % ("", str(var_name) + ":", var_value)
Michael Walsh23e7f492017-01-10 11:34:47 -0600690
Michael Walshde791732016-09-06 14:25:24 -0500691 return buffer
692
693 return ""
694
Michael Walshde791732016-09-06 14:25:24 -0500695
Michael Walshfd2733c2017-11-13 11:36:20 -0600696def sprint_var(var_value,
697 hex=0,
698 loc_col1_indent=col1_indent,
699 loc_col1_width=col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500700 trailing_char="\n",
701 key_list=None):
Michael Walshde791732016-09-06 14:25:24 -0500702 r"""
703 Figure out the name of the first argument for you and then call
704 sprint_varx with it. Therefore, the following 2 calls are equivalent:
705 sprint_varx("var1", var1)
706 sprint_var(var1)
707 """
708
709 # Get the name of the first variable passed to this function.
710 stack_frame = 2
Michael Walsh7423c012016-10-04 10:27:21 -0500711 caller_func_name = sprint_func_name(2)
712 if caller_func_name.endswith("print_var"):
Michael Walshde791732016-09-06 14:25:24 -0500713 stack_frame += 1
714 var_name = get_arg_name(None, 1, stack_frame)
Michael Walshfd2733c2017-11-13 11:36:20 -0600715 return sprint_varx(var_name, var_value=var_value, hex=hex,
716 loc_col1_indent=loc_col1_indent,
717 loc_col1_width=loc_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500718 trailing_char=trailing_char,
719 key_list=key_list)
Michael Walshde791732016-09-06 14:25:24 -0500720
721
Michael Walsh18176322016-11-15 15:11:21 -0600722def sprint_vars(*args):
Michael Walsh18176322016-11-15 15:11:21 -0600723 r"""
724 Sprint the values of one or more variables.
725
726 Description of args:
727 args:
728 If the first argument is an integer, it will be interpreted to be the
729 "indent" value.
730 If the second argument is an integer, it will be interpreted to be the
731 "col1_width" value.
732 If the third argument is an integer, it will be interpreted to be the
733 "hex" value.
734 All remaining parms are considered variable names which are to be
735 sprinted.
736 """
737
738 if len(args) == 0:
739 return
740
741 # Get the name of the first variable passed to this function.
742 stack_frame = 2
743 caller_func_name = sprint_func_name(2)
744 if caller_func_name.endswith("print_vars"):
745 stack_frame += 1
746
747 parm_num = 1
748
749 # Create list from args (which is a tuple) so that it can be modified.
750 args_list = list(args)
751
752 var_name = get_arg_name(None, parm_num, stack_frame)
753 # See if parm 1 is to be interpreted as "indent".
754 try:
755 if type(int(var_name)) is int:
756 indent = int(var_name)
757 args_list.pop(0)
758 parm_num += 1
759 except ValueError:
760 indent = 0
761
762 var_name = get_arg_name(None, parm_num, stack_frame)
763 # See if parm 1 is to be interpreted as "col1_width".
764 try:
765 if type(int(var_name)) is int:
766 loc_col1_width = int(var_name)
767 args_list.pop(0)
768 parm_num += 1
769 except ValueError:
770 loc_col1_width = col1_width
771
772 var_name = get_arg_name(None, parm_num, stack_frame)
773 # See if parm 1 is to be interpreted as "hex".
774 try:
775 if type(int(var_name)) is int:
776 hex = int(var_name)
777 args_list.pop(0)
778 parm_num += 1
779 except ValueError:
780 hex = 0
781
782 buffer = ""
783 for var_value in args_list:
784 var_name = get_arg_name(None, parm_num, stack_frame)
785 buffer += sprint_varx(var_name, var_value, hex, indent, loc_col1_width)
786 parm_num += 1
787
788 return buffer
789
Michael Walsh18176322016-11-15 15:11:21 -0600790
Michael Walsh7423c012016-10-04 10:27:21 -0500791def sprint_dashes(indent=col1_indent,
792 width=80,
793 line_feed=1,
794 char="-"):
Michael Walshde791732016-09-06 14:25:24 -0500795 r"""
796 Return a string of dashes to the caller.
797
Michael Walsh2ee77cd2017-03-08 11:50:17 -0600798 Description of arguments:
Michael Walshde791732016-09-06 14:25:24 -0500799 indent The number of characters to indent the
800 output.
801 width The width of the string of dashes.
802 line_feed Indicates whether the output should end
803 with a line feed.
Michael Walsh7423c012016-10-04 10:27:21 -0500804 char The character to be repeated in the output
805 string.
Michael Walshde791732016-09-06 14:25:24 -0500806 """
807
Michael Walsh7423c012016-10-04 10:27:21 -0500808 width = int(width)
Michael Walsh23e7f492017-01-10 11:34:47 -0600809 buffer = " " * int(indent) + char * width
Michael Walshde791732016-09-06 14:25:24 -0500810 if line_feed:
811 buffer += "\n"
812
813 return buffer
814
Michael Walshde791732016-09-06 14:25:24 -0500815
Michael Walsh7423c012016-10-04 10:27:21 -0500816def sindent(text="",
817 indent=0):
Michael Walsh7423c012016-10-04 10:27:21 -0500818 r"""
819 Pre-pend the specified number of characters to the text string (i.e.
820 indent it) and return it.
821
822 Description of arguments:
823 text The string to be indented.
824 indent The number of characters to indent the
825 string.
826 """
827
828 format_string = "%" + str(indent) + "s%s"
829 buffer = format_string % ("", text)
830
831 return buffer
832
Michael Walsh7423c012016-10-04 10:27:21 -0500833
Michael Walsh7423c012016-10-04 10:27:21 -0500834def sprint_call_stack(indent=0,
835 stack_frame_ix=0):
Michael Walshde791732016-09-06 14:25:24 -0500836 r"""
837 Return a call stack report for the given point in the program with line
838 numbers, function names and function parameters and arguments.
839
840 Sample output:
841
842 -------------------------------------------------------------------------
843 Python function call stack
844
845 Line # Function name and arguments
846 ------ ------------------------------------------------------------------
847 424 sprint_call_stack ()
848 4 print_call_stack ()
849 31 func1 (last_name = 'walsh', first_name = 'mikey')
850 59 /tmp/scr5.py
851 -------------------------------------------------------------------------
852
853 Description of arguments:
854 indent The number of characters to indent each
855 line of output.
856 stack_frame_ix The index of the first stack frame which
857 is to be returned.
858 """
859
860 buffer = ""
Michael Walsh7423c012016-10-04 10:27:21 -0500861 buffer += sprint_dashes(indent)
862 buffer += sindent("Python function call stack\n\n", indent)
863 buffer += sindent("Line # Function name and arguments\n", indent)
864 buffer += sprint_dashes(indent, 6, 0) + " " + sprint_dashes(0, 73)
Michael Walshde791732016-09-06 14:25:24 -0500865
866 # Grab the current program stack.
867 current_stack = inspect.stack()
868
869 # Process each frame in turn.
870 format_string = "%6s %s\n"
Michael Walsh7423c012016-10-04 10:27:21 -0500871 ix = 0
Michael Walshde791732016-09-06 14:25:24 -0500872 for stack_frame in current_stack:
Michael Walsh7423c012016-10-04 10:27:21 -0500873 if ix < stack_frame_ix:
874 ix += 1
875 continue
Michael Walsh23e7f492017-01-10 11:34:47 -0600876 # I want the line number shown to be the line where you find the line
877 # shown.
878 try:
879 line_num = str(current_stack[ix + 1][2])
880 except IndexError:
881 line_num = ""
Michael Walshde791732016-09-06 14:25:24 -0500882 func_name = str(stack_frame[3])
883 if func_name == "?":
884 # "?" is the name used when code is not in a function.
885 func_name = "(none)"
886
887 if func_name == "<module>":
Michael Walsh7423c012016-10-04 10:27:21 -0500888 # If the func_name is the "main" program, we simply get the
889 # command line call string.
Michael Walshde791732016-09-06 14:25:24 -0500890 func_and_args = ' '.join(sys.argv)
891 else:
892 # Get the program arguments.
893 arg_vals = inspect.getargvalues(stack_frame[0])
894 function_parms = arg_vals[0]
895 frame_locals = arg_vals[3]
896
Michael Walsh7423c012016-10-04 10:27:21 -0500897 args_list = []
Michael Walshde791732016-09-06 14:25:24 -0500898 for arg_name in function_parms:
899 # Get the arg value from frame locals.
900 arg_value = frame_locals[arg_name]
Michael Walsh7423c012016-10-04 10:27:21 -0500901 args_list.append(arg_name + " = " + repr(arg_value))
902 args_str = "(" + ', '.join(map(str, args_list)) + ")"
Michael Walshde791732016-09-06 14:25:24 -0500903
904 # Now we need to print this in a nicely-wrapped way.
905 func_and_args = func_name + " " + args_str
906
Michael Walsh23e7f492017-01-10 11:34:47 -0600907 buffer += sindent(format_string % (line_num, func_and_args), indent)
Michael Walsh7423c012016-10-04 10:27:21 -0500908 ix += 1
Michael Walshde791732016-09-06 14:25:24 -0500909
Michael Walsh7423c012016-10-04 10:27:21 -0500910 buffer += sprint_dashes(indent)
Michael Walshde791732016-09-06 14:25:24 -0500911
912 return buffer
913
Michael Walshde791732016-09-06 14:25:24 -0500914
Michael Walshde791732016-09-06 14:25:24 -0500915def sprint_executing(stack_frame_ix=None):
Michael Walshde791732016-09-06 14:25:24 -0500916 r"""
917 Print a line indicating what function is executing and with what parameter
918 values. This is useful for debugging.
919
920 Sample output:
921
922 #(CDT) 2016/08/25 17:54:27 - Executing: func1 (x = 1)
923
924 Description of arguments:
925 stack_frame_ix The index of the stack frame whose
926 function info should be returned. If the
Michael Walsh2ee77cd2017-03-08 11:50:17 -0600927 caller does not specify a value, this
Michael Walshde791732016-09-06 14:25:24 -0500928 function will set the value to 1 which is
929 the index of the caller's stack frame. If
930 the caller is the wrapper function
931 "print_executing", this function will bump
932 it up by 1.
933 """
934
935 # If user wants default stack_frame_ix.
936 if stack_frame_ix is None:
937 func_name = sys._getframe().f_code.co_name
938 caller_func_name = sys._getframe(1).f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -0500939 if caller_func_name.endswith(func_name[1:]):
Michael Walshde791732016-09-06 14:25:24 -0500940 stack_frame_ix = 2
941 else:
942 stack_frame_ix = 1
943
944 stack_frame = inspect.stack()[stack_frame_ix]
945
946 func_name = str(stack_frame[3])
947 if func_name == "?":
948 # "?" is the name used when code is not in a function.
949 func_name = "(none)"
950
951 if func_name == "<module>":
952 # If the func_name is the "main" program, we simply get the command
953 # line call string.
954 func_and_args = ' '.join(sys.argv)
955 else:
956 # Get the program arguments.
957 arg_vals = inspect.getargvalues(stack_frame[0])
958 function_parms = arg_vals[0]
959 frame_locals = arg_vals[3]
960
Michael Walsh7423c012016-10-04 10:27:21 -0500961 args_list = []
Michael Walshde791732016-09-06 14:25:24 -0500962 for arg_name in function_parms:
963 # Get the arg value from frame locals.
964 arg_value = frame_locals[arg_name]
Michael Walsh7423c012016-10-04 10:27:21 -0500965 args_list.append(arg_name + " = " + repr(arg_value))
966 args_str = "(" + ', '.join(map(str, args_list)) + ")"
Michael Walshde791732016-09-06 14:25:24 -0500967
968 # Now we need to print this in a nicely-wrapped way.
969 func_and_args = func_name + " " + args_str
970
971 return sprint_time() + "Executing: " + func_and_args + "\n"
972
Michael Walshde791732016-09-06 14:25:24 -0500973
Michael Walshbec416d2016-11-10 08:54:52 -0600974def sprint_pgm_header(indent=0,
975 linefeed=1):
Michael Walshde791732016-09-06 14:25:24 -0500976 r"""
977 Return a standardized header that programs should print at the beginning
978 of the run. It includes useful information like command line, pid,
979 userid, program parameters, etc.
980
Michael Walsh7423c012016-10-04 10:27:21 -0500981 Description of arguments:
982 indent The number of characters to indent each
983 line of output.
Michael Walshbec416d2016-11-10 08:54:52 -0600984 linefeed Indicates whether a line feed be included
985 at the beginning and end of the report.
Michael Walshde791732016-09-06 14:25:24 -0500986 """
987
Michael Walshbec416d2016-11-10 08:54:52 -0600988 loc_col1_width = col1_width + indent
989
990 buffer = ""
991 if linefeed:
992 buffer = "\n"
Michael Walsh7423c012016-10-04 10:27:21 -0500993
Michael Walshdb6e68a2017-05-23 17:55:31 -0500994 if robot_env:
995 suite_name = BuiltIn().get_variable_value("${suite_name}")
996 buffer += sindent(sprint_time("Running test suite \"" + suite_name +
Gunnar Mills096cd562018-03-26 10:19:12 -0500997 "\".\n"), indent)
Michael Walshdb6e68a2017-05-23 17:55:31 -0500998
Michael Walsh7423c012016-10-04 10:27:21 -0500999 buffer += sindent(sprint_time() + "Running " + pgm_name + ".\n", indent)
1000 buffer += sindent(sprint_time() + "Program parameter values, etc.:\n\n",
1001 indent)
Michael Walshbec416d2016-11-10 08:54:52 -06001002 buffer += sprint_varx("command_line", ' '.join(sys.argv), 0, indent,
1003 loc_col1_width)
Michael Walsh7423c012016-10-04 10:27:21 -05001004 # We want the output to show a customized name for the pid and pgid but
1005 # we want it to look like a valid variable name. Therefore, we'll use
Michael Walshde791732016-09-06 14:25:24 -05001006 # pgm_name_var_name which was set when this module was imported.
Michael Walshbec416d2016-11-10 08:54:52 -06001007 buffer += sprint_varx(pgm_name_var_name + "_pid", os.getpid(), 0, indent,
1008 loc_col1_width)
1009 buffer += sprint_varx(pgm_name_var_name + "_pgid", os.getpgrp(), 0, indent,
1010 loc_col1_width)
Michael Walsh86de0d22016-12-05 10:13:15 -06001011 userid_num = str(os.geteuid())
1012 try:
1013 username = os.getlogin()
1014 except OSError:
1015 if userid_num == "0":
1016 username = "root"
1017 else:
1018 username = "?"
1019 buffer += sprint_varx("uid", userid_num + " (" + username +
Michael Walshbec416d2016-11-10 08:54:52 -06001020 ")", 0, indent, loc_col1_width)
Michael Walshde791732016-09-06 14:25:24 -05001021 buffer += sprint_varx("gid", str(os.getgid()) + " (" +
Michael Walsh7423c012016-10-04 10:27:21 -05001022 str(grp.getgrgid(os.getgid()).gr_name) + ")", 0,
Michael Walshbec416d2016-11-10 08:54:52 -06001023 indent, loc_col1_width)
1024 buffer += sprint_varx("host_name", socket.gethostname(), 0, indent,
1025 loc_col1_width)
Michael Walsh86de0d22016-12-05 10:13:15 -06001026 try:
1027 DISPLAY = os.environ['DISPLAY']
1028 except KeyError:
1029 DISPLAY = ""
1030 buffer += sprint_varx("DISPLAY", DISPLAY, 0, indent,
Michael Walshbec416d2016-11-10 08:54:52 -06001031 loc_col1_width)
Michael Walshde791732016-09-06 14:25:24 -05001032 # I want to add code to print caller's parms.
1033
Michael Walsh7423c012016-10-04 10:27:21 -05001034 # __builtin__.arg_obj is created by the get_arg module function,
1035 # gen_get_options.
1036 try:
1037 buffer += ga.sprint_args(__builtin__.arg_obj, indent)
1038 except AttributeError:
1039 pass
1040
Michael Walshdb6e68a2017-05-23 17:55:31 -05001041 if robot_env:
1042 # Get value of global parm_list.
1043 parm_list = BuiltIn().get_variable_value("${parm_list}")
1044
1045 for parm in parm_list:
1046 parm_value = BuiltIn().get_variable_value("${" + parm + "}")
1047 buffer += sprint_varx(parm, parm_value, 0, indent, loc_col1_width)
1048
1049 # Setting global program_pid.
1050 BuiltIn().set_global_variable("${program_pid}", os.getpid())
1051
Michael Walshbec416d2016-11-10 08:54:52 -06001052 if linefeed:
1053 buffer += "\n"
Michael Walshde791732016-09-06 14:25:24 -05001054
1055 return buffer
1056
Michael Walshde791732016-09-06 14:25:24 -05001057
Michael Walsh7423c012016-10-04 10:27:21 -05001058def sprint_error_report(error_text="\n",
Michael Walshdb6e68a2017-05-23 17:55:31 -05001059 indent=2,
1060 format=None):
Michael Walsh7423c012016-10-04 10:27:21 -05001061 r"""
1062 Return a string with a standardized report which includes the caller's
1063 error text, the call stack and the program header.
1064
1065 Description of args:
1066 error_text The error text to be included in the
1067 report. The caller should include any
1068 needed linefeeds.
1069 indent The number of characters to indent each
1070 line of output.
Michael Walshdb6e68a2017-05-23 17:55:31 -05001071 format Long or short format. Long includes
1072 extras like lines of dashes, call stack,
1073 etc.
Michael Walsh7423c012016-10-04 10:27:21 -05001074 """
1075
Michael Walshdb6e68a2017-05-23 17:55:31 -05001076 # Process input.
1077 indent = int(indent)
1078 if format is None:
1079 if robot_env:
1080 format = 'short'
1081 else:
1082 format = 'long'
1083 error_text = error_text.rstrip('\n') + '\n'
1084
1085 if format == 'short':
1086 return sprint_error(error_text)
1087
Michael Walsh7423c012016-10-04 10:27:21 -05001088 buffer = ""
1089 buffer += sprint_dashes(width=120, char="=")
1090 buffer += sprint_error(error_text)
1091 buffer += "\n"
1092 # Calling sprint_call_stack with stack_frame_ix of 0 causes it to show
1093 # itself and this function in the call stack. This is not helpful to a
1094 # debugger and is therefore clutter. We will adjust the stack_frame_ix to
1095 # hide that information.
Michael Walsh9c75f672017-09-12 17:11:35 -05001096 stack_frame_ix = 1
Michael Walsh7423c012016-10-04 10:27:21 -05001097 caller_func_name = sprint_func_name(2)
1098 if caller_func_name.endswith("print_error_report"):
1099 stack_frame_ix += 1
Michael Walshdb6e68a2017-05-23 17:55:31 -05001100 if not robot_env:
1101 buffer += sprint_call_stack(indent, stack_frame_ix)
Michael Walsh7423c012016-10-04 10:27:21 -05001102 buffer += sprint_pgm_header(indent)
1103 buffer += sprint_dashes(width=120, char="=")
1104
1105 return buffer
1106
Michael Walsh7423c012016-10-04 10:27:21 -05001107
Michael Walsh18176322016-11-15 15:11:21 -06001108def sprint_issuing(cmd_buf,
1109 test_mode=0):
Michael Walshde791732016-09-06 14:25:24 -05001110 r"""
1111 Return a line indicating a command that the program is about to execute.
1112
1113 Sample output for a cmd_buf of "ls"
1114
1115 #(CDT) 2016/08/25 17:57:36 - Issuing: ls
Michael Walshbec416d2016-11-10 08:54:52 -06001116
Michael Walshde791732016-09-06 14:25:24 -05001117 Description of args:
1118 cmd_buf The command to be executed by caller.
Michael Walshbec416d2016-11-10 08:54:52 -06001119 test_mode With test_mode set, your output will look
1120 like this:
1121
1122 #(CDT) 2016/08/25 17:57:36 - (test_mode) Issuing: ls
1123
Michael Walshde791732016-09-06 14:25:24 -05001124 """
1125
Michael Walshbec416d2016-11-10 08:54:52 -06001126 buffer = sprint_time()
1127 if test_mode:
1128 buffer += "(test_mode) "
1129 buffer += "Issuing: " + cmd_buf + "\n"
Michael Walshde791732016-09-06 14:25:24 -05001130
1131 return buffer
1132
Michael Walshde791732016-09-06 14:25:24 -05001133
Michael Walshde791732016-09-06 14:25:24 -05001134def sprint_pgm_footer():
Michael Walshde791732016-09-06 14:25:24 -05001135 r"""
1136 Return a standardized footer that programs should print at the end of the
1137 program run. It includes useful information like total run time, etc.
1138 """
1139
1140 buffer = "\n" + sprint_time() + "Finished running " + pgm_name + ".\n\n"
1141
1142 total_time = time.time() - start_time
1143 total_time_string = "%0.6f" % total_time
1144
Michael Walsh7423c012016-10-04 10:27:21 -05001145 buffer += sprint_varx(pgm_name_var_name + "_runtime", total_time_string)
Michael Walshbec416d2016-11-10 08:54:52 -06001146 buffer += "\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001147
1148 return buffer
1149
Michael Walsh7423c012016-10-04 10:27:21 -05001150
Michael Walsh7423c012016-10-04 10:27:21 -05001151def sprint(buffer=""):
Michael Walsh7423c012016-10-04 10:27:21 -05001152 r"""
1153 Simply return the user's buffer. This function is used by the qprint and
1154 dprint functions defined dynamically below, i.e. it would not normally be
1155 called for general use.
1156
1157 Description of arguments.
1158 buffer This will be returned to the caller.
1159 """
Michael Walshde791732016-09-06 14:25:24 -05001160
Michael Walsh95e45102018-02-09 12:44:43 -06001161 try:
1162 return str(buffer)
1163 except UnicodeEncodeError:
1164 return buffer
Michael Walshbec416d2016-11-10 08:54:52 -06001165
Michael Walshbec416d2016-11-10 08:54:52 -06001166
Michael Walshbec416d2016-11-10 08:54:52 -06001167def sprintn(buffer=""):
Michael Walshbec416d2016-11-10 08:54:52 -06001168 r"""
1169 Simply return the user's buffer with a line feed. This function is used
1170 by the qprint and dprint functions defined dynamically below, i.e. it
1171 would not normally be called for general use.
1172
1173 Description of arguments.
1174 buffer This will be returned to the caller.
1175 """
1176
Michael Walsh95e45102018-02-09 12:44:43 -06001177 try:
1178 buffer = str(buffer) + "\n"
1179 except UnicodeEncodeError:
1180 buffer = buffer + "\n"
Michael Walshbec416d2016-11-10 08:54:52 -06001181
Michael Walshde791732016-09-06 14:25:24 -05001182 return buffer
1183
Michael Walsh168eb0f2017-12-01 15:35:32 -06001184
Michael Walshfd2733c2017-11-13 11:36:20 -06001185def gp_print(buffer,
1186 stream='stdout'):
Michael Walshfd2733c2017-11-13 11:36:20 -06001187 r"""
1188 Print the buffer using either sys.stdout.write or BuiltIn().log_to_console
1189 depending on whether we are running in a robot environment.
1190
1191 This function is intended for use only by other functions in this module.
1192
1193 Description of arguments:
1194 buffer The string to be printed.
1195 stream Either "stdout" or "stderr".
1196 """
1197
1198 if robot_env:
1199 BuiltIn().log_to_console(buffer, stream=stream, no_newline=True)
1200 else:
1201 if stream == "stdout":
1202 sys.stdout.write(buffer)
1203 sys.stdout.flush()
1204 else:
1205 sys.stderr.write(buffer)
1206 sys.stderr.flush()
Michael Walshde791732016-09-06 14:25:24 -05001207
1208
Michael Walsh168eb0f2017-12-01 15:35:32 -06001209def gp_log(buffer):
Michael Walsh168eb0f2017-12-01 15:35:32 -06001210 r"""
1211 Log the buffer using either python logging or BuiltIn().log depending on
1212 whether we are running in a robot environment.
1213
1214 This function is intended for use only by other functions in this module.
1215
1216 Description of arguments:
1217 buffer The string to be logged.
1218 """
1219
1220 if robot_env:
1221 BuiltIn().log(buffer)
1222 else:
1223 logging.warning(buffer)
1224
1225
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001226def gp_debug_print(buffer):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001227 r"""
Michael Walshfd2733c2017-11-13 11:36:20 -06001228 Print with gp_print only if gen_print_debug is set.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001229
1230 This function is intended for use only by other functions in this module.
1231
1232 Description of arguments:
1233 buffer The string to be printed.
1234 """
1235
1236 if not gen_print_debug:
1237 return
1238
Michael Walshfd2733c2017-11-13 11:36:20 -06001239 gp_print(buffer)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001240
1241
Michael Walshb1500152017-04-12 15:42:43 -05001242def get_var_value(var_value=None,
1243 default=1,
1244 var_name=None):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001245 r"""
Michael Walshb1500152017-04-12 15:42:43 -05001246 Return either var_value, the corresponding global value or default.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001247
Michael Walshb1500152017-04-12 15:42:43 -05001248 If var_value is not None, it will simply be returned.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001249
Michael Walshb1500152017-04-12 15:42:43 -05001250 If var_value is None, this function will return the corresponding global
1251 value of the variable in question.
1252
1253 Note: For global values, if we are in a robot environment,
1254 get_variable_value will be used. Otherwise, the __builtin__ version of
1255 the variable is returned (which are set by gen_arg.py functions).
1256
1257 If there is no global value associated with the variable, default is
1258 returned.
1259
1260 This function is useful for other functions in setting default values for
1261 parameters.
1262
1263 Example use:
1264
1265 def my_func(quiet=None):
1266
1267 quiet = int(get_var_value(quiet, 0))
1268
1269 Example calls to my_func():
1270
1271 In the following example, the caller is explicitly asking to have quiet be
1272 set to 1.
1273
1274 my_func(quiet=1)
1275
1276 In the following example, quiet will be set to the global value of quiet,
1277 if defined, or to 0 (the default).
1278
1279 my_func()
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001280
1281 Description of arguments:
Michael Walshb1500152017-04-12 15:42:43 -05001282 var_value The value to be returned (if not equal to
1283 None).
1284 default The value that is returned if var_value is
1285 None and there is no corresponding global
1286 value defined.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001287 var_name The name of the variable whose value is to
Michael Walshb1500152017-04-12 15:42:43 -05001288 be returned. Under most circumstances,
1289 this value need not be provided. This
1290 function can figure out the name of the
1291 variable passed as var_value. One
1292 exception to this would be if this
1293 function is called directly from a .robot
1294 file.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001295 """
1296
Michael Walshb1500152017-04-12 15:42:43 -05001297 if var_value is not None:
1298 return var_value
1299
1300 if var_name is None:
1301 var_name = get_arg_name(None, 1, 2)
1302
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001303 if robot_env:
Michael Walshc6537442017-06-06 15:33:52 -05001304 var_value = BuiltIn().get_variable_value("${" + var_name + "}",
1305 default)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001306 else:
1307 var_value = getattr(__builtin__, var_name, default)
1308
1309 return var_value
1310
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001311
Michael Walsh82acf002017-05-04 14:33:05 -05001312# hidden_text is a list of passwords which are to be replaced with asterisks
1313# by print functions defined in this module.
1314hidden_text = []
1315# password_regex is created based on the contents of hidden_text.
1316password_regex = ""
1317
1318
Michael Walsh82acf002017-05-04 14:33:05 -05001319def register_passwords(*args):
Michael Walsh82acf002017-05-04 14:33:05 -05001320 r"""
1321 Register one or more passwords which are to be hidden in output produced
1322 by the print functions in this module.
1323
1324 Note: Blank password values are NOT registered. They are simply ignored.
1325
1326 Description of argument(s):
1327 args One or more password values. If a given
1328 password value is already registered, this
1329 function will simply do nothing.
1330 """
1331
1332 global hidden_text
1333 global password_regex
1334
1335 for password in args:
1336 if password == "":
1337 break
1338 if password in hidden_text:
1339 break
1340
1341 # Place the password into the hidden_text list.
1342 hidden_text.append(password)
1343 # Create a corresponding password regular expression. Escape regex
1344 # special characters too.
1345 password_regex = '(' +\
1346 '|'.join([re.escape(x) for x in hidden_text]) + ')'
1347
Michael Walsh82acf002017-05-04 14:33:05 -05001348
Michael Walsh82acf002017-05-04 14:33:05 -05001349def replace_passwords(buffer):
Michael Walsh82acf002017-05-04 14:33:05 -05001350 r"""
1351 Return the buffer but with all registered passwords replaced by a string
1352 of asterisks.
1353
1354
1355 Description of argument(s):
1356 buffer The string to be returned but with
1357 passwords replaced.
1358 """
1359
1360 global password_regex
1361
1362 if int(os.environ.get("DEBUG_SHOW_PASSWORDS", "0")):
1363 return buffer
1364
1365 if password_regex == "":
1366 # No passwords to replace.
1367 return buffer
1368
1369 return re.sub(password_regex, "********", buffer)
1370
Michael Walshfd2733c2017-11-13 11:36:20 -06001371
1372def create_print_wrapper_funcs(func_names,
1373 stderr_func_names,
1374 replace_dict):
Michael Walshfd2733c2017-11-13 11:36:20 -06001375 r"""
1376 Generate code for print wrapper functions and return the generated code as
1377 a string.
1378
1379 To illustrate, suppose there is a "print_foo_bar" function in the
1380 func_names list.
1381 This function will...
1382 - Expect that there is an sprint_foo_bar function already in existence.
1383 - Create a print_foo_bar function which calls sprint_foo_bar and prints
1384 the result.
1385 - Create a qprint_foo_bar function which calls upon sprint_foo_bar only if
1386 global value quiet is 0.
1387 - Create a dprint_foo_bar function which calls upon sprint_foo_bar only if
1388 global value debug is 1.
1389
1390 Also, code will be generated to define aliases for each function as well.
1391 Each alias will be created by replacing "print_" in the function name with
1392 "p" For example, the alias for print_foo_bar will be pfoo_bar.
1393
1394 Description of argument(s):
1395 func_names A list of functions for which print
1396 wrapper function code is to be generated.
1397 stderr_func_names A list of functions whose generated code
1398 should print to stderr rather than to
1399 stdout.
1400 replace_dict Please see the create_func_def_string
1401 function in wrap_utils.py for details on
1402 this parameter. This parameter will be
1403 passed directly to create_func_def_string.
1404 """
1405
1406 buffer = ""
1407
1408 for func_name in func_names:
1409 if func_name in stderr_func_names:
1410 replace_dict['output_stream'] = "stderr"
1411 else:
1412 replace_dict['output_stream'] = "stdout"
1413
1414 s_func_name = "s" + func_name
1415 q_func_name = "q" + func_name
1416 d_func_name = "d" + func_name
1417
1418 # We don't want to try to redefine the "print" function, thus the
1419 # following if statement.
1420 if func_name != "print":
1421 func_def = create_func_def_string(s_func_name, func_name,
1422 print_func_template,
1423 replace_dict)
1424 buffer += func_def
1425
1426 func_def = create_func_def_string(s_func_name, "q" + func_name,
1427 qprint_func_template, replace_dict)
1428 buffer += func_def
1429
1430 func_def = create_func_def_string(s_func_name, "d" + func_name,
1431 dprint_func_template, replace_dict)
1432 buffer += func_def
1433
Michael Walsh168eb0f2017-12-01 15:35:32 -06001434 func_def = create_func_def_string(s_func_name, "l" + func_name,
1435 lprint_func_template, replace_dict)
1436 buffer += func_def
1437
Michael Walshfd2733c2017-11-13 11:36:20 -06001438 # Create abbreviated aliases (e.g. spvar is an alias for sprint_var).
1439 alias = re.sub("print_", "p", func_name)
1440 alias = re.sub("print", "p", alias)
Michael Walsh168eb0f2017-12-01 15:35:32 -06001441 prefixes = ["", "s", "q", "d", "l"]
Michael Walshfd2733c2017-11-13 11:36:20 -06001442 for prefix in prefixes:
1443 if alias == "p":
1444 continue
1445 func_def = prefix + alias + " = " + prefix + func_name
1446 buffer += func_def + "\n"
1447
1448 return buffer
Michael Walsh82acf002017-05-04 14:33:05 -05001449
1450
Michael Walshde791732016-09-06 14:25:24 -05001451# In the following section of code, we will dynamically create print versions
1452# for each of the sprint functions defined above. So, for example, where we
1453# have an sprint_time() function defined above that returns the time to the
Michael Walsh7423c012016-10-04 10:27:21 -05001454# caller in a string, we will create a corresponding print_time() function
1455# that will print that string directly to stdout.
Michael Walshde791732016-09-06 14:25:24 -05001456
Michael Walshfd2733c2017-11-13 11:36:20 -06001457# It can be complicated to follow what's being created by below. Here is an
1458# example of the print_time() function that will be created:
Michael Walshde791732016-09-06 14:25:24 -05001459
Michael Walshfd2733c2017-11-13 11:36:20 -06001460# def print_time(buffer=''):
1461# sys.stdout.write(replace_passwords(sprint_time(buffer=buffer)))
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001462# sys.stdout.flush()
Michael Walshde791732016-09-06 14:25:24 -05001463
Michael Walshfd2733c2017-11-13 11:36:20 -06001464# Templates for the various print wrapper functions.
1465print_func_template = \
1466 [
Michael Walsh81c02342018-01-05 15:43:28 -06001467 " <mod_qualifier>gp_print(<mod_qualifier>replace_passwords(" +
1468 "<call_line>), stream='<output_stream>')"
Michael Walshfd2733c2017-11-13 11:36:20 -06001469 ]
1470
1471qprint_func_template = \
1472 [
Michael Walsh81c02342018-01-05 15:43:28 -06001473 " if int(<mod_qualifier>get_var_value(None, 0, \"quiet\")): return"
Michael Walshfd2733c2017-11-13 11:36:20 -06001474 ] + print_func_template
1475
1476dprint_func_template = \
1477 [
Michael Walsh81c02342018-01-05 15:43:28 -06001478 " if not int(<mod_qualifier>get_var_value(None, 0, \"debug\")):" +
Michael Walshfd2733c2017-11-13 11:36:20 -06001479 " return"
1480 ] + print_func_template
1481
Michael Walsh168eb0f2017-12-01 15:35:32 -06001482lprint_func_template = \
1483 [
Michael Walsh81c02342018-01-05 15:43:28 -06001484 " gp_log(<mod_qualifier>replace_passwords(<call_line>))"
Michael Walsh168eb0f2017-12-01 15:35:32 -06001485 ]
1486
Michael Walsh81c02342018-01-05 15:43:28 -06001487replace_dict = {'output_stream': 'stdout', 'mod_qualifier': ''}
Michael Walshfd2733c2017-11-13 11:36:20 -06001488
1489
1490gp_debug_print("robot_env: " + str(robot_env))
Michael Walshde791732016-09-06 14:25:24 -05001491
1492# func_names contains a list of all print functions which should be created
1493# from their sprint counterparts.
1494func_names = ['print_time', 'print_timen', 'print_error', 'print_varx',
Michael Walsh18176322016-11-15 15:11:21 -06001495 'print_var', 'print_vars', 'print_dashes', 'indent',
1496 'print_call_stack', 'print_func_name', 'print_executing',
1497 'print_pgm_header', 'print_issuing', 'print_pgm_footer',
1498 'print_error_report', 'print', 'printn']
Michael Walshde791732016-09-06 14:25:24 -05001499
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001500# stderr_func_names is a list of functions whose output should go to stderr
1501# rather than stdout.
1502stderr_func_names = ['print_error', 'print_error_report']
Michael Walshde791732016-09-06 14:25:24 -05001503
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001504
Michael Walshfd2733c2017-11-13 11:36:20 -06001505func_defs = create_print_wrapper_funcs(func_names, stderr_func_names,
1506 replace_dict)
1507gp_debug_print(func_defs)
1508exec(func_defs)