blob: 2f917bc85e185498f356e88ce298cd60ce93adde [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
Michael Walsh2750b442018-05-18 14:49:11 -0500125 allowed by the target function. If
126 arg_num contains the special value 0,
127 get_arg_name will return the lvalue, which
128 is anything to the left of the assignment
129 operator (i.e. "="). For example, if a
130 programmer codes "var1 = my_function", and
131 my_function calls this function with
132 "get_arg_name(0, 0, stack_frame_ix=2)",
133 this function will return "var1".
134 Likewise, if a programmer codes "var1,
135 var2 = my_function", and my_function calls
136 this function with "get_arg_name(0, 0,
137 stack_frame_ix=2)", this function will
138 return "var1, var2". No manipulation of
139 the lvalue string is done by this function
140 (i.e. compressing spaces, converting to a
141 list, etc.).
Michael Walshde791732016-09-06 14:25:24 -0500142 stack_frame_ix The stack frame index of the target
143 function. This value must be 1 or
144 greater. 1 would indicate get_arg_name's
145 stack frame. 2 would be the caller of
146 get_arg_name's stack frame, etc.
147
148 Example 1:
149
150 my_var = "mike"
151 var_name = get_arg_name(my_var)
152
153 In this example, var_name will receive the value "my_var".
154
155 Example 2:
156
157 def test1(var):
158 # Getting the var name of the first arg to this function, test1.
159 # Note, in this case, it doesn't matter what you pass as the first arg
160 # to get_arg_name since it is the caller's variable name that matters.
161 dummy = 1
162 arg_num = 1
163 stack_frame = 2
164 var_name = get_arg_name(dummy, arg_num, stack_frame)
165
166 # Mainline...
167
168 another_var = "whatever"
169 test1(another_var)
170
171 In this example, var_name will be set to "another_var".
172
173 """
174
175 # Note: I wish to avoid recursion so I refrain from calling any function
176 # that calls this function (i.e. sprint_var, valid_value, etc.).
177
Michael Walsh23e7f492017-01-10 11:34:47 -0600178 # The user can set environment variable "GET_ARG_NAME_DEBUG" to get debug
179 # output from this function.
180 local_debug = int(os.environ.get('GET_ARG_NAME_DEBUG', 0))
181 # In addition to GET_ARG_NAME_DEBUG, the user can set environment
182 # variable "GET_ARG_NAME_SHOW_SOURCE" to have this function include source
183 # code in the debug output.
184 local_debug_show_source = int(
185 os.environ.get('GET_ARG_NAME_SHOW_SOURCE', 0))
Michael Walshde791732016-09-06 14:25:24 -0500186
Michael Walsh2750b442018-05-18 14:49:11 -0500187 if arg_num < 0:
Michael Walshde791732016-09-06 14:25:24 -0500188 print_error("Programmer error - Variable \"arg_num\" has an invalid" +
189 " value of \"" + str(arg_num) + "\". The value must be" +
Michael Walsh2750b442018-05-18 14:49:11 -0500190 " an integer that is 0 or greater.\n")
Michael Walshde791732016-09-06 14:25:24 -0500191 # What is the best way to handle errors? Raise exception? I'll
192 # revisit later.
193 return
194 if stack_frame_ix < 1:
195 print_error("Programmer error - Variable \"stack_frame_ix\" has an" +
196 " invalid value of \"" + str(stack_frame_ix) + "\". The" +
197 " value must be an integer that is greater than or equal" +
198 " to 1.\n")
199 return
200
201 if local_debug:
202 debug_indent = 2
Michael Walsh23e7f492017-01-10 11:34:47 -0600203 print("")
204 print_dashes(0, 120)
Michael Walshde791732016-09-06 14:25:24 -0500205 print(sprint_func_name() + "() parms:")
206 print_varx("var", var, 0, debug_indent)
207 print_varx("arg_num", arg_num, 0, debug_indent)
208 print_varx("stack_frame_ix", stack_frame_ix, 0, debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600209 print("")
210 print_call_stack(debug_indent, 2)
Michael Walshde791732016-09-06 14:25:24 -0500211
Michael Walsh23e7f492017-01-10 11:34:47 -0600212 for count in range(0, 2):
213 try:
214 frame, filename, cur_line_no, function_name, lines, index = \
215 inspect.stack()[stack_frame_ix]
216 except IndexError:
217 print_error("Programmer error - The caller has asked for" +
218 " information about the stack frame at index \"" +
219 str(stack_frame_ix) + "\". However, the stack" +
220 " only contains " + str(len(inspect.stack())) +
221 " entries. Therefore the stack frame index is out" +
222 " of range.\n")
223 return
224 if filename != "<string>":
225 break
226 # filename of "<string>" may mean that the function in question was
227 # defined dynamically and therefore its code stack is inaccessible.
228 # This may happen with functions like "rqprint_var". In this case,
229 # we'll increment the stack_frame_ix and try again.
230 stack_frame_ix += 1
231 if local_debug:
232 print("Adjusted stack_frame_ix...")
233 print_varx("stack_frame_ix", stack_frame_ix, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500234
235 called_func_name = sprint_func_name(stack_frame_ix)
Michael Walsh23e7f492017-01-10 11:34:47 -0600236
237 module = inspect.getmodule(frame)
238
239 # Though I would expect inspect.getsourcelines(frame) to get all module
240 # source lines if the frame is "<module>", it doesn't do that. Therefore,
241 # for this special case, I will do inspect.getsourcelines(module).
242 if function_name == "<module>":
243 source_lines, source_line_num =\
244 inspect.getsourcelines(module)
245 line_ix = cur_line_no - source_line_num - 1
246 else:
247 source_lines, source_line_num =\
248 inspect.getsourcelines(frame)
249 line_ix = cur_line_no - source_line_num
250
251 if local_debug:
252 print("\n Variables retrieved from inspect.stack() function:")
253 print_varx("frame", frame, 0, debug_indent + 2)
254 print_varx("filename", filename, 0, debug_indent + 2)
255 print_varx("cur_line_no", cur_line_no, 0, debug_indent + 2)
256 print_varx("function_name", function_name, 0, debug_indent + 2)
257 print_varx("lines", lines, 0, debug_indent + 2)
258 print_varx("index", index, 0, debug_indent + 2)
259 print_varx("source_line_num", source_line_num, 0, debug_indent)
260 print_varx("line_ix", line_ix, 0, debug_indent)
261 if local_debug_show_source:
262 print_varx("source_lines", source_lines, 0, debug_indent)
263 print_varx("called_func_name", called_func_name, 0, debug_indent)
264
265 # Get a list of all functions defined for the module. Note that this
266 # doesn't work consistently when _run_exitfuncs is at the top of the stack
267 # (i.e. if we're running an exit function). I've coded a work-around
268 # below for this deficiency.
269 all_functions = inspect.getmembers(module, inspect.isfunction)
270
271 # Get called_func_id by searching for our function in the list of all
272 # functions.
273 called_func_id = None
274 for func_name, function in all_functions:
275 if func_name == called_func_name:
276 called_func_id = id(function)
277 break
278 # NOTE: The only time I've found that called_func_id can't be found is
279 # when we're running from an exit function.
280
281 # Look for other functions in module with matching id.
282 aliases = set([called_func_name])
283 for func_name, function in all_functions:
284 if func_name == called_func_name:
285 continue
286 func_id = id(function)
287 if func_id == called_func_id:
288 aliases.add(func_name)
289
290 # In most cases, my general purpose code above will find all aliases.
291 # However, for the odd case (i.e. running from exit function), I've added
292 # code to handle pvar, qpvar, dpvar, etc. aliases explicitly since they
293 # are defined in this module and used frequently.
294 # pvar is an alias for print_var.
295 aliases.add(re.sub("print_var", "pvar", called_func_name))
296
297 func_regex = ".*(" + '|'.join(aliases) + ")[ ]*\("
298
299 # Search backward through source lines looking for the calling function
300 # name.
301 found = False
302 for start_line_ix in range(line_ix, 0, -1):
303 # Skip comment lines.
304 if re.match(r"[ ]*#", source_lines[start_line_ix]):
305 continue
306 if re.match(func_regex, source_lines[start_line_ix]):
307 found = True
308 break
309 if not found:
310 print_error("Programmer error - Could not find the source line with" +
311 " a reference to function \"" + called_func_name + "\".\n")
312 return
313
Michael Walsh82acf002017-05-04 14:33:05 -0500314 # Search forward through the source lines looking for a line whose
315 # indentation is the same or less than the start line. The end of our
316 # composite line should be the line preceding that line.
Michael Walsh23e7f492017-01-10 11:34:47 -0600317 start_indent = len(source_lines[start_line_ix]) -\
318 len(source_lines[start_line_ix].lstrip(' '))
319 end_line_ix = line_ix
320 for end_line_ix in range(line_ix + 1, len(source_lines)):
321 if source_lines[end_line_ix].strip() == "":
322 continue
323 line_indent = len(source_lines[end_line_ix]) -\
324 len(source_lines[end_line_ix].lstrip(' '))
Michael Walsh82acf002017-05-04 14:33:05 -0500325 if line_indent <= start_indent:
Michael Walsh23e7f492017-01-10 11:34:47 -0600326 end_line_ix -= 1
327 break
328
329 # Join the start line through the end line into a composite line.
330 composite_line = ''.join(map(str.strip,
Gunnar Mills096cd562018-03-26 10:19:12 -0500331 source_lines[start_line_ix:end_line_ix + 1]))
Michael Walsh7423c012016-10-04 10:27:21 -0500332
Michael Walshbec416d2016-11-10 08:54:52 -0600333 # arg_list_etc = re.sub(".*" + called_func_name, "", composite_line)
334 arg_list_etc = "(" + re.sub(func_regex, "", composite_line)
Michael Walsh2750b442018-05-18 14:49:11 -0500335 lvalue = re.sub("[ ]+=[ ]+" + called_func_name + ".*", "", composite_line)
Michael Walshde791732016-09-06 14:25:24 -0500336 if local_debug:
Michael Walsh23e7f492017-01-10 11:34:47 -0600337 print_varx("aliases", aliases, 0, debug_indent)
Michael Walshbec416d2016-11-10 08:54:52 -0600338 print_varx("func_regex", func_regex, 0, debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600339 print_varx("start_line_ix", start_line_ix, 0, debug_indent)
340 print_varx("end_line_ix", end_line_ix, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500341 print_varx("composite_line", composite_line, 0, debug_indent)
Michael Walsh2750b442018-05-18 14:49:11 -0500342 print_varx("lvalue", lvalue, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500343 print_varx("arg_list_etc", arg_list_etc, 0, debug_indent)
344
345 # Parse arg list...
346 # Initialize...
347 nest_level = -1
348 arg_ix = 0
Michael Walsh7423c012016-10-04 10:27:21 -0500349 args_list = [""]
Michael Walshde791732016-09-06 14:25:24 -0500350 for ix in range(0, len(arg_list_etc)):
351 char = arg_list_etc[ix]
352 # Set the nest_level based on whether we've encounted a parenthesis.
353 if char == "(":
354 nest_level += 1
355 if nest_level == 0:
356 continue
357 elif char == ")":
358 nest_level -= 1
359 if nest_level < 0:
360 break
361
362 # If we reach a comma at base nest level, we are done processing an
Michael Walsh7423c012016-10-04 10:27:21 -0500363 # argument so we increment arg_ix and initialize a new args_list entry.
Michael Walshde791732016-09-06 14:25:24 -0500364 if char == "," and nest_level == 0:
365 arg_ix += 1
Michael Walsh7423c012016-10-04 10:27:21 -0500366 args_list.append("")
Michael Walshde791732016-09-06 14:25:24 -0500367 continue
368
Michael Walsh7423c012016-10-04 10:27:21 -0500369 # For any other character, we append it it to the current arg list
Michael Walshde791732016-09-06 14:25:24 -0500370 # entry.
Michael Walsh7423c012016-10-04 10:27:21 -0500371 args_list[arg_ix] += char
Michael Walshde791732016-09-06 14:25:24 -0500372
373 # Trim whitespace from each list entry.
Michael Walsh7423c012016-10-04 10:27:21 -0500374 args_list = [arg.strip() for arg in args_list]
Michael Walshde791732016-09-06 14:25:24 -0500375
Michael Walsh7423c012016-10-04 10:27:21 -0500376 if arg_num > len(args_list):
Michael Walshde791732016-09-06 14:25:24 -0500377 print_error("Programmer error - The caller has asked for the name of" +
378 " argument number \"" + str(arg_num) + "\" but there " +
Michael Walsh7423c012016-10-04 10:27:21 -0500379 "were only \"" + str(len(args_list)) + "\" args used:\n" +
380 sprint_varx("args_list", args_list))
Michael Walshde791732016-09-06 14:25:24 -0500381 return
382
Michael Walsh2750b442018-05-18 14:49:11 -0500383 if arg_num == 0:
384 argument = lvalue
385 else:
386 argument = args_list[arg_num - 1]
Michael Walshde791732016-09-06 14:25:24 -0500387
388 if local_debug:
Michael Walsh7423c012016-10-04 10:27:21 -0500389 print_varx("args_list", args_list, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500390 print_varx("argument", argument, 0, debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600391 print_dashes(0, 120)
Michael Walshde791732016-09-06 14:25:24 -0500392
393 return argument
394
Michael Walshde791732016-09-06 14:25:24 -0500395
Michael Walshde791732016-09-06 14:25:24 -0500396def sprint_time(buffer=""):
Michael Walshde791732016-09-06 14:25:24 -0500397 r"""
398 Return the time in the following format.
399
400 Example:
401
402 The following python code...
403
404 sys.stdout.write(sprint_time())
405 sys.stdout.write("Hi.\n")
406
407 Will result in the following type of output:
408
409 #(CDT) 2016/07/08 15:25:35 - Hi.
410
411 Example:
412
413 The following python code...
414
415 sys.stdout.write(sprint_time("Hi.\n"))
416
417 Will result in the following type of output:
418
419 #(CDT) 2016/08/03 17:12:05 - Hi.
420
421 The following environment variables will affect the formatting as
422 described:
423 NANOSECONDS This will cause the time stamps to be
424 precise to the microsecond (Yes, it
425 probably should have been named
426 MICROSECONDS but the convention was set
427 long ago so we're sticking with it).
428 Example of the output when environment
429 variable NANOSECONDS=1.
430
431 #(CDT) 2016/08/03 17:16:25.510469 - Hi.
432
433 SHOW_ELAPSED_TIME This will cause the elapsed time to be
434 included in the output. This is the
435 amount of time that has elapsed since the
436 last time this function was called. The
437 precision of the elapsed time field is
438 also affected by the value of the
439 NANOSECONDS environment variable. Example
440 of the output when environment variable
441 NANOSECONDS=0 and SHOW_ELAPSED_TIME=1.
442
443 #(CDT) 2016/08/03 17:17:40 - 0 - Hi.
444
445 Example of the output when environment variable NANOSECONDS=1 and
446 SHOW_ELAPSED_TIME=1.
447
448 #(CDT) 2016/08/03 17:18:47.317339 - 0.000046 - Hi.
449
450 Description of arguments.
451 buffer This will be appended to the formatted
452 time string.
453 """
454
455 global NANOSECONDS
456 global SHOW_ELAPSED_TIME
457 global sprint_time_last_seconds
458
459 seconds = time.time()
460 loc_time = time.localtime(seconds)
461 nanoseconds = "%0.6f" % seconds
462 pos = nanoseconds.find(".")
463 nanoseconds = nanoseconds[pos:]
464
465 time_string = time.strftime("#(%Z) %Y/%m/%d %H:%M:%S", loc_time)
466 if NANOSECONDS == "1":
467 time_string = time_string + nanoseconds
468
469 if SHOW_ELAPSED_TIME == "1":
470 cur_time_seconds = seconds
471 math_string = "%9.9f" % cur_time_seconds + " - " + "%9.9f" % \
472 sprint_time_last_seconds
473 elapsed_seconds = eval(math_string)
474 if NANOSECONDS == "1":
475 elapsed_seconds = "%11.6f" % elapsed_seconds
476 else:
477 elapsed_seconds = "%4i" % elapsed_seconds
478 sprint_time_last_seconds = cur_time_seconds
479 time_string = time_string + " - " + elapsed_seconds
480
481 return time_string + " - " + buffer
482
Michael Walshde791732016-09-06 14:25:24 -0500483
Michael Walshde791732016-09-06 14:25:24 -0500484def sprint_timen(buffer=""):
Michael Walshde791732016-09-06 14:25:24 -0500485 r"""
486 Append a line feed to the buffer, pass it to sprint_time and return the
487 result.
488 """
489
490 return sprint_time(buffer + "\n")
491
Michael Walshde791732016-09-06 14:25:24 -0500492
Michael Walshde791732016-09-06 14:25:24 -0500493def sprint_error(buffer=""):
Michael Walshde791732016-09-06 14:25:24 -0500494 r"""
495 Return a standardized error string. This includes:
496 - A time stamp
497 - The "**ERROR**" string
498 - The caller's buffer string.
499
500 Example:
501
502 The following python code...
503
504 print(sprint_error("Oops.\n"))
505
506 Will result in the following type of output:
507
508 #(CDT) 2016/08/03 17:12:05 - **ERROR** Oops.
509
510 Description of arguments.
511 buffer This will be appended to the formatted
512 error string.
513 """
514
515 return sprint_time() + "**ERROR** " + buffer
516
Michael Walshde791732016-09-06 14:25:24 -0500517
Michael Walshde791732016-09-06 14:25:24 -0500518def sprint_varx(var_name,
519 var_value,
520 hex=0,
521 loc_col1_indent=col1_indent,
Michael Walsh7423c012016-10-04 10:27:21 -0500522 loc_col1_width=col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500523 trailing_char="\n",
524 key_list=None):
Michael Walshde791732016-09-06 14:25:24 -0500525 r"""
526 Print the var name/value passed to it. If the caller lets loc_col1_width
527 default, the printing lines up nicely with output generated by the
528 print_time functions.
529
530 Note that the sprint_var function (defined below) can be used to call this
531 function so that the programmer does not need to pass the var_name.
532 sprint_var will figure out the var_name. The sprint_var function is the
533 one that would normally be used by the general user.
534
535 For example, the following python code:
536
537 first_name = "Mike"
538 print_time("Doing this...\n")
539 print_varx("first_name", first_name)
540 print_time("Doing that...\n")
541
542 Will generate output like this:
543
544 #(CDT) 2016/08/10 17:34:42.847374 - 0.001285 - Doing this...
545 first_name: Mike
546 #(CDT) 2016/08/10 17:34:42.847510 - 0.000136 - Doing that...
547
548 This function recognizes several complex types of data such as dict, list
549 or tuple.
550
551 For example, the following python code:
552
553 my_dict = dict(one=1, two=2, three=3)
554 print_var(my_dict)
555
556 Will generate the following output:
557
558 my_dict:
559 my_dict[three]: 3
560 my_dict[two]: 2
561 my_dict[one]: 1
562
563 Description of arguments.
564 var_name The name of the variable to be printed.
565 var_value The value of the variable to be printed.
566 hex This indicates that the value should be
567 printed in hex format. It is the user's
568 responsibility to ensure that a var_value
Michael Walshbec416d2016-11-10 08:54:52 -0600569 contains a valid hex number. For string
570 var_values, this will be interpreted as
571 show_blanks which means that blank values
Michael Walshd995cb02017-02-07 14:46:01 -0600572 will be printed as "<blank>". For dict
573 var_values, this will be interpreted as
574 terse format where keys are not repeated
575 in the output.
Michael Walshde791732016-09-06 14:25:24 -0500576 loc_col1_indent The number of spaces to indent the output.
577 loc_col1_width The width of the output column containing
578 the variable name. The default value of
579 this is adjusted so that the var_value
580 lines up with text printed via the
581 print_time function.
Michael Walsh7423c012016-10-04 10:27:21 -0500582 trailing_char The character to be used at the end of the
583 returned string. The default value is a
584 line feed.
Michael Walshd2869032018-03-22 16:12:11 -0500585 key_list A list of which dictionary keys should be
586 printed. All others keys will be skipped.
587 Each value in key_list will be regarded
588 as a regular expression and it will be
589 regarded as anchored to the beginning and
590 ends of the dictionary key being
591 referenced. For example if key_list is
592 ["one", "two"], the resulting regex used
593 will be "^one|two$", i.e. only keys "one"
594 and "two" from the var_value dictionary
595 will be printed. As another example, if
596 the caller were to specify a key_list of
597 ["one.*"], then only dictionary keys whose
598 names begin with "one" will be printed.
599 Note: This argument pertains only to
600 var_values which are dictionaries.
Michael Walsh7423c012016-10-04 10:27:21 -0500601 """
Michael Walshde791732016-09-06 14:25:24 -0500602
603 # Determine the type
Michael Walsh92ac3d02018-03-30 14:38:15 -0500604 if type(var_value) in (int, long, float, bool, str, unicode) \
Michael Walshde791732016-09-06 14:25:24 -0500605 or var_value is None:
606 # The data type is simple in the sense that it has no subordinate
607 # parts.
Michael Walsh7423c012016-10-04 10:27:21 -0500608 # Adjust loc_col1_width.
609 loc_col1_width = loc_col1_width - loc_col1_indent
Michael Walshde791732016-09-06 14:25:24 -0500610 # See if the user wants the output in hex format.
611 if hex:
Michael Walsh18176322016-11-15 15:11:21 -0600612 if type(var_value) not in (int, long):
Michael Walshbec416d2016-11-10 08:54:52 -0600613 value_format = "%s"
Michael Walsh2795edc2016-12-13 16:00:33 -0600614 if var_value == "":
Michael Walshbec416d2016-11-10 08:54:52 -0600615 var_value = "<blank>"
616 else:
Michael Walsh92ac3d02018-03-30 14:38:15 -0500617 if type(var_value) is long or var_value >= 0x100000000:
618 value_format = "0x%16x"
619 else:
620 value_format = "0x%08x"
Michael Walshde791732016-09-06 14:25:24 -0500621 else:
622 value_format = "%s"
623 format_string = "%" + str(loc_col1_indent) + "s%-" \
Michael Walsh7423c012016-10-04 10:27:21 -0500624 + str(loc_col1_width) + "s" + value_format + trailing_char
Michael Walsh3383e652017-09-01 17:10:59 -0500625 if value_format == "0x%08x":
626 return format_string % ("", str(var_name) + ":",
627 var_value & 0xffffffff)
628 else:
629 return format_string % ("", str(var_name) + ":", var_value)
Michael Walsh20a87ab2017-06-30 17:00:30 -0500630 elif type(var_value) is type:
631 return sprint_varx(var_name, str(var_value).split("'")[1], hex,
Michael Walshd2869032018-03-22 16:12:11 -0500632 loc_col1_indent, loc_col1_width, trailing_char,
633 key_list)
Michael Walshde791732016-09-06 14:25:24 -0500634 else:
635 # The data type is complex in the sense that it has subordinate parts.
636 format_string = "%" + str(loc_col1_indent) + "s%s\n"
637 buffer = format_string % ("", var_name + ":")
638 loc_col1_indent += 2
Michael Walsh7423c012016-10-04 10:27:21 -0500639 try:
640 length = len(var_value)
641 except TypeError:
Michael Walsh23e7f492017-01-10 11:34:47 -0600642 length = 0
Michael Walsh7423c012016-10-04 10:27:21 -0500643 ix = 0
644 loc_trailing_char = "\n"
Michael Walshbec416d2016-11-10 08:54:52 -0600645 type_is_dict = 0
Michael Walsh23e7f492017-01-10 11:34:47 -0600646 if type(var_value) is dict:
647 type_is_dict = 1
Michael Walsh8e6deb42017-01-27 14:22:41 -0600648 try:
649 if type(var_value) is collections.OrderedDict:
650 type_is_dict = 1
651 except AttributeError:
652 pass
653 try:
654 if type(var_value) is DotDict:
655 type_is_dict = 1
656 except NameError:
657 pass
658 try:
659 if type(var_value) is NormalizedDict:
660 type_is_dict = 1
661 except NameError:
662 pass
Michael Walshbec416d2016-11-10 08:54:52 -0600663 if type_is_dict:
Michael Walshde791732016-09-06 14:25:24 -0500664 for key, value in var_value.iteritems():
Michael Walshd2869032018-03-22 16:12:11 -0500665 if key_list is not None:
666 key_list_regex = "^" + "|".join(key_list) + "$"
667 if not re.match(key_list_regex, key):
668 continue
Michael Walsh7423c012016-10-04 10:27:21 -0500669 ix += 1
670 if ix == length:
671 loc_trailing_char = trailing_char
Michael Walshd995cb02017-02-07 14:46:01 -0600672 if hex:
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600673 # Since hex is being used as a format type, we want it
674 # turned off when processing integer dictionary values so
675 # it is not interpreted as a hex indicator.
676 loc_hex = not (type(value) is int)
Michael Walshf7b8a002017-08-29 10:38:39 -0500677 buffer += sprint_varx("[" + key + "]", value,
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600678 loc_hex, loc_col1_indent,
679 loc_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500680 loc_trailing_char,
681 key_list)
Michael Walshd995cb02017-02-07 14:46:01 -0600682 else:
683 buffer += sprint_varx(var_name + "[" + key + "]", value,
684 hex, loc_col1_indent, loc_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500685 loc_trailing_char, key_list)
Michael Walsh7423c012016-10-04 10:27:21 -0500686 elif type(var_value) in (list, tuple, set):
Michael Walshde791732016-09-06 14:25:24 -0500687 for key, value in enumerate(var_value):
Michael Walsh7423c012016-10-04 10:27:21 -0500688 ix += 1
689 if ix == length:
690 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500691 buffer += sprint_varx(var_name + "[" + str(key) + "]", value,
Michael Walsh7423c012016-10-04 10:27:21 -0500692 hex, loc_col1_indent, loc_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500693 loc_trailing_char, key_list)
Michael Walshde791732016-09-06 14:25:24 -0500694 elif type(var_value) is argparse.Namespace:
695 for key in var_value.__dict__:
Michael Walsh7423c012016-10-04 10:27:21 -0500696 ix += 1
697 if ix == length:
698 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500699 cmd_buf = "buffer += sprint_varx(var_name + \".\" + str(key)" \
Michael Walsh7423c012016-10-04 10:27:21 -0500700 + ", var_value." + key + ", hex, loc_col1_indent," \
Michael Walshd2869032018-03-22 16:12:11 -0500701 + " loc_col1_width, loc_trailing_char, key_list)"
Michael Walshde791732016-09-06 14:25:24 -0500702 exec(cmd_buf)
703 else:
704 var_type = type(var_value).__name__
705 func_name = sys._getframe().f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -0500706 var_value = "<" + var_type + " type not supported by " + \
707 func_name + "()>"
Michael Walshde791732016-09-06 14:25:24 -0500708 value_format = "%s"
709 loc_col1_indent -= 2
Michael Walsh7423c012016-10-04 10:27:21 -0500710 # Adjust loc_col1_width.
711 loc_col1_width = loc_col1_width - loc_col1_indent
Michael Walshde791732016-09-06 14:25:24 -0500712 format_string = "%" + str(loc_col1_indent) + "s%-" \
Michael Walsh7423c012016-10-04 10:27:21 -0500713 + str(loc_col1_width) + "s" + value_format + trailing_char
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600714 return format_string % ("", str(var_name) + ":", var_value)
Michael Walsh23e7f492017-01-10 11:34:47 -0600715
Michael Walshde791732016-09-06 14:25:24 -0500716 return buffer
717
718 return ""
719
Michael Walshde791732016-09-06 14:25:24 -0500720
Michael Walshfd2733c2017-11-13 11:36:20 -0600721def sprint_var(var_value,
722 hex=0,
723 loc_col1_indent=col1_indent,
724 loc_col1_width=col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500725 trailing_char="\n",
726 key_list=None):
Michael Walshde791732016-09-06 14:25:24 -0500727 r"""
728 Figure out the name of the first argument for you and then call
729 sprint_varx with it. Therefore, the following 2 calls are equivalent:
730 sprint_varx("var1", var1)
731 sprint_var(var1)
732 """
733
734 # Get the name of the first variable passed to this function.
735 stack_frame = 2
Michael Walsh7423c012016-10-04 10:27:21 -0500736 caller_func_name = sprint_func_name(2)
737 if caller_func_name.endswith("print_var"):
Michael Walshde791732016-09-06 14:25:24 -0500738 stack_frame += 1
739 var_name = get_arg_name(None, 1, stack_frame)
Michael Walshfd2733c2017-11-13 11:36:20 -0600740 return sprint_varx(var_name, var_value=var_value, hex=hex,
741 loc_col1_indent=loc_col1_indent,
742 loc_col1_width=loc_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500743 trailing_char=trailing_char,
744 key_list=key_list)
Michael Walshde791732016-09-06 14:25:24 -0500745
746
Michael Walsh18176322016-11-15 15:11:21 -0600747def sprint_vars(*args):
Michael Walsh18176322016-11-15 15:11:21 -0600748 r"""
749 Sprint the values of one or more variables.
750
751 Description of args:
752 args:
753 If the first argument is an integer, it will be interpreted to be the
754 "indent" value.
755 If the second argument is an integer, it will be interpreted to be the
756 "col1_width" value.
757 If the third argument is an integer, it will be interpreted to be the
758 "hex" value.
759 All remaining parms are considered variable names which are to be
760 sprinted.
761 """
762
763 if len(args) == 0:
764 return
765
766 # Get the name of the first variable passed to this function.
767 stack_frame = 2
768 caller_func_name = sprint_func_name(2)
769 if caller_func_name.endswith("print_vars"):
770 stack_frame += 1
771
772 parm_num = 1
773
774 # Create list from args (which is a tuple) so that it can be modified.
775 args_list = list(args)
776
777 var_name = get_arg_name(None, parm_num, stack_frame)
778 # See if parm 1 is to be interpreted as "indent".
779 try:
780 if type(int(var_name)) is int:
781 indent = int(var_name)
782 args_list.pop(0)
783 parm_num += 1
784 except ValueError:
785 indent = 0
786
787 var_name = get_arg_name(None, parm_num, stack_frame)
788 # See if parm 1 is to be interpreted as "col1_width".
789 try:
790 if type(int(var_name)) is int:
791 loc_col1_width = int(var_name)
792 args_list.pop(0)
793 parm_num += 1
794 except ValueError:
795 loc_col1_width = col1_width
796
797 var_name = get_arg_name(None, parm_num, stack_frame)
798 # See if parm 1 is to be interpreted as "hex".
799 try:
800 if type(int(var_name)) is int:
801 hex = int(var_name)
802 args_list.pop(0)
803 parm_num += 1
804 except ValueError:
805 hex = 0
806
807 buffer = ""
808 for var_value in args_list:
809 var_name = get_arg_name(None, parm_num, stack_frame)
810 buffer += sprint_varx(var_name, var_value, hex, indent, loc_col1_width)
811 parm_num += 1
812
813 return buffer
814
Michael Walsh18176322016-11-15 15:11:21 -0600815
Michael Walsh7423c012016-10-04 10:27:21 -0500816def sprint_dashes(indent=col1_indent,
817 width=80,
818 line_feed=1,
819 char="-"):
Michael Walshde791732016-09-06 14:25:24 -0500820 r"""
821 Return a string of dashes to the caller.
822
Michael Walsh2ee77cd2017-03-08 11:50:17 -0600823 Description of arguments:
Michael Walshde791732016-09-06 14:25:24 -0500824 indent The number of characters to indent the
825 output.
826 width The width of the string of dashes.
827 line_feed Indicates whether the output should end
828 with a line feed.
Michael Walsh7423c012016-10-04 10:27:21 -0500829 char The character to be repeated in the output
830 string.
Michael Walshde791732016-09-06 14:25:24 -0500831 """
832
Michael Walsh7423c012016-10-04 10:27:21 -0500833 width = int(width)
Michael Walsh23e7f492017-01-10 11:34:47 -0600834 buffer = " " * int(indent) + char * width
Michael Walshde791732016-09-06 14:25:24 -0500835 if line_feed:
836 buffer += "\n"
837
838 return buffer
839
Michael Walshde791732016-09-06 14:25:24 -0500840
Michael Walsh7423c012016-10-04 10:27:21 -0500841def sindent(text="",
842 indent=0):
Michael Walsh7423c012016-10-04 10:27:21 -0500843 r"""
844 Pre-pend the specified number of characters to the text string (i.e.
845 indent it) and return it.
846
847 Description of arguments:
848 text The string to be indented.
849 indent The number of characters to indent the
850 string.
851 """
852
853 format_string = "%" + str(indent) + "s%s"
854 buffer = format_string % ("", text)
855
856 return buffer
857
Michael Walsh7423c012016-10-04 10:27:21 -0500858
Michael Walsh7423c012016-10-04 10:27:21 -0500859def sprint_call_stack(indent=0,
860 stack_frame_ix=0):
Michael Walshde791732016-09-06 14:25:24 -0500861 r"""
862 Return a call stack report for the given point in the program with line
863 numbers, function names and function parameters and arguments.
864
865 Sample output:
866
867 -------------------------------------------------------------------------
868 Python function call stack
869
870 Line # Function name and arguments
871 ------ ------------------------------------------------------------------
872 424 sprint_call_stack ()
873 4 print_call_stack ()
874 31 func1 (last_name = 'walsh', first_name = 'mikey')
875 59 /tmp/scr5.py
876 -------------------------------------------------------------------------
877
878 Description of arguments:
879 indent The number of characters to indent each
880 line of output.
881 stack_frame_ix The index of the first stack frame which
882 is to be returned.
883 """
884
885 buffer = ""
Michael Walsh7423c012016-10-04 10:27:21 -0500886 buffer += sprint_dashes(indent)
887 buffer += sindent("Python function call stack\n\n", indent)
888 buffer += sindent("Line # Function name and arguments\n", indent)
889 buffer += sprint_dashes(indent, 6, 0) + " " + sprint_dashes(0, 73)
Michael Walshde791732016-09-06 14:25:24 -0500890
891 # Grab the current program stack.
892 current_stack = inspect.stack()
893
894 # Process each frame in turn.
895 format_string = "%6s %s\n"
Michael Walsh7423c012016-10-04 10:27:21 -0500896 ix = 0
Michael Walshde791732016-09-06 14:25:24 -0500897 for stack_frame in current_stack:
Michael Walsh7423c012016-10-04 10:27:21 -0500898 if ix < stack_frame_ix:
899 ix += 1
900 continue
Michael Walsh23e7f492017-01-10 11:34:47 -0600901 # I want the line number shown to be the line where you find the line
902 # shown.
903 try:
904 line_num = str(current_stack[ix + 1][2])
905 except IndexError:
906 line_num = ""
Michael Walshde791732016-09-06 14:25:24 -0500907 func_name = str(stack_frame[3])
908 if func_name == "?":
909 # "?" is the name used when code is not in a function.
910 func_name = "(none)"
911
912 if func_name == "<module>":
Michael Walsh7423c012016-10-04 10:27:21 -0500913 # If the func_name is the "main" program, we simply get the
914 # command line call string.
Michael Walshde791732016-09-06 14:25:24 -0500915 func_and_args = ' '.join(sys.argv)
916 else:
917 # Get the program arguments.
918 arg_vals = inspect.getargvalues(stack_frame[0])
919 function_parms = arg_vals[0]
920 frame_locals = arg_vals[3]
921
Michael Walsh7423c012016-10-04 10:27:21 -0500922 args_list = []
Michael Walshde791732016-09-06 14:25:24 -0500923 for arg_name in function_parms:
924 # Get the arg value from frame locals.
925 arg_value = frame_locals[arg_name]
Michael Walsh7423c012016-10-04 10:27:21 -0500926 args_list.append(arg_name + " = " + repr(arg_value))
927 args_str = "(" + ', '.join(map(str, args_list)) + ")"
Michael Walshde791732016-09-06 14:25:24 -0500928
929 # Now we need to print this in a nicely-wrapped way.
930 func_and_args = func_name + " " + args_str
931
Michael Walsh23e7f492017-01-10 11:34:47 -0600932 buffer += sindent(format_string % (line_num, func_and_args), indent)
Michael Walsh7423c012016-10-04 10:27:21 -0500933 ix += 1
Michael Walshde791732016-09-06 14:25:24 -0500934
Michael Walsh7423c012016-10-04 10:27:21 -0500935 buffer += sprint_dashes(indent)
Michael Walshde791732016-09-06 14:25:24 -0500936
937 return buffer
938
Michael Walshde791732016-09-06 14:25:24 -0500939
Michael Walshde791732016-09-06 14:25:24 -0500940def sprint_executing(stack_frame_ix=None):
Michael Walshde791732016-09-06 14:25:24 -0500941 r"""
942 Print a line indicating what function is executing and with what parameter
943 values. This is useful for debugging.
944
945 Sample output:
946
947 #(CDT) 2016/08/25 17:54:27 - Executing: func1 (x = 1)
948
949 Description of arguments:
950 stack_frame_ix The index of the stack frame whose
951 function info should be returned. If the
Michael Walsh2ee77cd2017-03-08 11:50:17 -0600952 caller does not specify a value, this
Michael Walshde791732016-09-06 14:25:24 -0500953 function will set the value to 1 which is
954 the index of the caller's stack frame. If
955 the caller is the wrapper function
956 "print_executing", this function will bump
957 it up by 1.
958 """
959
960 # If user wants default stack_frame_ix.
961 if stack_frame_ix is None:
962 func_name = sys._getframe().f_code.co_name
963 caller_func_name = sys._getframe(1).f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -0500964 if caller_func_name.endswith(func_name[1:]):
Michael Walshde791732016-09-06 14:25:24 -0500965 stack_frame_ix = 2
966 else:
967 stack_frame_ix = 1
968
969 stack_frame = inspect.stack()[stack_frame_ix]
970
971 func_name = str(stack_frame[3])
972 if func_name == "?":
973 # "?" is the name used when code is not in a function.
974 func_name = "(none)"
975
976 if func_name == "<module>":
977 # If the func_name is the "main" program, we simply get the command
978 # line call string.
979 func_and_args = ' '.join(sys.argv)
980 else:
981 # Get the program arguments.
982 arg_vals = inspect.getargvalues(stack_frame[0])
983 function_parms = arg_vals[0]
984 frame_locals = arg_vals[3]
985
Michael Walsh7423c012016-10-04 10:27:21 -0500986 args_list = []
Michael Walshde791732016-09-06 14:25:24 -0500987 for arg_name in function_parms:
988 # Get the arg value from frame locals.
989 arg_value = frame_locals[arg_name]
Michael Walsh7423c012016-10-04 10:27:21 -0500990 args_list.append(arg_name + " = " + repr(arg_value))
991 args_str = "(" + ', '.join(map(str, args_list)) + ")"
Michael Walshde791732016-09-06 14:25:24 -0500992
993 # Now we need to print this in a nicely-wrapped way.
994 func_and_args = func_name + " " + args_str
995
996 return sprint_time() + "Executing: " + func_and_args + "\n"
997
Michael Walshde791732016-09-06 14:25:24 -0500998
Michael Walshbec416d2016-11-10 08:54:52 -0600999def sprint_pgm_header(indent=0,
1000 linefeed=1):
Michael Walshde791732016-09-06 14:25:24 -05001001 r"""
1002 Return a standardized header that programs should print at the beginning
1003 of the run. It includes useful information like command line, pid,
1004 userid, program parameters, etc.
1005
Michael Walsh7423c012016-10-04 10:27:21 -05001006 Description of arguments:
1007 indent The number of characters to indent each
1008 line of output.
Michael Walshbec416d2016-11-10 08:54:52 -06001009 linefeed Indicates whether a line feed be included
1010 at the beginning and end of the report.
Michael Walshde791732016-09-06 14:25:24 -05001011 """
1012
Michael Walshbec416d2016-11-10 08:54:52 -06001013 loc_col1_width = col1_width + indent
1014
1015 buffer = ""
1016 if linefeed:
1017 buffer = "\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001018
Michael Walshdb6e68a2017-05-23 17:55:31 -05001019 if robot_env:
1020 suite_name = BuiltIn().get_variable_value("${suite_name}")
1021 buffer += sindent(sprint_time("Running test suite \"" + suite_name +
Gunnar Mills096cd562018-03-26 10:19:12 -05001022 "\".\n"), indent)
Michael Walshdb6e68a2017-05-23 17:55:31 -05001023
Michael Walsh7423c012016-10-04 10:27:21 -05001024 buffer += sindent(sprint_time() + "Running " + pgm_name + ".\n", indent)
1025 buffer += sindent(sprint_time() + "Program parameter values, etc.:\n\n",
1026 indent)
Michael Walshbec416d2016-11-10 08:54:52 -06001027 buffer += sprint_varx("command_line", ' '.join(sys.argv), 0, indent,
1028 loc_col1_width)
Michael Walsh7423c012016-10-04 10:27:21 -05001029 # We want the output to show a customized name for the pid and pgid but
1030 # we want it to look like a valid variable name. Therefore, we'll use
Michael Walshde791732016-09-06 14:25:24 -05001031 # pgm_name_var_name which was set when this module was imported.
Michael Walshbec416d2016-11-10 08:54:52 -06001032 buffer += sprint_varx(pgm_name_var_name + "_pid", os.getpid(), 0, indent,
1033 loc_col1_width)
1034 buffer += sprint_varx(pgm_name_var_name + "_pgid", os.getpgrp(), 0, indent,
1035 loc_col1_width)
Michael Walsh86de0d22016-12-05 10:13:15 -06001036 userid_num = str(os.geteuid())
1037 try:
1038 username = os.getlogin()
1039 except OSError:
1040 if userid_num == "0":
1041 username = "root"
1042 else:
1043 username = "?"
1044 buffer += sprint_varx("uid", userid_num + " (" + username +
Michael Walshbec416d2016-11-10 08:54:52 -06001045 ")", 0, indent, loc_col1_width)
Michael Walshde791732016-09-06 14:25:24 -05001046 buffer += sprint_varx("gid", str(os.getgid()) + " (" +
Michael Walsh7423c012016-10-04 10:27:21 -05001047 str(grp.getgrgid(os.getgid()).gr_name) + ")", 0,
Michael Walshbec416d2016-11-10 08:54:52 -06001048 indent, loc_col1_width)
1049 buffer += sprint_varx("host_name", socket.gethostname(), 0, indent,
1050 loc_col1_width)
Michael Walsh86de0d22016-12-05 10:13:15 -06001051 try:
1052 DISPLAY = os.environ['DISPLAY']
1053 except KeyError:
1054 DISPLAY = ""
1055 buffer += sprint_varx("DISPLAY", DISPLAY, 0, indent,
Michael Walshbec416d2016-11-10 08:54:52 -06001056 loc_col1_width)
Michael Walshde791732016-09-06 14:25:24 -05001057 # I want to add code to print caller's parms.
1058
Michael Walsh7423c012016-10-04 10:27:21 -05001059 # __builtin__.arg_obj is created by the get_arg module function,
1060 # gen_get_options.
1061 try:
1062 buffer += ga.sprint_args(__builtin__.arg_obj, indent)
1063 except AttributeError:
1064 pass
1065
Michael Walshdb6e68a2017-05-23 17:55:31 -05001066 if robot_env:
1067 # Get value of global parm_list.
1068 parm_list = BuiltIn().get_variable_value("${parm_list}")
1069
1070 for parm in parm_list:
1071 parm_value = BuiltIn().get_variable_value("${" + parm + "}")
1072 buffer += sprint_varx(parm, parm_value, 0, indent, loc_col1_width)
1073
1074 # Setting global program_pid.
1075 BuiltIn().set_global_variable("${program_pid}", os.getpid())
1076
Michael Walshbec416d2016-11-10 08:54:52 -06001077 if linefeed:
1078 buffer += "\n"
Michael Walshde791732016-09-06 14:25:24 -05001079
1080 return buffer
1081
Michael Walshde791732016-09-06 14:25:24 -05001082
Michael Walsh7423c012016-10-04 10:27:21 -05001083def sprint_error_report(error_text="\n",
Michael Walshdb6e68a2017-05-23 17:55:31 -05001084 indent=2,
1085 format=None):
Michael Walsh7423c012016-10-04 10:27:21 -05001086 r"""
1087 Return a string with a standardized report which includes the caller's
1088 error text, the call stack and the program header.
1089
1090 Description of args:
1091 error_text The error text to be included in the
1092 report. The caller should include any
1093 needed linefeeds.
1094 indent The number of characters to indent each
1095 line of output.
Michael Walshdb6e68a2017-05-23 17:55:31 -05001096 format Long or short format. Long includes
1097 extras like lines of dashes, call stack,
1098 etc.
Michael Walsh7423c012016-10-04 10:27:21 -05001099 """
1100
Michael Walshdb6e68a2017-05-23 17:55:31 -05001101 # Process input.
1102 indent = int(indent)
1103 if format is None:
1104 if robot_env:
1105 format = 'short'
1106 else:
1107 format = 'long'
1108 error_text = error_text.rstrip('\n') + '\n'
1109
1110 if format == 'short':
1111 return sprint_error(error_text)
1112
Michael Walsh7423c012016-10-04 10:27:21 -05001113 buffer = ""
1114 buffer += sprint_dashes(width=120, char="=")
1115 buffer += sprint_error(error_text)
1116 buffer += "\n"
1117 # Calling sprint_call_stack with stack_frame_ix of 0 causes it to show
1118 # itself and this function in the call stack. This is not helpful to a
1119 # debugger and is therefore clutter. We will adjust the stack_frame_ix to
1120 # hide that information.
Michael Walsh9c75f672017-09-12 17:11:35 -05001121 stack_frame_ix = 1
Michael Walsh7423c012016-10-04 10:27:21 -05001122 caller_func_name = sprint_func_name(2)
1123 if caller_func_name.endswith("print_error_report"):
1124 stack_frame_ix += 1
Michael Walshdb6e68a2017-05-23 17:55:31 -05001125 if not robot_env:
1126 buffer += sprint_call_stack(indent, stack_frame_ix)
Michael Walsh7423c012016-10-04 10:27:21 -05001127 buffer += sprint_pgm_header(indent)
1128 buffer += sprint_dashes(width=120, char="=")
1129
1130 return buffer
1131
Michael Walsh7423c012016-10-04 10:27:21 -05001132
Michael Walsh18176322016-11-15 15:11:21 -06001133def sprint_issuing(cmd_buf,
1134 test_mode=0):
Michael Walshde791732016-09-06 14:25:24 -05001135 r"""
1136 Return a line indicating a command that the program is about to execute.
1137
1138 Sample output for a cmd_buf of "ls"
1139
1140 #(CDT) 2016/08/25 17:57:36 - Issuing: ls
Michael Walshbec416d2016-11-10 08:54:52 -06001141
Michael Walshde791732016-09-06 14:25:24 -05001142 Description of args:
1143 cmd_buf The command to be executed by caller.
Michael Walshbec416d2016-11-10 08:54:52 -06001144 test_mode With test_mode set, your output will look
1145 like this:
1146
1147 #(CDT) 2016/08/25 17:57:36 - (test_mode) Issuing: ls
1148
Michael Walshde791732016-09-06 14:25:24 -05001149 """
1150
Michael Walshbec416d2016-11-10 08:54:52 -06001151 buffer = sprint_time()
1152 if test_mode:
1153 buffer += "(test_mode) "
1154 buffer += "Issuing: " + cmd_buf + "\n"
Michael Walshde791732016-09-06 14:25:24 -05001155
1156 return buffer
1157
Michael Walshde791732016-09-06 14:25:24 -05001158
Michael Walshde791732016-09-06 14:25:24 -05001159def sprint_pgm_footer():
Michael Walshde791732016-09-06 14:25:24 -05001160 r"""
1161 Return a standardized footer that programs should print at the end of the
1162 program run. It includes useful information like total run time, etc.
1163 """
1164
1165 buffer = "\n" + sprint_time() + "Finished running " + pgm_name + ".\n\n"
1166
1167 total_time = time.time() - start_time
1168 total_time_string = "%0.6f" % total_time
1169
Michael Walsh7423c012016-10-04 10:27:21 -05001170 buffer += sprint_varx(pgm_name_var_name + "_runtime", total_time_string)
Michael Walshbec416d2016-11-10 08:54:52 -06001171 buffer += "\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001172
1173 return buffer
1174
Michael Walsh7423c012016-10-04 10:27:21 -05001175
Michael Walsh7423c012016-10-04 10:27:21 -05001176def sprint(buffer=""):
Michael Walsh7423c012016-10-04 10:27:21 -05001177 r"""
1178 Simply return the user's buffer. This function is used by the qprint and
1179 dprint functions defined dynamically below, i.e. it would not normally be
1180 called for general use.
1181
1182 Description of arguments.
1183 buffer This will be returned to the caller.
1184 """
Michael Walshde791732016-09-06 14:25:24 -05001185
Michael Walsh95e45102018-02-09 12:44:43 -06001186 try:
1187 return str(buffer)
1188 except UnicodeEncodeError:
1189 return buffer
Michael Walshbec416d2016-11-10 08:54:52 -06001190
Michael Walshbec416d2016-11-10 08:54:52 -06001191
Michael Walshbec416d2016-11-10 08:54:52 -06001192def sprintn(buffer=""):
Michael Walshbec416d2016-11-10 08:54:52 -06001193 r"""
1194 Simply return the user's buffer with a line feed. This function is used
1195 by the qprint and dprint functions defined dynamically below, i.e. it
1196 would not normally be called for general use.
1197
1198 Description of arguments.
1199 buffer This will be returned to the caller.
1200 """
1201
Michael Walsh95e45102018-02-09 12:44:43 -06001202 try:
1203 buffer = str(buffer) + "\n"
1204 except UnicodeEncodeError:
1205 buffer = buffer + "\n"
Michael Walshbec416d2016-11-10 08:54:52 -06001206
Michael Walshde791732016-09-06 14:25:24 -05001207 return buffer
1208
Michael Walsh168eb0f2017-12-01 15:35:32 -06001209
Michael Walshfd2733c2017-11-13 11:36:20 -06001210def gp_print(buffer,
1211 stream='stdout'):
Michael Walshfd2733c2017-11-13 11:36:20 -06001212 r"""
1213 Print the buffer using either sys.stdout.write or BuiltIn().log_to_console
1214 depending on whether we are running in a robot environment.
1215
1216 This function is intended for use only by other functions in this module.
1217
1218 Description of arguments:
1219 buffer The string to be printed.
1220 stream Either "stdout" or "stderr".
1221 """
1222
1223 if robot_env:
1224 BuiltIn().log_to_console(buffer, stream=stream, no_newline=True)
1225 else:
1226 if stream == "stdout":
1227 sys.stdout.write(buffer)
1228 sys.stdout.flush()
1229 else:
1230 sys.stderr.write(buffer)
1231 sys.stderr.flush()
Michael Walshde791732016-09-06 14:25:24 -05001232
1233
Michael Walsh168eb0f2017-12-01 15:35:32 -06001234def gp_log(buffer):
Michael Walsh168eb0f2017-12-01 15:35:32 -06001235 r"""
1236 Log the buffer using either python logging or BuiltIn().log depending on
1237 whether we are running in a robot environment.
1238
1239 This function is intended for use only by other functions in this module.
1240
1241 Description of arguments:
1242 buffer The string to be logged.
1243 """
1244
1245 if robot_env:
1246 BuiltIn().log(buffer)
1247 else:
1248 logging.warning(buffer)
1249
1250
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001251def gp_debug_print(buffer):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001252 r"""
Michael Walshfd2733c2017-11-13 11:36:20 -06001253 Print with gp_print only if gen_print_debug is set.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001254
1255 This function is intended for use only by other functions in this module.
1256
1257 Description of arguments:
1258 buffer The string to be printed.
1259 """
1260
1261 if not gen_print_debug:
1262 return
1263
Michael Walshfd2733c2017-11-13 11:36:20 -06001264 gp_print(buffer)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001265
1266
Michael Walshb1500152017-04-12 15:42:43 -05001267def get_var_value(var_value=None,
1268 default=1,
1269 var_name=None):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001270 r"""
Michael Walshb1500152017-04-12 15:42:43 -05001271 Return either var_value, the corresponding global value or default.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001272
Michael Walshb1500152017-04-12 15:42:43 -05001273 If var_value is not None, it will simply be returned.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001274
Michael Walshb1500152017-04-12 15:42:43 -05001275 If var_value is None, this function will return the corresponding global
1276 value of the variable in question.
1277
1278 Note: For global values, if we are in a robot environment,
1279 get_variable_value will be used. Otherwise, the __builtin__ version of
1280 the variable is returned (which are set by gen_arg.py functions).
1281
1282 If there is no global value associated with the variable, default is
1283 returned.
1284
1285 This function is useful for other functions in setting default values for
1286 parameters.
1287
1288 Example use:
1289
1290 def my_func(quiet=None):
1291
1292 quiet = int(get_var_value(quiet, 0))
1293
1294 Example calls to my_func():
1295
1296 In the following example, the caller is explicitly asking to have quiet be
1297 set to 1.
1298
1299 my_func(quiet=1)
1300
1301 In the following example, quiet will be set to the global value of quiet,
1302 if defined, or to 0 (the default).
1303
1304 my_func()
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001305
1306 Description of arguments:
Michael Walshb1500152017-04-12 15:42:43 -05001307 var_value The value to be returned (if not equal to
1308 None).
1309 default The value that is returned if var_value is
1310 None and there is no corresponding global
1311 value defined.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001312 var_name The name of the variable whose value is to
Michael Walshb1500152017-04-12 15:42:43 -05001313 be returned. Under most circumstances,
1314 this value need not be provided. This
1315 function can figure out the name of the
1316 variable passed as var_value. One
1317 exception to this would be if this
1318 function is called directly from a .robot
1319 file.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001320 """
1321
Michael Walshb1500152017-04-12 15:42:43 -05001322 if var_value is not None:
1323 return var_value
1324
1325 if var_name is None:
1326 var_name = get_arg_name(None, 1, 2)
1327
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001328 if robot_env:
Michael Walshc6537442017-06-06 15:33:52 -05001329 var_value = BuiltIn().get_variable_value("${" + var_name + "}",
1330 default)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001331 else:
1332 var_value = getattr(__builtin__, var_name, default)
1333
1334 return var_value
1335
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001336
Michael Walsh82acf002017-05-04 14:33:05 -05001337# hidden_text is a list of passwords which are to be replaced with asterisks
1338# by print functions defined in this module.
1339hidden_text = []
1340# password_regex is created based on the contents of hidden_text.
1341password_regex = ""
1342
1343
Michael Walsh82acf002017-05-04 14:33:05 -05001344def register_passwords(*args):
Michael Walsh82acf002017-05-04 14:33:05 -05001345 r"""
1346 Register one or more passwords which are to be hidden in output produced
1347 by the print functions in this module.
1348
1349 Note: Blank password values are NOT registered. They are simply ignored.
1350
1351 Description of argument(s):
1352 args One or more password values. If a given
1353 password value is already registered, this
1354 function will simply do nothing.
1355 """
1356
1357 global hidden_text
1358 global password_regex
1359
1360 for password in args:
1361 if password == "":
1362 break
1363 if password in hidden_text:
1364 break
1365
1366 # Place the password into the hidden_text list.
1367 hidden_text.append(password)
1368 # Create a corresponding password regular expression. Escape regex
1369 # special characters too.
1370 password_regex = '(' +\
1371 '|'.join([re.escape(x) for x in hidden_text]) + ')'
1372
Michael Walsh82acf002017-05-04 14:33:05 -05001373
Michael Walsh82acf002017-05-04 14:33:05 -05001374def replace_passwords(buffer):
Michael Walsh82acf002017-05-04 14:33:05 -05001375 r"""
1376 Return the buffer but with all registered passwords replaced by a string
1377 of asterisks.
1378
1379
1380 Description of argument(s):
1381 buffer The string to be returned but with
1382 passwords replaced.
1383 """
1384
1385 global password_regex
1386
1387 if int(os.environ.get("DEBUG_SHOW_PASSWORDS", "0")):
1388 return buffer
1389
1390 if password_regex == "":
1391 # No passwords to replace.
1392 return buffer
1393
1394 return re.sub(password_regex, "********", buffer)
1395
Michael Walshfd2733c2017-11-13 11:36:20 -06001396
1397def create_print_wrapper_funcs(func_names,
1398 stderr_func_names,
1399 replace_dict):
Michael Walshfd2733c2017-11-13 11:36:20 -06001400 r"""
1401 Generate code for print wrapper functions and return the generated code as
1402 a string.
1403
1404 To illustrate, suppose there is a "print_foo_bar" function in the
1405 func_names list.
1406 This function will...
1407 - Expect that there is an sprint_foo_bar function already in existence.
1408 - Create a print_foo_bar function which calls sprint_foo_bar and prints
1409 the result.
1410 - Create a qprint_foo_bar function which calls upon sprint_foo_bar only if
1411 global value quiet is 0.
1412 - Create a dprint_foo_bar function which calls upon sprint_foo_bar only if
1413 global value debug is 1.
1414
1415 Also, code will be generated to define aliases for each function as well.
1416 Each alias will be created by replacing "print_" in the function name with
1417 "p" For example, the alias for print_foo_bar will be pfoo_bar.
1418
1419 Description of argument(s):
1420 func_names A list of functions for which print
1421 wrapper function code is to be generated.
1422 stderr_func_names A list of functions whose generated code
1423 should print to stderr rather than to
1424 stdout.
1425 replace_dict Please see the create_func_def_string
1426 function in wrap_utils.py for details on
1427 this parameter. This parameter will be
1428 passed directly to create_func_def_string.
1429 """
1430
1431 buffer = ""
1432
1433 for func_name in func_names:
1434 if func_name in stderr_func_names:
1435 replace_dict['output_stream'] = "stderr"
1436 else:
1437 replace_dict['output_stream'] = "stdout"
1438
1439 s_func_name = "s" + func_name
1440 q_func_name = "q" + func_name
1441 d_func_name = "d" + func_name
1442
1443 # We don't want to try to redefine the "print" function, thus the
1444 # following if statement.
1445 if func_name != "print":
1446 func_def = create_func_def_string(s_func_name, func_name,
1447 print_func_template,
1448 replace_dict)
1449 buffer += func_def
1450
1451 func_def = create_func_def_string(s_func_name, "q" + func_name,
1452 qprint_func_template, replace_dict)
1453 buffer += func_def
1454
1455 func_def = create_func_def_string(s_func_name, "d" + func_name,
1456 dprint_func_template, replace_dict)
1457 buffer += func_def
1458
Michael Walsh168eb0f2017-12-01 15:35:32 -06001459 func_def = create_func_def_string(s_func_name, "l" + func_name,
1460 lprint_func_template, replace_dict)
1461 buffer += func_def
1462
Michael Walshfd2733c2017-11-13 11:36:20 -06001463 # Create abbreviated aliases (e.g. spvar is an alias for sprint_var).
1464 alias = re.sub("print_", "p", func_name)
1465 alias = re.sub("print", "p", alias)
Michael Walsh168eb0f2017-12-01 15:35:32 -06001466 prefixes = ["", "s", "q", "d", "l"]
Michael Walshfd2733c2017-11-13 11:36:20 -06001467 for prefix in prefixes:
1468 if alias == "p":
1469 continue
1470 func_def = prefix + alias + " = " + prefix + func_name
1471 buffer += func_def + "\n"
1472
1473 return buffer
Michael Walsh82acf002017-05-04 14:33:05 -05001474
1475
Michael Walshde791732016-09-06 14:25:24 -05001476# In the following section of code, we will dynamically create print versions
1477# for each of the sprint functions defined above. So, for example, where we
1478# have an sprint_time() function defined above that returns the time to the
Michael Walsh7423c012016-10-04 10:27:21 -05001479# caller in a string, we will create a corresponding print_time() function
1480# that will print that string directly to stdout.
Michael Walshde791732016-09-06 14:25:24 -05001481
Michael Walshfd2733c2017-11-13 11:36:20 -06001482# It can be complicated to follow what's being created by below. Here is an
1483# example of the print_time() function that will be created:
Michael Walshde791732016-09-06 14:25:24 -05001484
Michael Walshfd2733c2017-11-13 11:36:20 -06001485# def print_time(buffer=''):
1486# sys.stdout.write(replace_passwords(sprint_time(buffer=buffer)))
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001487# sys.stdout.flush()
Michael Walshde791732016-09-06 14:25:24 -05001488
Michael Walshfd2733c2017-11-13 11:36:20 -06001489# Templates for the various print wrapper functions.
1490print_func_template = \
1491 [
Michael Walsh81c02342018-01-05 15:43:28 -06001492 " <mod_qualifier>gp_print(<mod_qualifier>replace_passwords(" +
1493 "<call_line>), stream='<output_stream>')"
Michael Walshfd2733c2017-11-13 11:36:20 -06001494 ]
1495
1496qprint_func_template = \
1497 [
Michael Walsh81c02342018-01-05 15:43:28 -06001498 " if int(<mod_qualifier>get_var_value(None, 0, \"quiet\")): return"
Michael Walshfd2733c2017-11-13 11:36:20 -06001499 ] + print_func_template
1500
1501dprint_func_template = \
1502 [
Michael Walsh81c02342018-01-05 15:43:28 -06001503 " if not int(<mod_qualifier>get_var_value(None, 0, \"debug\")):" +
Michael Walshfd2733c2017-11-13 11:36:20 -06001504 " return"
1505 ] + print_func_template
1506
Michael Walsh168eb0f2017-12-01 15:35:32 -06001507lprint_func_template = \
1508 [
Michael Walsh81c02342018-01-05 15:43:28 -06001509 " gp_log(<mod_qualifier>replace_passwords(<call_line>))"
Michael Walsh168eb0f2017-12-01 15:35:32 -06001510 ]
1511
Michael Walsh81c02342018-01-05 15:43:28 -06001512replace_dict = {'output_stream': 'stdout', 'mod_qualifier': ''}
Michael Walshfd2733c2017-11-13 11:36:20 -06001513
1514
1515gp_debug_print("robot_env: " + str(robot_env))
Michael Walshde791732016-09-06 14:25:24 -05001516
1517# func_names contains a list of all print functions which should be created
1518# from their sprint counterparts.
1519func_names = ['print_time', 'print_timen', 'print_error', 'print_varx',
Michael Walsh18176322016-11-15 15:11:21 -06001520 'print_var', 'print_vars', 'print_dashes', 'indent',
1521 'print_call_stack', 'print_func_name', 'print_executing',
1522 'print_pgm_header', 'print_issuing', 'print_pgm_footer',
1523 'print_error_report', 'print', 'printn']
Michael Walshde791732016-09-06 14:25:24 -05001524
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001525# stderr_func_names is a list of functions whose output should go to stderr
1526# rather than stdout.
1527stderr_func_names = ['print_error', 'print_error_report']
Michael Walshde791732016-09-06 14:25:24 -05001528
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001529
Michael Walshfd2733c2017-11-13 11:36:20 -06001530func_defs = create_print_wrapper_funcs(func_names, stderr_func_names,
1531 replace_dict)
1532gp_debug_print(func_defs)
1533exec(func_defs)