blob: ad067573b37b6ecf7db658e59ef3de4b3be5b679 [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:
21 from robot.utils import DotDict
22except ImportError:
Michael Walsha6723f22016-11-22 11:12:01 -060023 pass
Michael Walsh7423c012016-10-04 10:27:21 -050024
25import gen_arg as ga
Michael Walshde791732016-09-06 14:25:24 -050026
27# Setting these variables for use both inside this module and by programs
28# importing this module.
29pgm_dir_path = sys.argv[0]
30pgm_name = os.path.basename(pgm_dir_path)
Michael Walsh7423c012016-10-04 10:27:21 -050031pgm_dir_name = re.sub("/" + pgm_name, "", pgm_dir_path) + "/"
32
Michael Walshde791732016-09-06 14:25:24 -050033
34# Some functions (e.g. sprint_pgm_header) have need of a program name value
35# that looks more like a valid variable name. Therefore, we'll swap odd
36# characters like "." out for underscores.
37pgm_name_var_name = pgm_name.replace(".", "_")
38
39# Initialize global values used as defaults by print_time, print_var, etc.
40col1_indent = 0
41
42# Calculate default column width for print_var functions based on environment
43# variable settings. The objective is to make the variable values line up
44# nicely with the time stamps.
45col1_width = 29
46if 'NANOSECONDS' in os.environ:
47 NANOSECONDS = os.environ['NANOSECONDS']
48else:
49 NANOSECONDS = 0
50
51if NANOSECONDS == "1":
52 col1_width = col1_width + 7
53
54if 'SHOW_ELAPSED_TIME' in os.environ:
55 SHOW_ELAPSED_TIME = os.environ['SHOW_ELAPSED_TIME']
56else:
57 SHOW_ELAPSED_TIME = 0
58
59if SHOW_ELAPSED_TIME == "1":
60 if NANOSECONDS == "1":
61 col1_width = col1_width + 14
62 else:
63 col1_width = col1_width + 7
64
65# Initialize some time variables used in module functions.
66start_time = time.time()
67sprint_time_last_seconds = start_time
68
Michael Walsh7423c012016-10-04 10:27:21 -050069try:
70 # The user can set environment variable "GEN_PRINT_DEBUG" to get debug
71 # output from this module.
Michael Walsha6723f22016-11-22 11:12:01 -060072 gen_print_debug = int(os.environ['GEN_PRINT_DEBUG'])
Michael Walsh7423c012016-10-04 10:27:21 -050073except KeyError:
74 gen_print_debug = 0
75
Michael Walshde791732016-09-06 14:25:24 -050076
77###############################################################################
78def sprint_func_name(stack_frame_ix=None):
79
80 r"""
81 Return the function name associated with the indicated stack frame.
82
83 Description of arguments:
84 stack_frame_ix The index of the stack frame whose
85 function name should be returned. If the
86 caller does not specifiy a value, this
87 function will set the value to 1 which is
88 the index of the caller's stack frame. If
89 the caller is the wrapper function
90 "print_func_name", this function will bump
91 it up by 1.
92 """
93
94 # If user specified no stack_frame_ix, we'll set it to a proper default
95 # value.
96 if stack_frame_ix is None:
97 func_name = sys._getframe().f_code.co_name
98 caller_func_name = sys._getframe(1).f_code.co_name
99 if func_name[1:] == caller_func_name:
100 stack_frame_ix = 2
101 else:
102 stack_frame_ix = 1
103
104 func_name = sys._getframe(stack_frame_ix).f_code.co_name
105
106 return func_name
107
108###############################################################################
109
110
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.
113###############################################################################
114def get_arg_name(var,
115 arg_num=1,
116 stack_frame_ix=1):
117
118 r"""
119 Return the "name" of an argument passed to a function. This could be a
120 literal or a variable name.
121
122 Description of arguements:
123 var The variable whose name you want returned.
124 arg_num The arg number (1 through n) whose name
125 you wish to have returned. This value
126 should not exceed the number of arguments
127 allowed by the target function.
128 stack_frame_ix The stack frame index of the target
129 function. This value must be 1 or
130 greater. 1 would indicate get_arg_name's
131 stack frame. 2 would be the caller of
132 get_arg_name's stack frame, etc.
133
134 Example 1:
135
136 my_var = "mike"
137 var_name = get_arg_name(my_var)
138
139 In this example, var_name will receive the value "my_var".
140
141 Example 2:
142
143 def test1(var):
144 # Getting the var name of the first arg to this function, test1.
145 # Note, in this case, it doesn't matter what you pass as the first arg
146 # to get_arg_name since it is the caller's variable name that matters.
147 dummy = 1
148 arg_num = 1
149 stack_frame = 2
150 var_name = get_arg_name(dummy, arg_num, stack_frame)
151
152 # Mainline...
153
154 another_var = "whatever"
155 test1(another_var)
156
157 In this example, var_name will be set to "another_var".
158
159 """
160
161 # Note: I wish to avoid recursion so I refrain from calling any function
162 # that calls this function (i.e. sprint_var, valid_value, etc.).
163
164 try:
165 # The user can set environment variable "GET_ARG_NAME_DEBUG" to get
166 # debug output from this function.
167 local_debug = os.environ['GET_ARG_NAME_DEBUG']
168 except KeyError:
169 local_debug = 0
170
171 if arg_num < 1:
172 print_error("Programmer error - Variable \"arg_num\" has an invalid" +
173 " value of \"" + str(arg_num) + "\". The value must be" +
174 " an integer that is greater than 0.\n")
175 # What is the best way to handle errors? Raise exception? I'll
176 # revisit later.
177 return
178 if stack_frame_ix < 1:
179 print_error("Programmer error - Variable \"stack_frame_ix\" has an" +
180 " invalid value of \"" + str(stack_frame_ix) + "\". The" +
181 " value must be an integer that is greater than or equal" +
182 " to 1.\n")
183 return
184
185 if local_debug:
186 debug_indent = 2
187 print(sprint_func_name() + "() parms:")
188 print_varx("var", var, 0, debug_indent)
189 print_varx("arg_num", arg_num, 0, debug_indent)
190 print_varx("stack_frame_ix", stack_frame_ix, 0, debug_indent)
191
192 try:
193 frame, filename, cur_line_no, function_name, lines, index = \
194 inspect.stack()[stack_frame_ix]
195 except IndexError:
196 print_error("Programmer error - The caller has asked for information" +
197 " about the stack frame at index \"" +
198 str(stack_frame_ix) + "\". However, the stack only" +
199 " contains " + str(len(inspect.stack())) + " entries." +
200 " Therefore the stack frame index is out of range.\n")
201 return
202
203 if local_debug:
204 print("\nVariables retrieved from inspect.stack() function:")
205 print_varx("frame", frame, 0, debug_indent)
206 print_varx("filename", filename, 0, debug_indent)
207 print_varx("cur_line_no", cur_line_no, 0, debug_indent)
208 print_varx("function_name", function_name, 0, debug_indent)
209 print_varx("lines", lines, 0, debug_indent)
210 print_varx("index", index, 0, debug_indent)
211
212 composite_line = lines[0].strip()
213
214 called_func_name = sprint_func_name(stack_frame_ix)
Michael Walshbec416d2016-11-10 08:54:52 -0600215 # Needed to add a right anchor to func_regex for cases like this where
216 # there is an arg whose name is a substring of the function name. So the
217 # function name needs to be bounded on the right by zero or more spaces
218 # and a left parenthesis.
219 # if not valid_value(whatever, valid_values=["one", "two"]):
220 func_regex = ".*" + called_func_name + "[ ]*\("
221 # if not re.match(r".*" + called_func_name, composite_line):
222 if not re.match(func_regex, composite_line):
Michael Walsh7423c012016-10-04 10:27:21 -0500223 # The called function name was not found in the composite line. The
224 # caller may be using a function alias.
225 # I added code to handle pvar, qpvar, dpvar, etc. aliases.
226 # pvar is an alias for print_var. However, when it is used,
227 # sprint_func_name() returns the non-alias version, i.e. "print_var".
228 # Adjusting for that here.
229 alias = re.sub("print_var", "pvar", called_func_name)
Michael Walshbec416d2016-11-10 08:54:52 -0600230 if local_debug:
231 print_varx("alias", alias, 0, debug_indent)
Michael Walsh7423c012016-10-04 10:27:21 -0500232 called_func_name = alias
Michael Walshbec416d2016-11-10 08:54:52 -0600233 func_regex = ".*" + called_func_name + "[ ]*\("
Michael Walsh7423c012016-10-04 10:27:21 -0500234
Michael Walshbec416d2016-11-10 08:54:52 -0600235 # arg_list_etc = re.sub(".*" + called_func_name, "", composite_line)
236 arg_list_etc = "(" + re.sub(func_regex, "", composite_line)
Michael Walshde791732016-09-06 14:25:24 -0500237 if local_debug:
Michael Walshbec416d2016-11-10 08:54:52 -0600238 print_varx("func_regex", func_regex, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500239 print_varx("called_func_name", called_func_name, 0, debug_indent)
240 print_varx("composite_line", composite_line, 0, debug_indent)
241 print_varx("arg_list_etc", arg_list_etc, 0, debug_indent)
242
243 # Parse arg list...
244 # Initialize...
245 nest_level = -1
246 arg_ix = 0
Michael Walsh7423c012016-10-04 10:27:21 -0500247 args_list = [""]
Michael Walshde791732016-09-06 14:25:24 -0500248 for ix in range(0, len(arg_list_etc)):
249 char = arg_list_etc[ix]
250 # Set the nest_level based on whether we've encounted a parenthesis.
251 if char == "(":
252 nest_level += 1
253 if nest_level == 0:
254 continue
255 elif char == ")":
256 nest_level -= 1
257 if nest_level < 0:
258 break
259
260 # If we reach a comma at base nest level, we are done processing an
Michael Walsh7423c012016-10-04 10:27:21 -0500261 # argument so we increment arg_ix and initialize a new args_list entry.
Michael Walshde791732016-09-06 14:25:24 -0500262 if char == "," and nest_level == 0:
263 arg_ix += 1
Michael Walsh7423c012016-10-04 10:27:21 -0500264 args_list.append("")
Michael Walshde791732016-09-06 14:25:24 -0500265 continue
266
Michael Walsh7423c012016-10-04 10:27:21 -0500267 # For any other character, we append it it to the current arg list
Michael Walshde791732016-09-06 14:25:24 -0500268 # entry.
Michael Walsh7423c012016-10-04 10:27:21 -0500269 args_list[arg_ix] += char
Michael Walshde791732016-09-06 14:25:24 -0500270
271 # Trim whitespace from each list entry.
Michael Walsh7423c012016-10-04 10:27:21 -0500272 args_list = [arg.strip() for arg in args_list]
Michael Walshde791732016-09-06 14:25:24 -0500273
Michael Walsh7423c012016-10-04 10:27:21 -0500274 if arg_num > len(args_list):
Michael Walshde791732016-09-06 14:25:24 -0500275 print_error("Programmer error - The caller has asked for the name of" +
276 " argument number \"" + str(arg_num) + "\" but there " +
Michael Walsh7423c012016-10-04 10:27:21 -0500277 "were only \"" + str(len(args_list)) + "\" args used:\n" +
278 sprint_varx("args_list", args_list))
Michael Walshde791732016-09-06 14:25:24 -0500279 return
280
Michael Walsh7423c012016-10-04 10:27:21 -0500281 argument = args_list[arg_num - 1]
Michael Walshde791732016-09-06 14:25:24 -0500282
283 if local_debug:
Michael Walsh7423c012016-10-04 10:27:21 -0500284 print_varx("args_list", args_list, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500285 print_varx("argument", argument, 0, debug_indent)
286
287 return argument
288
289###############################################################################
290
291
292###############################################################################
293def sprint_time(buffer=""):
294
295 r"""
296 Return the time in the following format.
297
298 Example:
299
300 The following python code...
301
302 sys.stdout.write(sprint_time())
303 sys.stdout.write("Hi.\n")
304
305 Will result in the following type of output:
306
307 #(CDT) 2016/07/08 15:25:35 - Hi.
308
309 Example:
310
311 The following python code...
312
313 sys.stdout.write(sprint_time("Hi.\n"))
314
315 Will result in the following type of output:
316
317 #(CDT) 2016/08/03 17:12:05 - Hi.
318
319 The following environment variables will affect the formatting as
320 described:
321 NANOSECONDS This will cause the time stamps to be
322 precise to the microsecond (Yes, it
323 probably should have been named
324 MICROSECONDS but the convention was set
325 long ago so we're sticking with it).
326 Example of the output when environment
327 variable NANOSECONDS=1.
328
329 #(CDT) 2016/08/03 17:16:25.510469 - Hi.
330
331 SHOW_ELAPSED_TIME This will cause the elapsed time to be
332 included in the output. This is the
333 amount of time that has elapsed since the
334 last time this function was called. The
335 precision of the elapsed time field is
336 also affected by the value of the
337 NANOSECONDS environment variable. Example
338 of the output when environment variable
339 NANOSECONDS=0 and SHOW_ELAPSED_TIME=1.
340
341 #(CDT) 2016/08/03 17:17:40 - 0 - Hi.
342
343 Example of the output when environment variable NANOSECONDS=1 and
344 SHOW_ELAPSED_TIME=1.
345
346 #(CDT) 2016/08/03 17:18:47.317339 - 0.000046 - Hi.
347
348 Description of arguments.
349 buffer This will be appended to the formatted
350 time string.
351 """
352
353 global NANOSECONDS
354 global SHOW_ELAPSED_TIME
355 global sprint_time_last_seconds
356
357 seconds = time.time()
358 loc_time = time.localtime(seconds)
359 nanoseconds = "%0.6f" % seconds
360 pos = nanoseconds.find(".")
361 nanoseconds = nanoseconds[pos:]
362
363 time_string = time.strftime("#(%Z) %Y/%m/%d %H:%M:%S", loc_time)
364 if NANOSECONDS == "1":
365 time_string = time_string + nanoseconds
366
367 if SHOW_ELAPSED_TIME == "1":
368 cur_time_seconds = seconds
369 math_string = "%9.9f" % cur_time_seconds + " - " + "%9.9f" % \
370 sprint_time_last_seconds
371 elapsed_seconds = eval(math_string)
372 if NANOSECONDS == "1":
373 elapsed_seconds = "%11.6f" % elapsed_seconds
374 else:
375 elapsed_seconds = "%4i" % elapsed_seconds
376 sprint_time_last_seconds = cur_time_seconds
377 time_string = time_string + " - " + elapsed_seconds
378
379 return time_string + " - " + buffer
380
381###############################################################################
382
383
384###############################################################################
385def sprint_timen(buffer=""):
386
387 r"""
388 Append a line feed to the buffer, pass it to sprint_time and return the
389 result.
390 """
391
392 return sprint_time(buffer + "\n")
393
394###############################################################################
395
396
397###############################################################################
398def sprint_error(buffer=""):
399
400 r"""
401 Return a standardized error string. This includes:
402 - A time stamp
403 - The "**ERROR**" string
404 - The caller's buffer string.
405
406 Example:
407
408 The following python code...
409
410 print(sprint_error("Oops.\n"))
411
412 Will result in the following type of output:
413
414 #(CDT) 2016/08/03 17:12:05 - **ERROR** Oops.
415
416 Description of arguments.
417 buffer This will be appended to the formatted
418 error string.
419 """
420
421 return sprint_time() + "**ERROR** " + buffer
422
423###############################################################################
424
425
426###############################################################################
427def sprint_varx(var_name,
428 var_value,
429 hex=0,
430 loc_col1_indent=col1_indent,
Michael Walsh7423c012016-10-04 10:27:21 -0500431 loc_col1_width=col1_width,
432 trailing_char="\n"):
Michael Walshde791732016-09-06 14:25:24 -0500433
434 r"""
435 Print the var name/value passed to it. If the caller lets loc_col1_width
436 default, the printing lines up nicely with output generated by the
437 print_time functions.
438
439 Note that the sprint_var function (defined below) can be used to call this
440 function so that the programmer does not need to pass the var_name.
441 sprint_var will figure out the var_name. The sprint_var function is the
442 one that would normally be used by the general user.
443
444 For example, the following python code:
445
446 first_name = "Mike"
447 print_time("Doing this...\n")
448 print_varx("first_name", first_name)
449 print_time("Doing that...\n")
450
451 Will generate output like this:
452
453 #(CDT) 2016/08/10 17:34:42.847374 - 0.001285 - Doing this...
454 first_name: Mike
455 #(CDT) 2016/08/10 17:34:42.847510 - 0.000136 - Doing that...
456
457 This function recognizes several complex types of data such as dict, list
458 or tuple.
459
460 For example, the following python code:
461
462 my_dict = dict(one=1, two=2, three=3)
463 print_var(my_dict)
464
465 Will generate the following output:
466
467 my_dict:
468 my_dict[three]: 3
469 my_dict[two]: 2
470 my_dict[one]: 1
471
472 Description of arguments.
473 var_name The name of the variable to be printed.
474 var_value The value of the variable to be printed.
475 hex This indicates that the value should be
476 printed in hex format. It is the user's
477 responsibility to ensure that a var_value
Michael Walshbec416d2016-11-10 08:54:52 -0600478 contains a valid hex number. For string
479 var_values, this will be interpreted as
480 show_blanks which means that blank values
481 will be printed as "<blank>".
Michael Walshde791732016-09-06 14:25:24 -0500482 loc_col1_indent The number of spaces to indent the output.
483 loc_col1_width The width of the output column containing
484 the variable name. The default value of
485 this is adjusted so that the var_value
486 lines up with text printed via the
487 print_time function.
Michael Walsh7423c012016-10-04 10:27:21 -0500488 trailing_char The character to be used at the end of the
489 returned string. The default value is a
490 line feed.
491 """
Michael Walshde791732016-09-06 14:25:24 -0500492
493 # Determine the type
494 if type(var_value) in (int, float, bool, str, unicode) \
495 or var_value is None:
496 # The data type is simple in the sense that it has no subordinate
497 # parts.
Michael Walsh7423c012016-10-04 10:27:21 -0500498 # Adjust loc_col1_width.
499 loc_col1_width = loc_col1_width - loc_col1_indent
Michael Walshde791732016-09-06 14:25:24 -0500500 # See if the user wants the output in hex format.
501 if hex:
Michael Walsh18176322016-11-15 15:11:21 -0600502 if type(var_value) not in (int, long):
Michael Walshbec416d2016-11-10 08:54:52 -0600503 value_format = "%s"
504 if var_value is "":
505 var_value = "<blank>"
506 else:
507 value_format = "0x%08x"
Michael Walshde791732016-09-06 14:25:24 -0500508 else:
509 value_format = "%s"
510 format_string = "%" + str(loc_col1_indent) + "s%-" \
Michael Walsh7423c012016-10-04 10:27:21 -0500511 + str(loc_col1_width) + "s" + value_format + trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500512 return format_string % ("", var_name + ":", var_value)
513 else:
514 # The data type is complex in the sense that it has subordinate parts.
515 format_string = "%" + str(loc_col1_indent) + "s%s\n"
516 buffer = format_string % ("", var_name + ":")
517 loc_col1_indent += 2
Michael Walsh7423c012016-10-04 10:27:21 -0500518 try:
519 length = len(var_value)
520 except TypeError:
521 pass
522 ix = 0
523 loc_trailing_char = "\n"
Michael Walshbec416d2016-11-10 08:54:52 -0600524 type_is_dict = 0
525 try:
526 if type(var_value) in (dict, collections.OrderedDict):
527 type_is_dict = 1
528 except AttributeError:
Michael Walsh86de0d22016-12-05 10:13:15 -0600529 pass
530 if not type_is_dict:
Michael Walsha6723f22016-11-22 11:12:01 -0600531 try:
532 if type(var_value) is DotDict:
533 type_is_dict = 1
534 except NameError:
535 pass
Michael Walshbec416d2016-11-10 08:54:52 -0600536 if type_is_dict:
Michael Walshde791732016-09-06 14:25:24 -0500537 for key, value in var_value.iteritems():
Michael Walsh7423c012016-10-04 10:27:21 -0500538 ix += 1
539 if ix == length:
540 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500541 buffer += sprint_varx(var_name + "[" + key + "]", value, hex,
Michael Walsh7423c012016-10-04 10:27:21 -0500542 loc_col1_indent, loc_col1_width,
543 loc_trailing_char)
544 elif type(var_value) in (list, tuple, set):
Michael Walshde791732016-09-06 14:25:24 -0500545 for key, value in enumerate(var_value):
Michael Walsh7423c012016-10-04 10:27:21 -0500546 ix += 1
547 if ix == length:
548 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500549 buffer += sprint_varx(var_name + "[" + str(key) + "]", value,
Michael Walsh7423c012016-10-04 10:27:21 -0500550 hex, loc_col1_indent, loc_col1_width,
551 loc_trailing_char)
Michael Walshde791732016-09-06 14:25:24 -0500552 elif type(var_value) is argparse.Namespace:
553 for key in var_value.__dict__:
Michael Walsh7423c012016-10-04 10:27:21 -0500554 ix += 1
555 if ix == length:
556 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500557 cmd_buf = "buffer += sprint_varx(var_name + \".\" + str(key)" \
Michael Walsh7423c012016-10-04 10:27:21 -0500558 + ", var_value." + key + ", hex, loc_col1_indent," \
559 + " loc_col1_width, loc_trailing_char)"
Michael Walshde791732016-09-06 14:25:24 -0500560 exec(cmd_buf)
561 else:
562 var_type = type(var_value).__name__
563 func_name = sys._getframe().f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -0500564 var_value = "<" + var_type + " type not supported by " + \
565 func_name + "()>"
Michael Walshde791732016-09-06 14:25:24 -0500566 value_format = "%s"
567 loc_col1_indent -= 2
Michael Walsh7423c012016-10-04 10:27:21 -0500568 # Adjust loc_col1_width.
569 loc_col1_width = loc_col1_width - loc_col1_indent
Michael Walshde791732016-09-06 14:25:24 -0500570 format_string = "%" + str(loc_col1_indent) + "s%-" \
Michael Walsh7423c012016-10-04 10:27:21 -0500571 + str(loc_col1_width) + "s" + value_format + trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500572 return format_string % ("", var_name + ":", var_value)
573 return buffer
574
575 return ""
576
577###############################################################################
578
579
580###############################################################################
581def sprint_var(*args):
582
583 r"""
584 Figure out the name of the first argument for you and then call
585 sprint_varx with it. Therefore, the following 2 calls are equivalent:
586 sprint_varx("var1", var1)
587 sprint_var(var1)
588 """
589
590 # Get the name of the first variable passed to this function.
591 stack_frame = 2
Michael Walsh7423c012016-10-04 10:27:21 -0500592 caller_func_name = sprint_func_name(2)
593 if caller_func_name.endswith("print_var"):
Michael Walshde791732016-09-06 14:25:24 -0500594 stack_frame += 1
595 var_name = get_arg_name(None, 1, stack_frame)
596 return sprint_varx(var_name, *args)
597
598###############################################################################
599
600
601###############################################################################
Michael Walsh18176322016-11-15 15:11:21 -0600602def sprint_vars(*args):
603
604 r"""
605 Sprint the values of one or more variables.
606
607 Description of args:
608 args:
609 If the first argument is an integer, it will be interpreted to be the
610 "indent" value.
611 If the second argument is an integer, it will be interpreted to be the
612 "col1_width" value.
613 If the third argument is an integer, it will be interpreted to be the
614 "hex" value.
615 All remaining parms are considered variable names which are to be
616 sprinted.
617 """
618
619 if len(args) == 0:
620 return
621
622 # Get the name of the first variable passed to this function.
623 stack_frame = 2
624 caller_func_name = sprint_func_name(2)
625 if caller_func_name.endswith("print_vars"):
626 stack_frame += 1
627
628 parm_num = 1
629
630 # Create list from args (which is a tuple) so that it can be modified.
631 args_list = list(args)
632
633 var_name = get_arg_name(None, parm_num, stack_frame)
634 # See if parm 1 is to be interpreted as "indent".
635 try:
636 if type(int(var_name)) is int:
637 indent = int(var_name)
638 args_list.pop(0)
639 parm_num += 1
640 except ValueError:
641 indent = 0
642
643 var_name = get_arg_name(None, parm_num, stack_frame)
644 # See if parm 1 is to be interpreted as "col1_width".
645 try:
646 if type(int(var_name)) is int:
647 loc_col1_width = int(var_name)
648 args_list.pop(0)
649 parm_num += 1
650 except ValueError:
651 loc_col1_width = col1_width
652
653 var_name = get_arg_name(None, parm_num, stack_frame)
654 # See if parm 1 is to be interpreted as "hex".
655 try:
656 if type(int(var_name)) is int:
657 hex = int(var_name)
658 args_list.pop(0)
659 parm_num += 1
660 except ValueError:
661 hex = 0
662
663 buffer = ""
664 for var_value in args_list:
665 var_name = get_arg_name(None, parm_num, stack_frame)
666 buffer += sprint_varx(var_name, var_value, hex, indent, loc_col1_width)
667 parm_num += 1
668
669 return buffer
670
671###############################################################################
672
673
674###############################################################################
Michael Walsh7423c012016-10-04 10:27:21 -0500675def lprint_varx(var_name,
676 var_value,
677 hex=0,
678 loc_col1_indent=col1_indent,
679 loc_col1_width=col1_width,
680 log_level=getattr(logging, 'INFO')):
681
682 r"""
683 Send sprint_varx output to logging.
684 """
685
686 logging.log(log_level, sprint_varx(var_name, var_value, hex,
687 loc_col1_indent, loc_col1_width, ""))
688
689###############################################################################
690
691
692###############################################################################
693def lprint_var(*args):
694
695 r"""
696 Figure out the name of the first argument for you and then call
697 lprint_varx with it. Therefore, the following 2 calls are equivalent:
698 lprint_varx("var1", var1)
699 lprint_var(var1)
700 """
701
702 # Get the name of the first variable passed to this function.
703 stack_frame = 2
704 caller_func_name = sprint_func_name(2)
705 if caller_func_name.endswith("print_var"):
706 stack_frame += 1
707 var_name = get_arg_name(None, 1, stack_frame)
708 lprint_varx(var_name, *args)
709
710###############################################################################
711
712
713###############################################################################
714def sprint_dashes(indent=col1_indent,
715 width=80,
716 line_feed=1,
717 char="-"):
Michael Walshde791732016-09-06 14:25:24 -0500718
719 r"""
720 Return a string of dashes to the caller.
721
722 Description of arguements:
723 indent The number of characters to indent the
724 output.
725 width The width of the string of dashes.
726 line_feed Indicates whether the output should end
727 with a line feed.
Michael Walsh7423c012016-10-04 10:27:21 -0500728 char The character to be repeated in the output
729 string.
Michael Walshde791732016-09-06 14:25:24 -0500730 """
731
Michael Walsh7423c012016-10-04 10:27:21 -0500732 width = int(width)
733 buffer = " "*int(indent) + char*width
Michael Walshde791732016-09-06 14:25:24 -0500734 if line_feed:
735 buffer += "\n"
736
737 return buffer
738
739###############################################################################
740
741
742###############################################################################
Michael Walsh7423c012016-10-04 10:27:21 -0500743def sindent(text="",
744 indent=0):
745
746 r"""
747 Pre-pend the specified number of characters to the text string (i.e.
748 indent it) and return it.
749
750 Description of arguments:
751 text The string to be indented.
752 indent The number of characters to indent the
753 string.
754 """
755
756 format_string = "%" + str(indent) + "s%s"
757 buffer = format_string % ("", text)
758
759 return buffer
760
761###############################################################################
762
763
764###############################################################################
765def sprint_call_stack(indent=0,
766 stack_frame_ix=0):
Michael Walshde791732016-09-06 14:25:24 -0500767
768 r"""
769 Return a call stack report for the given point in the program with line
770 numbers, function names and function parameters and arguments.
771
772 Sample output:
773
774 -------------------------------------------------------------------------
775 Python function call stack
776
777 Line # Function name and arguments
778 ------ ------------------------------------------------------------------
779 424 sprint_call_stack ()
780 4 print_call_stack ()
781 31 func1 (last_name = 'walsh', first_name = 'mikey')
782 59 /tmp/scr5.py
783 -------------------------------------------------------------------------
784
785 Description of arguments:
786 indent The number of characters to indent each
787 line of output.
788 stack_frame_ix The index of the first stack frame which
789 is to be returned.
790 """
791
792 buffer = ""
Michael Walsh7423c012016-10-04 10:27:21 -0500793 buffer += sprint_dashes(indent)
794 buffer += sindent("Python function call stack\n\n", indent)
795 buffer += sindent("Line # Function name and arguments\n", indent)
796 buffer += sprint_dashes(indent, 6, 0) + " " + sprint_dashes(0, 73)
Michael Walshde791732016-09-06 14:25:24 -0500797
798 # Grab the current program stack.
799 current_stack = inspect.stack()
800
801 # Process each frame in turn.
802 format_string = "%6s %s\n"
Michael Walsh7423c012016-10-04 10:27:21 -0500803 ix = 0
Michael Walshde791732016-09-06 14:25:24 -0500804 for stack_frame in current_stack:
Michael Walsh7423c012016-10-04 10:27:21 -0500805 if ix < stack_frame_ix:
806 ix += 1
807 continue
Michael Walshde791732016-09-06 14:25:24 -0500808 lineno = str(stack_frame[2])
809 func_name = str(stack_frame[3])
810 if func_name == "?":
811 # "?" is the name used when code is not in a function.
812 func_name = "(none)"
813
814 if func_name == "<module>":
Michael Walsh7423c012016-10-04 10:27:21 -0500815 # If the func_name is the "main" program, we simply get the
816 # command line call string.
Michael Walshde791732016-09-06 14:25:24 -0500817 func_and_args = ' '.join(sys.argv)
818 else:
819 # Get the program arguments.
820 arg_vals = inspect.getargvalues(stack_frame[0])
821 function_parms = arg_vals[0]
822 frame_locals = arg_vals[3]
823
Michael Walsh7423c012016-10-04 10:27:21 -0500824 args_list = []
Michael Walshde791732016-09-06 14:25:24 -0500825 for arg_name in function_parms:
826 # Get the arg value from frame locals.
827 arg_value = frame_locals[arg_name]
Michael Walsh7423c012016-10-04 10:27:21 -0500828 args_list.append(arg_name + " = " + repr(arg_value))
829 args_str = "(" + ', '.join(map(str, args_list)) + ")"
Michael Walshde791732016-09-06 14:25:24 -0500830
831 # Now we need to print this in a nicely-wrapped way.
832 func_and_args = func_name + " " + args_str
833
Michael Walsh7423c012016-10-04 10:27:21 -0500834 buffer += sindent(format_string % (lineno, func_and_args), indent)
835 ix += 1
Michael Walshde791732016-09-06 14:25:24 -0500836
Michael Walsh7423c012016-10-04 10:27:21 -0500837 buffer += sprint_dashes(indent)
Michael Walshde791732016-09-06 14:25:24 -0500838
839 return buffer
840
841###############################################################################
842
843
844###############################################################################
845def sprint_executing(stack_frame_ix=None):
846
847 r"""
848 Print a line indicating what function is executing and with what parameter
849 values. This is useful for debugging.
850
851 Sample output:
852
853 #(CDT) 2016/08/25 17:54:27 - Executing: func1 (x = 1)
854
855 Description of arguments:
856 stack_frame_ix The index of the stack frame whose
857 function info should be returned. If the
858 caller does not specifiy a value, this
859 function will set the value to 1 which is
860 the index of the caller's stack frame. If
861 the caller is the wrapper function
862 "print_executing", this function will bump
863 it up by 1.
864 """
865
866 # If user wants default stack_frame_ix.
867 if stack_frame_ix is None:
868 func_name = sys._getframe().f_code.co_name
869 caller_func_name = sys._getframe(1).f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -0500870 if caller_func_name.endswith(func_name[1:]):
Michael Walshde791732016-09-06 14:25:24 -0500871 stack_frame_ix = 2
872 else:
873 stack_frame_ix = 1
874
875 stack_frame = inspect.stack()[stack_frame_ix]
876
877 func_name = str(stack_frame[3])
878 if func_name == "?":
879 # "?" is the name used when code is not in a function.
880 func_name = "(none)"
881
882 if func_name == "<module>":
883 # If the func_name is the "main" program, we simply get the command
884 # line call string.
885 func_and_args = ' '.join(sys.argv)
886 else:
887 # Get the program arguments.
888 arg_vals = inspect.getargvalues(stack_frame[0])
889 function_parms = arg_vals[0]
890 frame_locals = arg_vals[3]
891
Michael Walsh7423c012016-10-04 10:27:21 -0500892 args_list = []
Michael Walshde791732016-09-06 14:25:24 -0500893 for arg_name in function_parms:
894 # Get the arg value from frame locals.
895 arg_value = frame_locals[arg_name]
Michael Walsh7423c012016-10-04 10:27:21 -0500896 args_list.append(arg_name + " = " + repr(arg_value))
897 args_str = "(" + ', '.join(map(str, args_list)) + ")"
Michael Walshde791732016-09-06 14:25:24 -0500898
899 # Now we need to print this in a nicely-wrapped way.
900 func_and_args = func_name + " " + args_str
901
902 return sprint_time() + "Executing: " + func_and_args + "\n"
903
904###############################################################################
905
906
907###############################################################################
Michael Walshbec416d2016-11-10 08:54:52 -0600908def sprint_pgm_header(indent=0,
909 linefeed=1):
Michael Walshde791732016-09-06 14:25:24 -0500910
911 r"""
912 Return a standardized header that programs should print at the beginning
913 of the run. It includes useful information like command line, pid,
914 userid, program parameters, etc.
915
Michael Walsh7423c012016-10-04 10:27:21 -0500916 Description of arguments:
917 indent The number of characters to indent each
918 line of output.
Michael Walshbec416d2016-11-10 08:54:52 -0600919 linefeed Indicates whether a line feed be included
920 at the beginning and end of the report.
Michael Walshde791732016-09-06 14:25:24 -0500921 """
922
Michael Walshbec416d2016-11-10 08:54:52 -0600923 loc_col1_width = col1_width + indent
924
925 buffer = ""
926 if linefeed:
927 buffer = "\n"
Michael Walsh7423c012016-10-04 10:27:21 -0500928
929 buffer += sindent(sprint_time() + "Running " + pgm_name + ".\n", indent)
930 buffer += sindent(sprint_time() + "Program parameter values, etc.:\n\n",
931 indent)
Michael Walshbec416d2016-11-10 08:54:52 -0600932 buffer += sprint_varx("command_line", ' '.join(sys.argv), 0, indent,
933 loc_col1_width)
Michael Walsh7423c012016-10-04 10:27:21 -0500934 # We want the output to show a customized name for the pid and pgid but
935 # we want it to look like a valid variable name. Therefore, we'll use
Michael Walshde791732016-09-06 14:25:24 -0500936 # pgm_name_var_name which was set when this module was imported.
Michael Walshbec416d2016-11-10 08:54:52 -0600937 buffer += sprint_varx(pgm_name_var_name + "_pid", os.getpid(), 0, indent,
938 loc_col1_width)
939 buffer += sprint_varx(pgm_name_var_name + "_pgid", os.getpgrp(), 0, indent,
940 loc_col1_width)
Michael Walsh86de0d22016-12-05 10:13:15 -0600941 userid_num = str(os.geteuid())
942 try:
943 username = os.getlogin()
944 except OSError:
945 if userid_num == "0":
946 username = "root"
947 else:
948 username = "?"
949 buffer += sprint_varx("uid", userid_num + " (" + username +
Michael Walshbec416d2016-11-10 08:54:52 -0600950 ")", 0, indent, loc_col1_width)
Michael Walshde791732016-09-06 14:25:24 -0500951 buffer += sprint_varx("gid", str(os.getgid()) + " (" +
Michael Walsh7423c012016-10-04 10:27:21 -0500952 str(grp.getgrgid(os.getgid()).gr_name) + ")", 0,
Michael Walshbec416d2016-11-10 08:54:52 -0600953 indent, loc_col1_width)
954 buffer += sprint_varx("host_name", socket.gethostname(), 0, indent,
955 loc_col1_width)
Michael Walsh86de0d22016-12-05 10:13:15 -0600956 try:
957 DISPLAY = os.environ['DISPLAY']
958 except KeyError:
959 DISPLAY = ""
960 buffer += sprint_varx("DISPLAY", DISPLAY, 0, indent,
Michael Walshbec416d2016-11-10 08:54:52 -0600961 loc_col1_width)
Michael Walshde791732016-09-06 14:25:24 -0500962 # I want to add code to print caller's parms.
963
Michael Walsh7423c012016-10-04 10:27:21 -0500964 # __builtin__.arg_obj is created by the get_arg module function,
965 # gen_get_options.
966 try:
967 buffer += ga.sprint_args(__builtin__.arg_obj, indent)
968 except AttributeError:
969 pass
970
Michael Walshbec416d2016-11-10 08:54:52 -0600971 if linefeed:
972 buffer += "\n"
Michael Walshde791732016-09-06 14:25:24 -0500973
974 return buffer
975
976###############################################################################
977
978
979###############################################################################
Michael Walsh7423c012016-10-04 10:27:21 -0500980def sprint_error_report(error_text="\n",
981 indent=2):
982
983 r"""
984 Return a string with a standardized report which includes the caller's
985 error text, the call stack and the program header.
986
987 Description of args:
988 error_text The error text to be included in the
989 report. The caller should include any
990 needed linefeeds.
991 indent The number of characters to indent each
992 line of output.
993 """
994
995 buffer = ""
996 buffer += sprint_dashes(width=120, char="=")
997 buffer += sprint_error(error_text)
998 buffer += "\n"
999 # Calling sprint_call_stack with stack_frame_ix of 0 causes it to show
1000 # itself and this function in the call stack. This is not helpful to a
1001 # debugger and is therefore clutter. We will adjust the stack_frame_ix to
1002 # hide that information.
1003 stack_frame_ix = 2
1004 caller_func_name = sprint_func_name(2)
1005 if caller_func_name.endswith("print_error_report"):
1006 stack_frame_ix += 1
1007 buffer += sprint_call_stack(indent, stack_frame_ix)
1008 buffer += sprint_pgm_header(indent)
1009 buffer += sprint_dashes(width=120, char="=")
1010
1011 return buffer
1012
1013###############################################################################
1014
1015
1016###############################################################################
Michael Walsh18176322016-11-15 15:11:21 -06001017def sprint_issuing(cmd_buf,
1018 test_mode=0):
Michael Walshde791732016-09-06 14:25:24 -05001019
1020 r"""
1021 Return a line indicating a command that the program is about to execute.
1022
1023 Sample output for a cmd_buf of "ls"
1024
1025 #(CDT) 2016/08/25 17:57:36 - Issuing: ls
Michael Walshbec416d2016-11-10 08:54:52 -06001026
Michael Walshde791732016-09-06 14:25:24 -05001027 Description of args:
1028 cmd_buf The command to be executed by caller.
Michael Walshbec416d2016-11-10 08:54:52 -06001029 test_mode With test_mode set, your output will look
1030 like this:
1031
1032 #(CDT) 2016/08/25 17:57:36 - (test_mode) Issuing: ls
1033
Michael Walshde791732016-09-06 14:25:24 -05001034 """
1035
Michael Walshbec416d2016-11-10 08:54:52 -06001036 buffer = sprint_time()
1037 if test_mode:
1038 buffer += "(test_mode) "
1039 buffer += "Issuing: " + cmd_buf + "\n"
Michael Walshde791732016-09-06 14:25:24 -05001040
1041 return buffer
1042
1043###############################################################################
1044
1045
1046###############################################################################
1047def sprint_pgm_footer():
1048
1049 r"""
1050 Return a standardized footer that programs should print at the end of the
1051 program run. It includes useful information like total run time, etc.
1052 """
1053
1054 buffer = "\n" + sprint_time() + "Finished running " + pgm_name + ".\n\n"
1055
1056 total_time = time.time() - start_time
1057 total_time_string = "%0.6f" % total_time
1058
Michael Walsh7423c012016-10-04 10:27:21 -05001059 buffer += sprint_varx(pgm_name_var_name + "_runtime", total_time_string)
Michael Walshbec416d2016-11-10 08:54:52 -06001060 buffer += "\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001061
1062 return buffer
1063
1064###############################################################################
1065
1066
1067###############################################################################
1068def sprint(buffer=""):
1069
1070 r"""
1071 Simply return the user's buffer. This function is used by the qprint and
1072 dprint functions defined dynamically below, i.e. it would not normally be
1073 called for general use.
1074
1075 Description of arguments.
1076 buffer This will be returned to the caller.
1077 """
Michael Walshde791732016-09-06 14:25:24 -05001078
Michael Walshbec416d2016-11-10 08:54:52 -06001079 return str(buffer)
1080
1081###############################################################################
1082
1083
1084###############################################################################
1085def sprintn(buffer=""):
1086
1087 r"""
1088 Simply return the user's buffer with a line feed. This function is used
1089 by the qprint and dprint functions defined dynamically below, i.e. it
1090 would not normally be called for general use.
1091
1092 Description of arguments.
1093 buffer This will be returned to the caller.
1094 """
1095
1096 buffer = str(buffer) + "\n"
1097
Michael Walshde791732016-09-06 14:25:24 -05001098 return buffer
1099
1100###############################################################################
1101
1102
1103###############################################################################
1104# In the following section of code, we will dynamically create print versions
1105# for each of the sprint functions defined above. So, for example, where we
1106# have an sprint_time() function defined above that returns the time to the
Michael Walsh7423c012016-10-04 10:27:21 -05001107# caller in a string, we will create a corresponding print_time() function
1108# that will print that string directly to stdout.
Michael Walshde791732016-09-06 14:25:24 -05001109
1110# It can be complicated to follow what's being creaed by the exec statement
1111# below. Here is an example of the print_time() function that will be created:
1112
1113# def print_time(*args):
1114# s_funcname = "s" + sys._getframe().f_code.co_name
1115# s_func = getattr(sys.modules[__name__], s_funcname)
1116# sys.stdout.write(s_func(*args))
1117
1118# Here are comments describing the 3 lines in the body of the created function.
1119# Calculate the "s" version of this function name (e.g. if this function name
1120# is print_time, we want s_funcname to be "sprint_time".
1121# Put a reference to the "s" version of this function in s_func.
Michael Walsh7423c012016-10-04 10:27:21 -05001122# Call the "s" version of this function passing it all of our arguments.
1123# Write the result to stdout.
Michael Walshde791732016-09-06 14:25:24 -05001124
1125# func_names contains a list of all print functions which should be created
1126# from their sprint counterparts.
1127func_names = ['print_time', 'print_timen', 'print_error', 'print_varx',
Michael Walsh18176322016-11-15 15:11:21 -06001128 'print_var', 'print_vars', 'print_dashes', 'indent',
1129 'print_call_stack', 'print_func_name', 'print_executing',
1130 'print_pgm_header', 'print_issuing', 'print_pgm_footer',
1131 'print_error_report', 'print', 'printn']
Michael Walshde791732016-09-06 14:25:24 -05001132
1133for func_name in func_names:
Michael Walsh7423c012016-10-04 10:27:21 -05001134 if func_name == "print":
1135 continue
Michael Walshde791732016-09-06 14:25:24 -05001136 # Create abbreviated aliases (e.g. spvar is an alias for sprint_var).
1137 alias = re.sub("print_", "p", func_name)
Michael Walsh7423c012016-10-04 10:27:21 -05001138 pgm_definition_string = "s" + alias + " = s" + func_name
1139 if gen_print_debug:
1140 print(pgm_definition_string)
1141 exec(pgm_definition_string)
Michael Walshde791732016-09-06 14:25:24 -05001142
1143for func_name in func_names:
Michael Walsh7423c012016-10-04 10:27:21 -05001144 if func_name == "print_error" or func_name == "print_error_report":
Michael Walshde791732016-09-06 14:25:24 -05001145 output_stream = "stderr"
1146 else:
1147 output_stream = "stdout"
1148 func_def = \
1149 [
1150 "def " + func_name + "(*args):",
1151 " s_func_name = \"s\" + sys._getframe().f_code.co_name",
1152 " s_func = getattr(sys.modules[__name__], s_func_name)",
1153 " sys." + output_stream + ".write(s_func(*args))",
1154 " sys." + output_stream + ".flush()"
1155 ]
Michael Walsh7423c012016-10-04 10:27:21 -05001156 if func_name != "print":
1157 pgm_definition_string = '\n'.join(func_def)
1158 if gen_print_debug:
1159 print(pgm_definition_string)
1160 exec(pgm_definition_string)
1161
1162 # Now define "q" versions of each print function.
1163 func_def = \
1164 [
1165 "def q" + func_name + "(*args):",
1166 " if __builtin__.quiet: return",
1167 " s_func_name = \"s" + func_name + "\"",
1168 " s_func = getattr(sys.modules[__name__], s_func_name)",
1169 " sys." + output_stream + ".write(s_func(*args))",
1170 " sys." + output_stream + ".flush()"
1171 ]
1172
Michael Walshde791732016-09-06 14:25:24 -05001173 pgm_definition_string = '\n'.join(func_def)
Michael Walsh7423c012016-10-04 10:27:21 -05001174 if gen_print_debug:
1175 print(pgm_definition_string)
Michael Walshde791732016-09-06 14:25:24 -05001176 exec(pgm_definition_string)
1177
Michael Walsh7423c012016-10-04 10:27:21 -05001178 # Now define "d" versions of each print function.
1179 func_def = \
1180 [
1181 "def d" + func_name + "(*args):",
1182 " if not __builtin__.debug: return",
1183 " s_func_name = \"s" + func_name + "\"",
1184 " s_func = getattr(sys.modules[__name__], s_func_name)",
1185 " sys." + output_stream + ".write(s_func(*args))",
1186 " sys." + output_stream + ".flush()"
1187 ]
1188
1189 pgm_definition_string = '\n'.join(func_def)
1190 if gen_print_debug:
1191 print(pgm_definition_string)
1192 exec(pgm_definition_string)
1193
1194 # Now define "l" versions of each print function.
1195 func_def = \
1196 [
1197 "def l" + func_name + "(*args):",
1198 " s_func_name = \"s" + func_name + "\"",
1199 " s_func = getattr(sys.modules[__name__], s_func_name)",
1200 " logging.log(getattr(logging, 'INFO'), s_func(*args))",
1201 ]
1202
1203 if func_name != "print_varx" and func_name != "print_var":
1204 pgm_definition_string = '\n'.join(func_def)
1205 if gen_print_debug:
1206 print(pgm_definition_string)
1207 exec(pgm_definition_string)
1208
1209 if func_name == "print":
1210 continue
1211
Michael Walshde791732016-09-06 14:25:24 -05001212 # Create abbreviated aliases (e.g. pvar is an alias for print_var).
1213 alias = re.sub("print_", "p", func_name)
Michael Walsh7423c012016-10-04 10:27:21 -05001214 pgm_definition_string = alias + " = " + func_name
1215 if gen_print_debug:
1216 print(pgm_definition_string)
1217 exec(pgm_definition_string)
1218
1219 # Create abbreviated aliases (e.g. qpvar is an alias for qprint_var).
1220 alias = re.sub("print_", "p", func_name)
1221 pgm_definition_string = "q" + alias + " = q" + func_name
1222 if gen_print_debug:
1223 print(pgm_definition_string)
1224 exec(pgm_definition_string)
1225
1226 # Create abbreviated aliases (e.g. dpvar is an alias for dprint_var).
1227 alias = re.sub("print_", "p", func_name)
1228 pgm_definition_string = "d" + alias + " = d" + func_name
1229 if gen_print_debug:
1230 print(pgm_definition_string)
1231 exec(pgm_definition_string)
1232
1233 # Create abbreviated aliases (e.g. lpvar is an alias for lprint_var).
1234 alias = re.sub("print_", "p", func_name)
1235 pgm_definition_string = "l" + alias + " = l" + func_name
1236 if gen_print_debug:
1237 print(pgm_definition_string)
1238 exec(pgm_definition_string)
Michael Walshde791732016-09-06 14:25:24 -05001239
1240###############################################################################