blob: ee39fecb9354a47796b7f936f9c406d25d355a18 [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
19
Michael Walshbec416d2016-11-10 08:54:52 -060020try:
Michael Walsh2ee77cd2017-03-08 11:50:17 -060021 robot_env = 1
Michael Walshbec416d2016-11-10 08:54:52 -060022 from robot.utils import DotDict
Michael Walsh8e6deb42017-01-27 14:22:41 -060023 from robot.utils import NormalizedDict
Michael Walsh2ee77cd2017-03-08 11:50:17 -060024 from robot.libraries.BuiltIn import BuiltIn
Michael Walshb1500152017-04-12 15:42:43 -050025 # Having access to the robot libraries alone does not indicate that we
26 # are in a robot environment. The following try block should confirm that.
27 try:
28 var_value = BuiltIn().get_variable_value("${SUITE_NAME}", "")
29 except:
30 robot_env = 0
Michael Walshbec416d2016-11-10 08:54:52 -060031except ImportError:
Michael Walsh2ee77cd2017-03-08 11:50:17 -060032 robot_env = 0
Michael Walsh7423c012016-10-04 10:27:21 -050033
34import gen_arg as ga
Michael Walshde791732016-09-06 14:25:24 -050035
36# Setting these variables for use both inside this module and by programs
37# importing this module.
38pgm_dir_path = sys.argv[0]
39pgm_name = os.path.basename(pgm_dir_path)
Michael Walsh7423c012016-10-04 10:27:21 -050040pgm_dir_name = re.sub("/" + pgm_name, "", pgm_dir_path) + "/"
41
Michael Walshde791732016-09-06 14:25:24 -050042
43# Some functions (e.g. sprint_pgm_header) have need of a program name value
44# that looks more like a valid variable name. Therefore, we'll swap odd
45# characters like "." out for underscores.
46pgm_name_var_name = pgm_name.replace(".", "_")
47
48# Initialize global values used as defaults by print_time, print_var, etc.
49col1_indent = 0
50
51# Calculate default column width for print_var functions based on environment
52# variable settings. The objective is to make the variable values line up
53# nicely with the time stamps.
54col1_width = 29
Michael Walshb1500152017-04-12 15:42:43 -050055
56NANOSECONDS = os.environ.get('NANOSECONDS', '1')
57
Michael Walshde791732016-09-06 14:25:24 -050058
59if NANOSECONDS == "1":
60 col1_width = col1_width + 7
61
Michael Walshb1500152017-04-12 15:42:43 -050062SHOW_ELAPSED_TIME = os.environ.get('SHOW_ELAPSED_TIME', '1')
Michael Walshde791732016-09-06 14:25:24 -050063
64if SHOW_ELAPSED_TIME == "1":
65 if NANOSECONDS == "1":
66 col1_width = col1_width + 14
67 else:
68 col1_width = col1_width + 7
69
70# Initialize some time variables used in module functions.
71start_time = time.time()
72sprint_time_last_seconds = start_time
73
Michael Walsh2ee77cd2017-03-08 11:50:17 -060074# The user can set environment variable "GEN_PRINT_DEBUG" to get debug output
75# from this module.
76gen_print_debug = int(os.environ.get('GEN_PRINT_DEBUG', 0))
Michael Walsh7423c012016-10-04 10:27:21 -050077
Michael Walshde791732016-09-06 14:25:24 -050078
79###############################################################################
80def 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
110###############################################################################
111
112
113# get_arg_name is not a print function per se. I have included it in this
114# module because it is used by sprint_var which is found in this module.
115###############################################################################
116def get_arg_name(var,
117 arg_num=1,
118 stack_frame_ix=1):
119
120 r"""
121 Return the "name" of an argument passed to a function. This could be a
122 literal or a variable name.
123
Michael Walsh2ee77cd2017-03-08 11:50:17 -0600124 Description of arguments:
Michael Walshde791732016-09-06 14:25:24 -0500125 var The variable whose name you want returned.
126 arg_num The arg number (1 through n) whose name
127 you wish to have returned. This value
128 should not exceed the number of arguments
129 allowed by the target function.
130 stack_frame_ix The stack frame index of the target
131 function. This value must be 1 or
132 greater. 1 would indicate get_arg_name's
133 stack frame. 2 would be the caller of
134 get_arg_name's stack frame, etc.
135
136 Example 1:
137
138 my_var = "mike"
139 var_name = get_arg_name(my_var)
140
141 In this example, var_name will receive the value "my_var".
142
143 Example 2:
144
145 def test1(var):
146 # Getting the var name of the first arg to this function, test1.
147 # Note, in this case, it doesn't matter what you pass as the first arg
148 # to get_arg_name since it is the caller's variable name that matters.
149 dummy = 1
150 arg_num = 1
151 stack_frame = 2
152 var_name = get_arg_name(dummy, arg_num, stack_frame)
153
154 # Mainline...
155
156 another_var = "whatever"
157 test1(another_var)
158
159 In this example, var_name will be set to "another_var".
160
161 """
162
163 # Note: I wish to avoid recursion so I refrain from calling any function
164 # that calls this function (i.e. sprint_var, valid_value, etc.).
165
Michael Walsh23e7f492017-01-10 11:34:47 -0600166 # The user can set environment variable "GET_ARG_NAME_DEBUG" to get debug
167 # output from this function.
168 local_debug = int(os.environ.get('GET_ARG_NAME_DEBUG', 0))
169 # In addition to GET_ARG_NAME_DEBUG, the user can set environment
170 # variable "GET_ARG_NAME_SHOW_SOURCE" to have this function include source
171 # code in the debug output.
172 local_debug_show_source = int(
173 os.environ.get('GET_ARG_NAME_SHOW_SOURCE', 0))
Michael Walshde791732016-09-06 14:25:24 -0500174
175 if arg_num < 1:
176 print_error("Programmer error - Variable \"arg_num\" has an invalid" +
177 " value of \"" + str(arg_num) + "\". The value must be" +
178 " an integer that is greater than 0.\n")
179 # What is the best way to handle errors? Raise exception? I'll
180 # revisit later.
181 return
182 if stack_frame_ix < 1:
183 print_error("Programmer error - Variable \"stack_frame_ix\" has an" +
184 " invalid value of \"" + str(stack_frame_ix) + "\". The" +
185 " value must be an integer that is greater than or equal" +
186 " to 1.\n")
187 return
188
189 if local_debug:
190 debug_indent = 2
Michael Walsh23e7f492017-01-10 11:34:47 -0600191 print("")
192 print_dashes(0, 120)
Michael Walshde791732016-09-06 14:25:24 -0500193 print(sprint_func_name() + "() parms:")
194 print_varx("var", var, 0, debug_indent)
195 print_varx("arg_num", arg_num, 0, debug_indent)
196 print_varx("stack_frame_ix", stack_frame_ix, 0, debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600197 print("")
198 print_call_stack(debug_indent, 2)
Michael Walshde791732016-09-06 14:25:24 -0500199
Michael Walsh23e7f492017-01-10 11:34:47 -0600200 for count in range(0, 2):
201 try:
202 frame, filename, cur_line_no, function_name, lines, index = \
203 inspect.stack()[stack_frame_ix]
204 except IndexError:
205 print_error("Programmer error - The caller has asked for" +
206 " information about the stack frame at index \"" +
207 str(stack_frame_ix) + "\". However, the stack" +
208 " only contains " + str(len(inspect.stack())) +
209 " entries. Therefore the stack frame index is out" +
210 " of range.\n")
211 return
212 if filename != "<string>":
213 break
214 # filename of "<string>" may mean that the function in question was
215 # defined dynamically and therefore its code stack is inaccessible.
216 # This may happen with functions like "rqprint_var". In this case,
217 # we'll increment the stack_frame_ix and try again.
218 stack_frame_ix += 1
219 if local_debug:
220 print("Adjusted stack_frame_ix...")
221 print_varx("stack_frame_ix", stack_frame_ix, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500222
223 called_func_name = sprint_func_name(stack_frame_ix)
Michael Walsh23e7f492017-01-10 11:34:47 -0600224
225 module = inspect.getmodule(frame)
226
227 # Though I would expect inspect.getsourcelines(frame) to get all module
228 # source lines if the frame is "<module>", it doesn't do that. Therefore,
229 # for this special case, I will do inspect.getsourcelines(module).
230 if function_name == "<module>":
231 source_lines, source_line_num =\
232 inspect.getsourcelines(module)
233 line_ix = cur_line_no - source_line_num - 1
234 else:
235 source_lines, source_line_num =\
236 inspect.getsourcelines(frame)
237 line_ix = cur_line_no - source_line_num
238
239 if local_debug:
240 print("\n Variables retrieved from inspect.stack() function:")
241 print_varx("frame", frame, 0, debug_indent + 2)
242 print_varx("filename", filename, 0, debug_indent + 2)
243 print_varx("cur_line_no", cur_line_no, 0, debug_indent + 2)
244 print_varx("function_name", function_name, 0, debug_indent + 2)
245 print_varx("lines", lines, 0, debug_indent + 2)
246 print_varx("index", index, 0, debug_indent + 2)
247 print_varx("source_line_num", source_line_num, 0, debug_indent)
248 print_varx("line_ix", line_ix, 0, debug_indent)
249 if local_debug_show_source:
250 print_varx("source_lines", source_lines, 0, debug_indent)
251 print_varx("called_func_name", called_func_name, 0, debug_indent)
252
253 # Get a list of all functions defined for the module. Note that this
254 # doesn't work consistently when _run_exitfuncs is at the top of the stack
255 # (i.e. if we're running an exit function). I've coded a work-around
256 # below for this deficiency.
257 all_functions = inspect.getmembers(module, inspect.isfunction)
258
259 # Get called_func_id by searching for our function in the list of all
260 # functions.
261 called_func_id = None
262 for func_name, function in all_functions:
263 if func_name == called_func_name:
264 called_func_id = id(function)
265 break
266 # NOTE: The only time I've found that called_func_id can't be found is
267 # when we're running from an exit function.
268
269 # Look for other functions in module with matching id.
270 aliases = set([called_func_name])
271 for func_name, function in all_functions:
272 if func_name == called_func_name:
273 continue
274 func_id = id(function)
275 if func_id == called_func_id:
276 aliases.add(func_name)
277
278 # In most cases, my general purpose code above will find all aliases.
279 # However, for the odd case (i.e. running from exit function), I've added
280 # code to handle pvar, qpvar, dpvar, etc. aliases explicitly since they
281 # are defined in this module and used frequently.
282 # pvar is an alias for print_var.
283 aliases.add(re.sub("print_var", "pvar", called_func_name))
284
285 func_regex = ".*(" + '|'.join(aliases) + ")[ ]*\("
286
287 # Search backward through source lines looking for the calling function
288 # name.
289 found = False
290 for start_line_ix in range(line_ix, 0, -1):
291 # Skip comment lines.
292 if re.match(r"[ ]*#", source_lines[start_line_ix]):
293 continue
294 if re.match(func_regex, source_lines[start_line_ix]):
295 found = True
296 break
297 if not found:
298 print_error("Programmer error - Could not find the source line with" +
299 " a reference to function \"" + called_func_name + "\".\n")
300 return
301
Michael Walsh82acf002017-05-04 14:33:05 -0500302 # Search forward through the source lines looking for a line whose
303 # indentation is the same or less than the start line. The end of our
304 # composite line should be the line preceding that line.
Michael Walsh23e7f492017-01-10 11:34:47 -0600305 start_indent = len(source_lines[start_line_ix]) -\
306 len(source_lines[start_line_ix].lstrip(' '))
307 end_line_ix = line_ix
308 for end_line_ix in range(line_ix + 1, len(source_lines)):
309 if source_lines[end_line_ix].strip() == "":
310 continue
311 line_indent = len(source_lines[end_line_ix]) -\
312 len(source_lines[end_line_ix].lstrip(' '))
Michael Walsh82acf002017-05-04 14:33:05 -0500313 if line_indent <= start_indent:
Michael Walsh23e7f492017-01-10 11:34:47 -0600314 end_line_ix -= 1
315 break
316
317 # Join the start line through the end line into a composite line.
318 composite_line = ''.join(map(str.strip,
319 source_lines[start_line_ix:end_line_ix + 1]))
Michael Walsh7423c012016-10-04 10:27:21 -0500320
Michael Walshbec416d2016-11-10 08:54:52 -0600321 # arg_list_etc = re.sub(".*" + called_func_name, "", composite_line)
322 arg_list_etc = "(" + re.sub(func_regex, "", composite_line)
Michael Walshde791732016-09-06 14:25:24 -0500323 if local_debug:
Michael Walsh23e7f492017-01-10 11:34:47 -0600324 print_varx("aliases", aliases, 0, debug_indent)
Michael Walshbec416d2016-11-10 08:54:52 -0600325 print_varx("func_regex", func_regex, 0, debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600326 print_varx("start_line_ix", start_line_ix, 0, debug_indent)
327 print_varx("end_line_ix", end_line_ix, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500328 print_varx("composite_line", composite_line, 0, debug_indent)
329 print_varx("arg_list_etc", arg_list_etc, 0, debug_indent)
330
331 # Parse arg list...
332 # Initialize...
333 nest_level = -1
334 arg_ix = 0
Michael Walsh7423c012016-10-04 10:27:21 -0500335 args_list = [""]
Michael Walshde791732016-09-06 14:25:24 -0500336 for ix in range(0, len(arg_list_etc)):
337 char = arg_list_etc[ix]
338 # Set the nest_level based on whether we've encounted a parenthesis.
339 if char == "(":
340 nest_level += 1
341 if nest_level == 0:
342 continue
343 elif char == ")":
344 nest_level -= 1
345 if nest_level < 0:
346 break
347
348 # If we reach a comma at base nest level, we are done processing an
Michael Walsh7423c012016-10-04 10:27:21 -0500349 # argument so we increment arg_ix and initialize a new args_list entry.
Michael Walshde791732016-09-06 14:25:24 -0500350 if char == "," and nest_level == 0:
351 arg_ix += 1
Michael Walsh7423c012016-10-04 10:27:21 -0500352 args_list.append("")
Michael Walshde791732016-09-06 14:25:24 -0500353 continue
354
Michael Walsh7423c012016-10-04 10:27:21 -0500355 # For any other character, we append it it to the current arg list
Michael Walshde791732016-09-06 14:25:24 -0500356 # entry.
Michael Walsh7423c012016-10-04 10:27:21 -0500357 args_list[arg_ix] += char
Michael Walshde791732016-09-06 14:25:24 -0500358
359 # Trim whitespace from each list entry.
Michael Walsh7423c012016-10-04 10:27:21 -0500360 args_list = [arg.strip() for arg in args_list]
Michael Walshde791732016-09-06 14:25:24 -0500361
Michael Walsh7423c012016-10-04 10:27:21 -0500362 if arg_num > len(args_list):
Michael Walshde791732016-09-06 14:25:24 -0500363 print_error("Programmer error - The caller has asked for the name of" +
364 " argument number \"" + str(arg_num) + "\" but there " +
Michael Walsh7423c012016-10-04 10:27:21 -0500365 "were only \"" + str(len(args_list)) + "\" args used:\n" +
366 sprint_varx("args_list", args_list))
Michael Walshde791732016-09-06 14:25:24 -0500367 return
368
Michael Walsh7423c012016-10-04 10:27:21 -0500369 argument = args_list[arg_num - 1]
Michael Walshde791732016-09-06 14:25:24 -0500370
371 if local_debug:
Michael Walsh7423c012016-10-04 10:27:21 -0500372 print_varx("args_list", args_list, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500373 print_varx("argument", argument, 0, debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600374 print_dashes(0, 120)
Michael Walshde791732016-09-06 14:25:24 -0500375
376 return argument
377
378###############################################################################
379
380
381###############################################################################
382def sprint_time(buffer=""):
383
384 r"""
385 Return the time in the following format.
386
387 Example:
388
389 The following python code...
390
391 sys.stdout.write(sprint_time())
392 sys.stdout.write("Hi.\n")
393
394 Will result in the following type of output:
395
396 #(CDT) 2016/07/08 15:25:35 - Hi.
397
398 Example:
399
400 The following python code...
401
402 sys.stdout.write(sprint_time("Hi.\n"))
403
404 Will result in the following type of output:
405
406 #(CDT) 2016/08/03 17:12:05 - Hi.
407
408 The following environment variables will affect the formatting as
409 described:
410 NANOSECONDS This will cause the time stamps to be
411 precise to the microsecond (Yes, it
412 probably should have been named
413 MICROSECONDS but the convention was set
414 long ago so we're sticking with it).
415 Example of the output when environment
416 variable NANOSECONDS=1.
417
418 #(CDT) 2016/08/03 17:16:25.510469 - Hi.
419
420 SHOW_ELAPSED_TIME This will cause the elapsed time to be
421 included in the output. This is the
422 amount of time that has elapsed since the
423 last time this function was called. The
424 precision of the elapsed time field is
425 also affected by the value of the
426 NANOSECONDS environment variable. Example
427 of the output when environment variable
428 NANOSECONDS=0 and SHOW_ELAPSED_TIME=1.
429
430 #(CDT) 2016/08/03 17:17:40 - 0 - Hi.
431
432 Example of the output when environment variable NANOSECONDS=1 and
433 SHOW_ELAPSED_TIME=1.
434
435 #(CDT) 2016/08/03 17:18:47.317339 - 0.000046 - Hi.
436
437 Description of arguments.
438 buffer This will be appended to the formatted
439 time string.
440 """
441
442 global NANOSECONDS
443 global SHOW_ELAPSED_TIME
444 global sprint_time_last_seconds
445
446 seconds = time.time()
447 loc_time = time.localtime(seconds)
448 nanoseconds = "%0.6f" % seconds
449 pos = nanoseconds.find(".")
450 nanoseconds = nanoseconds[pos:]
451
452 time_string = time.strftime("#(%Z) %Y/%m/%d %H:%M:%S", loc_time)
453 if NANOSECONDS == "1":
454 time_string = time_string + nanoseconds
455
456 if SHOW_ELAPSED_TIME == "1":
457 cur_time_seconds = seconds
458 math_string = "%9.9f" % cur_time_seconds + " - " + "%9.9f" % \
459 sprint_time_last_seconds
460 elapsed_seconds = eval(math_string)
461 if NANOSECONDS == "1":
462 elapsed_seconds = "%11.6f" % elapsed_seconds
463 else:
464 elapsed_seconds = "%4i" % elapsed_seconds
465 sprint_time_last_seconds = cur_time_seconds
466 time_string = time_string + " - " + elapsed_seconds
467
468 return time_string + " - " + buffer
469
470###############################################################################
471
472
473###############################################################################
474def sprint_timen(buffer=""):
475
476 r"""
477 Append a line feed to the buffer, pass it to sprint_time and return the
478 result.
479 """
480
481 return sprint_time(buffer + "\n")
482
483###############################################################################
484
485
486###############################################################################
487def sprint_error(buffer=""):
488
489 r"""
490 Return a standardized error string. This includes:
491 - A time stamp
492 - The "**ERROR**" string
493 - The caller's buffer string.
494
495 Example:
496
497 The following python code...
498
499 print(sprint_error("Oops.\n"))
500
501 Will result in the following type of output:
502
503 #(CDT) 2016/08/03 17:12:05 - **ERROR** Oops.
504
505 Description of arguments.
506 buffer This will be appended to the formatted
507 error string.
508 """
509
510 return sprint_time() + "**ERROR** " + buffer
511
512###############################################################################
513
514
515###############################################################################
516def sprint_varx(var_name,
517 var_value,
518 hex=0,
519 loc_col1_indent=col1_indent,
Michael Walsh7423c012016-10-04 10:27:21 -0500520 loc_col1_width=col1_width,
521 trailing_char="\n"):
Michael Walshde791732016-09-06 14:25:24 -0500522
523 r"""
524 Print the var name/value passed to it. If the caller lets loc_col1_width
525 default, the printing lines up nicely with output generated by the
526 print_time functions.
527
528 Note that the sprint_var function (defined below) can be used to call this
529 function so that the programmer does not need to pass the var_name.
530 sprint_var will figure out the var_name. The sprint_var function is the
531 one that would normally be used by the general user.
532
533 For example, the following python code:
534
535 first_name = "Mike"
536 print_time("Doing this...\n")
537 print_varx("first_name", first_name)
538 print_time("Doing that...\n")
539
540 Will generate output like this:
541
542 #(CDT) 2016/08/10 17:34:42.847374 - 0.001285 - Doing this...
543 first_name: Mike
544 #(CDT) 2016/08/10 17:34:42.847510 - 0.000136 - Doing that...
545
546 This function recognizes several complex types of data such as dict, list
547 or tuple.
548
549 For example, the following python code:
550
551 my_dict = dict(one=1, two=2, three=3)
552 print_var(my_dict)
553
554 Will generate the following output:
555
556 my_dict:
557 my_dict[three]: 3
558 my_dict[two]: 2
559 my_dict[one]: 1
560
561 Description of arguments.
562 var_name The name of the variable to be printed.
563 var_value The value of the variable to be printed.
564 hex This indicates that the value should be
565 printed in hex format. It is the user's
566 responsibility to ensure that a var_value
Michael Walshbec416d2016-11-10 08:54:52 -0600567 contains a valid hex number. For string
568 var_values, this will be interpreted as
569 show_blanks which means that blank values
Michael Walshd995cb02017-02-07 14:46:01 -0600570 will be printed as "<blank>". For dict
571 var_values, this will be interpreted as
572 terse format where keys are not repeated
573 in the output.
Michael Walshde791732016-09-06 14:25:24 -0500574 loc_col1_indent The number of spaces to indent the output.
575 loc_col1_width The width of the output column containing
576 the variable name. The default value of
577 this is adjusted so that the var_value
578 lines up with text printed via the
579 print_time function.
Michael Walsh7423c012016-10-04 10:27:21 -0500580 trailing_char The character to be used at the end of the
581 returned string. The default value is a
582 line feed.
583 """
Michael Walshde791732016-09-06 14:25:24 -0500584
585 # Determine the type
586 if type(var_value) in (int, float, bool, str, unicode) \
587 or var_value is None:
588 # The data type is simple in the sense that it has no subordinate
589 # parts.
Michael Walsh7423c012016-10-04 10:27:21 -0500590 # Adjust loc_col1_width.
591 loc_col1_width = loc_col1_width - loc_col1_indent
Michael Walshde791732016-09-06 14:25:24 -0500592 # See if the user wants the output in hex format.
593 if hex:
Michael Walsh18176322016-11-15 15:11:21 -0600594 if type(var_value) not in (int, long):
Michael Walshbec416d2016-11-10 08:54:52 -0600595 value_format = "%s"
Michael Walsh2795edc2016-12-13 16:00:33 -0600596 if var_value == "":
Michael Walshbec416d2016-11-10 08:54:52 -0600597 var_value = "<blank>"
598 else:
599 value_format = "0x%08x"
Michael Walshde791732016-09-06 14:25:24 -0500600 else:
601 value_format = "%s"
602 format_string = "%" + str(loc_col1_indent) + "s%-" \
Michael Walsh7423c012016-10-04 10:27:21 -0500603 + str(loc_col1_width) + "s" + value_format + trailing_char
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600604 return format_string % ("", str(var_name) + ":", var_value)
Michael Walshde791732016-09-06 14:25:24 -0500605 else:
606 # The data type is complex in the sense that it has subordinate parts.
607 format_string = "%" + str(loc_col1_indent) + "s%s\n"
608 buffer = format_string % ("", var_name + ":")
609 loc_col1_indent += 2
Michael Walsh7423c012016-10-04 10:27:21 -0500610 try:
611 length = len(var_value)
612 except TypeError:
Michael Walsh23e7f492017-01-10 11:34:47 -0600613 length = 0
Michael Walsh7423c012016-10-04 10:27:21 -0500614 ix = 0
615 loc_trailing_char = "\n"
Michael Walshbec416d2016-11-10 08:54:52 -0600616 type_is_dict = 0
Michael Walsh23e7f492017-01-10 11:34:47 -0600617 if type(var_value) is dict:
618 type_is_dict = 1
Michael Walsh8e6deb42017-01-27 14:22:41 -0600619 try:
620 if type(var_value) is collections.OrderedDict:
621 type_is_dict = 1
622 except AttributeError:
623 pass
624 try:
625 if type(var_value) is DotDict:
626 type_is_dict = 1
627 except NameError:
628 pass
629 try:
630 if type(var_value) is NormalizedDict:
631 type_is_dict = 1
632 except NameError:
633 pass
Michael Walshbec416d2016-11-10 08:54:52 -0600634 if type_is_dict:
Michael Walshde791732016-09-06 14:25:24 -0500635 for key, value in var_value.iteritems():
Michael Walsh7423c012016-10-04 10:27:21 -0500636 ix += 1
637 if ix == length:
638 loc_trailing_char = trailing_char
Michael Walshd995cb02017-02-07 14:46:01 -0600639 if hex:
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600640 # Since hex is being used as a format type, we want it
641 # turned off when processing integer dictionary values so
642 # it is not interpreted as a hex indicator.
643 loc_hex = not (type(value) is int)
Michael Walshd995cb02017-02-07 14:46:01 -0600644 buffer += sprint_varx(key, value,
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600645 loc_hex, loc_col1_indent,
646 loc_col1_width,
Michael Walshd995cb02017-02-07 14:46:01 -0600647 loc_trailing_char)
648 else:
649 buffer += sprint_varx(var_name + "[" + key + "]", value,
650 hex, loc_col1_indent, loc_col1_width,
651 loc_trailing_char)
Michael Walsh7423c012016-10-04 10:27:21 -0500652 elif type(var_value) in (list, tuple, set):
Michael Walshde791732016-09-06 14:25:24 -0500653 for key, value in enumerate(var_value):
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 buffer += sprint_varx(var_name + "[" + str(key) + "]", value,
Michael Walsh7423c012016-10-04 10:27:21 -0500658 hex, loc_col1_indent, loc_col1_width,
659 loc_trailing_char)
Michael Walshde791732016-09-06 14:25:24 -0500660 elif type(var_value) is argparse.Namespace:
661 for key in var_value.__dict__:
Michael Walsh7423c012016-10-04 10:27:21 -0500662 ix += 1
663 if ix == length:
664 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500665 cmd_buf = "buffer += sprint_varx(var_name + \".\" + str(key)" \
Michael Walsh7423c012016-10-04 10:27:21 -0500666 + ", var_value." + key + ", hex, loc_col1_indent," \
667 + " loc_col1_width, loc_trailing_char)"
Michael Walshde791732016-09-06 14:25:24 -0500668 exec(cmd_buf)
669 else:
670 var_type = type(var_value).__name__
671 func_name = sys._getframe().f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -0500672 var_value = "<" + var_type + " type not supported by " + \
673 func_name + "()>"
Michael Walshde791732016-09-06 14:25:24 -0500674 value_format = "%s"
675 loc_col1_indent -= 2
Michael Walsh7423c012016-10-04 10:27:21 -0500676 # Adjust loc_col1_width.
677 loc_col1_width = loc_col1_width - loc_col1_indent
Michael Walshde791732016-09-06 14:25:24 -0500678 format_string = "%" + str(loc_col1_indent) + "s%-" \
Michael Walsh7423c012016-10-04 10:27:21 -0500679 + str(loc_col1_width) + "s" + value_format + trailing_char
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600680 return format_string % ("", str(var_name) + ":", var_value)
Michael Walsh23e7f492017-01-10 11:34:47 -0600681
Michael Walshde791732016-09-06 14:25:24 -0500682 return buffer
683
684 return ""
685
686###############################################################################
687
688
689###############################################################################
690def sprint_var(*args):
691
692 r"""
693 Figure out the name of the first argument for you and then call
694 sprint_varx with it. Therefore, the following 2 calls are equivalent:
695 sprint_varx("var1", var1)
696 sprint_var(var1)
697 """
698
699 # Get the name of the first variable passed to this function.
700 stack_frame = 2
Michael Walsh7423c012016-10-04 10:27:21 -0500701 caller_func_name = sprint_func_name(2)
702 if caller_func_name.endswith("print_var"):
Michael Walshde791732016-09-06 14:25:24 -0500703 stack_frame += 1
704 var_name = get_arg_name(None, 1, stack_frame)
705 return sprint_varx(var_name, *args)
706
707###############################################################################
708
709
710###############################################################################
Michael Walsh18176322016-11-15 15:11:21 -0600711def sprint_vars(*args):
712
713 r"""
714 Sprint the values of one or more variables.
715
716 Description of args:
717 args:
718 If the first argument is an integer, it will be interpreted to be the
719 "indent" value.
720 If the second argument is an integer, it will be interpreted to be the
721 "col1_width" value.
722 If the third argument is an integer, it will be interpreted to be the
723 "hex" value.
724 All remaining parms are considered variable names which are to be
725 sprinted.
726 """
727
728 if len(args) == 0:
729 return
730
731 # Get the name of the first variable passed to this function.
732 stack_frame = 2
733 caller_func_name = sprint_func_name(2)
734 if caller_func_name.endswith("print_vars"):
735 stack_frame += 1
736
737 parm_num = 1
738
739 # Create list from args (which is a tuple) so that it can be modified.
740 args_list = list(args)
741
742 var_name = get_arg_name(None, parm_num, stack_frame)
743 # See if parm 1 is to be interpreted as "indent".
744 try:
745 if type(int(var_name)) is int:
746 indent = int(var_name)
747 args_list.pop(0)
748 parm_num += 1
749 except ValueError:
750 indent = 0
751
752 var_name = get_arg_name(None, parm_num, stack_frame)
753 # See if parm 1 is to be interpreted as "col1_width".
754 try:
755 if type(int(var_name)) is int:
756 loc_col1_width = int(var_name)
757 args_list.pop(0)
758 parm_num += 1
759 except ValueError:
760 loc_col1_width = col1_width
761
762 var_name = get_arg_name(None, parm_num, stack_frame)
763 # See if parm 1 is to be interpreted as "hex".
764 try:
765 if type(int(var_name)) is int:
766 hex = int(var_name)
767 args_list.pop(0)
768 parm_num += 1
769 except ValueError:
770 hex = 0
771
772 buffer = ""
773 for var_value in args_list:
774 var_name = get_arg_name(None, parm_num, stack_frame)
775 buffer += sprint_varx(var_name, var_value, hex, indent, loc_col1_width)
776 parm_num += 1
777
778 return buffer
779
780###############################################################################
781
782
783###############################################################################
Michael Walsh7423c012016-10-04 10:27:21 -0500784def lprint_varx(var_name,
785 var_value,
786 hex=0,
787 loc_col1_indent=col1_indent,
788 loc_col1_width=col1_width,
789 log_level=getattr(logging, 'INFO')):
790
791 r"""
792 Send sprint_varx output to logging.
793 """
794
795 logging.log(log_level, sprint_varx(var_name, var_value, hex,
796 loc_col1_indent, loc_col1_width, ""))
797
798###############################################################################
799
800
801###############################################################################
802def lprint_var(*args):
803
804 r"""
805 Figure out the name of the first argument for you and then call
806 lprint_varx with it. Therefore, the following 2 calls are equivalent:
807 lprint_varx("var1", var1)
808 lprint_var(var1)
809 """
810
811 # Get the name of the first variable passed to this function.
812 stack_frame = 2
813 caller_func_name = sprint_func_name(2)
814 if caller_func_name.endswith("print_var"):
815 stack_frame += 1
816 var_name = get_arg_name(None, 1, stack_frame)
817 lprint_varx(var_name, *args)
818
819###############################################################################
820
821
822###############################################################################
823def sprint_dashes(indent=col1_indent,
824 width=80,
825 line_feed=1,
826 char="-"):
Michael Walshde791732016-09-06 14:25:24 -0500827
828 r"""
829 Return a string of dashes to the caller.
830
Michael Walsh2ee77cd2017-03-08 11:50:17 -0600831 Description of arguments:
Michael Walshde791732016-09-06 14:25:24 -0500832 indent The number of characters to indent the
833 output.
834 width The width of the string of dashes.
835 line_feed Indicates whether the output should end
836 with a line feed.
Michael Walsh7423c012016-10-04 10:27:21 -0500837 char The character to be repeated in the output
838 string.
Michael Walshde791732016-09-06 14:25:24 -0500839 """
840
Michael Walsh7423c012016-10-04 10:27:21 -0500841 width = int(width)
Michael Walsh23e7f492017-01-10 11:34:47 -0600842 buffer = " " * int(indent) + char * width
Michael Walshde791732016-09-06 14:25:24 -0500843 if line_feed:
844 buffer += "\n"
845
846 return buffer
847
848###############################################################################
849
850
851###############################################################################
Michael Walsh7423c012016-10-04 10:27:21 -0500852def sindent(text="",
853 indent=0):
854
855 r"""
856 Pre-pend the specified number of characters to the text string (i.e.
857 indent it) and return it.
858
859 Description of arguments:
860 text The string to be indented.
861 indent The number of characters to indent the
862 string.
863 """
864
865 format_string = "%" + str(indent) + "s%s"
866 buffer = format_string % ("", text)
867
868 return buffer
869
870###############################################################################
871
872
873###############################################################################
874def sprint_call_stack(indent=0,
875 stack_frame_ix=0):
Michael Walshde791732016-09-06 14:25:24 -0500876
877 r"""
878 Return a call stack report for the given point in the program with line
879 numbers, function names and function parameters and arguments.
880
881 Sample output:
882
883 -------------------------------------------------------------------------
884 Python function call stack
885
886 Line # Function name and arguments
887 ------ ------------------------------------------------------------------
888 424 sprint_call_stack ()
889 4 print_call_stack ()
890 31 func1 (last_name = 'walsh', first_name = 'mikey')
891 59 /tmp/scr5.py
892 -------------------------------------------------------------------------
893
894 Description of arguments:
895 indent The number of characters to indent each
896 line of output.
897 stack_frame_ix The index of the first stack frame which
898 is to be returned.
899 """
900
901 buffer = ""
Michael Walsh7423c012016-10-04 10:27:21 -0500902 buffer += sprint_dashes(indent)
903 buffer += sindent("Python function call stack\n\n", indent)
904 buffer += sindent("Line # Function name and arguments\n", indent)
905 buffer += sprint_dashes(indent, 6, 0) + " " + sprint_dashes(0, 73)
Michael Walshde791732016-09-06 14:25:24 -0500906
907 # Grab the current program stack.
908 current_stack = inspect.stack()
909
910 # Process each frame in turn.
911 format_string = "%6s %s\n"
Michael Walsh7423c012016-10-04 10:27:21 -0500912 ix = 0
Michael Walshde791732016-09-06 14:25:24 -0500913 for stack_frame in current_stack:
Michael Walsh7423c012016-10-04 10:27:21 -0500914 if ix < stack_frame_ix:
915 ix += 1
916 continue
Michael Walsh23e7f492017-01-10 11:34:47 -0600917 # I want the line number shown to be the line where you find the line
918 # shown.
919 try:
920 line_num = str(current_stack[ix + 1][2])
921 except IndexError:
922 line_num = ""
Michael Walshde791732016-09-06 14:25:24 -0500923 func_name = str(stack_frame[3])
924 if func_name == "?":
925 # "?" is the name used when code is not in a function.
926 func_name = "(none)"
927
928 if func_name == "<module>":
Michael Walsh7423c012016-10-04 10:27:21 -0500929 # If the func_name is the "main" program, we simply get the
930 # command line call string.
Michael Walshde791732016-09-06 14:25:24 -0500931 func_and_args = ' '.join(sys.argv)
932 else:
933 # Get the program arguments.
934 arg_vals = inspect.getargvalues(stack_frame[0])
935 function_parms = arg_vals[0]
936 frame_locals = arg_vals[3]
937
Michael Walsh7423c012016-10-04 10:27:21 -0500938 args_list = []
Michael Walshde791732016-09-06 14:25:24 -0500939 for arg_name in function_parms:
940 # Get the arg value from frame locals.
941 arg_value = frame_locals[arg_name]
Michael Walsh7423c012016-10-04 10:27:21 -0500942 args_list.append(arg_name + " = " + repr(arg_value))
943 args_str = "(" + ', '.join(map(str, args_list)) + ")"
Michael Walshde791732016-09-06 14:25:24 -0500944
945 # Now we need to print this in a nicely-wrapped way.
946 func_and_args = func_name + " " + args_str
947
Michael Walsh23e7f492017-01-10 11:34:47 -0600948 buffer += sindent(format_string % (line_num, func_and_args), indent)
Michael Walsh7423c012016-10-04 10:27:21 -0500949 ix += 1
Michael Walshde791732016-09-06 14:25:24 -0500950
Michael Walsh7423c012016-10-04 10:27:21 -0500951 buffer += sprint_dashes(indent)
Michael Walshde791732016-09-06 14:25:24 -0500952
953 return buffer
954
955###############################################################################
956
957
958###############################################################################
959def sprint_executing(stack_frame_ix=None):
960
961 r"""
962 Print a line indicating what function is executing and with what parameter
963 values. This is useful for debugging.
964
965 Sample output:
966
967 #(CDT) 2016/08/25 17:54:27 - Executing: func1 (x = 1)
968
969 Description of arguments:
970 stack_frame_ix The index of the stack frame whose
971 function info should be returned. If the
Michael Walsh2ee77cd2017-03-08 11:50:17 -0600972 caller does not specify a value, this
Michael Walshde791732016-09-06 14:25:24 -0500973 function will set the value to 1 which is
974 the index of the caller's stack frame. If
975 the caller is the wrapper function
976 "print_executing", this function will bump
977 it up by 1.
978 """
979
980 # If user wants default stack_frame_ix.
981 if stack_frame_ix is None:
982 func_name = sys._getframe().f_code.co_name
983 caller_func_name = sys._getframe(1).f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -0500984 if caller_func_name.endswith(func_name[1:]):
Michael Walshde791732016-09-06 14:25:24 -0500985 stack_frame_ix = 2
986 else:
987 stack_frame_ix = 1
988
989 stack_frame = inspect.stack()[stack_frame_ix]
990
991 func_name = str(stack_frame[3])
992 if func_name == "?":
993 # "?" is the name used when code is not in a function.
994 func_name = "(none)"
995
996 if func_name == "<module>":
997 # If the func_name is the "main" program, we simply get the command
998 # line call string.
999 func_and_args = ' '.join(sys.argv)
1000 else:
1001 # Get the program arguments.
1002 arg_vals = inspect.getargvalues(stack_frame[0])
1003 function_parms = arg_vals[0]
1004 frame_locals = arg_vals[3]
1005
Michael Walsh7423c012016-10-04 10:27:21 -05001006 args_list = []
Michael Walshde791732016-09-06 14:25:24 -05001007 for arg_name in function_parms:
1008 # Get the arg value from frame locals.
1009 arg_value = frame_locals[arg_name]
Michael Walsh7423c012016-10-04 10:27:21 -05001010 args_list.append(arg_name + " = " + repr(arg_value))
1011 args_str = "(" + ', '.join(map(str, args_list)) + ")"
Michael Walshde791732016-09-06 14:25:24 -05001012
1013 # Now we need to print this in a nicely-wrapped way.
1014 func_and_args = func_name + " " + args_str
1015
1016 return sprint_time() + "Executing: " + func_and_args + "\n"
1017
1018###############################################################################
1019
1020
1021###############################################################################
Michael Walshbec416d2016-11-10 08:54:52 -06001022def sprint_pgm_header(indent=0,
1023 linefeed=1):
Michael Walshde791732016-09-06 14:25:24 -05001024
1025 r"""
1026 Return a standardized header that programs should print at the beginning
1027 of the run. It includes useful information like command line, pid,
1028 userid, program parameters, etc.
1029
Michael Walsh7423c012016-10-04 10:27:21 -05001030 Description of arguments:
1031 indent The number of characters to indent each
1032 line of output.
Michael Walshbec416d2016-11-10 08:54:52 -06001033 linefeed Indicates whether a line feed be included
1034 at the beginning and end of the report.
Michael Walshde791732016-09-06 14:25:24 -05001035 """
1036
Michael Walshbec416d2016-11-10 08:54:52 -06001037 loc_col1_width = col1_width + indent
1038
1039 buffer = ""
1040 if linefeed:
1041 buffer = "\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001042
1043 buffer += sindent(sprint_time() + "Running " + pgm_name + ".\n", indent)
1044 buffer += sindent(sprint_time() + "Program parameter values, etc.:\n\n",
1045 indent)
Michael Walshbec416d2016-11-10 08:54:52 -06001046 buffer += sprint_varx("command_line", ' '.join(sys.argv), 0, indent,
1047 loc_col1_width)
Michael Walsh7423c012016-10-04 10:27:21 -05001048 # We want the output to show a customized name for the pid and pgid but
1049 # we want it to look like a valid variable name. Therefore, we'll use
Michael Walshde791732016-09-06 14:25:24 -05001050 # pgm_name_var_name which was set when this module was imported.
Michael Walshbec416d2016-11-10 08:54:52 -06001051 buffer += sprint_varx(pgm_name_var_name + "_pid", os.getpid(), 0, indent,
1052 loc_col1_width)
1053 buffer += sprint_varx(pgm_name_var_name + "_pgid", os.getpgrp(), 0, indent,
1054 loc_col1_width)
Michael Walsh86de0d22016-12-05 10:13:15 -06001055 userid_num = str(os.geteuid())
1056 try:
1057 username = os.getlogin()
1058 except OSError:
1059 if userid_num == "0":
1060 username = "root"
1061 else:
1062 username = "?"
1063 buffer += sprint_varx("uid", userid_num + " (" + username +
Michael Walshbec416d2016-11-10 08:54:52 -06001064 ")", 0, indent, loc_col1_width)
Michael Walshde791732016-09-06 14:25:24 -05001065 buffer += sprint_varx("gid", str(os.getgid()) + " (" +
Michael Walsh7423c012016-10-04 10:27:21 -05001066 str(grp.getgrgid(os.getgid()).gr_name) + ")", 0,
Michael Walshbec416d2016-11-10 08:54:52 -06001067 indent, loc_col1_width)
1068 buffer += sprint_varx("host_name", socket.gethostname(), 0, indent,
1069 loc_col1_width)
Michael Walsh86de0d22016-12-05 10:13:15 -06001070 try:
1071 DISPLAY = os.environ['DISPLAY']
1072 except KeyError:
1073 DISPLAY = ""
1074 buffer += sprint_varx("DISPLAY", DISPLAY, 0, indent,
Michael Walshbec416d2016-11-10 08:54:52 -06001075 loc_col1_width)
Michael Walshde791732016-09-06 14:25:24 -05001076 # I want to add code to print caller's parms.
1077
Michael Walsh7423c012016-10-04 10:27:21 -05001078 # __builtin__.arg_obj is created by the get_arg module function,
1079 # gen_get_options.
1080 try:
1081 buffer += ga.sprint_args(__builtin__.arg_obj, indent)
1082 except AttributeError:
1083 pass
1084
Michael Walshbec416d2016-11-10 08:54:52 -06001085 if linefeed:
1086 buffer += "\n"
Michael Walshde791732016-09-06 14:25:24 -05001087
1088 return buffer
1089
1090###############################################################################
1091
1092
1093###############################################################################
Michael Walsh7423c012016-10-04 10:27:21 -05001094def sprint_error_report(error_text="\n",
1095 indent=2):
1096
1097 r"""
1098 Return a string with a standardized report which includes the caller's
1099 error text, the call stack and the program header.
1100
1101 Description of args:
1102 error_text The error text to be included in the
1103 report. The caller should include any
1104 needed linefeeds.
1105 indent The number of characters to indent each
1106 line of output.
1107 """
1108
1109 buffer = ""
1110 buffer += sprint_dashes(width=120, char="=")
1111 buffer += sprint_error(error_text)
1112 buffer += "\n"
1113 # Calling sprint_call_stack with stack_frame_ix of 0 causes it to show
1114 # itself and this function in the call stack. This is not helpful to a
1115 # debugger and is therefore clutter. We will adjust the stack_frame_ix to
1116 # hide that information.
1117 stack_frame_ix = 2
1118 caller_func_name = sprint_func_name(2)
1119 if caller_func_name.endswith("print_error_report"):
1120 stack_frame_ix += 1
1121 buffer += sprint_call_stack(indent, stack_frame_ix)
1122 buffer += sprint_pgm_header(indent)
1123 buffer += sprint_dashes(width=120, char="=")
1124
1125 return buffer
1126
1127###############################################################################
1128
1129
1130###############################################################################
Michael Walsh18176322016-11-15 15:11:21 -06001131def sprint_issuing(cmd_buf,
1132 test_mode=0):
Michael Walshde791732016-09-06 14:25:24 -05001133
1134 r"""
1135 Return a line indicating a command that the program is about to execute.
1136
1137 Sample output for a cmd_buf of "ls"
1138
1139 #(CDT) 2016/08/25 17:57:36 - Issuing: ls
Michael Walshbec416d2016-11-10 08:54:52 -06001140
Michael Walshde791732016-09-06 14:25:24 -05001141 Description of args:
1142 cmd_buf The command to be executed by caller.
Michael Walshbec416d2016-11-10 08:54:52 -06001143 test_mode With test_mode set, your output will look
1144 like this:
1145
1146 #(CDT) 2016/08/25 17:57:36 - (test_mode) Issuing: ls
1147
Michael Walshde791732016-09-06 14:25:24 -05001148 """
1149
Michael Walshbec416d2016-11-10 08:54:52 -06001150 buffer = sprint_time()
1151 if test_mode:
1152 buffer += "(test_mode) "
1153 buffer += "Issuing: " + cmd_buf + "\n"
Michael Walshde791732016-09-06 14:25:24 -05001154
1155 return buffer
1156
1157###############################################################################
1158
1159
1160###############################################################################
1161def sprint_pgm_footer():
1162
1163 r"""
1164 Return a standardized footer that programs should print at the end of the
1165 program run. It includes useful information like total run time, etc.
1166 """
1167
1168 buffer = "\n" + sprint_time() + "Finished running " + pgm_name + ".\n\n"
1169
1170 total_time = time.time() - start_time
1171 total_time_string = "%0.6f" % total_time
1172
Michael Walsh7423c012016-10-04 10:27:21 -05001173 buffer += sprint_varx(pgm_name_var_name + "_runtime", total_time_string)
Michael Walshbec416d2016-11-10 08:54:52 -06001174 buffer += "\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001175
1176 return buffer
1177
1178###############################################################################
1179
1180
1181###############################################################################
1182def sprint(buffer=""):
1183
1184 r"""
1185 Simply return the user's buffer. This function is used by the qprint and
1186 dprint functions defined dynamically below, i.e. it would not normally be
1187 called for general use.
1188
1189 Description of arguments.
1190 buffer This will be returned to the caller.
1191 """
Michael Walshde791732016-09-06 14:25:24 -05001192
Michael Walshbec416d2016-11-10 08:54:52 -06001193 return str(buffer)
1194
1195###############################################################################
1196
1197
1198###############################################################################
1199def sprintn(buffer=""):
1200
1201 r"""
1202 Simply return the user's buffer with a line feed. This function is used
1203 by the qprint and dprint functions defined dynamically below, i.e. it
1204 would not normally be called for general use.
1205
1206 Description of arguments.
1207 buffer This will be returned to the caller.
1208 """
1209
1210 buffer = str(buffer) + "\n"
1211
Michael Walshde791732016-09-06 14:25:24 -05001212 return buffer
1213
1214###############################################################################
1215
1216
1217###############################################################################
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001218def gp_debug_print(buffer):
1219
1220 r"""
1221 Print buffer to stdout only if gen_print_debug is set.
1222
1223 This function is intended for use only by other functions in this module.
1224
1225 Description of arguments:
1226 buffer The string to be printed.
1227 """
1228
1229 if not gen_print_debug:
1230 return
1231
1232 if robot_env:
1233 BuiltIn().log_to_console(buffer)
1234 else:
1235 print(buffer)
1236
1237###############################################################################
1238
1239
1240###############################################################################
Michael Walshb1500152017-04-12 15:42:43 -05001241def get_var_value(var_value=None,
1242 default=1,
1243 var_name=None):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001244
1245 r"""
Michael Walshb1500152017-04-12 15:42:43 -05001246 Return either var_value, the corresponding global value or default.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001247
Michael Walshb1500152017-04-12 15:42:43 -05001248 If var_value is not None, it will simply be returned.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001249
Michael Walshb1500152017-04-12 15:42:43 -05001250 If var_value is None, this function will return the corresponding global
1251 value of the variable in question.
1252
1253 Note: For global values, if we are in a robot environment,
1254 get_variable_value will be used. Otherwise, the __builtin__ version of
1255 the variable is returned (which are set by gen_arg.py functions).
1256
1257 If there is no global value associated with the variable, default is
1258 returned.
1259
1260 This function is useful for other functions in setting default values for
1261 parameters.
1262
1263 Example use:
1264
1265 def my_func(quiet=None):
1266
1267 quiet = int(get_var_value(quiet, 0))
1268
1269 Example calls to my_func():
1270
1271 In the following example, the caller is explicitly asking to have quiet be
1272 set to 1.
1273
1274 my_func(quiet=1)
1275
1276 In the following example, quiet will be set to the global value of quiet,
1277 if defined, or to 0 (the default).
1278
1279 my_func()
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001280
1281 Description of arguments:
Michael Walshb1500152017-04-12 15:42:43 -05001282 var_value The value to be returned (if not equal to
1283 None).
1284 default The value that is returned if var_value is
1285 None and there is no corresponding global
1286 value defined.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001287 var_name The name of the variable whose value is to
Michael Walshb1500152017-04-12 15:42:43 -05001288 be returned. Under most circumstances,
1289 this value need not be provided. This
1290 function can figure out the name of the
1291 variable passed as var_value. One
1292 exception to this would be if this
1293 function is called directly from a .robot
1294 file.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001295 """
1296
Michael Walshb1500152017-04-12 15:42:43 -05001297 if var_value is not None:
1298 return var_value
1299
1300 if var_name is None:
1301 var_name = get_arg_name(None, 1, 2)
1302
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001303 if robot_env:
1304 var_value = int(BuiltIn().get_variable_value("${" + var_name + "}",
1305 default))
1306 else:
1307 var_value = getattr(__builtin__, var_name, default)
1308
1309 return var_value
1310
1311###############################################################################
1312
1313
Michael Walsh82acf002017-05-04 14:33:05 -05001314# hidden_text is a list of passwords which are to be replaced with asterisks
1315# by print functions defined in this module.
1316hidden_text = []
1317# password_regex is created based on the contents of hidden_text.
1318password_regex = ""
1319
1320
1321###############################################################################
1322def register_passwords(*args):
1323
1324 r"""
1325 Register one or more passwords which are to be hidden in output produced
1326 by the print functions in this module.
1327
1328 Note: Blank password values are NOT registered. They are simply ignored.
1329
1330 Description of argument(s):
1331 args One or more password values. If a given
1332 password value is already registered, this
1333 function will simply do nothing.
1334 """
1335
1336 global hidden_text
1337 global password_regex
1338
1339 for password in args:
1340 if password == "":
1341 break
1342 if password in hidden_text:
1343 break
1344
1345 # Place the password into the hidden_text list.
1346 hidden_text.append(password)
1347 # Create a corresponding password regular expression. Escape regex
1348 # special characters too.
1349 password_regex = '(' +\
1350 '|'.join([re.escape(x) for x in hidden_text]) + ')'
1351
1352###############################################################################
1353
1354
1355###############################################################################
1356def replace_passwords(buffer):
1357
1358 r"""
1359 Return the buffer but with all registered passwords replaced by a string
1360 of asterisks.
1361
1362
1363 Description of argument(s):
1364 buffer The string to be returned but with
1365 passwords replaced.
1366 """
1367
1368 global password_regex
1369
1370 if int(os.environ.get("DEBUG_SHOW_PASSWORDS", "0")):
1371 return buffer
1372
1373 if password_regex == "":
1374 # No passwords to replace.
1375 return buffer
1376
1377 return re.sub(password_regex, "********", buffer)
1378
1379###############################################################################
1380
1381
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001382###############################################################################
Michael Walshde791732016-09-06 14:25:24 -05001383# In the following section of code, we will dynamically create print versions
1384# for each of the sprint functions defined above. So, for example, where we
1385# have an sprint_time() function defined above that returns the time to the
Michael Walsh7423c012016-10-04 10:27:21 -05001386# caller in a string, we will create a corresponding print_time() function
1387# that will print that string directly to stdout.
Michael Walshde791732016-09-06 14:25:24 -05001388
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001389# It can be complicated to follow what's being created by the exec statements
Michael Walshde791732016-09-06 14:25:24 -05001390# below. Here is an example of the print_time() function that will be created:
1391
1392# def print_time(*args):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001393# s_func = getattr(sys.modules[__name__], "sprint_time")
1394# sys.stdout.write(s_func(*args))
1395# sys.stdout.flush()
Michael Walshde791732016-09-06 14:25:24 -05001396
1397# Here are comments describing the 3 lines in the body of the created function.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001398# Create a reference to the "s" version of the given function in s_func (e.g.
1399# if this function name is print_time, we want s_funcname to be "sprint_time").
Michael Walsh7423c012016-10-04 10:27:21 -05001400# Call the "s" version of this function passing it all of our arguments.
1401# Write the result to stdout.
Michael Walshde791732016-09-06 14:25:24 -05001402
1403# func_names contains a list of all print functions which should be created
1404# from their sprint counterparts.
1405func_names = ['print_time', 'print_timen', 'print_error', 'print_varx',
Michael Walsh18176322016-11-15 15:11:21 -06001406 'print_var', 'print_vars', 'print_dashes', 'indent',
1407 'print_call_stack', 'print_func_name', 'print_executing',
1408 'print_pgm_header', 'print_issuing', 'print_pgm_footer',
1409 'print_error_report', 'print', 'printn']
Michael Walshde791732016-09-06 14:25:24 -05001410
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001411# stderr_func_names is a list of functions whose output should go to stderr
1412# rather than stdout.
1413stderr_func_names = ['print_error', 'print_error_report']
Michael Walshde791732016-09-06 14:25:24 -05001414
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001415gp_debug_print("robot_env: " + str(robot_env))
Michael Walshde791732016-09-06 14:25:24 -05001416for func_name in func_names:
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001417 gp_debug_print("func_name: " + func_name)
1418 if func_name in stderr_func_names:
Michael Walshde791732016-09-06 14:25:24 -05001419 output_stream = "stderr"
1420 else:
1421 output_stream = "stdout"
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001422
1423 func_def_line = "def " + func_name + "(*args):"
1424 s_func_line = " s_func = getattr(sys.modules[__name__], \"s" +\
1425 func_name + "\")"
1426 # Generate the code to do the printing.
1427 if robot_env:
1428 func_print_lines = \
1429 [
Michael Walsh82acf002017-05-04 14:33:05 -05001430 " BuiltIn().log_to_console(replace_passwords" +
1431 "(s_func(*args)),"
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001432 " stream='" + output_stream + "',"
1433 " no_newline=True)"
1434 ]
1435 else:
1436 func_print_lines = \
1437 [
Michael Walsh82acf002017-05-04 14:33:05 -05001438 " sys." + output_stream +
1439 ".write(replace_passwords(s_func(*args)))",
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001440 " sys." + output_stream + ".flush()"
1441 ]
1442
1443 # Create an array containing the lines of the function we wish to create.
1444 func_def = [func_def_line, s_func_line] + func_print_lines
1445 # We don't want to try to redefine the "print" function, thus the if
1446 # statement.
Michael Walsh7423c012016-10-04 10:27:21 -05001447 if func_name != "print":
1448 pgm_definition_string = '\n'.join(func_def)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001449 gp_debug_print(pgm_definition_string)
Michael Walsh7423c012016-10-04 10:27:21 -05001450 exec(pgm_definition_string)
1451
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001452 # Insert a blank line which will be overwritten by the next several
1453 # definitions.
1454 func_def.insert(1, "")
Michael Walsh7423c012016-10-04 10:27:21 -05001455
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001456 # Define the "q" (i.e. quiet) version of the given print function.
1457 func_def[0] = "def q" + func_name + "(*args):"
Michael Walshb1500152017-04-12 15:42:43 -05001458 func_def[1] = " if get_var_value(None, 0, \"quiet\"): return"
Michael Walshde791732016-09-06 14:25:24 -05001459 pgm_definition_string = '\n'.join(func_def)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001460 gp_debug_print(pgm_definition_string)
Michael Walshde791732016-09-06 14:25:24 -05001461 exec(pgm_definition_string)
1462
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001463 # Define the "d" (i.e. debug) version of the given print function.
1464 func_def[0] = "def d" + func_name + "(*args):"
Michael Walshb1500152017-04-12 15:42:43 -05001465 func_def[1] = " if not get_var_value(None, 0, \"debug\"): return"
Michael Walsh7423c012016-10-04 10:27:21 -05001466 pgm_definition_string = '\n'.join(func_def)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001467 gp_debug_print(pgm_definition_string)
Michael Walsh7423c012016-10-04 10:27:21 -05001468 exec(pgm_definition_string)
1469
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001470 # Define the "l" (i.e. log) version of the given print function.
1471 func_def_line = "def l" + func_name + "(*args):"
1472 func_print_lines = \
Michael Walsh7423c012016-10-04 10:27:21 -05001473 [
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001474 " logging.log(getattr(logging, 'INFO'), s_func(*args))"
Michael Walsh7423c012016-10-04 10:27:21 -05001475 ]
1476
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001477 func_def = [func_def_line, s_func_line] + func_print_lines
Michael Walsh7423c012016-10-04 10:27:21 -05001478 if func_name != "print_varx" and func_name != "print_var":
1479 pgm_definition_string = '\n'.join(func_def)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001480 gp_debug_print(pgm_definition_string)
Michael Walsh7423c012016-10-04 10:27:21 -05001481 exec(pgm_definition_string)
1482
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001483 if func_name == "print" or func_name == "printn":
1484 gp_debug_print("")
Michael Walsh7423c012016-10-04 10:27:21 -05001485 continue
1486
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001487 # Create abbreviated aliases (e.g. spvar is an alias for sprint_var).
Michael Walshde791732016-09-06 14:25:24 -05001488 alias = re.sub("print_", "p", func_name)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001489 prefixes = ["", "s", "q", "d", "l"]
1490 for prefix in prefixes:
1491 pgm_definition_string = prefix + alias + " = " + prefix + func_name
1492 gp_debug_print(pgm_definition_string)
1493 exec(pgm_definition_string)
Michael Walsh7423c012016-10-04 10:27:21 -05001494
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001495 gp_debug_print("")
Michael Walshde791732016-09-06 14:25:24 -05001496
1497###############################################################################