blob: 61cb726b95f9a5ae58b06db572472b2d99b9f7c8 [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)
Michael Walsh3ba8ecd2018-04-24 11:33:25 -050041pgm_dir_path = os.path.normpath(re.sub("/" + pgm_name, "", pgm_file_path)) +\
42 os.path.sep
Michael Walsh7423c012016-10-04 10:27:21 -050043
Michael Walshde791732016-09-06 14:25:24 -050044
45# Some functions (e.g. sprint_pgm_header) have need of a program name value
46# that looks more like a valid variable name. Therefore, we'll swap odd
47# characters like "." out for underscores.
48pgm_name_var_name = pgm_name.replace(".", "_")
49
50# Initialize global values used as defaults by print_time, print_var, etc.
51col1_indent = 0
52
53# Calculate default column width for print_var functions based on environment
54# variable settings. The objective is to make the variable values line up
55# nicely with the time stamps.
56col1_width = 29
Michael Walshb1500152017-04-12 15:42:43 -050057
58NANOSECONDS = os.environ.get('NANOSECONDS', '1')
59
Michael Walshde791732016-09-06 14:25:24 -050060
61if NANOSECONDS == "1":
62 col1_width = col1_width + 7
63
Michael Walshb1500152017-04-12 15:42:43 -050064SHOW_ELAPSED_TIME = os.environ.get('SHOW_ELAPSED_TIME', '1')
Michael Walshde791732016-09-06 14:25:24 -050065
66if SHOW_ELAPSED_TIME == "1":
67 if NANOSECONDS == "1":
68 col1_width = col1_width + 14
69 else:
70 col1_width = col1_width + 7
71
72# Initialize some time variables used in module functions.
73start_time = time.time()
74sprint_time_last_seconds = start_time
75
Michael Walsh2ee77cd2017-03-08 11:50:17 -060076# The user can set environment variable "GEN_PRINT_DEBUG" to get debug output
77# from this module.
78gen_print_debug = int(os.environ.get('GEN_PRINT_DEBUG', 0))
Michael Walsh7423c012016-10-04 10:27:21 -050079
Michael Walshde791732016-09-06 14:25:24 -050080
Michael Walshde791732016-09-06 14:25:24 -050081def sprint_func_name(stack_frame_ix=None):
Michael Walshde791732016-09-06 14:25:24 -050082 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):
Michael Walshde791732016-09-06 14:25:24 -0500116 r"""
117 Return the "name" of an argument passed to a function. This could be a
118 literal or a variable name.
119
Michael Walsh2ee77cd2017-03-08 11:50:17 -0600120 Description of arguments:
Michael Walshde791732016-09-06 14:25:24 -0500121 var The variable whose name you want returned.
122 arg_num The arg number (1 through n) whose name
123 you wish to have returned. This value
124 should not exceed the number of arguments
125 allowed by the target function.
126 stack_frame_ix The stack frame index of the target
127 function. This value must be 1 or
128 greater. 1 would indicate get_arg_name's
129 stack frame. 2 would be the caller of
130 get_arg_name's stack frame, etc.
131
132 Example 1:
133
134 my_var = "mike"
135 var_name = get_arg_name(my_var)
136
137 In this example, var_name will receive the value "my_var".
138
139 Example 2:
140
141 def test1(var):
142 # Getting the var name of the first arg to this function, test1.
143 # Note, in this case, it doesn't matter what you pass as the first arg
144 # to get_arg_name since it is the caller's variable name that matters.
145 dummy = 1
146 arg_num = 1
147 stack_frame = 2
148 var_name = get_arg_name(dummy, arg_num, stack_frame)
149
150 # Mainline...
151
152 another_var = "whatever"
153 test1(another_var)
154
155 In this example, var_name will be set to "another_var".
156
157 """
158
159 # Note: I wish to avoid recursion so I refrain from calling any function
160 # that calls this function (i.e. sprint_var, valid_value, etc.).
161
Michael Walsh23e7f492017-01-10 11:34:47 -0600162 # The user can set environment variable "GET_ARG_NAME_DEBUG" to get debug
163 # output from this function.
164 local_debug = int(os.environ.get('GET_ARG_NAME_DEBUG', 0))
165 # In addition to GET_ARG_NAME_DEBUG, the user can set environment
166 # variable "GET_ARG_NAME_SHOW_SOURCE" to have this function include source
167 # code in the debug output.
168 local_debug_show_source = int(
169 os.environ.get('GET_ARG_NAME_SHOW_SOURCE', 0))
Michael Walshde791732016-09-06 14:25:24 -0500170
171 if arg_num < 1:
172 print_error("Programmer error - Variable \"arg_num\" has an invalid" +
173 " value of \"" + str(arg_num) + "\". The value must be" +
174 " an integer that is greater than 0.\n")
175 # What is the best way to handle errors? Raise exception? I'll
176 # revisit later.
177 return
178 if stack_frame_ix < 1:
179 print_error("Programmer error - Variable \"stack_frame_ix\" has an" +
180 " invalid value of \"" + str(stack_frame_ix) + "\". The" +
181 " value must be an integer that is greater than or equal" +
182 " to 1.\n")
183 return
184
185 if local_debug:
186 debug_indent = 2
Michael Walsh23e7f492017-01-10 11:34:47 -0600187 print("")
188 print_dashes(0, 120)
Michael Walshde791732016-09-06 14:25:24 -0500189 print(sprint_func_name() + "() parms:")
190 print_varx("var", var, 0, debug_indent)
191 print_varx("arg_num", arg_num, 0, debug_indent)
192 print_varx("stack_frame_ix", stack_frame_ix, 0, debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600193 print("")
194 print_call_stack(debug_indent, 2)
Michael Walshde791732016-09-06 14:25:24 -0500195
Michael Walsh23e7f492017-01-10 11:34:47 -0600196 for count in range(0, 2):
197 try:
198 frame, filename, cur_line_no, function_name, lines, index = \
199 inspect.stack()[stack_frame_ix]
200 except IndexError:
201 print_error("Programmer error - The caller has asked for" +
202 " information about the stack frame at index \"" +
203 str(stack_frame_ix) + "\". However, the stack" +
204 " only contains " + str(len(inspect.stack())) +
205 " entries. Therefore the stack frame index is out" +
206 " of range.\n")
207 return
208 if filename != "<string>":
209 break
210 # filename of "<string>" may mean that the function in question was
211 # defined dynamically and therefore its code stack is inaccessible.
212 # This may happen with functions like "rqprint_var". In this case,
213 # we'll increment the stack_frame_ix and try again.
214 stack_frame_ix += 1
215 if local_debug:
216 print("Adjusted stack_frame_ix...")
217 print_varx("stack_frame_ix", stack_frame_ix, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500218
219 called_func_name = sprint_func_name(stack_frame_ix)
Michael Walsh23e7f492017-01-10 11:34:47 -0600220
221 module = inspect.getmodule(frame)
222
223 # Though I would expect inspect.getsourcelines(frame) to get all module
224 # source lines if the frame is "<module>", it doesn't do that. Therefore,
225 # for this special case, I will do inspect.getsourcelines(module).
226 if function_name == "<module>":
227 source_lines, source_line_num =\
228 inspect.getsourcelines(module)
229 line_ix = cur_line_no - source_line_num - 1
230 else:
231 source_lines, source_line_num =\
232 inspect.getsourcelines(frame)
233 line_ix = cur_line_no - source_line_num
234
235 if local_debug:
236 print("\n Variables retrieved from inspect.stack() function:")
237 print_varx("frame", frame, 0, debug_indent + 2)
238 print_varx("filename", filename, 0, debug_indent + 2)
239 print_varx("cur_line_no", cur_line_no, 0, debug_indent + 2)
240 print_varx("function_name", function_name, 0, debug_indent + 2)
241 print_varx("lines", lines, 0, debug_indent + 2)
242 print_varx("index", index, 0, debug_indent + 2)
243 print_varx("source_line_num", source_line_num, 0, debug_indent)
244 print_varx("line_ix", line_ix, 0, debug_indent)
245 if local_debug_show_source:
246 print_varx("source_lines", source_lines, 0, debug_indent)
247 print_varx("called_func_name", called_func_name, 0, debug_indent)
248
249 # Get a list of all functions defined for the module. Note that this
250 # doesn't work consistently when _run_exitfuncs is at the top of the stack
251 # (i.e. if we're running an exit function). I've coded a work-around
252 # below for this deficiency.
253 all_functions = inspect.getmembers(module, inspect.isfunction)
254
255 # Get called_func_id by searching for our function in the list of all
256 # functions.
257 called_func_id = None
258 for func_name, function in all_functions:
259 if func_name == called_func_name:
260 called_func_id = id(function)
261 break
262 # NOTE: The only time I've found that called_func_id can't be found is
263 # when we're running from an exit function.
264
265 # Look for other functions in module with matching id.
266 aliases = set([called_func_name])
267 for func_name, function in all_functions:
268 if func_name == called_func_name:
269 continue
270 func_id = id(function)
271 if func_id == called_func_id:
272 aliases.add(func_name)
273
274 # In most cases, my general purpose code above will find all aliases.
275 # However, for the odd case (i.e. running from exit function), I've added
276 # code to handle pvar, qpvar, dpvar, etc. aliases explicitly since they
277 # are defined in this module and used frequently.
278 # pvar is an alias for print_var.
279 aliases.add(re.sub("print_var", "pvar", called_func_name))
280
281 func_regex = ".*(" + '|'.join(aliases) + ")[ ]*\("
282
283 # Search backward through source lines looking for the calling function
284 # name.
285 found = False
286 for start_line_ix in range(line_ix, 0, -1):
287 # Skip comment lines.
288 if re.match(r"[ ]*#", source_lines[start_line_ix]):
289 continue
290 if re.match(func_regex, source_lines[start_line_ix]):
291 found = True
292 break
293 if not found:
294 print_error("Programmer error - Could not find the source line with" +
295 " a reference to function \"" + called_func_name + "\".\n")
296 return
297
Michael Walsh82acf002017-05-04 14:33:05 -0500298 # Search forward through the source lines looking for a line whose
299 # indentation is the same or less than the start line. The end of our
300 # composite line should be the line preceding that line.
Michael Walsh23e7f492017-01-10 11:34:47 -0600301 start_indent = len(source_lines[start_line_ix]) -\
302 len(source_lines[start_line_ix].lstrip(' '))
303 end_line_ix = line_ix
304 for end_line_ix in range(line_ix + 1, len(source_lines)):
305 if source_lines[end_line_ix].strip() == "":
306 continue
307 line_indent = len(source_lines[end_line_ix]) -\
308 len(source_lines[end_line_ix].lstrip(' '))
Michael Walsh82acf002017-05-04 14:33:05 -0500309 if line_indent <= start_indent:
Michael Walsh23e7f492017-01-10 11:34:47 -0600310 end_line_ix -= 1
311 break
312
313 # Join the start line through the end line into a composite line.
314 composite_line = ''.join(map(str.strip,
Gunnar Mills096cd562018-03-26 10:19:12 -0500315 source_lines[start_line_ix:end_line_ix + 1]))
Michael Walsh7423c012016-10-04 10:27:21 -0500316
Michael Walshbec416d2016-11-10 08:54:52 -0600317 # arg_list_etc = re.sub(".*" + called_func_name, "", composite_line)
318 arg_list_etc = "(" + re.sub(func_regex, "", composite_line)
Michael Walshde791732016-09-06 14:25:24 -0500319 if local_debug:
Michael Walsh23e7f492017-01-10 11:34:47 -0600320 print_varx("aliases", aliases, 0, debug_indent)
Michael Walshbec416d2016-11-10 08:54:52 -0600321 print_varx("func_regex", func_regex, 0, debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600322 print_varx("start_line_ix", start_line_ix, 0, debug_indent)
323 print_varx("end_line_ix", end_line_ix, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500324 print_varx("composite_line", composite_line, 0, debug_indent)
325 print_varx("arg_list_etc", arg_list_etc, 0, debug_indent)
326
327 # Parse arg list...
328 # Initialize...
329 nest_level = -1
330 arg_ix = 0
Michael Walsh7423c012016-10-04 10:27:21 -0500331 args_list = [""]
Michael Walshde791732016-09-06 14:25:24 -0500332 for ix in range(0, len(arg_list_etc)):
333 char = arg_list_etc[ix]
334 # Set the nest_level based on whether we've encounted a parenthesis.
335 if char == "(":
336 nest_level += 1
337 if nest_level == 0:
338 continue
339 elif char == ")":
340 nest_level -= 1
341 if nest_level < 0:
342 break
343
344 # If we reach a comma at base nest level, we are done processing an
Michael Walsh7423c012016-10-04 10:27:21 -0500345 # argument so we increment arg_ix and initialize a new args_list entry.
Michael Walshde791732016-09-06 14:25:24 -0500346 if char == "," and nest_level == 0:
347 arg_ix += 1
Michael Walsh7423c012016-10-04 10:27:21 -0500348 args_list.append("")
Michael Walshde791732016-09-06 14:25:24 -0500349 continue
350
Michael Walsh7423c012016-10-04 10:27:21 -0500351 # For any other character, we append it it to the current arg list
Michael Walshde791732016-09-06 14:25:24 -0500352 # entry.
Michael Walsh7423c012016-10-04 10:27:21 -0500353 args_list[arg_ix] += char
Michael Walshde791732016-09-06 14:25:24 -0500354
355 # Trim whitespace from each list entry.
Michael Walsh7423c012016-10-04 10:27:21 -0500356 args_list = [arg.strip() for arg in args_list]
Michael Walshde791732016-09-06 14:25:24 -0500357
Michael Walsh7423c012016-10-04 10:27:21 -0500358 if arg_num > len(args_list):
Michael Walshde791732016-09-06 14:25:24 -0500359 print_error("Programmer error - The caller has asked for the name of" +
360 " argument number \"" + str(arg_num) + "\" but there " +
Michael Walsh7423c012016-10-04 10:27:21 -0500361 "were only \"" + str(len(args_list)) + "\" args used:\n" +
362 sprint_varx("args_list", args_list))
Michael Walshde791732016-09-06 14:25:24 -0500363 return
364
Michael Walsh7423c012016-10-04 10:27:21 -0500365 argument = args_list[arg_num - 1]
Michael Walshde791732016-09-06 14:25:24 -0500366
367 if local_debug:
Michael Walsh7423c012016-10-04 10:27:21 -0500368 print_varx("args_list", args_list, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500369 print_varx("argument", argument, 0, debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600370 print_dashes(0, 120)
Michael Walshde791732016-09-06 14:25:24 -0500371
372 return argument
373
Michael Walshde791732016-09-06 14:25:24 -0500374
Michael Walshde791732016-09-06 14:25:24 -0500375def sprint_time(buffer=""):
Michael Walshde791732016-09-06 14:25:24 -0500376 r"""
377 Return the time in the following format.
378
379 Example:
380
381 The following python code...
382
383 sys.stdout.write(sprint_time())
384 sys.stdout.write("Hi.\n")
385
386 Will result in the following type of output:
387
388 #(CDT) 2016/07/08 15:25:35 - Hi.
389
390 Example:
391
392 The following python code...
393
394 sys.stdout.write(sprint_time("Hi.\n"))
395
396 Will result in the following type of output:
397
398 #(CDT) 2016/08/03 17:12:05 - Hi.
399
400 The following environment variables will affect the formatting as
401 described:
402 NANOSECONDS This will cause the time stamps to be
403 precise to the microsecond (Yes, it
404 probably should have been named
405 MICROSECONDS but the convention was set
406 long ago so we're sticking with it).
407 Example of the output when environment
408 variable NANOSECONDS=1.
409
410 #(CDT) 2016/08/03 17:16:25.510469 - Hi.
411
412 SHOW_ELAPSED_TIME This will cause the elapsed time to be
413 included in the output. This is the
414 amount of time that has elapsed since the
415 last time this function was called. The
416 precision of the elapsed time field is
417 also affected by the value of the
418 NANOSECONDS environment variable. Example
419 of the output when environment variable
420 NANOSECONDS=0 and SHOW_ELAPSED_TIME=1.
421
422 #(CDT) 2016/08/03 17:17:40 - 0 - Hi.
423
424 Example of the output when environment variable NANOSECONDS=1 and
425 SHOW_ELAPSED_TIME=1.
426
427 #(CDT) 2016/08/03 17:18:47.317339 - 0.000046 - Hi.
428
429 Description of arguments.
430 buffer This will be appended to the formatted
431 time string.
432 """
433
434 global NANOSECONDS
435 global SHOW_ELAPSED_TIME
436 global sprint_time_last_seconds
437
438 seconds = time.time()
439 loc_time = time.localtime(seconds)
440 nanoseconds = "%0.6f" % seconds
441 pos = nanoseconds.find(".")
442 nanoseconds = nanoseconds[pos:]
443
444 time_string = time.strftime("#(%Z) %Y/%m/%d %H:%M:%S", loc_time)
445 if NANOSECONDS == "1":
446 time_string = time_string + nanoseconds
447
448 if SHOW_ELAPSED_TIME == "1":
449 cur_time_seconds = seconds
450 math_string = "%9.9f" % cur_time_seconds + " - " + "%9.9f" % \
451 sprint_time_last_seconds
452 elapsed_seconds = eval(math_string)
453 if NANOSECONDS == "1":
454 elapsed_seconds = "%11.6f" % elapsed_seconds
455 else:
456 elapsed_seconds = "%4i" % elapsed_seconds
457 sprint_time_last_seconds = cur_time_seconds
458 time_string = time_string + " - " + elapsed_seconds
459
460 return time_string + " - " + buffer
461
Michael Walshde791732016-09-06 14:25:24 -0500462
Michael Walshde791732016-09-06 14:25:24 -0500463def sprint_timen(buffer=""):
Michael Walshde791732016-09-06 14:25:24 -0500464 r"""
465 Append a line feed to the buffer, pass it to sprint_time and return the
466 result.
467 """
468
469 return sprint_time(buffer + "\n")
470
Michael Walshde791732016-09-06 14:25:24 -0500471
Michael Walshde791732016-09-06 14:25:24 -0500472def sprint_error(buffer=""):
Michael Walshde791732016-09-06 14:25:24 -0500473 r"""
474 Return a standardized error string. This includes:
475 - A time stamp
476 - The "**ERROR**" string
477 - The caller's buffer string.
478
479 Example:
480
481 The following python code...
482
483 print(sprint_error("Oops.\n"))
484
485 Will result in the following type of output:
486
487 #(CDT) 2016/08/03 17:12:05 - **ERROR** Oops.
488
489 Description of arguments.
490 buffer This will be appended to the formatted
491 error string.
492 """
493
494 return sprint_time() + "**ERROR** " + buffer
495
Michael Walshde791732016-09-06 14:25:24 -0500496
Michael Walshde791732016-09-06 14:25:24 -0500497def sprint_varx(var_name,
498 var_value,
499 hex=0,
500 loc_col1_indent=col1_indent,
Michael Walsh7423c012016-10-04 10:27:21 -0500501 loc_col1_width=col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500502 trailing_char="\n",
503 key_list=None):
Michael Walshde791732016-09-06 14:25:24 -0500504 r"""
505 Print the var name/value passed to it. If the caller lets loc_col1_width
506 default, the printing lines up nicely with output generated by the
507 print_time functions.
508
509 Note that the sprint_var function (defined below) can be used to call this
510 function so that the programmer does not need to pass the var_name.
511 sprint_var will figure out the var_name. The sprint_var function is the
512 one that would normally be used by the general user.
513
514 For example, the following python code:
515
516 first_name = "Mike"
517 print_time("Doing this...\n")
518 print_varx("first_name", first_name)
519 print_time("Doing that...\n")
520
521 Will generate output like this:
522
523 #(CDT) 2016/08/10 17:34:42.847374 - 0.001285 - Doing this...
524 first_name: Mike
525 #(CDT) 2016/08/10 17:34:42.847510 - 0.000136 - Doing that...
526
527 This function recognizes several complex types of data such as dict, list
528 or tuple.
529
530 For example, the following python code:
531
532 my_dict = dict(one=1, two=2, three=3)
533 print_var(my_dict)
534
535 Will generate the following output:
536
537 my_dict:
538 my_dict[three]: 3
539 my_dict[two]: 2
540 my_dict[one]: 1
541
542 Description of arguments.
543 var_name The name of the variable to be printed.
544 var_value The value of the variable to be printed.
545 hex This indicates that the value should be
546 printed in hex format. It is the user's
547 responsibility to ensure that a var_value
Michael Walshbec416d2016-11-10 08:54:52 -0600548 contains a valid hex number. For string
549 var_values, this will be interpreted as
550 show_blanks which means that blank values
Michael Walshd995cb02017-02-07 14:46:01 -0600551 will be printed as "<blank>". For dict
552 var_values, this will be interpreted as
553 terse format where keys are not repeated
554 in the output.
Michael Walshde791732016-09-06 14:25:24 -0500555 loc_col1_indent The number of spaces to indent the output.
556 loc_col1_width The width of the output column containing
557 the variable name. The default value of
558 this is adjusted so that the var_value
559 lines up with text printed via the
560 print_time function.
Michael Walsh7423c012016-10-04 10:27:21 -0500561 trailing_char The character to be used at the end of the
562 returned string. The default value is a
563 line feed.
Michael Walshd2869032018-03-22 16:12:11 -0500564 key_list A list of which dictionary keys should be
565 printed. All others keys will be skipped.
566 Each value in key_list will be regarded
567 as a regular expression and it will be
568 regarded as anchored to the beginning and
569 ends of the dictionary key being
570 referenced. For example if key_list is
571 ["one", "two"], the resulting regex used
572 will be "^one|two$", i.e. only keys "one"
573 and "two" from the var_value dictionary
574 will be printed. As another example, if
575 the caller were to specify a key_list of
576 ["one.*"], then only dictionary keys whose
577 names begin with "one" will be printed.
578 Note: This argument pertains only to
579 var_values which are dictionaries.
Michael Walsh7423c012016-10-04 10:27:21 -0500580 """
Michael Walshde791732016-09-06 14:25:24 -0500581
582 # Determine the type
Michael Walsh92ac3d02018-03-30 14:38:15 -0500583 if type(var_value) in (int, long, float, bool, str, unicode) \
Michael Walshde791732016-09-06 14:25:24 -0500584 or var_value is None:
585 # The data type is simple in the sense that it has no subordinate
586 # parts.
Michael Walsh7423c012016-10-04 10:27:21 -0500587 # Adjust loc_col1_width.
588 loc_col1_width = loc_col1_width - loc_col1_indent
Michael Walshde791732016-09-06 14:25:24 -0500589 # See if the user wants the output in hex format.
590 if hex:
Michael Walsh18176322016-11-15 15:11:21 -0600591 if type(var_value) not in (int, long):
Michael Walshbec416d2016-11-10 08:54:52 -0600592 value_format = "%s"
Michael Walsh2795edc2016-12-13 16:00:33 -0600593 if var_value == "":
Michael Walshbec416d2016-11-10 08:54:52 -0600594 var_value = "<blank>"
595 else:
Michael Walsh92ac3d02018-03-30 14:38:15 -0500596 if type(var_value) is long or var_value >= 0x100000000:
597 value_format = "0x%16x"
598 else:
599 value_format = "0x%08x"
Michael Walshde791732016-09-06 14:25:24 -0500600 else:
601 value_format = "%s"
602 format_string = "%" + str(loc_col1_indent) + "s%-" \
Michael Walsh7423c012016-10-04 10:27:21 -0500603 + str(loc_col1_width) + "s" + value_format + trailing_char
Michael Walsh3383e652017-09-01 17:10:59 -0500604 if value_format == "0x%08x":
605 return format_string % ("", str(var_name) + ":",
606 var_value & 0xffffffff)
607 else:
608 return format_string % ("", str(var_name) + ":", var_value)
Michael Walsh20a87ab2017-06-30 17:00:30 -0500609 elif type(var_value) is type:
610 return sprint_varx(var_name, str(var_value).split("'")[1], hex,
Michael Walshd2869032018-03-22 16:12:11 -0500611 loc_col1_indent, loc_col1_width, trailing_char,
612 key_list)
Michael Walshde791732016-09-06 14:25:24 -0500613 else:
614 # The data type is complex in the sense that it has subordinate parts.
615 format_string = "%" + str(loc_col1_indent) + "s%s\n"
616 buffer = format_string % ("", var_name + ":")
617 loc_col1_indent += 2
Michael Walsh7423c012016-10-04 10:27:21 -0500618 try:
619 length = len(var_value)
620 except TypeError:
Michael Walsh23e7f492017-01-10 11:34:47 -0600621 length = 0
Michael Walsh7423c012016-10-04 10:27:21 -0500622 ix = 0
623 loc_trailing_char = "\n"
Michael Walshbec416d2016-11-10 08:54:52 -0600624 type_is_dict = 0
Michael Walsh23e7f492017-01-10 11:34:47 -0600625 if type(var_value) is dict:
626 type_is_dict = 1
Michael Walsh8e6deb42017-01-27 14:22:41 -0600627 try:
628 if type(var_value) is collections.OrderedDict:
629 type_is_dict = 1
630 except AttributeError:
631 pass
632 try:
633 if type(var_value) is DotDict:
634 type_is_dict = 1
635 except NameError:
636 pass
637 try:
638 if type(var_value) is NormalizedDict:
639 type_is_dict = 1
640 except NameError:
641 pass
Michael Walshbec416d2016-11-10 08:54:52 -0600642 if type_is_dict:
Michael Walshde791732016-09-06 14:25:24 -0500643 for key, value in var_value.iteritems():
Michael Walshd2869032018-03-22 16:12:11 -0500644 if key_list is not None:
645 key_list_regex = "^" + "|".join(key_list) + "$"
646 if not re.match(key_list_regex, key):
647 continue
Michael Walsh7423c012016-10-04 10:27:21 -0500648 ix += 1
649 if ix == length:
650 loc_trailing_char = trailing_char
Michael Walshd995cb02017-02-07 14:46:01 -0600651 if hex:
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600652 # Since hex is being used as a format type, we want it
653 # turned off when processing integer dictionary values so
654 # it is not interpreted as a hex indicator.
655 loc_hex = not (type(value) is int)
Michael Walshf7b8a002017-08-29 10:38:39 -0500656 buffer += sprint_varx("[" + key + "]", value,
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600657 loc_hex, loc_col1_indent,
658 loc_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500659 loc_trailing_char,
660 key_list)
Michael Walshd995cb02017-02-07 14:46:01 -0600661 else:
662 buffer += sprint_varx(var_name + "[" + key + "]", value,
663 hex, loc_col1_indent, loc_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500664 loc_trailing_char, key_list)
Michael Walsh7423c012016-10-04 10:27:21 -0500665 elif type(var_value) in (list, tuple, set):
Michael Walshde791732016-09-06 14:25:24 -0500666 for key, value in enumerate(var_value):
Michael Walsh7423c012016-10-04 10:27:21 -0500667 ix += 1
668 if ix == length:
669 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500670 buffer += sprint_varx(var_name + "[" + str(key) + "]", value,
Michael Walsh7423c012016-10-04 10:27:21 -0500671 hex, loc_col1_indent, loc_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500672 loc_trailing_char, key_list)
Michael Walshde791732016-09-06 14:25:24 -0500673 elif type(var_value) is argparse.Namespace:
674 for key in var_value.__dict__:
Michael Walsh7423c012016-10-04 10:27:21 -0500675 ix += 1
676 if ix == length:
677 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500678 cmd_buf = "buffer += sprint_varx(var_name + \".\" + str(key)" \
Michael Walsh7423c012016-10-04 10:27:21 -0500679 + ", var_value." + key + ", hex, loc_col1_indent," \
Michael Walshd2869032018-03-22 16:12:11 -0500680 + " loc_col1_width, loc_trailing_char, key_list)"
Michael Walshde791732016-09-06 14:25:24 -0500681 exec(cmd_buf)
682 else:
683 var_type = type(var_value).__name__
684 func_name = sys._getframe().f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -0500685 var_value = "<" + var_type + " type not supported by " + \
686 func_name + "()>"
Michael Walshde791732016-09-06 14:25:24 -0500687 value_format = "%s"
688 loc_col1_indent -= 2
Michael Walsh7423c012016-10-04 10:27:21 -0500689 # Adjust loc_col1_width.
690 loc_col1_width = loc_col1_width - loc_col1_indent
Michael Walshde791732016-09-06 14:25:24 -0500691 format_string = "%" + str(loc_col1_indent) + "s%-" \
Michael Walsh7423c012016-10-04 10:27:21 -0500692 + str(loc_col1_width) + "s" + value_format + trailing_char
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600693 return format_string % ("", str(var_name) + ":", var_value)
Michael Walsh23e7f492017-01-10 11:34:47 -0600694
Michael Walshde791732016-09-06 14:25:24 -0500695 return buffer
696
697 return ""
698
Michael Walshde791732016-09-06 14:25:24 -0500699
Michael Walshfd2733c2017-11-13 11:36:20 -0600700def sprint_var(var_value,
701 hex=0,
702 loc_col1_indent=col1_indent,
703 loc_col1_width=col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500704 trailing_char="\n",
705 key_list=None):
Michael Walshde791732016-09-06 14:25:24 -0500706 r"""
707 Figure out the name of the first argument for you and then call
708 sprint_varx with it. Therefore, the following 2 calls are equivalent:
709 sprint_varx("var1", var1)
710 sprint_var(var1)
711 """
712
713 # Get the name of the first variable passed to this function.
714 stack_frame = 2
Michael Walsh7423c012016-10-04 10:27:21 -0500715 caller_func_name = sprint_func_name(2)
716 if caller_func_name.endswith("print_var"):
Michael Walshde791732016-09-06 14:25:24 -0500717 stack_frame += 1
718 var_name = get_arg_name(None, 1, stack_frame)
Michael Walshfd2733c2017-11-13 11:36:20 -0600719 return sprint_varx(var_name, var_value=var_value, hex=hex,
720 loc_col1_indent=loc_col1_indent,
721 loc_col1_width=loc_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500722 trailing_char=trailing_char,
723 key_list=key_list)
Michael Walshde791732016-09-06 14:25:24 -0500724
725
Michael Walsh18176322016-11-15 15:11:21 -0600726def sprint_vars(*args):
Michael Walsh18176322016-11-15 15:11:21 -0600727 r"""
728 Sprint the values of one or more variables.
729
730 Description of args:
731 args:
732 If the first argument is an integer, it will be interpreted to be the
733 "indent" value.
734 If the second argument is an integer, it will be interpreted to be the
735 "col1_width" value.
736 If the third argument is an integer, it will be interpreted to be the
737 "hex" value.
738 All remaining parms are considered variable names which are to be
739 sprinted.
740 """
741
742 if len(args) == 0:
743 return
744
745 # Get the name of the first variable passed to this function.
746 stack_frame = 2
747 caller_func_name = sprint_func_name(2)
748 if caller_func_name.endswith("print_vars"):
749 stack_frame += 1
750
751 parm_num = 1
752
753 # Create list from args (which is a tuple) so that it can be modified.
754 args_list = list(args)
755
756 var_name = get_arg_name(None, parm_num, stack_frame)
757 # See if parm 1 is to be interpreted as "indent".
758 try:
759 if type(int(var_name)) is int:
760 indent = int(var_name)
761 args_list.pop(0)
762 parm_num += 1
763 except ValueError:
764 indent = 0
765
766 var_name = get_arg_name(None, parm_num, stack_frame)
767 # See if parm 1 is to be interpreted as "col1_width".
768 try:
769 if type(int(var_name)) is int:
770 loc_col1_width = int(var_name)
771 args_list.pop(0)
772 parm_num += 1
773 except ValueError:
774 loc_col1_width = col1_width
775
776 var_name = get_arg_name(None, parm_num, stack_frame)
777 # See if parm 1 is to be interpreted as "hex".
778 try:
779 if type(int(var_name)) is int:
780 hex = int(var_name)
781 args_list.pop(0)
782 parm_num += 1
783 except ValueError:
784 hex = 0
785
786 buffer = ""
787 for var_value in args_list:
788 var_name = get_arg_name(None, parm_num, stack_frame)
789 buffer += sprint_varx(var_name, var_value, hex, indent, loc_col1_width)
790 parm_num += 1
791
792 return buffer
793
Michael Walsh18176322016-11-15 15:11:21 -0600794
Michael Walsh7423c012016-10-04 10:27:21 -0500795def sprint_dashes(indent=col1_indent,
796 width=80,
797 line_feed=1,
798 char="-"):
Michael Walshde791732016-09-06 14:25:24 -0500799 r"""
800 Return a string of dashes to the caller.
801
Michael Walsh2ee77cd2017-03-08 11:50:17 -0600802 Description of arguments:
Michael Walshde791732016-09-06 14:25:24 -0500803 indent The number of characters to indent the
804 output.
805 width The width of the string of dashes.
806 line_feed Indicates whether the output should end
807 with a line feed.
Michael Walsh7423c012016-10-04 10:27:21 -0500808 char The character to be repeated in the output
809 string.
Michael Walshde791732016-09-06 14:25:24 -0500810 """
811
Michael Walsh7423c012016-10-04 10:27:21 -0500812 width = int(width)
Michael Walsh23e7f492017-01-10 11:34:47 -0600813 buffer = " " * int(indent) + char * width
Michael Walshde791732016-09-06 14:25:24 -0500814 if line_feed:
815 buffer += "\n"
816
817 return buffer
818
Michael Walshde791732016-09-06 14:25:24 -0500819
Michael Walsh7423c012016-10-04 10:27:21 -0500820def sindent(text="",
821 indent=0):
Michael Walsh7423c012016-10-04 10:27:21 -0500822 r"""
823 Pre-pend the specified number of characters to the text string (i.e.
824 indent it) and return it.
825
826 Description of arguments:
827 text The string to be indented.
828 indent The number of characters to indent the
829 string.
830 """
831
832 format_string = "%" + str(indent) + "s%s"
833 buffer = format_string % ("", text)
834
835 return buffer
836
Michael Walsh7423c012016-10-04 10:27:21 -0500837
Michael Walsh7423c012016-10-04 10:27:21 -0500838def sprint_call_stack(indent=0,
839 stack_frame_ix=0):
Michael Walshde791732016-09-06 14:25:24 -0500840 r"""
841 Return a call stack report for the given point in the program with line
842 numbers, function names and function parameters and arguments.
843
844 Sample output:
845
846 -------------------------------------------------------------------------
847 Python function call stack
848
849 Line # Function name and arguments
850 ------ ------------------------------------------------------------------
851 424 sprint_call_stack ()
852 4 print_call_stack ()
853 31 func1 (last_name = 'walsh', first_name = 'mikey')
854 59 /tmp/scr5.py
855 -------------------------------------------------------------------------
856
857 Description of arguments:
858 indent The number of characters to indent each
859 line of output.
860 stack_frame_ix The index of the first stack frame which
861 is to be returned.
862 """
863
864 buffer = ""
Michael Walsh7423c012016-10-04 10:27:21 -0500865 buffer += sprint_dashes(indent)
866 buffer += sindent("Python function call stack\n\n", indent)
867 buffer += sindent("Line # Function name and arguments\n", indent)
868 buffer += sprint_dashes(indent, 6, 0) + " " + sprint_dashes(0, 73)
Michael Walshde791732016-09-06 14:25:24 -0500869
870 # Grab the current program stack.
871 current_stack = inspect.stack()
872
873 # Process each frame in turn.
874 format_string = "%6s %s\n"
Michael Walsh7423c012016-10-04 10:27:21 -0500875 ix = 0
Michael Walshde791732016-09-06 14:25:24 -0500876 for stack_frame in current_stack:
Michael Walsh7423c012016-10-04 10:27:21 -0500877 if ix < stack_frame_ix:
878 ix += 1
879 continue
Michael Walsh23e7f492017-01-10 11:34:47 -0600880 # I want the line number shown to be the line where you find the line
881 # shown.
882 try:
883 line_num = str(current_stack[ix + 1][2])
884 except IndexError:
885 line_num = ""
Michael Walshde791732016-09-06 14:25:24 -0500886 func_name = str(stack_frame[3])
887 if func_name == "?":
888 # "?" is the name used when code is not in a function.
889 func_name = "(none)"
890
891 if func_name == "<module>":
Michael Walsh7423c012016-10-04 10:27:21 -0500892 # If the func_name is the "main" program, we simply get the
893 # command line call string.
Michael Walshde791732016-09-06 14:25:24 -0500894 func_and_args = ' '.join(sys.argv)
895 else:
896 # Get the program arguments.
897 arg_vals = inspect.getargvalues(stack_frame[0])
898 function_parms = arg_vals[0]
899 frame_locals = arg_vals[3]
900
Michael Walsh7423c012016-10-04 10:27:21 -0500901 args_list = []
Michael Walshde791732016-09-06 14:25:24 -0500902 for arg_name in function_parms:
903 # Get the arg value from frame locals.
904 arg_value = frame_locals[arg_name]
Michael Walsh7423c012016-10-04 10:27:21 -0500905 args_list.append(arg_name + " = " + repr(arg_value))
906 args_str = "(" + ', '.join(map(str, args_list)) + ")"
Michael Walshde791732016-09-06 14:25:24 -0500907
908 # Now we need to print this in a nicely-wrapped way.
909 func_and_args = func_name + " " + args_str
910
Michael Walsh23e7f492017-01-10 11:34:47 -0600911 buffer += sindent(format_string % (line_num, func_and_args), indent)
Michael Walsh7423c012016-10-04 10:27:21 -0500912 ix += 1
Michael Walshde791732016-09-06 14:25:24 -0500913
Michael Walsh7423c012016-10-04 10:27:21 -0500914 buffer += sprint_dashes(indent)
Michael Walshde791732016-09-06 14:25:24 -0500915
916 return buffer
917
Michael Walshde791732016-09-06 14:25:24 -0500918
Michael Walshde791732016-09-06 14:25:24 -0500919def sprint_executing(stack_frame_ix=None):
Michael Walshde791732016-09-06 14:25:24 -0500920 r"""
921 Print a line indicating what function is executing and with what parameter
922 values. This is useful for debugging.
923
924 Sample output:
925
926 #(CDT) 2016/08/25 17:54:27 - Executing: func1 (x = 1)
927
928 Description of arguments:
929 stack_frame_ix The index of the stack frame whose
930 function info should be returned. If the
Michael Walsh2ee77cd2017-03-08 11:50:17 -0600931 caller does not specify a value, this
Michael Walshde791732016-09-06 14:25:24 -0500932 function will set the value to 1 which is
933 the index of the caller's stack frame. If
934 the caller is the wrapper function
935 "print_executing", this function will bump
936 it up by 1.
937 """
938
939 # If user wants default stack_frame_ix.
940 if stack_frame_ix is None:
941 func_name = sys._getframe().f_code.co_name
942 caller_func_name = sys._getframe(1).f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -0500943 if caller_func_name.endswith(func_name[1:]):
Michael Walshde791732016-09-06 14:25:24 -0500944 stack_frame_ix = 2
945 else:
946 stack_frame_ix = 1
947
948 stack_frame = inspect.stack()[stack_frame_ix]
949
950 func_name = str(stack_frame[3])
951 if func_name == "?":
952 # "?" is the name used when code is not in a function.
953 func_name = "(none)"
954
955 if func_name == "<module>":
956 # If the func_name is the "main" program, we simply get the command
957 # line call string.
958 func_and_args = ' '.join(sys.argv)
959 else:
960 # Get the program arguments.
961 arg_vals = inspect.getargvalues(stack_frame[0])
962 function_parms = arg_vals[0]
963 frame_locals = arg_vals[3]
964
Michael Walsh7423c012016-10-04 10:27:21 -0500965 args_list = []
Michael Walshde791732016-09-06 14:25:24 -0500966 for arg_name in function_parms:
967 # Get the arg value from frame locals.
968 arg_value = frame_locals[arg_name]
Michael Walsh7423c012016-10-04 10:27:21 -0500969 args_list.append(arg_name + " = " + repr(arg_value))
970 args_str = "(" + ', '.join(map(str, args_list)) + ")"
Michael Walshde791732016-09-06 14:25:24 -0500971
972 # Now we need to print this in a nicely-wrapped way.
973 func_and_args = func_name + " " + args_str
974
975 return sprint_time() + "Executing: " + func_and_args + "\n"
976
Michael Walshde791732016-09-06 14:25:24 -0500977
Michael Walshbec416d2016-11-10 08:54:52 -0600978def sprint_pgm_header(indent=0,
979 linefeed=1):
Michael Walshde791732016-09-06 14:25:24 -0500980 r"""
981 Return a standardized header that programs should print at the beginning
982 of the run. It includes useful information like command line, pid,
983 userid, program parameters, etc.
984
Michael Walsh7423c012016-10-04 10:27:21 -0500985 Description of arguments:
986 indent The number of characters to indent each
987 line of output.
Michael Walshbec416d2016-11-10 08:54:52 -0600988 linefeed Indicates whether a line feed be included
989 at the beginning and end of the report.
Michael Walshde791732016-09-06 14:25:24 -0500990 """
991
Michael Walshbec416d2016-11-10 08:54:52 -0600992 loc_col1_width = col1_width + indent
993
994 buffer = ""
995 if linefeed:
996 buffer = "\n"
Michael Walsh7423c012016-10-04 10:27:21 -0500997
Michael Walshdb6e68a2017-05-23 17:55:31 -0500998 if robot_env:
999 suite_name = BuiltIn().get_variable_value("${suite_name}")
1000 buffer += sindent(sprint_time("Running test suite \"" + suite_name +
Gunnar Mills096cd562018-03-26 10:19:12 -05001001 "\".\n"), indent)
Michael Walshdb6e68a2017-05-23 17:55:31 -05001002
Michael Walsh7423c012016-10-04 10:27:21 -05001003 buffer += sindent(sprint_time() + "Running " + pgm_name + ".\n", indent)
1004 buffer += sindent(sprint_time() + "Program parameter values, etc.:\n\n",
1005 indent)
Michael Walshbec416d2016-11-10 08:54:52 -06001006 buffer += sprint_varx("command_line", ' '.join(sys.argv), 0, indent,
1007 loc_col1_width)
Michael Walsh7423c012016-10-04 10:27:21 -05001008 # We want the output to show a customized name for the pid and pgid but
1009 # we want it to look like a valid variable name. Therefore, we'll use
Michael Walshde791732016-09-06 14:25:24 -05001010 # pgm_name_var_name which was set when this module was imported.
Michael Walshbec416d2016-11-10 08:54:52 -06001011 buffer += sprint_varx(pgm_name_var_name + "_pid", os.getpid(), 0, indent,
1012 loc_col1_width)
1013 buffer += sprint_varx(pgm_name_var_name + "_pgid", os.getpgrp(), 0, indent,
1014 loc_col1_width)
Michael Walsh86de0d22016-12-05 10:13:15 -06001015 userid_num = str(os.geteuid())
1016 try:
1017 username = os.getlogin()
1018 except OSError:
1019 if userid_num == "0":
1020 username = "root"
1021 else:
1022 username = "?"
1023 buffer += sprint_varx("uid", userid_num + " (" + username +
Michael Walshbec416d2016-11-10 08:54:52 -06001024 ")", 0, indent, loc_col1_width)
Michael Walshde791732016-09-06 14:25:24 -05001025 buffer += sprint_varx("gid", str(os.getgid()) + " (" +
Michael Walsh7423c012016-10-04 10:27:21 -05001026 str(grp.getgrgid(os.getgid()).gr_name) + ")", 0,
Michael Walshbec416d2016-11-10 08:54:52 -06001027 indent, loc_col1_width)
1028 buffer += sprint_varx("host_name", socket.gethostname(), 0, indent,
1029 loc_col1_width)
Michael Walsh86de0d22016-12-05 10:13:15 -06001030 try:
1031 DISPLAY = os.environ['DISPLAY']
1032 except KeyError:
1033 DISPLAY = ""
1034 buffer += sprint_varx("DISPLAY", DISPLAY, 0, indent,
Michael Walshbec416d2016-11-10 08:54:52 -06001035 loc_col1_width)
Michael Walshde791732016-09-06 14:25:24 -05001036 # I want to add code to print caller's parms.
1037
Michael Walsh7423c012016-10-04 10:27:21 -05001038 # __builtin__.arg_obj is created by the get_arg module function,
1039 # gen_get_options.
1040 try:
1041 buffer += ga.sprint_args(__builtin__.arg_obj, indent)
1042 except AttributeError:
1043 pass
1044
Michael Walshdb6e68a2017-05-23 17:55:31 -05001045 if robot_env:
1046 # Get value of global parm_list.
1047 parm_list = BuiltIn().get_variable_value("${parm_list}")
1048
1049 for parm in parm_list:
1050 parm_value = BuiltIn().get_variable_value("${" + parm + "}")
1051 buffer += sprint_varx(parm, parm_value, 0, indent, loc_col1_width)
1052
1053 # Setting global program_pid.
1054 BuiltIn().set_global_variable("${program_pid}", os.getpid())
1055
Michael Walshbec416d2016-11-10 08:54:52 -06001056 if linefeed:
1057 buffer += "\n"
Michael Walshde791732016-09-06 14:25:24 -05001058
1059 return buffer
1060
Michael Walshde791732016-09-06 14:25:24 -05001061
Michael Walsh7423c012016-10-04 10:27:21 -05001062def sprint_error_report(error_text="\n",
Michael Walshdb6e68a2017-05-23 17:55:31 -05001063 indent=2,
1064 format=None):
Michael Walsh7423c012016-10-04 10:27:21 -05001065 r"""
1066 Return a string with a standardized report which includes the caller's
1067 error text, the call stack and the program header.
1068
1069 Description of args:
1070 error_text The error text to be included in the
1071 report. The caller should include any
1072 needed linefeeds.
1073 indent The number of characters to indent each
1074 line of output.
Michael Walshdb6e68a2017-05-23 17:55:31 -05001075 format Long or short format. Long includes
1076 extras like lines of dashes, call stack,
1077 etc.
Michael Walsh7423c012016-10-04 10:27:21 -05001078 """
1079
Michael Walshdb6e68a2017-05-23 17:55:31 -05001080 # Process input.
1081 indent = int(indent)
1082 if format is None:
1083 if robot_env:
1084 format = 'short'
1085 else:
1086 format = 'long'
1087 error_text = error_text.rstrip('\n') + '\n'
1088
1089 if format == 'short':
1090 return sprint_error(error_text)
1091
Michael Walsh7423c012016-10-04 10:27:21 -05001092 buffer = ""
1093 buffer += sprint_dashes(width=120, char="=")
1094 buffer += sprint_error(error_text)
1095 buffer += "\n"
1096 # Calling sprint_call_stack with stack_frame_ix of 0 causes it to show
1097 # itself and this function in the call stack. This is not helpful to a
1098 # debugger and is therefore clutter. We will adjust the stack_frame_ix to
1099 # hide that information.
Michael Walsh9c75f672017-09-12 17:11:35 -05001100 stack_frame_ix = 1
Michael Walsh7423c012016-10-04 10:27:21 -05001101 caller_func_name = sprint_func_name(2)
1102 if caller_func_name.endswith("print_error_report"):
1103 stack_frame_ix += 1
Michael Walshdb6e68a2017-05-23 17:55:31 -05001104 if not robot_env:
1105 buffer += sprint_call_stack(indent, stack_frame_ix)
Michael Walsh7423c012016-10-04 10:27:21 -05001106 buffer += sprint_pgm_header(indent)
1107 buffer += sprint_dashes(width=120, char="=")
1108
1109 return buffer
1110
Michael Walsh7423c012016-10-04 10:27:21 -05001111
Michael Walsh18176322016-11-15 15:11:21 -06001112def sprint_issuing(cmd_buf,
1113 test_mode=0):
Michael Walshde791732016-09-06 14:25:24 -05001114 r"""
1115 Return a line indicating a command that the program is about to execute.
1116
1117 Sample output for a cmd_buf of "ls"
1118
1119 #(CDT) 2016/08/25 17:57:36 - Issuing: ls
Michael Walshbec416d2016-11-10 08:54:52 -06001120
Michael Walshde791732016-09-06 14:25:24 -05001121 Description of args:
1122 cmd_buf The command to be executed by caller.
Michael Walshbec416d2016-11-10 08:54:52 -06001123 test_mode With test_mode set, your output will look
1124 like this:
1125
1126 #(CDT) 2016/08/25 17:57:36 - (test_mode) Issuing: ls
1127
Michael Walshde791732016-09-06 14:25:24 -05001128 """
1129
Michael Walshbec416d2016-11-10 08:54:52 -06001130 buffer = sprint_time()
1131 if test_mode:
1132 buffer += "(test_mode) "
1133 buffer += "Issuing: " + cmd_buf + "\n"
Michael Walshde791732016-09-06 14:25:24 -05001134
1135 return buffer
1136
Michael Walshde791732016-09-06 14:25:24 -05001137
Michael Walshde791732016-09-06 14:25:24 -05001138def sprint_pgm_footer():
Michael Walshde791732016-09-06 14:25:24 -05001139 r"""
1140 Return a standardized footer that programs should print at the end of the
1141 program run. It includes useful information like total run time, etc.
1142 """
1143
1144 buffer = "\n" + sprint_time() + "Finished running " + pgm_name + ".\n\n"
1145
1146 total_time = time.time() - start_time
1147 total_time_string = "%0.6f" % total_time
1148
Michael Walsh7423c012016-10-04 10:27:21 -05001149 buffer += sprint_varx(pgm_name_var_name + "_runtime", total_time_string)
Michael Walshbec416d2016-11-10 08:54:52 -06001150 buffer += "\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001151
1152 return buffer
1153
Michael Walsh7423c012016-10-04 10:27:21 -05001154
Michael Walsh7423c012016-10-04 10:27:21 -05001155def sprint(buffer=""):
Michael Walsh7423c012016-10-04 10:27:21 -05001156 r"""
1157 Simply return the user's buffer. This function is used by the qprint and
1158 dprint functions defined dynamically below, i.e. it would not normally be
1159 called for general use.
1160
1161 Description of arguments.
1162 buffer This will be returned to the caller.
1163 """
Michael Walshde791732016-09-06 14:25:24 -05001164
Michael Walsh95e45102018-02-09 12:44:43 -06001165 try:
1166 return str(buffer)
1167 except UnicodeEncodeError:
1168 return buffer
Michael Walshbec416d2016-11-10 08:54:52 -06001169
Michael Walshbec416d2016-11-10 08:54:52 -06001170
Michael Walshbec416d2016-11-10 08:54:52 -06001171def sprintn(buffer=""):
Michael Walshbec416d2016-11-10 08:54:52 -06001172 r"""
1173 Simply return the user's buffer with a line feed. This function is used
1174 by the qprint and dprint functions defined dynamically below, i.e. it
1175 would not normally be called for general use.
1176
1177 Description of arguments.
1178 buffer This will be returned to the caller.
1179 """
1180
Michael Walsh95e45102018-02-09 12:44:43 -06001181 try:
1182 buffer = str(buffer) + "\n"
1183 except UnicodeEncodeError:
1184 buffer = buffer + "\n"
Michael Walshbec416d2016-11-10 08:54:52 -06001185
Michael Walshde791732016-09-06 14:25:24 -05001186 return buffer
1187
Michael Walsh168eb0f2017-12-01 15:35:32 -06001188
Michael Walshfd2733c2017-11-13 11:36:20 -06001189def gp_print(buffer,
1190 stream='stdout'):
Michael Walshfd2733c2017-11-13 11:36:20 -06001191 r"""
1192 Print the buffer using either sys.stdout.write or BuiltIn().log_to_console
1193 depending on whether we are running in a robot environment.
1194
1195 This function is intended for use only by other functions in this module.
1196
1197 Description of arguments:
1198 buffer The string to be printed.
1199 stream Either "stdout" or "stderr".
1200 """
1201
1202 if robot_env:
1203 BuiltIn().log_to_console(buffer, stream=stream, no_newline=True)
1204 else:
1205 if stream == "stdout":
1206 sys.stdout.write(buffer)
1207 sys.stdout.flush()
1208 else:
1209 sys.stderr.write(buffer)
1210 sys.stderr.flush()
Michael Walshde791732016-09-06 14:25:24 -05001211
1212
Michael Walsh168eb0f2017-12-01 15:35:32 -06001213def gp_log(buffer):
Michael Walsh168eb0f2017-12-01 15:35:32 -06001214 r"""
1215 Log the buffer using either python logging or BuiltIn().log depending on
1216 whether we are running in a robot environment.
1217
1218 This function is intended for use only by other functions in this module.
1219
1220 Description of arguments:
1221 buffer The string to be logged.
1222 """
1223
1224 if robot_env:
1225 BuiltIn().log(buffer)
1226 else:
1227 logging.warning(buffer)
1228
1229
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001230def gp_debug_print(buffer):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001231 r"""
Michael Walshfd2733c2017-11-13 11:36:20 -06001232 Print with gp_print only if gen_print_debug is set.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001233
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 printed.
1238 """
1239
1240 if not gen_print_debug:
1241 return
1242
Michael Walshfd2733c2017-11-13 11:36:20 -06001243 gp_print(buffer)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001244
1245
Michael Walshb1500152017-04-12 15:42:43 -05001246def get_var_value(var_value=None,
1247 default=1,
1248 var_name=None):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001249 r"""
Michael Walshb1500152017-04-12 15:42:43 -05001250 Return either var_value, the corresponding global value or default.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001251
Michael Walshb1500152017-04-12 15:42:43 -05001252 If var_value is not None, it will simply be returned.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001253
Michael Walshb1500152017-04-12 15:42:43 -05001254 If var_value is None, this function will return the corresponding global
1255 value of the variable in question.
1256
1257 Note: For global values, if we are in a robot environment,
1258 get_variable_value will be used. Otherwise, the __builtin__ version of
1259 the variable is returned (which are set by gen_arg.py functions).
1260
1261 If there is no global value associated with the variable, default is
1262 returned.
1263
1264 This function is useful for other functions in setting default values for
1265 parameters.
1266
1267 Example use:
1268
1269 def my_func(quiet=None):
1270
1271 quiet = int(get_var_value(quiet, 0))
1272
1273 Example calls to my_func():
1274
1275 In the following example, the caller is explicitly asking to have quiet be
1276 set to 1.
1277
1278 my_func(quiet=1)
1279
1280 In the following example, quiet will be set to the global value of quiet,
1281 if defined, or to 0 (the default).
1282
1283 my_func()
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001284
1285 Description of arguments:
Michael Walshb1500152017-04-12 15:42:43 -05001286 var_value The value to be returned (if not equal to
1287 None).
1288 default The value that is returned if var_value is
1289 None and there is no corresponding global
1290 value defined.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001291 var_name The name of the variable whose value is to
Michael Walshb1500152017-04-12 15:42:43 -05001292 be returned. Under most circumstances,
1293 this value need not be provided. This
1294 function can figure out the name of the
1295 variable passed as var_value. One
1296 exception to this would be if this
1297 function is called directly from a .robot
1298 file.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001299 """
1300
Michael Walshb1500152017-04-12 15:42:43 -05001301 if var_value is not None:
1302 return var_value
1303
1304 if var_name is None:
1305 var_name = get_arg_name(None, 1, 2)
1306
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001307 if robot_env:
Michael Walshc6537442017-06-06 15:33:52 -05001308 var_value = BuiltIn().get_variable_value("${" + var_name + "}",
1309 default)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001310 else:
1311 var_value = getattr(__builtin__, var_name, default)
1312
1313 return var_value
1314
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001315
Michael Walsh82acf002017-05-04 14:33:05 -05001316# hidden_text is a list of passwords which are to be replaced with asterisks
1317# by print functions defined in this module.
1318hidden_text = []
1319# password_regex is created based on the contents of hidden_text.
1320password_regex = ""
1321
1322
Michael Walsh82acf002017-05-04 14:33:05 -05001323def register_passwords(*args):
Michael Walsh82acf002017-05-04 14:33:05 -05001324 r"""
1325 Register one or more passwords which are to be hidden in output produced
1326 by the print functions in this module.
1327
1328 Note: Blank password values are NOT registered. They are simply ignored.
1329
1330 Description of argument(s):
1331 args One or more password values. If a given
1332 password value is already registered, this
1333 function will simply do nothing.
1334 """
1335
1336 global hidden_text
1337 global password_regex
1338
1339 for password in args:
1340 if password == "":
1341 break
1342 if password in hidden_text:
1343 break
1344
1345 # Place the password into the hidden_text list.
1346 hidden_text.append(password)
1347 # Create a corresponding password regular expression. Escape regex
1348 # special characters too.
1349 password_regex = '(' +\
1350 '|'.join([re.escape(x) for x in hidden_text]) + ')'
1351
Michael Walsh82acf002017-05-04 14:33:05 -05001352
Michael Walsh82acf002017-05-04 14:33:05 -05001353def replace_passwords(buffer):
Michael Walsh82acf002017-05-04 14:33:05 -05001354 r"""
1355 Return the buffer but with all registered passwords replaced by a string
1356 of asterisks.
1357
1358
1359 Description of argument(s):
1360 buffer The string to be returned but with
1361 passwords replaced.
1362 """
1363
1364 global password_regex
1365
1366 if int(os.environ.get("DEBUG_SHOW_PASSWORDS", "0")):
1367 return buffer
1368
1369 if password_regex == "":
1370 # No passwords to replace.
1371 return buffer
1372
1373 return re.sub(password_regex, "********", buffer)
1374
Michael Walshfd2733c2017-11-13 11:36:20 -06001375
1376def create_print_wrapper_funcs(func_names,
1377 stderr_func_names,
1378 replace_dict):
Michael Walshfd2733c2017-11-13 11:36:20 -06001379 r"""
1380 Generate code for print wrapper functions and return the generated code as
1381 a string.
1382
1383 To illustrate, suppose there is a "print_foo_bar" function in the
1384 func_names list.
1385 This function will...
1386 - Expect that there is an sprint_foo_bar function already in existence.
1387 - Create a print_foo_bar function which calls sprint_foo_bar and prints
1388 the result.
1389 - Create a qprint_foo_bar function which calls upon sprint_foo_bar only if
1390 global value quiet is 0.
1391 - Create a dprint_foo_bar function which calls upon sprint_foo_bar only if
1392 global value debug is 1.
1393
1394 Also, code will be generated to define aliases for each function as well.
1395 Each alias will be created by replacing "print_" in the function name with
1396 "p" For example, the alias for print_foo_bar will be pfoo_bar.
1397
1398 Description of argument(s):
1399 func_names A list of functions for which print
1400 wrapper function code is to be generated.
1401 stderr_func_names A list of functions whose generated code
1402 should print to stderr rather than to
1403 stdout.
1404 replace_dict Please see the create_func_def_string
1405 function in wrap_utils.py for details on
1406 this parameter. This parameter will be
1407 passed directly to create_func_def_string.
1408 """
1409
1410 buffer = ""
1411
1412 for func_name in func_names:
1413 if func_name in stderr_func_names:
1414 replace_dict['output_stream'] = "stderr"
1415 else:
1416 replace_dict['output_stream'] = "stdout"
1417
1418 s_func_name = "s" + func_name
1419 q_func_name = "q" + func_name
1420 d_func_name = "d" + func_name
1421
1422 # We don't want to try to redefine the "print" function, thus the
1423 # following if statement.
1424 if func_name != "print":
1425 func_def = create_func_def_string(s_func_name, func_name,
1426 print_func_template,
1427 replace_dict)
1428 buffer += func_def
1429
1430 func_def = create_func_def_string(s_func_name, "q" + func_name,
1431 qprint_func_template, replace_dict)
1432 buffer += func_def
1433
1434 func_def = create_func_def_string(s_func_name, "d" + func_name,
1435 dprint_func_template, replace_dict)
1436 buffer += func_def
1437
Michael Walsh168eb0f2017-12-01 15:35:32 -06001438 func_def = create_func_def_string(s_func_name, "l" + func_name,
1439 lprint_func_template, replace_dict)
1440 buffer += func_def
1441
Michael Walshfd2733c2017-11-13 11:36:20 -06001442 # Create abbreviated aliases (e.g. spvar is an alias for sprint_var).
1443 alias = re.sub("print_", "p", func_name)
1444 alias = re.sub("print", "p", alias)
Michael Walsh168eb0f2017-12-01 15:35:32 -06001445 prefixes = ["", "s", "q", "d", "l"]
Michael Walshfd2733c2017-11-13 11:36:20 -06001446 for prefix in prefixes:
1447 if alias == "p":
1448 continue
1449 func_def = prefix + alias + " = " + prefix + func_name
1450 buffer += func_def + "\n"
1451
1452 return buffer
Michael Walsh82acf002017-05-04 14:33:05 -05001453
1454
Michael Walshde791732016-09-06 14:25:24 -05001455# In the following section of code, we will dynamically create print versions
1456# for each of the sprint functions defined above. So, for example, where we
1457# have an sprint_time() function defined above that returns the time to the
Michael Walsh7423c012016-10-04 10:27:21 -05001458# caller in a string, we will create a corresponding print_time() function
1459# that will print that string directly to stdout.
Michael Walshde791732016-09-06 14:25:24 -05001460
Michael Walshfd2733c2017-11-13 11:36:20 -06001461# It can be complicated to follow what's being created by below. Here is an
1462# example of the print_time() function that will be created:
Michael Walshde791732016-09-06 14:25:24 -05001463
Michael Walshfd2733c2017-11-13 11:36:20 -06001464# def print_time(buffer=''):
1465# sys.stdout.write(replace_passwords(sprint_time(buffer=buffer)))
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001466# sys.stdout.flush()
Michael Walshde791732016-09-06 14:25:24 -05001467
Michael Walshfd2733c2017-11-13 11:36:20 -06001468# Templates for the various print wrapper functions.
1469print_func_template = \
1470 [
Michael Walsh81c02342018-01-05 15:43:28 -06001471 " <mod_qualifier>gp_print(<mod_qualifier>replace_passwords(" +
1472 "<call_line>), stream='<output_stream>')"
Michael Walshfd2733c2017-11-13 11:36:20 -06001473 ]
1474
1475qprint_func_template = \
1476 [
Michael Walsh81c02342018-01-05 15:43:28 -06001477 " if int(<mod_qualifier>get_var_value(None, 0, \"quiet\")): return"
Michael Walshfd2733c2017-11-13 11:36:20 -06001478 ] + print_func_template
1479
1480dprint_func_template = \
1481 [
Michael Walsh81c02342018-01-05 15:43:28 -06001482 " if not int(<mod_qualifier>get_var_value(None, 0, \"debug\")):" +
Michael Walshfd2733c2017-11-13 11:36:20 -06001483 " return"
1484 ] + print_func_template
1485
Michael Walsh168eb0f2017-12-01 15:35:32 -06001486lprint_func_template = \
1487 [
Michael Walsh81c02342018-01-05 15:43:28 -06001488 " gp_log(<mod_qualifier>replace_passwords(<call_line>))"
Michael Walsh168eb0f2017-12-01 15:35:32 -06001489 ]
1490
Michael Walsh81c02342018-01-05 15:43:28 -06001491replace_dict = {'output_stream': 'stdout', 'mod_qualifier': ''}
Michael Walshfd2733c2017-11-13 11:36:20 -06001492
1493
1494gp_debug_print("robot_env: " + str(robot_env))
Michael Walshde791732016-09-06 14:25:24 -05001495
1496# func_names contains a list of all print functions which should be created
1497# from their sprint counterparts.
1498func_names = ['print_time', 'print_timen', 'print_error', 'print_varx',
Michael Walsh18176322016-11-15 15:11:21 -06001499 'print_var', 'print_vars', 'print_dashes', 'indent',
1500 'print_call_stack', 'print_func_name', 'print_executing',
1501 'print_pgm_header', 'print_issuing', 'print_pgm_footer',
1502 'print_error_report', 'print', 'printn']
Michael Walshde791732016-09-06 14:25:24 -05001503
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001504# stderr_func_names is a list of functions whose output should go to stderr
1505# rather than stdout.
1506stderr_func_names = ['print_error', 'print_error_report']
Michael Walshde791732016-09-06 14:25:24 -05001507
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001508
Michael Walshfd2733c2017-11-13 11:36:20 -06001509func_defs = create_print_wrapper_funcs(func_names, stderr_func_names,
1510 replace_dict)
1511gp_debug_print(func_defs)
1512exec(func_defs)