blob: 2a3ed19d1e643bf60953710a4c7a46df2a99d30e [file] [log] [blame]
Michael Walshde791732016-09-06 14:25:24 -05001#!/usr/bin/env python
2
3r"""
4This module provides many valuable print functions such as sprint_var,
5sprint_time, sprint_error, sprint_call_stack.
6"""
7
8import sys
9import os
10import time
11import inspect
12import re
13import grp
14import socket
15import argparse
Michael Walsh7423c012016-10-04 10:27:21 -050016import __builtin__
17import logging
Michael Walshbec416d2016-11-10 08:54:52 -060018import collections
Michael Walshfd2733c2017-11-13 11:36:20 -060019from wrap_utils import *
Michael Walshbec416d2016-11-10 08:54:52 -060020
Michael Walshbec416d2016-11-10 08:54:52 -060021try:
Michael Walsh2ee77cd2017-03-08 11:50:17 -060022 robot_env = 1
Michael Walshbec416d2016-11-10 08:54:52 -060023 from robot.utils import DotDict
Michael Walsh8e6deb42017-01-27 14:22:41 -060024 from robot.utils import NormalizedDict
Michael Walsh2ee77cd2017-03-08 11:50:17 -060025 from robot.libraries.BuiltIn import BuiltIn
Michael Walshb1500152017-04-12 15:42:43 -050026 # Having access to the robot libraries alone does not indicate that we
27 # are in a robot environment. The following try block should confirm that.
28 try:
29 var_value = BuiltIn().get_variable_value("${SUITE_NAME}", "")
30 except:
31 robot_env = 0
Michael Walshbec416d2016-11-10 08:54:52 -060032except ImportError:
Michael Walsh2ee77cd2017-03-08 11:50:17 -060033 robot_env = 0
Michael Walsh7423c012016-10-04 10:27:21 -050034
35import gen_arg as ga
Michael Walshde791732016-09-06 14:25:24 -050036
37# Setting these variables for use both inside this module and by programs
38# importing this module.
Michael Walshbf605652017-09-01 12:33:26 -050039pgm_file_path = sys.argv[0]
40pgm_name = os.path.basename(pgm_file_path)
41pgm_dir_path = re.sub("/" + pgm_name, "", pgm_file_path) + "/"
Michael Walsh7423c012016-10-04 10:27:21 -050042
Michael Walshde791732016-09-06 14:25:24 -050043
44# Some functions (e.g. sprint_pgm_header) have need of a program name value
45# that looks more like a valid variable name. Therefore, we'll swap odd
46# characters like "." out for underscores.
47pgm_name_var_name = pgm_name.replace(".", "_")
48
49# Initialize global values used as defaults by print_time, print_var, etc.
50col1_indent = 0
51
52# Calculate default column width for print_var functions based on environment
53# variable settings. The objective is to make the variable values line up
54# nicely with the time stamps.
55col1_width = 29
Michael Walshb1500152017-04-12 15:42:43 -050056
57NANOSECONDS = os.environ.get('NANOSECONDS', '1')
58
Michael Walshde791732016-09-06 14:25:24 -050059
60if NANOSECONDS == "1":
61 col1_width = col1_width + 7
62
Michael Walshb1500152017-04-12 15:42:43 -050063SHOW_ELAPSED_TIME = os.environ.get('SHOW_ELAPSED_TIME', '1')
Michael Walshde791732016-09-06 14:25:24 -050064
65if SHOW_ELAPSED_TIME == "1":
66 if NANOSECONDS == "1":
67 col1_width = col1_width + 14
68 else:
69 col1_width = col1_width + 7
70
71# Initialize some time variables used in module functions.
72start_time = time.time()
73sprint_time_last_seconds = start_time
74
Michael Walsh2ee77cd2017-03-08 11:50:17 -060075# The user can set environment variable "GEN_PRINT_DEBUG" to get debug output
76# from this module.
77gen_print_debug = int(os.environ.get('GEN_PRINT_DEBUG', 0))
Michael Walsh7423c012016-10-04 10:27:21 -050078
Michael Walshde791732016-09-06 14:25:24 -050079
Michael Walshde791732016-09-06 14:25:24 -050080def sprint_func_name(stack_frame_ix=None):
81
82 r"""
83 Return the function name associated with the indicated stack frame.
84
85 Description of arguments:
86 stack_frame_ix The index of the stack frame whose
87 function name should be returned. If the
Michael Walsh2ee77cd2017-03-08 11:50:17 -060088 caller does not specify a value, this
Michael Walshde791732016-09-06 14:25:24 -050089 function will set the value to 1 which is
90 the index of the caller's stack frame. If
91 the caller is the wrapper function
92 "print_func_name", this function will bump
93 it up by 1.
94 """
95
96 # If user specified no stack_frame_ix, we'll set it to a proper default
97 # value.
98 if stack_frame_ix is None:
99 func_name = sys._getframe().f_code.co_name
100 caller_func_name = sys._getframe(1).f_code.co_name
101 if func_name[1:] == caller_func_name:
102 stack_frame_ix = 2
103 else:
104 stack_frame_ix = 1
105
106 func_name = sys._getframe(stack_frame_ix).f_code.co_name
107
108 return func_name
109
Michael Walshde791732016-09-06 14:25:24 -0500110
111# get_arg_name is not a print function per se. I have included it in this
112# module because it is used by sprint_var which is found in this module.
Michael Walshde791732016-09-06 14:25:24 -0500113def get_arg_name(var,
114 arg_num=1,
115 stack_frame_ix=1):
116
117 r"""
118 Return the "name" of an argument passed to a function. This could be a
119 literal or a variable name.
120
Michael Walsh2ee77cd2017-03-08 11:50:17 -0600121 Description of arguments:
Michael Walshde791732016-09-06 14:25:24 -0500122 var The variable whose name you want returned.
123 arg_num The arg number (1 through n) whose name
124 you wish to have returned. This value
125 should not exceed the number of arguments
126 allowed by the target function.
127 stack_frame_ix The stack frame index of the target
128 function. This value must be 1 or
129 greater. 1 would indicate get_arg_name's
130 stack frame. 2 would be the caller of
131 get_arg_name's stack frame, etc.
132
133 Example 1:
134
135 my_var = "mike"
136 var_name = get_arg_name(my_var)
137
138 In this example, var_name will receive the value "my_var".
139
140 Example 2:
141
142 def test1(var):
143 # Getting the var name of the first arg to this function, test1.
144 # Note, in this case, it doesn't matter what you pass as the first arg
145 # to get_arg_name since it is the caller's variable name that matters.
146 dummy = 1
147 arg_num = 1
148 stack_frame = 2
149 var_name = get_arg_name(dummy, arg_num, stack_frame)
150
151 # Mainline...
152
153 another_var = "whatever"
154 test1(another_var)
155
156 In this example, var_name will be set to "another_var".
157
158 """
159
160 # Note: I wish to avoid recursion so I refrain from calling any function
161 # that calls this function (i.e. sprint_var, valid_value, etc.).
162
Michael Walsh23e7f492017-01-10 11:34:47 -0600163 # The user can set environment variable "GET_ARG_NAME_DEBUG" to get debug
164 # output from this function.
165 local_debug = int(os.environ.get('GET_ARG_NAME_DEBUG', 0))
166 # In addition to GET_ARG_NAME_DEBUG, the user can set environment
167 # variable "GET_ARG_NAME_SHOW_SOURCE" to have this function include source
168 # code in the debug output.
169 local_debug_show_source = int(
170 os.environ.get('GET_ARG_NAME_SHOW_SOURCE', 0))
Michael Walshde791732016-09-06 14:25:24 -0500171
172 if arg_num < 1:
173 print_error("Programmer error - Variable \"arg_num\" has an invalid" +
174 " value of \"" + str(arg_num) + "\". The value must be" +
175 " an integer that is greater than 0.\n")
176 # What is the best way to handle errors? Raise exception? I'll
177 # revisit later.
178 return
179 if stack_frame_ix < 1:
180 print_error("Programmer error - Variable \"stack_frame_ix\" has an" +
181 " invalid value of \"" + str(stack_frame_ix) + "\". The" +
182 " value must be an integer that is greater than or equal" +
183 " to 1.\n")
184 return
185
186 if local_debug:
187 debug_indent = 2
Michael Walsh23e7f492017-01-10 11:34:47 -0600188 print("")
189 print_dashes(0, 120)
Michael Walshde791732016-09-06 14:25:24 -0500190 print(sprint_func_name() + "() parms:")
191 print_varx("var", var, 0, debug_indent)
192 print_varx("arg_num", arg_num, 0, debug_indent)
193 print_varx("stack_frame_ix", stack_frame_ix, 0, debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600194 print("")
195 print_call_stack(debug_indent, 2)
Michael Walshde791732016-09-06 14:25:24 -0500196
Michael Walsh23e7f492017-01-10 11:34:47 -0600197 for count in range(0, 2):
198 try:
199 frame, filename, cur_line_no, function_name, lines, index = \
200 inspect.stack()[stack_frame_ix]
201 except IndexError:
202 print_error("Programmer error - The caller has asked for" +
203 " information about the stack frame at index \"" +
204 str(stack_frame_ix) + "\". However, the stack" +
205 " only contains " + str(len(inspect.stack())) +
206 " entries. Therefore the stack frame index is out" +
207 " of range.\n")
208 return
209 if filename != "<string>":
210 break
211 # filename of "<string>" may mean that the function in question was
212 # defined dynamically and therefore its code stack is inaccessible.
213 # This may happen with functions like "rqprint_var". In this case,
214 # we'll increment the stack_frame_ix and try again.
215 stack_frame_ix += 1
216 if local_debug:
217 print("Adjusted stack_frame_ix...")
218 print_varx("stack_frame_ix", stack_frame_ix, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500219
220 called_func_name = sprint_func_name(stack_frame_ix)
Michael Walsh23e7f492017-01-10 11:34:47 -0600221
222 module = inspect.getmodule(frame)
223
224 # Though I would expect inspect.getsourcelines(frame) to get all module
225 # source lines if the frame is "<module>", it doesn't do that. Therefore,
226 # for this special case, I will do inspect.getsourcelines(module).
227 if function_name == "<module>":
228 source_lines, source_line_num =\
229 inspect.getsourcelines(module)
230 line_ix = cur_line_no - source_line_num - 1
231 else:
232 source_lines, source_line_num =\
233 inspect.getsourcelines(frame)
234 line_ix = cur_line_no - source_line_num
235
236 if local_debug:
237 print("\n Variables retrieved from inspect.stack() function:")
238 print_varx("frame", frame, 0, debug_indent + 2)
239 print_varx("filename", filename, 0, debug_indent + 2)
240 print_varx("cur_line_no", cur_line_no, 0, debug_indent + 2)
241 print_varx("function_name", function_name, 0, debug_indent + 2)
242 print_varx("lines", lines, 0, debug_indent + 2)
243 print_varx("index", index, 0, debug_indent + 2)
244 print_varx("source_line_num", source_line_num, 0, debug_indent)
245 print_varx("line_ix", line_ix, 0, debug_indent)
246 if local_debug_show_source:
247 print_varx("source_lines", source_lines, 0, debug_indent)
248 print_varx("called_func_name", called_func_name, 0, debug_indent)
249
250 # Get a list of all functions defined for the module. Note that this
251 # doesn't work consistently when _run_exitfuncs is at the top of the stack
252 # (i.e. if we're running an exit function). I've coded a work-around
253 # below for this deficiency.
254 all_functions = inspect.getmembers(module, inspect.isfunction)
255
256 # Get called_func_id by searching for our function in the list of all
257 # functions.
258 called_func_id = None
259 for func_name, function in all_functions:
260 if func_name == called_func_name:
261 called_func_id = id(function)
262 break
263 # NOTE: The only time I've found that called_func_id can't be found is
264 # when we're running from an exit function.
265
266 # Look for other functions in module with matching id.
267 aliases = set([called_func_name])
268 for func_name, function in all_functions:
269 if func_name == called_func_name:
270 continue
271 func_id = id(function)
272 if func_id == called_func_id:
273 aliases.add(func_name)
274
275 # In most cases, my general purpose code above will find all aliases.
276 # However, for the odd case (i.e. running from exit function), I've added
277 # code to handle pvar, qpvar, dpvar, etc. aliases explicitly since they
278 # are defined in this module and used frequently.
279 # pvar is an alias for print_var.
280 aliases.add(re.sub("print_var", "pvar", called_func_name))
281
282 func_regex = ".*(" + '|'.join(aliases) + ")[ ]*\("
283
284 # Search backward through source lines looking for the calling function
285 # name.
286 found = False
287 for start_line_ix in range(line_ix, 0, -1):
288 # Skip comment lines.
289 if re.match(r"[ ]*#", source_lines[start_line_ix]):
290 continue
291 if re.match(func_regex, source_lines[start_line_ix]):
292 found = True
293 break
294 if not found:
295 print_error("Programmer error - Could not find the source line with" +
296 " a reference to function \"" + called_func_name + "\".\n")
297 return
298
Michael Walsh82acf002017-05-04 14:33:05 -0500299 # Search forward through the source lines looking for a line whose
300 # indentation is the same or less than the start line. The end of our
301 # composite line should be the line preceding that line.
Michael Walsh23e7f492017-01-10 11:34:47 -0600302 start_indent = len(source_lines[start_line_ix]) -\
303 len(source_lines[start_line_ix].lstrip(' '))
304 end_line_ix = line_ix
305 for end_line_ix in range(line_ix + 1, len(source_lines)):
306 if source_lines[end_line_ix].strip() == "":
307 continue
308 line_indent = len(source_lines[end_line_ix]) -\
309 len(source_lines[end_line_ix].lstrip(' '))
Michael Walsh82acf002017-05-04 14:33:05 -0500310 if line_indent <= start_indent:
Michael Walsh23e7f492017-01-10 11:34:47 -0600311 end_line_ix -= 1
312 break
313
314 # Join the start line through the end line into a composite line.
315 composite_line = ''.join(map(str.strip,
316 source_lines[start_line_ix:end_line_ix + 1]))
Michael Walsh7423c012016-10-04 10:27:21 -0500317
Michael Walshbec416d2016-11-10 08:54:52 -0600318 # arg_list_etc = re.sub(".*" + called_func_name, "", composite_line)
319 arg_list_etc = "(" + re.sub(func_regex, "", composite_line)
Michael Walshde791732016-09-06 14:25:24 -0500320 if local_debug:
Michael Walsh23e7f492017-01-10 11:34:47 -0600321 print_varx("aliases", aliases, 0, debug_indent)
Michael Walshbec416d2016-11-10 08:54:52 -0600322 print_varx("func_regex", func_regex, 0, debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600323 print_varx("start_line_ix", start_line_ix, 0, debug_indent)
324 print_varx("end_line_ix", end_line_ix, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500325 print_varx("composite_line", composite_line, 0, debug_indent)
326 print_varx("arg_list_etc", arg_list_etc, 0, debug_indent)
327
328 # Parse arg list...
329 # Initialize...
330 nest_level = -1
331 arg_ix = 0
Michael Walsh7423c012016-10-04 10:27:21 -0500332 args_list = [""]
Michael Walshde791732016-09-06 14:25:24 -0500333 for ix in range(0, len(arg_list_etc)):
334 char = arg_list_etc[ix]
335 # Set the nest_level based on whether we've encounted a parenthesis.
336 if char == "(":
337 nest_level += 1
338 if nest_level == 0:
339 continue
340 elif char == ")":
341 nest_level -= 1
342 if nest_level < 0:
343 break
344
345 # If we reach a comma at base nest level, we are done processing an
Michael Walsh7423c012016-10-04 10:27:21 -0500346 # argument so we increment arg_ix and initialize a new args_list entry.
Michael Walshde791732016-09-06 14:25:24 -0500347 if char == "," and nest_level == 0:
348 arg_ix += 1
Michael Walsh7423c012016-10-04 10:27:21 -0500349 args_list.append("")
Michael Walshde791732016-09-06 14:25:24 -0500350 continue
351
Michael Walsh7423c012016-10-04 10:27:21 -0500352 # For any other character, we append it it to the current arg list
Michael Walshde791732016-09-06 14:25:24 -0500353 # entry.
Michael Walsh7423c012016-10-04 10:27:21 -0500354 args_list[arg_ix] += char
Michael Walshde791732016-09-06 14:25:24 -0500355
356 # Trim whitespace from each list entry.
Michael Walsh7423c012016-10-04 10:27:21 -0500357 args_list = [arg.strip() for arg in args_list]
Michael Walshde791732016-09-06 14:25:24 -0500358
Michael Walsh7423c012016-10-04 10:27:21 -0500359 if arg_num > len(args_list):
Michael Walshde791732016-09-06 14:25:24 -0500360 print_error("Programmer error - The caller has asked for the name of" +
361 " argument number \"" + str(arg_num) + "\" but there " +
Michael Walsh7423c012016-10-04 10:27:21 -0500362 "were only \"" + str(len(args_list)) + "\" args used:\n" +
363 sprint_varx("args_list", args_list))
Michael Walshde791732016-09-06 14:25:24 -0500364 return
365
Michael Walsh7423c012016-10-04 10:27:21 -0500366 argument = args_list[arg_num - 1]
Michael Walshde791732016-09-06 14:25:24 -0500367
368 if local_debug:
Michael Walsh7423c012016-10-04 10:27:21 -0500369 print_varx("args_list", args_list, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500370 print_varx("argument", argument, 0, debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600371 print_dashes(0, 120)
Michael Walshde791732016-09-06 14:25:24 -0500372
373 return argument
374
Michael Walshde791732016-09-06 14:25:24 -0500375
Michael Walshde791732016-09-06 14:25:24 -0500376def sprint_time(buffer=""):
377
378 r"""
379 Return the time in the following format.
380
381 Example:
382
383 The following python code...
384
385 sys.stdout.write(sprint_time())
386 sys.stdout.write("Hi.\n")
387
388 Will result in the following type of output:
389
390 #(CDT) 2016/07/08 15:25:35 - Hi.
391
392 Example:
393
394 The following python code...
395
396 sys.stdout.write(sprint_time("Hi.\n"))
397
398 Will result in the following type of output:
399
400 #(CDT) 2016/08/03 17:12:05 - Hi.
401
402 The following environment variables will affect the formatting as
403 described:
404 NANOSECONDS This will cause the time stamps to be
405 precise to the microsecond (Yes, it
406 probably should have been named
407 MICROSECONDS but the convention was set
408 long ago so we're sticking with it).
409 Example of the output when environment
410 variable NANOSECONDS=1.
411
412 #(CDT) 2016/08/03 17:16:25.510469 - Hi.
413
414 SHOW_ELAPSED_TIME This will cause the elapsed time to be
415 included in the output. This is the
416 amount of time that has elapsed since the
417 last time this function was called. The
418 precision of the elapsed time field is
419 also affected by the value of the
420 NANOSECONDS environment variable. Example
421 of the output when environment variable
422 NANOSECONDS=0 and SHOW_ELAPSED_TIME=1.
423
424 #(CDT) 2016/08/03 17:17:40 - 0 - Hi.
425
426 Example of the output when environment variable NANOSECONDS=1 and
427 SHOW_ELAPSED_TIME=1.
428
429 #(CDT) 2016/08/03 17:18:47.317339 - 0.000046 - Hi.
430
431 Description of arguments.
432 buffer This will be appended to the formatted
433 time string.
434 """
435
436 global NANOSECONDS
437 global SHOW_ELAPSED_TIME
438 global sprint_time_last_seconds
439
440 seconds = time.time()
441 loc_time = time.localtime(seconds)
442 nanoseconds = "%0.6f" % seconds
443 pos = nanoseconds.find(".")
444 nanoseconds = nanoseconds[pos:]
445
446 time_string = time.strftime("#(%Z) %Y/%m/%d %H:%M:%S", loc_time)
447 if NANOSECONDS == "1":
448 time_string = time_string + nanoseconds
449
450 if SHOW_ELAPSED_TIME == "1":
451 cur_time_seconds = seconds
452 math_string = "%9.9f" % cur_time_seconds + " - " + "%9.9f" % \
453 sprint_time_last_seconds
454 elapsed_seconds = eval(math_string)
455 if NANOSECONDS == "1":
456 elapsed_seconds = "%11.6f" % elapsed_seconds
457 else:
458 elapsed_seconds = "%4i" % elapsed_seconds
459 sprint_time_last_seconds = cur_time_seconds
460 time_string = time_string + " - " + elapsed_seconds
461
462 return time_string + " - " + buffer
463
Michael Walshde791732016-09-06 14:25:24 -0500464
Michael Walshde791732016-09-06 14:25:24 -0500465def sprint_timen(buffer=""):
466
467 r"""
468 Append a line feed to the buffer, pass it to sprint_time and return the
469 result.
470 """
471
472 return sprint_time(buffer + "\n")
473
Michael Walshde791732016-09-06 14:25:24 -0500474
Michael Walshde791732016-09-06 14:25:24 -0500475def sprint_error(buffer=""):
476
477 r"""
478 Return a standardized error string. This includes:
479 - A time stamp
480 - The "**ERROR**" string
481 - The caller's buffer string.
482
483 Example:
484
485 The following python code...
486
487 print(sprint_error("Oops.\n"))
488
489 Will result in the following type of output:
490
491 #(CDT) 2016/08/03 17:12:05 - **ERROR** Oops.
492
493 Description of arguments.
494 buffer This will be appended to the formatted
495 error string.
496 """
497
498 return sprint_time() + "**ERROR** " + buffer
499
Michael Walshde791732016-09-06 14:25:24 -0500500
Michael Walshde791732016-09-06 14:25:24 -0500501def sprint_varx(var_name,
502 var_value,
503 hex=0,
504 loc_col1_indent=col1_indent,
Michael Walsh7423c012016-10-04 10:27:21 -0500505 loc_col1_width=col1_width,
506 trailing_char="\n"):
Michael Walshde791732016-09-06 14:25:24 -0500507
508 r"""
509 Print the var name/value passed to it. If the caller lets loc_col1_width
510 default, the printing lines up nicely with output generated by the
511 print_time functions.
512
513 Note that the sprint_var function (defined below) can be used to call this
514 function so that the programmer does not need to pass the var_name.
515 sprint_var will figure out the var_name. The sprint_var function is the
516 one that would normally be used by the general user.
517
518 For example, the following python code:
519
520 first_name = "Mike"
521 print_time("Doing this...\n")
522 print_varx("first_name", first_name)
523 print_time("Doing that...\n")
524
525 Will generate output like this:
526
527 #(CDT) 2016/08/10 17:34:42.847374 - 0.001285 - Doing this...
528 first_name: Mike
529 #(CDT) 2016/08/10 17:34:42.847510 - 0.000136 - Doing that...
530
531 This function recognizes several complex types of data such as dict, list
532 or tuple.
533
534 For example, the following python code:
535
536 my_dict = dict(one=1, two=2, three=3)
537 print_var(my_dict)
538
539 Will generate the following output:
540
541 my_dict:
542 my_dict[three]: 3
543 my_dict[two]: 2
544 my_dict[one]: 1
545
546 Description of arguments.
547 var_name The name of the variable to be printed.
548 var_value The value of the variable to be printed.
549 hex This indicates that the value should be
550 printed in hex format. It is the user's
551 responsibility to ensure that a var_value
Michael Walshbec416d2016-11-10 08:54:52 -0600552 contains a valid hex number. For string
553 var_values, this will be interpreted as
554 show_blanks which means that blank values
Michael Walshd995cb02017-02-07 14:46:01 -0600555 will be printed as "<blank>". For dict
556 var_values, this will be interpreted as
557 terse format where keys are not repeated
558 in the output.
Michael Walshde791732016-09-06 14:25:24 -0500559 loc_col1_indent The number of spaces to indent the output.
560 loc_col1_width The width of the output column containing
561 the variable name. The default value of
562 this is adjusted so that the var_value
563 lines up with text printed via the
564 print_time function.
Michael Walsh7423c012016-10-04 10:27:21 -0500565 trailing_char The character to be used at the end of the
566 returned string. The default value is a
567 line feed.
568 """
Michael Walshde791732016-09-06 14:25:24 -0500569
570 # Determine the type
571 if type(var_value) in (int, float, bool, str, unicode) \
572 or var_value is None:
573 # The data type is simple in the sense that it has no subordinate
574 # parts.
Michael Walsh7423c012016-10-04 10:27:21 -0500575 # Adjust loc_col1_width.
576 loc_col1_width = loc_col1_width - loc_col1_indent
Michael Walshde791732016-09-06 14:25:24 -0500577 # See if the user wants the output in hex format.
578 if hex:
Michael Walsh18176322016-11-15 15:11:21 -0600579 if type(var_value) not in (int, long):
Michael Walshbec416d2016-11-10 08:54:52 -0600580 value_format = "%s"
Michael Walsh2795edc2016-12-13 16:00:33 -0600581 if var_value == "":
Michael Walshbec416d2016-11-10 08:54:52 -0600582 var_value = "<blank>"
583 else:
584 value_format = "0x%08x"
Michael Walshde791732016-09-06 14:25:24 -0500585 else:
586 value_format = "%s"
587 format_string = "%" + str(loc_col1_indent) + "s%-" \
Michael Walsh7423c012016-10-04 10:27:21 -0500588 + str(loc_col1_width) + "s" + value_format + trailing_char
Michael Walsh3383e652017-09-01 17:10:59 -0500589 if value_format == "0x%08x":
590 return format_string % ("", str(var_name) + ":",
591 var_value & 0xffffffff)
592 else:
593 return format_string % ("", str(var_name) + ":", var_value)
Michael Walsh20a87ab2017-06-30 17:00:30 -0500594 elif type(var_value) is type:
595 return sprint_varx(var_name, str(var_value).split("'")[1], hex,
596 loc_col1_indent, loc_col1_width, trailing_char)
Michael Walshde791732016-09-06 14:25:24 -0500597 else:
598 # The data type is complex in the sense that it has subordinate parts.
599 format_string = "%" + str(loc_col1_indent) + "s%s\n"
600 buffer = format_string % ("", var_name + ":")
601 loc_col1_indent += 2
Michael Walsh7423c012016-10-04 10:27:21 -0500602 try:
603 length = len(var_value)
604 except TypeError:
Michael Walsh23e7f492017-01-10 11:34:47 -0600605 length = 0
Michael Walsh7423c012016-10-04 10:27:21 -0500606 ix = 0
607 loc_trailing_char = "\n"
Michael Walshbec416d2016-11-10 08:54:52 -0600608 type_is_dict = 0
Michael Walsh23e7f492017-01-10 11:34:47 -0600609 if type(var_value) is dict:
610 type_is_dict = 1
Michael Walsh8e6deb42017-01-27 14:22:41 -0600611 try:
612 if type(var_value) is collections.OrderedDict:
613 type_is_dict = 1
614 except AttributeError:
615 pass
616 try:
617 if type(var_value) is DotDict:
618 type_is_dict = 1
619 except NameError:
620 pass
621 try:
622 if type(var_value) is NormalizedDict:
623 type_is_dict = 1
624 except NameError:
625 pass
Michael Walshbec416d2016-11-10 08:54:52 -0600626 if type_is_dict:
Michael Walshde791732016-09-06 14:25:24 -0500627 for key, value in var_value.iteritems():
Michael Walsh7423c012016-10-04 10:27:21 -0500628 ix += 1
629 if ix == length:
630 loc_trailing_char = trailing_char
Michael Walshd995cb02017-02-07 14:46:01 -0600631 if hex:
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600632 # Since hex is being used as a format type, we want it
633 # turned off when processing integer dictionary values so
634 # it is not interpreted as a hex indicator.
635 loc_hex = not (type(value) is int)
Michael Walshf7b8a002017-08-29 10:38:39 -0500636 buffer += sprint_varx("[" + key + "]", value,
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600637 loc_hex, loc_col1_indent,
638 loc_col1_width,
Michael Walshd995cb02017-02-07 14:46:01 -0600639 loc_trailing_char)
640 else:
641 buffer += sprint_varx(var_name + "[" + key + "]", value,
642 hex, loc_col1_indent, loc_col1_width,
643 loc_trailing_char)
Michael Walsh7423c012016-10-04 10:27:21 -0500644 elif type(var_value) in (list, tuple, set):
Michael Walshde791732016-09-06 14:25:24 -0500645 for key, value in enumerate(var_value):
Michael Walsh7423c012016-10-04 10:27:21 -0500646 ix += 1
647 if ix == length:
648 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500649 buffer += sprint_varx(var_name + "[" + str(key) + "]", value,
Michael Walsh7423c012016-10-04 10:27:21 -0500650 hex, loc_col1_indent, loc_col1_width,
651 loc_trailing_char)
Michael Walshde791732016-09-06 14:25:24 -0500652 elif type(var_value) is argparse.Namespace:
653 for key in var_value.__dict__:
Michael Walsh7423c012016-10-04 10:27:21 -0500654 ix += 1
655 if ix == length:
656 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500657 cmd_buf = "buffer += sprint_varx(var_name + \".\" + str(key)" \
Michael Walsh7423c012016-10-04 10:27:21 -0500658 + ", var_value." + key + ", hex, loc_col1_indent," \
659 + " loc_col1_width, loc_trailing_char)"
Michael Walshde791732016-09-06 14:25:24 -0500660 exec(cmd_buf)
661 else:
662 var_type = type(var_value).__name__
663 func_name = sys._getframe().f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -0500664 var_value = "<" + var_type + " type not supported by " + \
665 func_name + "()>"
Michael Walshde791732016-09-06 14:25:24 -0500666 value_format = "%s"
667 loc_col1_indent -= 2
Michael Walsh7423c012016-10-04 10:27:21 -0500668 # Adjust loc_col1_width.
669 loc_col1_width = loc_col1_width - loc_col1_indent
Michael Walshde791732016-09-06 14:25:24 -0500670 format_string = "%" + str(loc_col1_indent) + "s%-" \
Michael Walsh7423c012016-10-04 10:27:21 -0500671 + str(loc_col1_width) + "s" + value_format + trailing_char
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600672 return format_string % ("", str(var_name) + ":", var_value)
Michael Walsh23e7f492017-01-10 11:34:47 -0600673
Michael Walshde791732016-09-06 14:25:24 -0500674 return buffer
675
676 return ""
677
Michael Walshde791732016-09-06 14:25:24 -0500678
Michael Walshfd2733c2017-11-13 11:36:20 -0600679def sprint_var(var_value,
680 hex=0,
681 loc_col1_indent=col1_indent,
682 loc_col1_width=col1_width,
683 trailing_char="\n"):
Michael Walshde791732016-09-06 14:25:24 -0500684
685 r"""
686 Figure out the name of the first argument for you and then call
687 sprint_varx with it. Therefore, the following 2 calls are equivalent:
688 sprint_varx("var1", var1)
689 sprint_var(var1)
690 """
691
692 # Get the name of the first variable passed to this function.
693 stack_frame = 2
Michael Walsh7423c012016-10-04 10:27:21 -0500694 caller_func_name = sprint_func_name(2)
695 if caller_func_name.endswith("print_var"):
Michael Walshde791732016-09-06 14:25:24 -0500696 stack_frame += 1
697 var_name = get_arg_name(None, 1, stack_frame)
Michael Walshfd2733c2017-11-13 11:36:20 -0600698 return sprint_varx(var_name, var_value=var_value, hex=hex,
699 loc_col1_indent=loc_col1_indent,
700 loc_col1_width=loc_col1_width,
701 trailing_char=trailing_char)
Michael Walshde791732016-09-06 14:25:24 -0500702
703
Michael Walsh18176322016-11-15 15:11:21 -0600704def sprint_vars(*args):
705
706 r"""
707 Sprint the values of one or more variables.
708
709 Description of args:
710 args:
711 If the first argument is an integer, it will be interpreted to be the
712 "indent" value.
713 If the second argument is an integer, it will be interpreted to be the
714 "col1_width" value.
715 If the third argument is an integer, it will be interpreted to be the
716 "hex" value.
717 All remaining parms are considered variable names which are to be
718 sprinted.
719 """
720
721 if len(args) == 0:
722 return
723
724 # Get the name of the first variable passed to this function.
725 stack_frame = 2
726 caller_func_name = sprint_func_name(2)
727 if caller_func_name.endswith("print_vars"):
728 stack_frame += 1
729
730 parm_num = 1
731
732 # Create list from args (which is a tuple) so that it can be modified.
733 args_list = list(args)
734
735 var_name = get_arg_name(None, parm_num, stack_frame)
736 # See if parm 1 is to be interpreted as "indent".
737 try:
738 if type(int(var_name)) is int:
739 indent = int(var_name)
740 args_list.pop(0)
741 parm_num += 1
742 except ValueError:
743 indent = 0
744
745 var_name = get_arg_name(None, parm_num, stack_frame)
746 # See if parm 1 is to be interpreted as "col1_width".
747 try:
748 if type(int(var_name)) is int:
749 loc_col1_width = int(var_name)
750 args_list.pop(0)
751 parm_num += 1
752 except ValueError:
753 loc_col1_width = col1_width
754
755 var_name = get_arg_name(None, parm_num, stack_frame)
756 # See if parm 1 is to be interpreted as "hex".
757 try:
758 if type(int(var_name)) is int:
759 hex = int(var_name)
760 args_list.pop(0)
761 parm_num += 1
762 except ValueError:
763 hex = 0
764
765 buffer = ""
766 for var_value in args_list:
767 var_name = get_arg_name(None, parm_num, stack_frame)
768 buffer += sprint_varx(var_name, var_value, hex, indent, loc_col1_width)
769 parm_num += 1
770
771 return buffer
772
Michael Walsh18176322016-11-15 15:11:21 -0600773
Michael Walsh7423c012016-10-04 10:27:21 -0500774def sprint_dashes(indent=col1_indent,
775 width=80,
776 line_feed=1,
777 char="-"):
Michael Walshde791732016-09-06 14:25:24 -0500778
779 r"""
780 Return a string of dashes to the caller.
781
Michael Walsh2ee77cd2017-03-08 11:50:17 -0600782 Description of arguments:
Michael Walshde791732016-09-06 14:25:24 -0500783 indent The number of characters to indent the
784 output.
785 width The width of the string of dashes.
786 line_feed Indicates whether the output should end
787 with a line feed.
Michael Walsh7423c012016-10-04 10:27:21 -0500788 char The character to be repeated in the output
789 string.
Michael Walshde791732016-09-06 14:25:24 -0500790 """
791
Michael Walsh7423c012016-10-04 10:27:21 -0500792 width = int(width)
Michael Walsh23e7f492017-01-10 11:34:47 -0600793 buffer = " " * int(indent) + char * width
Michael Walshde791732016-09-06 14:25:24 -0500794 if line_feed:
795 buffer += "\n"
796
797 return buffer
798
Michael Walshde791732016-09-06 14:25:24 -0500799
Michael Walsh7423c012016-10-04 10:27:21 -0500800def sindent(text="",
801 indent=0):
802
803 r"""
804 Pre-pend the specified number of characters to the text string (i.e.
805 indent it) and return it.
806
807 Description of arguments:
808 text The string to be indented.
809 indent The number of characters to indent the
810 string.
811 """
812
813 format_string = "%" + str(indent) + "s%s"
814 buffer = format_string % ("", text)
815
816 return buffer
817
Michael Walsh7423c012016-10-04 10:27:21 -0500818
Michael Walsh7423c012016-10-04 10:27:21 -0500819def sprint_call_stack(indent=0,
820 stack_frame_ix=0):
Michael Walshde791732016-09-06 14:25:24 -0500821
822 r"""
823 Return a call stack report for the given point in the program with line
824 numbers, function names and function parameters and arguments.
825
826 Sample output:
827
828 -------------------------------------------------------------------------
829 Python function call stack
830
831 Line # Function name and arguments
832 ------ ------------------------------------------------------------------
833 424 sprint_call_stack ()
834 4 print_call_stack ()
835 31 func1 (last_name = 'walsh', first_name = 'mikey')
836 59 /tmp/scr5.py
837 -------------------------------------------------------------------------
838
839 Description of arguments:
840 indent The number of characters to indent each
841 line of output.
842 stack_frame_ix The index of the first stack frame which
843 is to be returned.
844 """
845
846 buffer = ""
Michael Walsh7423c012016-10-04 10:27:21 -0500847 buffer += sprint_dashes(indent)
848 buffer += sindent("Python function call stack\n\n", indent)
849 buffer += sindent("Line # Function name and arguments\n", indent)
850 buffer += sprint_dashes(indent, 6, 0) + " " + sprint_dashes(0, 73)
Michael Walshde791732016-09-06 14:25:24 -0500851
852 # Grab the current program stack.
853 current_stack = inspect.stack()
854
855 # Process each frame in turn.
856 format_string = "%6s %s\n"
Michael Walsh7423c012016-10-04 10:27:21 -0500857 ix = 0
Michael Walshde791732016-09-06 14:25:24 -0500858 for stack_frame in current_stack:
Michael Walsh7423c012016-10-04 10:27:21 -0500859 if ix < stack_frame_ix:
860 ix += 1
861 continue
Michael Walsh23e7f492017-01-10 11:34:47 -0600862 # I want the line number shown to be the line where you find the line
863 # shown.
864 try:
865 line_num = str(current_stack[ix + 1][2])
866 except IndexError:
867 line_num = ""
Michael Walshde791732016-09-06 14:25:24 -0500868 func_name = str(stack_frame[3])
869 if func_name == "?":
870 # "?" is the name used when code is not in a function.
871 func_name = "(none)"
872
873 if func_name == "<module>":
Michael Walsh7423c012016-10-04 10:27:21 -0500874 # If the func_name is the "main" program, we simply get the
875 # command line call string.
Michael Walshde791732016-09-06 14:25:24 -0500876 func_and_args = ' '.join(sys.argv)
877 else:
878 # Get the program arguments.
879 arg_vals = inspect.getargvalues(stack_frame[0])
880 function_parms = arg_vals[0]
881 frame_locals = arg_vals[3]
882
Michael Walsh7423c012016-10-04 10:27:21 -0500883 args_list = []
Michael Walshde791732016-09-06 14:25:24 -0500884 for arg_name in function_parms:
885 # Get the arg value from frame locals.
886 arg_value = frame_locals[arg_name]
Michael Walsh7423c012016-10-04 10:27:21 -0500887 args_list.append(arg_name + " = " + repr(arg_value))
888 args_str = "(" + ', '.join(map(str, args_list)) + ")"
Michael Walshde791732016-09-06 14:25:24 -0500889
890 # Now we need to print this in a nicely-wrapped way.
891 func_and_args = func_name + " " + args_str
892
Michael Walsh23e7f492017-01-10 11:34:47 -0600893 buffer += sindent(format_string % (line_num, func_and_args), indent)
Michael Walsh7423c012016-10-04 10:27:21 -0500894 ix += 1
Michael Walshde791732016-09-06 14:25:24 -0500895
Michael Walsh7423c012016-10-04 10:27:21 -0500896 buffer += sprint_dashes(indent)
Michael Walshde791732016-09-06 14:25:24 -0500897
898 return buffer
899
Michael Walshde791732016-09-06 14:25:24 -0500900
Michael Walshde791732016-09-06 14:25:24 -0500901def sprint_executing(stack_frame_ix=None):
902
903 r"""
904 Print a line indicating what function is executing and with what parameter
905 values. This is useful for debugging.
906
907 Sample output:
908
909 #(CDT) 2016/08/25 17:54:27 - Executing: func1 (x = 1)
910
911 Description of arguments:
912 stack_frame_ix The index of the stack frame whose
913 function info should be returned. If the
Michael Walsh2ee77cd2017-03-08 11:50:17 -0600914 caller does not specify a value, this
Michael Walshde791732016-09-06 14:25:24 -0500915 function will set the value to 1 which is
916 the index of the caller's stack frame. If
917 the caller is the wrapper function
918 "print_executing", this function will bump
919 it up by 1.
920 """
921
922 # If user wants default stack_frame_ix.
923 if stack_frame_ix is None:
924 func_name = sys._getframe().f_code.co_name
925 caller_func_name = sys._getframe(1).f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -0500926 if caller_func_name.endswith(func_name[1:]):
Michael Walshde791732016-09-06 14:25:24 -0500927 stack_frame_ix = 2
928 else:
929 stack_frame_ix = 1
930
931 stack_frame = inspect.stack()[stack_frame_ix]
932
933 func_name = str(stack_frame[3])
934 if func_name == "?":
935 # "?" is the name used when code is not in a function.
936 func_name = "(none)"
937
938 if func_name == "<module>":
939 # If the func_name is the "main" program, we simply get the command
940 # line call string.
941 func_and_args = ' '.join(sys.argv)
942 else:
943 # Get the program arguments.
944 arg_vals = inspect.getargvalues(stack_frame[0])
945 function_parms = arg_vals[0]
946 frame_locals = arg_vals[3]
947
Michael Walsh7423c012016-10-04 10:27:21 -0500948 args_list = []
Michael Walshde791732016-09-06 14:25:24 -0500949 for arg_name in function_parms:
950 # Get the arg value from frame locals.
951 arg_value = frame_locals[arg_name]
Michael Walsh7423c012016-10-04 10:27:21 -0500952 args_list.append(arg_name + " = " + repr(arg_value))
953 args_str = "(" + ', '.join(map(str, args_list)) + ")"
Michael Walshde791732016-09-06 14:25:24 -0500954
955 # Now we need to print this in a nicely-wrapped way.
956 func_and_args = func_name + " " + args_str
957
958 return sprint_time() + "Executing: " + func_and_args + "\n"
959
Michael Walshde791732016-09-06 14:25:24 -0500960
Michael Walshbec416d2016-11-10 08:54:52 -0600961def sprint_pgm_header(indent=0,
962 linefeed=1):
Michael Walshde791732016-09-06 14:25:24 -0500963
964 r"""
965 Return a standardized header that programs should print at the beginning
966 of the run. It includes useful information like command line, pid,
967 userid, program parameters, etc.
968
Michael Walsh7423c012016-10-04 10:27:21 -0500969 Description of arguments:
970 indent The number of characters to indent each
971 line of output.
Michael Walshbec416d2016-11-10 08:54:52 -0600972 linefeed Indicates whether a line feed be included
973 at the beginning and end of the report.
Michael Walshde791732016-09-06 14:25:24 -0500974 """
975
Michael Walshbec416d2016-11-10 08:54:52 -0600976 loc_col1_width = col1_width + indent
977
978 buffer = ""
979 if linefeed:
980 buffer = "\n"
Michael Walsh7423c012016-10-04 10:27:21 -0500981
Michael Walshdb6e68a2017-05-23 17:55:31 -0500982 if robot_env:
983 suite_name = BuiltIn().get_variable_value("${suite_name}")
984 buffer += sindent(sprint_time("Running test suite \"" + suite_name +
985 "\".\n"), indent)
986
Michael Walsh7423c012016-10-04 10:27:21 -0500987 buffer += sindent(sprint_time() + "Running " + pgm_name + ".\n", indent)
988 buffer += sindent(sprint_time() + "Program parameter values, etc.:\n\n",
989 indent)
Michael Walshbec416d2016-11-10 08:54:52 -0600990 buffer += sprint_varx("command_line", ' '.join(sys.argv), 0, indent,
991 loc_col1_width)
Michael Walsh7423c012016-10-04 10:27:21 -0500992 # We want the output to show a customized name for the pid and pgid but
993 # we want it to look like a valid variable name. Therefore, we'll use
Michael Walshde791732016-09-06 14:25:24 -0500994 # pgm_name_var_name which was set when this module was imported.
Michael Walshbec416d2016-11-10 08:54:52 -0600995 buffer += sprint_varx(pgm_name_var_name + "_pid", os.getpid(), 0, indent,
996 loc_col1_width)
997 buffer += sprint_varx(pgm_name_var_name + "_pgid", os.getpgrp(), 0, indent,
998 loc_col1_width)
Michael Walsh86de0d22016-12-05 10:13:15 -0600999 userid_num = str(os.geteuid())
1000 try:
1001 username = os.getlogin()
1002 except OSError:
1003 if userid_num == "0":
1004 username = "root"
1005 else:
1006 username = "?"
1007 buffer += sprint_varx("uid", userid_num + " (" + username +
Michael Walshbec416d2016-11-10 08:54:52 -06001008 ")", 0, indent, loc_col1_width)
Michael Walshde791732016-09-06 14:25:24 -05001009 buffer += sprint_varx("gid", str(os.getgid()) + " (" +
Michael Walsh7423c012016-10-04 10:27:21 -05001010 str(grp.getgrgid(os.getgid()).gr_name) + ")", 0,
Michael Walshbec416d2016-11-10 08:54:52 -06001011 indent, loc_col1_width)
1012 buffer += sprint_varx("host_name", socket.gethostname(), 0, indent,
1013 loc_col1_width)
Michael Walsh86de0d22016-12-05 10:13:15 -06001014 try:
1015 DISPLAY = os.environ['DISPLAY']
1016 except KeyError:
1017 DISPLAY = ""
1018 buffer += sprint_varx("DISPLAY", DISPLAY, 0, indent,
Michael Walshbec416d2016-11-10 08:54:52 -06001019 loc_col1_width)
Michael Walshde791732016-09-06 14:25:24 -05001020 # I want to add code to print caller's parms.
1021
Michael Walsh7423c012016-10-04 10:27:21 -05001022 # __builtin__.arg_obj is created by the get_arg module function,
1023 # gen_get_options.
1024 try:
1025 buffer += ga.sprint_args(__builtin__.arg_obj, indent)
1026 except AttributeError:
1027 pass
1028
Michael Walshdb6e68a2017-05-23 17:55:31 -05001029 if robot_env:
1030 # Get value of global parm_list.
1031 parm_list = BuiltIn().get_variable_value("${parm_list}")
1032
1033 for parm in parm_list:
1034 parm_value = BuiltIn().get_variable_value("${" + parm + "}")
1035 buffer += sprint_varx(parm, parm_value, 0, indent, loc_col1_width)
1036
1037 # Setting global program_pid.
1038 BuiltIn().set_global_variable("${program_pid}", os.getpid())
1039
Michael Walshbec416d2016-11-10 08:54:52 -06001040 if linefeed:
1041 buffer += "\n"
Michael Walshde791732016-09-06 14:25:24 -05001042
1043 return buffer
1044
Michael Walshde791732016-09-06 14:25:24 -05001045
Michael Walsh7423c012016-10-04 10:27:21 -05001046def sprint_error_report(error_text="\n",
Michael Walshdb6e68a2017-05-23 17:55:31 -05001047 indent=2,
1048 format=None):
Michael Walsh7423c012016-10-04 10:27:21 -05001049
1050 r"""
1051 Return a string with a standardized report which includes the caller's
1052 error text, the call stack and the program header.
1053
1054 Description of args:
1055 error_text The error text to be included in the
1056 report. The caller should include any
1057 needed linefeeds.
1058 indent The number of characters to indent each
1059 line of output.
Michael Walshdb6e68a2017-05-23 17:55:31 -05001060 format Long or short format. Long includes
1061 extras like lines of dashes, call stack,
1062 etc.
Michael Walsh7423c012016-10-04 10:27:21 -05001063 """
1064
Michael Walshdb6e68a2017-05-23 17:55:31 -05001065 # Process input.
1066 indent = int(indent)
1067 if format is None:
1068 if robot_env:
1069 format = 'short'
1070 else:
1071 format = 'long'
1072 error_text = error_text.rstrip('\n') + '\n'
1073
1074 if format == 'short':
1075 return sprint_error(error_text)
1076
Michael Walsh7423c012016-10-04 10:27:21 -05001077 buffer = ""
1078 buffer += sprint_dashes(width=120, char="=")
1079 buffer += sprint_error(error_text)
1080 buffer += "\n"
1081 # Calling sprint_call_stack with stack_frame_ix of 0 causes it to show
1082 # itself and this function in the call stack. This is not helpful to a
1083 # debugger and is therefore clutter. We will adjust the stack_frame_ix to
1084 # hide that information.
Michael Walsh9c75f672017-09-12 17:11:35 -05001085 stack_frame_ix = 1
Michael Walsh7423c012016-10-04 10:27:21 -05001086 caller_func_name = sprint_func_name(2)
1087 if caller_func_name.endswith("print_error_report"):
1088 stack_frame_ix += 1
Michael Walshdb6e68a2017-05-23 17:55:31 -05001089 if not robot_env:
1090 buffer += sprint_call_stack(indent, stack_frame_ix)
Michael Walsh7423c012016-10-04 10:27:21 -05001091 buffer += sprint_pgm_header(indent)
1092 buffer += sprint_dashes(width=120, char="=")
1093
1094 return buffer
1095
Michael Walsh7423c012016-10-04 10:27:21 -05001096
Michael Walsh18176322016-11-15 15:11:21 -06001097def sprint_issuing(cmd_buf,
1098 test_mode=0):
Michael Walshde791732016-09-06 14:25:24 -05001099
1100 r"""
1101 Return a line indicating a command that the program is about to execute.
1102
1103 Sample output for a cmd_buf of "ls"
1104
1105 #(CDT) 2016/08/25 17:57:36 - Issuing: ls
Michael Walshbec416d2016-11-10 08:54:52 -06001106
Michael Walshde791732016-09-06 14:25:24 -05001107 Description of args:
1108 cmd_buf The command to be executed by caller.
Michael Walshbec416d2016-11-10 08:54:52 -06001109 test_mode With test_mode set, your output will look
1110 like this:
1111
1112 #(CDT) 2016/08/25 17:57:36 - (test_mode) Issuing: ls
1113
Michael Walshde791732016-09-06 14:25:24 -05001114 """
1115
Michael Walshbec416d2016-11-10 08:54:52 -06001116 buffer = sprint_time()
1117 if test_mode:
1118 buffer += "(test_mode) "
1119 buffer += "Issuing: " + cmd_buf + "\n"
Michael Walshde791732016-09-06 14:25:24 -05001120
1121 return buffer
1122
Michael Walshde791732016-09-06 14:25:24 -05001123
Michael Walshde791732016-09-06 14:25:24 -05001124def sprint_pgm_footer():
1125
1126 r"""
1127 Return a standardized footer that programs should print at the end of the
1128 program run. It includes useful information like total run time, etc.
1129 """
1130
1131 buffer = "\n" + sprint_time() + "Finished running " + pgm_name + ".\n\n"
1132
1133 total_time = time.time() - start_time
1134 total_time_string = "%0.6f" % total_time
1135
Michael Walsh7423c012016-10-04 10:27:21 -05001136 buffer += sprint_varx(pgm_name_var_name + "_runtime", total_time_string)
Michael Walshbec416d2016-11-10 08:54:52 -06001137 buffer += "\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001138
1139 return buffer
1140
Michael Walsh7423c012016-10-04 10:27:21 -05001141
Michael Walsh7423c012016-10-04 10:27:21 -05001142def sprint(buffer=""):
1143
1144 r"""
1145 Simply return the user's buffer. This function is used by the qprint and
1146 dprint functions defined dynamically below, i.e. it would not normally be
1147 called for general use.
1148
1149 Description of arguments.
1150 buffer This will be returned to the caller.
1151 """
Michael Walshde791732016-09-06 14:25:24 -05001152
Michael Walshbec416d2016-11-10 08:54:52 -06001153 return str(buffer)
1154
Michael Walshbec416d2016-11-10 08:54:52 -06001155
Michael Walshbec416d2016-11-10 08:54:52 -06001156def sprintn(buffer=""):
1157
1158 r"""
1159 Simply return the user's buffer with a line feed. This function is used
1160 by the qprint and dprint functions defined dynamically below, i.e. it
1161 would not normally be called for general use.
1162
1163 Description of arguments.
1164 buffer This will be returned to the caller.
1165 """
1166
1167 buffer = str(buffer) + "\n"
1168
Michael Walshde791732016-09-06 14:25:24 -05001169 return buffer
1170
Michael Walsh168eb0f2017-12-01 15:35:32 -06001171
Michael Walshfd2733c2017-11-13 11:36:20 -06001172def gp_print(buffer,
1173 stream='stdout'):
1174
1175 r"""
1176 Print the buffer using either sys.stdout.write or BuiltIn().log_to_console
1177 depending on whether we are running in a robot environment.
1178
1179 This function is intended for use only by other functions in this module.
1180
1181 Description of arguments:
1182 buffer The string to be printed.
1183 stream Either "stdout" or "stderr".
1184 """
1185
1186 if robot_env:
1187 BuiltIn().log_to_console(buffer, stream=stream, no_newline=True)
1188 else:
1189 if stream == "stdout":
1190 sys.stdout.write(buffer)
1191 sys.stdout.flush()
1192 else:
1193 sys.stderr.write(buffer)
1194 sys.stderr.flush()
Michael Walshde791732016-09-06 14:25:24 -05001195
1196
Michael Walsh168eb0f2017-12-01 15:35:32 -06001197def gp_log(buffer):
1198
1199 r"""
1200 Log the buffer using either python logging or BuiltIn().log depending on
1201 whether we are running in a robot environment.
1202
1203 This function is intended for use only by other functions in this module.
1204
1205 Description of arguments:
1206 buffer The string to be logged.
1207 """
1208
1209 if robot_env:
1210 BuiltIn().log(buffer)
1211 else:
1212 logging.warning(buffer)
1213
1214
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001215def gp_debug_print(buffer):
1216
1217 r"""
Michael Walshfd2733c2017-11-13 11:36:20 -06001218 Print with gp_print only if gen_print_debug is set.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001219
1220 This function is intended for use only by other functions in this module.
1221
1222 Description of arguments:
1223 buffer The string to be printed.
1224 """
1225
1226 if not gen_print_debug:
1227 return
1228
Michael Walshfd2733c2017-11-13 11:36:20 -06001229 gp_print(buffer)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001230
1231
Michael Walshb1500152017-04-12 15:42:43 -05001232def get_var_value(var_value=None,
1233 default=1,
1234 var_name=None):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001235
1236 r"""
Michael Walshb1500152017-04-12 15:42:43 -05001237 Return either var_value, the corresponding global value or default.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001238
Michael Walshb1500152017-04-12 15:42:43 -05001239 If var_value is not None, it will simply be returned.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001240
Michael Walshb1500152017-04-12 15:42:43 -05001241 If var_value is None, this function will return the corresponding global
1242 value of the variable in question.
1243
1244 Note: For global values, if we are in a robot environment,
1245 get_variable_value will be used. Otherwise, the __builtin__ version of
1246 the variable is returned (which are set by gen_arg.py functions).
1247
1248 If there is no global value associated with the variable, default is
1249 returned.
1250
1251 This function is useful for other functions in setting default values for
1252 parameters.
1253
1254 Example use:
1255
1256 def my_func(quiet=None):
1257
1258 quiet = int(get_var_value(quiet, 0))
1259
1260 Example calls to my_func():
1261
1262 In the following example, the caller is explicitly asking to have quiet be
1263 set to 1.
1264
1265 my_func(quiet=1)
1266
1267 In the following example, quiet will be set to the global value of quiet,
1268 if defined, or to 0 (the default).
1269
1270 my_func()
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001271
1272 Description of arguments:
Michael Walshb1500152017-04-12 15:42:43 -05001273 var_value The value to be returned (if not equal to
1274 None).
1275 default The value that is returned if var_value is
1276 None and there is no corresponding global
1277 value defined.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001278 var_name The name of the variable whose value is to
Michael Walshb1500152017-04-12 15:42:43 -05001279 be returned. Under most circumstances,
1280 this value need not be provided. This
1281 function can figure out the name of the
1282 variable passed as var_value. One
1283 exception to this would be if this
1284 function is called directly from a .robot
1285 file.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001286 """
1287
Michael Walshb1500152017-04-12 15:42:43 -05001288 if var_value is not None:
1289 return var_value
1290
1291 if var_name is None:
1292 var_name = get_arg_name(None, 1, 2)
1293
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001294 if robot_env:
Michael Walshc6537442017-06-06 15:33:52 -05001295 var_value = BuiltIn().get_variable_value("${" + var_name + "}",
1296 default)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001297 else:
1298 var_value = getattr(__builtin__, var_name, default)
1299
1300 return var_value
1301
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001302
Michael Walsh82acf002017-05-04 14:33:05 -05001303# hidden_text is a list of passwords which are to be replaced with asterisks
1304# by print functions defined in this module.
1305hidden_text = []
1306# password_regex is created based on the contents of hidden_text.
1307password_regex = ""
1308
1309
Michael Walsh82acf002017-05-04 14:33:05 -05001310def register_passwords(*args):
1311
1312 r"""
1313 Register one or more passwords which are to be hidden in output produced
1314 by the print functions in this module.
1315
1316 Note: Blank password values are NOT registered. They are simply ignored.
1317
1318 Description of argument(s):
1319 args One or more password values. If a given
1320 password value is already registered, this
1321 function will simply do nothing.
1322 """
1323
1324 global hidden_text
1325 global password_regex
1326
1327 for password in args:
1328 if password == "":
1329 break
1330 if password in hidden_text:
1331 break
1332
1333 # Place the password into the hidden_text list.
1334 hidden_text.append(password)
1335 # Create a corresponding password regular expression. Escape regex
1336 # special characters too.
1337 password_regex = '(' +\
1338 '|'.join([re.escape(x) for x in hidden_text]) + ')'
1339
Michael Walsh82acf002017-05-04 14:33:05 -05001340
Michael Walsh82acf002017-05-04 14:33:05 -05001341def replace_passwords(buffer):
1342
1343 r"""
1344 Return the buffer but with all registered passwords replaced by a string
1345 of asterisks.
1346
1347
1348 Description of argument(s):
1349 buffer The string to be returned but with
1350 passwords replaced.
1351 """
1352
1353 global password_regex
1354
1355 if int(os.environ.get("DEBUG_SHOW_PASSWORDS", "0")):
1356 return buffer
1357
1358 if password_regex == "":
1359 # No passwords to replace.
1360 return buffer
1361
1362 return re.sub(password_regex, "********", buffer)
1363
Michael Walshfd2733c2017-11-13 11:36:20 -06001364
1365def create_print_wrapper_funcs(func_names,
1366 stderr_func_names,
1367 replace_dict):
1368
1369 r"""
1370 Generate code for print wrapper functions and return the generated code as
1371 a string.
1372
1373 To illustrate, suppose there is a "print_foo_bar" function in the
1374 func_names list.
1375 This function will...
1376 - Expect that there is an sprint_foo_bar function already in existence.
1377 - Create a print_foo_bar function which calls sprint_foo_bar and prints
1378 the result.
1379 - Create a qprint_foo_bar function which calls upon sprint_foo_bar only if
1380 global value quiet is 0.
1381 - Create a dprint_foo_bar function which calls upon sprint_foo_bar only if
1382 global value debug is 1.
1383
1384 Also, code will be generated to define aliases for each function as well.
1385 Each alias will be created by replacing "print_" in the function name with
1386 "p" For example, the alias for print_foo_bar will be pfoo_bar.
1387
1388 Description of argument(s):
1389 func_names A list of functions for which print
1390 wrapper function code is to be generated.
1391 stderr_func_names A list of functions whose generated code
1392 should print to stderr rather than to
1393 stdout.
1394 replace_dict Please see the create_func_def_string
1395 function in wrap_utils.py for details on
1396 this parameter. This parameter will be
1397 passed directly to create_func_def_string.
1398 """
1399
1400 buffer = ""
1401
1402 for func_name in func_names:
1403 if func_name in stderr_func_names:
1404 replace_dict['output_stream'] = "stderr"
1405 else:
1406 replace_dict['output_stream'] = "stdout"
1407
1408 s_func_name = "s" + func_name
1409 q_func_name = "q" + func_name
1410 d_func_name = "d" + func_name
1411
1412 # We don't want to try to redefine the "print" function, thus the
1413 # following if statement.
1414 if func_name != "print":
1415 func_def = create_func_def_string(s_func_name, func_name,
1416 print_func_template,
1417 replace_dict)
1418 buffer += func_def
1419
1420 func_def = create_func_def_string(s_func_name, "q" + func_name,
1421 qprint_func_template, replace_dict)
1422 buffer += func_def
1423
1424 func_def = create_func_def_string(s_func_name, "d" + func_name,
1425 dprint_func_template, replace_dict)
1426 buffer += func_def
1427
Michael Walsh168eb0f2017-12-01 15:35:32 -06001428 func_def = create_func_def_string(s_func_name, "l" + func_name,
1429 lprint_func_template, replace_dict)
1430 buffer += func_def
1431
Michael Walshfd2733c2017-11-13 11:36:20 -06001432 # Create abbreviated aliases (e.g. spvar is an alias for sprint_var).
1433 alias = re.sub("print_", "p", func_name)
1434 alias = re.sub("print", "p", alias)
Michael Walsh168eb0f2017-12-01 15:35:32 -06001435 prefixes = ["", "s", "q", "d", "l"]
Michael Walshfd2733c2017-11-13 11:36:20 -06001436 for prefix in prefixes:
1437 if alias == "p":
1438 continue
1439 func_def = prefix + alias + " = " + prefix + func_name
1440 buffer += func_def + "\n"
1441
1442 return buffer
Michael Walsh82acf002017-05-04 14:33:05 -05001443
1444
Michael Walshde791732016-09-06 14:25:24 -05001445# In the following section of code, we will dynamically create print versions
1446# for each of the sprint functions defined above. So, for example, where we
1447# have an sprint_time() function defined above that returns the time to the
Michael Walsh7423c012016-10-04 10:27:21 -05001448# caller in a string, we will create a corresponding print_time() function
1449# that will print that string directly to stdout.
Michael Walshde791732016-09-06 14:25:24 -05001450
Michael Walshfd2733c2017-11-13 11:36:20 -06001451# It can be complicated to follow what's being created by below. Here is an
1452# example of the print_time() function that will be created:
Michael Walshde791732016-09-06 14:25:24 -05001453
Michael Walshfd2733c2017-11-13 11:36:20 -06001454# def print_time(buffer=''):
1455# sys.stdout.write(replace_passwords(sprint_time(buffer=buffer)))
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001456# sys.stdout.flush()
Michael Walshde791732016-09-06 14:25:24 -05001457
Michael Walshfd2733c2017-11-13 11:36:20 -06001458# Templates for the various print wrapper functions.
1459print_func_template = \
1460 [
1461 " gp_print(<mod_qualifer>replace_passwords(<call_line>), stream" +
1462 "='<output_stream>')"
1463 ]
1464
1465qprint_func_template = \
1466 [
1467 " if int(<mod_qualifer>get_var_value(None, 0, \"quiet\")): return"
1468 ] + print_func_template
1469
1470dprint_func_template = \
1471 [
1472 " if not int(<mod_qualifer>get_var_value(None, 0, \"debug\")):" +
1473 " return"
1474 ] + print_func_template
1475
Michael Walsh168eb0f2017-12-01 15:35:32 -06001476lprint_func_template = \
1477 [
1478 " gp_log(<mod_qualifer>replace_passwords(<call_line>))"
1479 ]
1480
Michael Walshfd2733c2017-11-13 11:36:20 -06001481replace_dict = {'output_stream': 'stdout', 'mod_qualifer': ''}
1482
1483
1484gp_debug_print("robot_env: " + str(robot_env))
Michael Walshde791732016-09-06 14:25:24 -05001485
1486# func_names contains a list of all print functions which should be created
1487# from their sprint counterparts.
1488func_names = ['print_time', 'print_timen', 'print_error', 'print_varx',
Michael Walsh18176322016-11-15 15:11:21 -06001489 'print_var', 'print_vars', 'print_dashes', 'indent',
1490 'print_call_stack', 'print_func_name', 'print_executing',
1491 'print_pgm_header', 'print_issuing', 'print_pgm_footer',
1492 'print_error_report', 'print', 'printn']
Michael Walshde791732016-09-06 14:25:24 -05001493
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001494# stderr_func_names is a list of functions whose output should go to stderr
1495# rather than stdout.
1496stderr_func_names = ['print_error', 'print_error_report']
Michael Walshde791732016-09-06 14:25:24 -05001497
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001498
Michael Walshfd2733c2017-11-13 11:36:20 -06001499func_defs = create_print_wrapper_funcs(func_names, stderr_func_names,
1500 replace_dict)
1501gp_debug_print(func_defs)
1502exec(func_defs)