blob: 631012254ae397aebe1b4bce5ef21d322d2018f9 [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
18
19import gen_arg as ga
Michael Walshde791732016-09-06 14:25:24 -050020
21# Setting these variables for use both inside this module and by programs
22# importing this module.
23pgm_dir_path = sys.argv[0]
24pgm_name = os.path.basename(pgm_dir_path)
Michael Walsh7423c012016-10-04 10:27:21 -050025pgm_dir_name = re.sub("/" + pgm_name, "", pgm_dir_path) + "/"
26
Michael Walshde791732016-09-06 14:25:24 -050027
28# Some functions (e.g. sprint_pgm_header) have need of a program name value
29# that looks more like a valid variable name. Therefore, we'll swap odd
30# characters like "." out for underscores.
31pgm_name_var_name = pgm_name.replace(".", "_")
32
33# Initialize global values used as defaults by print_time, print_var, etc.
34col1_indent = 0
35
36# Calculate default column width for print_var functions based on environment
37# variable settings. The objective is to make the variable values line up
38# nicely with the time stamps.
39col1_width = 29
40if 'NANOSECONDS' in os.environ:
41 NANOSECONDS = os.environ['NANOSECONDS']
42else:
43 NANOSECONDS = 0
44
45if NANOSECONDS == "1":
46 col1_width = col1_width + 7
47
48if 'SHOW_ELAPSED_TIME' in os.environ:
49 SHOW_ELAPSED_TIME = os.environ['SHOW_ELAPSED_TIME']
50else:
51 SHOW_ELAPSED_TIME = 0
52
53if SHOW_ELAPSED_TIME == "1":
54 if NANOSECONDS == "1":
55 col1_width = col1_width + 14
56 else:
57 col1_width = col1_width + 7
58
59# Initialize some time variables used in module functions.
60start_time = time.time()
61sprint_time_last_seconds = start_time
62
Michael Walsh7423c012016-10-04 10:27:21 -050063try:
64 # The user can set environment variable "GEN_PRINT_DEBUG" to get debug
65 # output from this module.
66 gen_print_debug = os.environ['GEN_PRINT_DEBUG']
67except KeyError:
68 gen_print_debug = 0
69
Michael Walshde791732016-09-06 14:25:24 -050070
71###############################################################################
72def sprint_func_name(stack_frame_ix=None):
73
74 r"""
75 Return the function name associated with the indicated stack frame.
76
77 Description of arguments:
78 stack_frame_ix The index of the stack frame whose
79 function name should be returned. If the
80 caller does not specifiy a value, this
81 function will set the value to 1 which is
82 the index of the caller's stack frame. If
83 the caller is the wrapper function
84 "print_func_name", this function will bump
85 it up by 1.
86 """
87
88 # If user specified no stack_frame_ix, we'll set it to a proper default
89 # value.
90 if stack_frame_ix is None:
91 func_name = sys._getframe().f_code.co_name
92 caller_func_name = sys._getframe(1).f_code.co_name
93 if func_name[1:] == caller_func_name:
94 stack_frame_ix = 2
95 else:
96 stack_frame_ix = 1
97
98 func_name = sys._getframe(stack_frame_ix).f_code.co_name
99
100 return func_name
101
102###############################################################################
103
104
105# get_arg_name is not a print function per se. I have included it in this
106# module because it is used by sprint_var which is found in this module.
107###############################################################################
108def get_arg_name(var,
109 arg_num=1,
110 stack_frame_ix=1):
111
112 r"""
113 Return the "name" of an argument passed to a function. This could be a
114 literal or a variable name.
115
116 Description of arguements:
117 var The variable whose name you want returned.
118 arg_num The arg number (1 through n) whose name
119 you wish to have returned. This value
120 should not exceed the number of arguments
121 allowed by the target function.
122 stack_frame_ix The stack frame index of the target
123 function. This value must be 1 or
124 greater. 1 would indicate get_arg_name's
125 stack frame. 2 would be the caller of
126 get_arg_name's stack frame, etc.
127
128 Example 1:
129
130 my_var = "mike"
131 var_name = get_arg_name(my_var)
132
133 In this example, var_name will receive the value "my_var".
134
135 Example 2:
136
137 def test1(var):
138 # Getting the var name of the first arg to this function, test1.
139 # Note, in this case, it doesn't matter what you pass as the first arg
140 # to get_arg_name since it is the caller's variable name that matters.
141 dummy = 1
142 arg_num = 1
143 stack_frame = 2
144 var_name = get_arg_name(dummy, arg_num, stack_frame)
145
146 # Mainline...
147
148 another_var = "whatever"
149 test1(another_var)
150
151 In this example, var_name will be set to "another_var".
152
153 """
154
155 # Note: I wish to avoid recursion so I refrain from calling any function
156 # that calls this function (i.e. sprint_var, valid_value, etc.).
157
158 try:
159 # The user can set environment variable "GET_ARG_NAME_DEBUG" to get
160 # debug output from this function.
161 local_debug = os.environ['GET_ARG_NAME_DEBUG']
162 except KeyError:
163 local_debug = 0
164
165 if arg_num < 1:
166 print_error("Programmer error - Variable \"arg_num\" has an invalid" +
167 " value of \"" + str(arg_num) + "\". The value must be" +
168 " an integer that is greater than 0.\n")
169 # What is the best way to handle errors? Raise exception? I'll
170 # revisit later.
171 return
172 if stack_frame_ix < 1:
173 print_error("Programmer error - Variable \"stack_frame_ix\" has an" +
174 " invalid value of \"" + str(stack_frame_ix) + "\". The" +
175 " value must be an integer that is greater than or equal" +
176 " to 1.\n")
177 return
178
179 if local_debug:
180 debug_indent = 2
181 print(sprint_func_name() + "() parms:")
182 print_varx("var", var, 0, debug_indent)
183 print_varx("arg_num", arg_num, 0, debug_indent)
184 print_varx("stack_frame_ix", stack_frame_ix, 0, debug_indent)
185
186 try:
187 frame, filename, cur_line_no, function_name, lines, index = \
188 inspect.stack()[stack_frame_ix]
189 except IndexError:
190 print_error("Programmer error - The caller has asked for information" +
191 " about the stack frame at index \"" +
192 str(stack_frame_ix) + "\". However, the stack only" +
193 " contains " + str(len(inspect.stack())) + " entries." +
194 " Therefore the stack frame index is out of range.\n")
195 return
196
197 if local_debug:
198 print("\nVariables retrieved from inspect.stack() function:")
199 print_varx("frame", frame, 0, debug_indent)
200 print_varx("filename", filename, 0, debug_indent)
201 print_varx("cur_line_no", cur_line_no, 0, debug_indent)
202 print_varx("function_name", function_name, 0, debug_indent)
203 print_varx("lines", lines, 0, debug_indent)
204 print_varx("index", index, 0, debug_indent)
205
206 composite_line = lines[0].strip()
207
208 called_func_name = sprint_func_name(stack_frame_ix)
Michael Walsh7423c012016-10-04 10:27:21 -0500209 if not re.match(r".*" + called_func_name, composite_line):
210 # The called function name was not found in the composite line. The
211 # caller may be using a function alias.
212 # I added code to handle pvar, qpvar, dpvar, etc. aliases.
213 # pvar is an alias for print_var. However, when it is used,
214 # sprint_func_name() returns the non-alias version, i.e. "print_var".
215 # Adjusting for that here.
216 alias = re.sub("print_var", "pvar", called_func_name)
217 called_func_name = alias
218
Michael Walshde791732016-09-06 14:25:24 -0500219 arg_list_etc = re.sub(".*" + called_func_name, "", composite_line)
220 if local_debug:
221 print_varx("called_func_name", called_func_name, 0, debug_indent)
222 print_varx("composite_line", composite_line, 0, debug_indent)
223 print_varx("arg_list_etc", arg_list_etc, 0, debug_indent)
224
225 # Parse arg list...
226 # Initialize...
227 nest_level = -1
228 arg_ix = 0
Michael Walsh7423c012016-10-04 10:27:21 -0500229 args_list = [""]
Michael Walshde791732016-09-06 14:25:24 -0500230 for ix in range(0, len(arg_list_etc)):
231 char = arg_list_etc[ix]
232 # Set the nest_level based on whether we've encounted a parenthesis.
233 if char == "(":
234 nest_level += 1
235 if nest_level == 0:
236 continue
237 elif char == ")":
238 nest_level -= 1
239 if nest_level < 0:
240 break
241
242 # If we reach a comma at base nest level, we are done processing an
Michael Walsh7423c012016-10-04 10:27:21 -0500243 # argument so we increment arg_ix and initialize a new args_list entry.
Michael Walshde791732016-09-06 14:25:24 -0500244 if char == "," and nest_level == 0:
245 arg_ix += 1
Michael Walsh7423c012016-10-04 10:27:21 -0500246 args_list.append("")
Michael Walshde791732016-09-06 14:25:24 -0500247 continue
248
Michael Walsh7423c012016-10-04 10:27:21 -0500249 # For any other character, we append it it to the current arg list
Michael Walshde791732016-09-06 14:25:24 -0500250 # entry.
Michael Walsh7423c012016-10-04 10:27:21 -0500251 args_list[arg_ix] += char
Michael Walshde791732016-09-06 14:25:24 -0500252
253 # Trim whitespace from each list entry.
Michael Walsh7423c012016-10-04 10:27:21 -0500254 args_list = [arg.strip() for arg in args_list]
Michael Walshde791732016-09-06 14:25:24 -0500255
Michael Walsh7423c012016-10-04 10:27:21 -0500256 if arg_num > len(args_list):
Michael Walshde791732016-09-06 14:25:24 -0500257 print_error("Programmer error - The caller has asked for the name of" +
258 " argument number \"" + str(arg_num) + "\" but there " +
Michael Walsh7423c012016-10-04 10:27:21 -0500259 "were only \"" + str(len(args_list)) + "\" args used:\n" +
260 sprint_varx("args_list", args_list))
Michael Walshde791732016-09-06 14:25:24 -0500261 return
262
Michael Walsh7423c012016-10-04 10:27:21 -0500263 argument = args_list[arg_num - 1]
Michael Walshde791732016-09-06 14:25:24 -0500264
265 if local_debug:
Michael Walsh7423c012016-10-04 10:27:21 -0500266 print_varx("args_list", args_list, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500267 print_varx("argument", argument, 0, debug_indent)
268
269 return argument
270
271###############################################################################
272
273
274###############################################################################
275def sprint_time(buffer=""):
276
277 r"""
278 Return the time in the following format.
279
280 Example:
281
282 The following python code...
283
284 sys.stdout.write(sprint_time())
285 sys.stdout.write("Hi.\n")
286
287 Will result in the following type of output:
288
289 #(CDT) 2016/07/08 15:25:35 - Hi.
290
291 Example:
292
293 The following python code...
294
295 sys.stdout.write(sprint_time("Hi.\n"))
296
297 Will result in the following type of output:
298
299 #(CDT) 2016/08/03 17:12:05 - Hi.
300
301 The following environment variables will affect the formatting as
302 described:
303 NANOSECONDS This will cause the time stamps to be
304 precise to the microsecond (Yes, it
305 probably should have been named
306 MICROSECONDS but the convention was set
307 long ago so we're sticking with it).
308 Example of the output when environment
309 variable NANOSECONDS=1.
310
311 #(CDT) 2016/08/03 17:16:25.510469 - Hi.
312
313 SHOW_ELAPSED_TIME This will cause the elapsed time to be
314 included in the output. This is the
315 amount of time that has elapsed since the
316 last time this function was called. The
317 precision of the elapsed time field is
318 also affected by the value of the
319 NANOSECONDS environment variable. Example
320 of the output when environment variable
321 NANOSECONDS=0 and SHOW_ELAPSED_TIME=1.
322
323 #(CDT) 2016/08/03 17:17:40 - 0 - Hi.
324
325 Example of the output when environment variable NANOSECONDS=1 and
326 SHOW_ELAPSED_TIME=1.
327
328 #(CDT) 2016/08/03 17:18:47.317339 - 0.000046 - Hi.
329
330 Description of arguments.
331 buffer This will be appended to the formatted
332 time string.
333 """
334
335 global NANOSECONDS
336 global SHOW_ELAPSED_TIME
337 global sprint_time_last_seconds
338
339 seconds = time.time()
340 loc_time = time.localtime(seconds)
341 nanoseconds = "%0.6f" % seconds
342 pos = nanoseconds.find(".")
343 nanoseconds = nanoseconds[pos:]
344
345 time_string = time.strftime("#(%Z) %Y/%m/%d %H:%M:%S", loc_time)
346 if NANOSECONDS == "1":
347 time_string = time_string + nanoseconds
348
349 if SHOW_ELAPSED_TIME == "1":
350 cur_time_seconds = seconds
351 math_string = "%9.9f" % cur_time_seconds + " - " + "%9.9f" % \
352 sprint_time_last_seconds
353 elapsed_seconds = eval(math_string)
354 if NANOSECONDS == "1":
355 elapsed_seconds = "%11.6f" % elapsed_seconds
356 else:
357 elapsed_seconds = "%4i" % elapsed_seconds
358 sprint_time_last_seconds = cur_time_seconds
359 time_string = time_string + " - " + elapsed_seconds
360
361 return time_string + " - " + buffer
362
363###############################################################################
364
365
366###############################################################################
367def sprint_timen(buffer=""):
368
369 r"""
370 Append a line feed to the buffer, pass it to sprint_time and return the
371 result.
372 """
373
374 return sprint_time(buffer + "\n")
375
376###############################################################################
377
378
379###############################################################################
380def sprint_error(buffer=""):
381
382 r"""
383 Return a standardized error string. This includes:
384 - A time stamp
385 - The "**ERROR**" string
386 - The caller's buffer string.
387
388 Example:
389
390 The following python code...
391
392 print(sprint_error("Oops.\n"))
393
394 Will result in the following type of output:
395
396 #(CDT) 2016/08/03 17:12:05 - **ERROR** Oops.
397
398 Description of arguments.
399 buffer This will be appended to the formatted
400 error string.
401 """
402
403 return sprint_time() + "**ERROR** " + buffer
404
405###############################################################################
406
407
408###############################################################################
409def sprint_varx(var_name,
410 var_value,
411 hex=0,
412 loc_col1_indent=col1_indent,
Michael Walsh7423c012016-10-04 10:27:21 -0500413 loc_col1_width=col1_width,
414 trailing_char="\n"):
Michael Walshde791732016-09-06 14:25:24 -0500415
416 r"""
417 Print the var name/value passed to it. If the caller lets loc_col1_width
418 default, the printing lines up nicely with output generated by the
419 print_time functions.
420
421 Note that the sprint_var function (defined below) can be used to call this
422 function so that the programmer does not need to pass the var_name.
423 sprint_var will figure out the var_name. The sprint_var function is the
424 one that would normally be used by the general user.
425
426 For example, the following python code:
427
428 first_name = "Mike"
429 print_time("Doing this...\n")
430 print_varx("first_name", first_name)
431 print_time("Doing that...\n")
432
433 Will generate output like this:
434
435 #(CDT) 2016/08/10 17:34:42.847374 - 0.001285 - Doing this...
436 first_name: Mike
437 #(CDT) 2016/08/10 17:34:42.847510 - 0.000136 - Doing that...
438
439 This function recognizes several complex types of data such as dict, list
440 or tuple.
441
442 For example, the following python code:
443
444 my_dict = dict(one=1, two=2, three=3)
445 print_var(my_dict)
446
447 Will generate the following output:
448
449 my_dict:
450 my_dict[three]: 3
451 my_dict[two]: 2
452 my_dict[one]: 1
453
454 Description of arguments.
455 var_name The name of the variable to be printed.
456 var_value The value of the variable to be printed.
457 hex This indicates that the value should be
458 printed in hex format. It is the user's
459 responsibility to ensure that a var_value
460 contains a valid hex number.
461 loc_col1_indent The number of spaces to indent the output.
462 loc_col1_width The width of the output column containing
463 the variable name. The default value of
464 this is adjusted so that the var_value
465 lines up with text printed via the
466 print_time function.
Michael Walsh7423c012016-10-04 10:27:21 -0500467 trailing_char The character to be used at the end of the
468 returned string. The default value is a
469 line feed.
470 """
Michael Walshde791732016-09-06 14:25:24 -0500471
472 # Determine the type
473 if type(var_value) in (int, float, bool, str, unicode) \
474 or var_value is None:
475 # The data type is simple in the sense that it has no subordinate
476 # parts.
Michael Walsh7423c012016-10-04 10:27:21 -0500477 # Adjust loc_col1_width.
478 loc_col1_width = loc_col1_width - loc_col1_indent
Michael Walshde791732016-09-06 14:25:24 -0500479 # See if the user wants the output in hex format.
480 if hex:
481 value_format = "0x%08x"
482 else:
483 value_format = "%s"
484 format_string = "%" + str(loc_col1_indent) + "s%-" \
Michael Walsh7423c012016-10-04 10:27:21 -0500485 + str(loc_col1_width) + "s" + value_format + trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500486 return format_string % ("", var_name + ":", var_value)
487 else:
488 # The data type is complex in the sense that it has subordinate parts.
489 format_string = "%" + str(loc_col1_indent) + "s%s\n"
490 buffer = format_string % ("", var_name + ":")
491 loc_col1_indent += 2
Michael Walsh7423c012016-10-04 10:27:21 -0500492 try:
493 length = len(var_value)
494 except TypeError:
495 pass
496 ix = 0
497 loc_trailing_char = "\n"
Michael Walshde791732016-09-06 14:25:24 -0500498 if type(var_value) is dict:
499 for key, value in var_value.iteritems():
Michael Walsh7423c012016-10-04 10:27:21 -0500500 ix += 1
501 if ix == length:
502 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500503 buffer += sprint_varx(var_name + "[" + key + "]", value, hex,
Michael Walsh7423c012016-10-04 10:27:21 -0500504 loc_col1_indent, loc_col1_width,
505 loc_trailing_char)
506 elif type(var_value) in (list, tuple, set):
Michael Walshde791732016-09-06 14:25:24 -0500507 for key, value in enumerate(var_value):
Michael Walsh7423c012016-10-04 10:27:21 -0500508 ix += 1
509 if ix == length:
510 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500511 buffer += sprint_varx(var_name + "[" + str(key) + "]", value,
Michael Walsh7423c012016-10-04 10:27:21 -0500512 hex, loc_col1_indent, loc_col1_width,
513 loc_trailing_char)
Michael Walshde791732016-09-06 14:25:24 -0500514 elif type(var_value) is argparse.Namespace:
515 for key in var_value.__dict__:
Michael Walsh7423c012016-10-04 10:27:21 -0500516 ix += 1
517 if ix == length:
518 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500519 cmd_buf = "buffer += sprint_varx(var_name + \".\" + str(key)" \
Michael Walsh7423c012016-10-04 10:27:21 -0500520 + ", var_value." + key + ", hex, loc_col1_indent," \
521 + " loc_col1_width, loc_trailing_char)"
Michael Walshde791732016-09-06 14:25:24 -0500522 exec(cmd_buf)
523 else:
524 var_type = type(var_value).__name__
525 func_name = sys._getframe().f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -0500526 var_value = "<" + var_type + " type not supported by " + \
527 func_name + "()>"
Michael Walshde791732016-09-06 14:25:24 -0500528 value_format = "%s"
529 loc_col1_indent -= 2
Michael Walsh7423c012016-10-04 10:27:21 -0500530 # Adjust loc_col1_width.
531 loc_col1_width = loc_col1_width - loc_col1_indent
Michael Walshde791732016-09-06 14:25:24 -0500532 format_string = "%" + str(loc_col1_indent) + "s%-" \
Michael Walsh7423c012016-10-04 10:27:21 -0500533 + str(loc_col1_width) + "s" + value_format + trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500534 return format_string % ("", var_name + ":", var_value)
535 return buffer
536
537 return ""
538
539###############################################################################
540
541
542###############################################################################
543def sprint_var(*args):
544
545 r"""
546 Figure out the name of the first argument for you and then call
547 sprint_varx with it. Therefore, the following 2 calls are equivalent:
548 sprint_varx("var1", var1)
549 sprint_var(var1)
550 """
551
552 # Get the name of the first variable passed to this function.
553 stack_frame = 2
Michael Walsh7423c012016-10-04 10:27:21 -0500554 caller_func_name = sprint_func_name(2)
555 if caller_func_name.endswith("print_var"):
Michael Walshde791732016-09-06 14:25:24 -0500556 stack_frame += 1
557 var_name = get_arg_name(None, 1, stack_frame)
558 return sprint_varx(var_name, *args)
559
560###############################################################################
561
562
563###############################################################################
Michael Walsh7423c012016-10-04 10:27:21 -0500564def lprint_varx(var_name,
565 var_value,
566 hex=0,
567 loc_col1_indent=col1_indent,
568 loc_col1_width=col1_width,
569 log_level=getattr(logging, 'INFO')):
570
571 r"""
572 Send sprint_varx output to logging.
573 """
574
575 logging.log(log_level, sprint_varx(var_name, var_value, hex,
576 loc_col1_indent, loc_col1_width, ""))
577
578###############################################################################
579
580
581###############################################################################
582def lprint_var(*args):
583
584 r"""
585 Figure out the name of the first argument for you and then call
586 lprint_varx with it. Therefore, the following 2 calls are equivalent:
587 lprint_varx("var1", var1)
588 lprint_var(var1)
589 """
590
591 # Get the name of the first variable passed to this function.
592 stack_frame = 2
593 caller_func_name = sprint_func_name(2)
594 if caller_func_name.endswith("print_var"):
595 stack_frame += 1
596 var_name = get_arg_name(None, 1, stack_frame)
597 lprint_varx(var_name, *args)
598
599###############################################################################
600
601
602###############################################################################
603def sprint_dashes(indent=col1_indent,
604 width=80,
605 line_feed=1,
606 char="-"):
Michael Walshde791732016-09-06 14:25:24 -0500607
608 r"""
609 Return a string of dashes to the caller.
610
611 Description of arguements:
612 indent The number of characters to indent the
613 output.
614 width The width of the string of dashes.
615 line_feed Indicates whether the output should end
616 with a line feed.
Michael Walsh7423c012016-10-04 10:27:21 -0500617 char The character to be repeated in the output
618 string.
Michael Walshde791732016-09-06 14:25:24 -0500619 """
620
Michael Walsh7423c012016-10-04 10:27:21 -0500621 width = int(width)
622 buffer = " "*int(indent) + char*width
Michael Walshde791732016-09-06 14:25:24 -0500623 if line_feed:
624 buffer += "\n"
625
626 return buffer
627
628###############################################################################
629
630
631###############################################################################
Michael Walsh7423c012016-10-04 10:27:21 -0500632def sindent(text="",
633 indent=0):
634
635 r"""
636 Pre-pend the specified number of characters to the text string (i.e.
637 indent it) and return it.
638
639 Description of arguments:
640 text The string to be indented.
641 indent The number of characters to indent the
642 string.
643 """
644
645 format_string = "%" + str(indent) + "s%s"
646 buffer = format_string % ("", text)
647
648 return buffer
649
650###############################################################################
651
652
653###############################################################################
654def sprint_call_stack(indent=0,
655 stack_frame_ix=0):
Michael Walshde791732016-09-06 14:25:24 -0500656
657 r"""
658 Return a call stack report for the given point in the program with line
659 numbers, function names and function parameters and arguments.
660
661 Sample output:
662
663 -------------------------------------------------------------------------
664 Python function call stack
665
666 Line # Function name and arguments
667 ------ ------------------------------------------------------------------
668 424 sprint_call_stack ()
669 4 print_call_stack ()
670 31 func1 (last_name = 'walsh', first_name = 'mikey')
671 59 /tmp/scr5.py
672 -------------------------------------------------------------------------
673
674 Description of arguments:
675 indent The number of characters to indent each
676 line of output.
677 stack_frame_ix The index of the first stack frame which
678 is to be returned.
679 """
680
681 buffer = ""
Michael Walsh7423c012016-10-04 10:27:21 -0500682 buffer += sprint_dashes(indent)
683 buffer += sindent("Python function call stack\n\n", indent)
684 buffer += sindent("Line # Function name and arguments\n", indent)
685 buffer += sprint_dashes(indent, 6, 0) + " " + sprint_dashes(0, 73)
Michael Walshde791732016-09-06 14:25:24 -0500686
687 # Grab the current program stack.
688 current_stack = inspect.stack()
689
690 # Process each frame in turn.
691 format_string = "%6s %s\n"
Michael Walsh7423c012016-10-04 10:27:21 -0500692 ix = 0
Michael Walshde791732016-09-06 14:25:24 -0500693 for stack_frame in current_stack:
Michael Walsh7423c012016-10-04 10:27:21 -0500694 if ix < stack_frame_ix:
695 ix += 1
696 continue
Michael Walshde791732016-09-06 14:25:24 -0500697 lineno = str(stack_frame[2])
698 func_name = str(stack_frame[3])
699 if func_name == "?":
700 # "?" is the name used when code is not in a function.
701 func_name = "(none)"
702
703 if func_name == "<module>":
Michael Walsh7423c012016-10-04 10:27:21 -0500704 # If the func_name is the "main" program, we simply get the
705 # command line call string.
Michael Walshde791732016-09-06 14:25:24 -0500706 func_and_args = ' '.join(sys.argv)
707 else:
708 # Get the program arguments.
709 arg_vals = inspect.getargvalues(stack_frame[0])
710 function_parms = arg_vals[0]
711 frame_locals = arg_vals[3]
712
Michael Walsh7423c012016-10-04 10:27:21 -0500713 args_list = []
Michael Walshde791732016-09-06 14:25:24 -0500714 for arg_name in function_parms:
715 # Get the arg value from frame locals.
716 arg_value = frame_locals[arg_name]
Michael Walsh7423c012016-10-04 10:27:21 -0500717 args_list.append(arg_name + " = " + repr(arg_value))
718 args_str = "(" + ', '.join(map(str, args_list)) + ")"
Michael Walshde791732016-09-06 14:25:24 -0500719
720 # Now we need to print this in a nicely-wrapped way.
721 func_and_args = func_name + " " + args_str
722
Michael Walsh7423c012016-10-04 10:27:21 -0500723 buffer += sindent(format_string % (lineno, func_and_args), indent)
724 ix += 1
Michael Walshde791732016-09-06 14:25:24 -0500725
Michael Walsh7423c012016-10-04 10:27:21 -0500726 buffer += sprint_dashes(indent)
Michael Walshde791732016-09-06 14:25:24 -0500727
728 return buffer
729
730###############################################################################
731
732
733###############################################################################
734def sprint_executing(stack_frame_ix=None):
735
736 r"""
737 Print a line indicating what function is executing and with what parameter
738 values. This is useful for debugging.
739
740 Sample output:
741
742 #(CDT) 2016/08/25 17:54:27 - Executing: func1 (x = 1)
743
744 Description of arguments:
745 stack_frame_ix The index of the stack frame whose
746 function info should be returned. If the
747 caller does not specifiy a value, this
748 function will set the value to 1 which is
749 the index of the caller's stack frame. If
750 the caller is the wrapper function
751 "print_executing", this function will bump
752 it up by 1.
753 """
754
755 # If user wants default stack_frame_ix.
756 if stack_frame_ix is None:
757 func_name = sys._getframe().f_code.co_name
758 caller_func_name = sys._getframe(1).f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -0500759 if caller_func_name.endswith(func_name[1:]):
Michael Walshde791732016-09-06 14:25:24 -0500760 stack_frame_ix = 2
761 else:
762 stack_frame_ix = 1
763
764 stack_frame = inspect.stack()[stack_frame_ix]
765
766 func_name = str(stack_frame[3])
767 if func_name == "?":
768 # "?" is the name used when code is not in a function.
769 func_name = "(none)"
770
771 if func_name == "<module>":
772 # If the func_name is the "main" program, we simply get the command
773 # line call string.
774 func_and_args = ' '.join(sys.argv)
775 else:
776 # Get the program arguments.
777 arg_vals = inspect.getargvalues(stack_frame[0])
778 function_parms = arg_vals[0]
779 frame_locals = arg_vals[3]
780
Michael Walsh7423c012016-10-04 10:27:21 -0500781 args_list = []
Michael Walshde791732016-09-06 14:25:24 -0500782 for arg_name in function_parms:
783 # Get the arg value from frame locals.
784 arg_value = frame_locals[arg_name]
Michael Walsh7423c012016-10-04 10:27:21 -0500785 args_list.append(arg_name + " = " + repr(arg_value))
786 args_str = "(" + ', '.join(map(str, args_list)) + ")"
Michael Walshde791732016-09-06 14:25:24 -0500787
788 # Now we need to print this in a nicely-wrapped way.
789 func_and_args = func_name + " " + args_str
790
791 return sprint_time() + "Executing: " + func_and_args + "\n"
792
793###############################################################################
794
795
796###############################################################################
Michael Walsh7423c012016-10-04 10:27:21 -0500797def sprint_pgm_header(indent=0):
Michael Walshde791732016-09-06 14:25:24 -0500798
799 r"""
800 Return a standardized header that programs should print at the beginning
801 of the run. It includes useful information like command line, pid,
802 userid, program parameters, etc.
803
Michael Walsh7423c012016-10-04 10:27:21 -0500804 Description of arguments:
805 indent The number of characters to indent each
806 line of output.
Michael Walshde791732016-09-06 14:25:24 -0500807 """
808
809 buffer = "\n"
Michael Walsh7423c012016-10-04 10:27:21 -0500810
811 buffer += sindent(sprint_time() + "Running " + pgm_name + ".\n", indent)
812 buffer += sindent(sprint_time() + "Program parameter values, etc.:\n\n",
813 indent)
814 buffer += sprint_varx("command_line", ' '.join(sys.argv), 0, indent)
815 # We want the output to show a customized name for the pid and pgid but
816 # we want it to look like a valid variable name. Therefore, we'll use
Michael Walshde791732016-09-06 14:25:24 -0500817 # pgm_name_var_name which was set when this module was imported.
Michael Walsh7423c012016-10-04 10:27:21 -0500818 buffer += sprint_varx(pgm_name_var_name + "_pid", os.getpid(), 0, indent)
819 buffer += sprint_varx(pgm_name_var_name + "_pgid", os.getpgrp(), 0, indent)
Michael Walshde791732016-09-06 14:25:24 -0500820 buffer += sprint_varx("uid", str(os.geteuid()) + " (" + os.getlogin() +
Michael Walsh7423c012016-10-04 10:27:21 -0500821 ")", 0, indent)
Michael Walshde791732016-09-06 14:25:24 -0500822 buffer += sprint_varx("gid", str(os.getgid()) + " (" +
Michael Walsh7423c012016-10-04 10:27:21 -0500823 str(grp.getgrgid(os.getgid()).gr_name) + ")", 0,
824 indent)
825 buffer += sprint_varx("host_name", socket.gethostname(), 0, indent)
826 buffer += sprint_varx("DISPLAY", os.environ['DISPLAY'], 0, indent)
Michael Walshde791732016-09-06 14:25:24 -0500827 # I want to add code to print caller's parms.
828
Michael Walsh7423c012016-10-04 10:27:21 -0500829 # __builtin__.arg_obj is created by the get_arg module function,
830 # gen_get_options.
831 try:
832 buffer += ga.sprint_args(__builtin__.arg_obj, indent)
833 except AttributeError:
834 pass
835
Michael Walshde791732016-09-06 14:25:24 -0500836 buffer += "\n"
837
838 return buffer
839
840###############################################################################
841
842
843###############################################################################
Michael Walsh7423c012016-10-04 10:27:21 -0500844def sprint_error_report(error_text="\n",
845 indent=2):
846
847 r"""
848 Return a string with a standardized report which includes the caller's
849 error text, the call stack and the program header.
850
851 Description of args:
852 error_text The error text to be included in the
853 report. The caller should include any
854 needed linefeeds.
855 indent The number of characters to indent each
856 line of output.
857 """
858
859 buffer = ""
860 buffer += sprint_dashes(width=120, char="=")
861 buffer += sprint_error(error_text)
862 buffer += "\n"
863 # Calling sprint_call_stack with stack_frame_ix of 0 causes it to show
864 # itself and this function in the call stack. This is not helpful to a
865 # debugger and is therefore clutter. We will adjust the stack_frame_ix to
866 # hide that information.
867 stack_frame_ix = 2
868 caller_func_name = sprint_func_name(2)
869 if caller_func_name.endswith("print_error_report"):
870 stack_frame_ix += 1
871 buffer += sprint_call_stack(indent, stack_frame_ix)
872 buffer += sprint_pgm_header(indent)
873 buffer += sprint_dashes(width=120, char="=")
874
875 return buffer
876
877###############################################################################
878
879
880###############################################################################
Michael Walshde791732016-09-06 14:25:24 -0500881def sissuing(cmd_buf):
882
883 r"""
884 Return a line indicating a command that the program is about to execute.
885
886 Sample output for a cmd_buf of "ls"
887
888 #(CDT) 2016/08/25 17:57:36 - Issuing: ls
889 Description of args:
890 cmd_buf The command to be executed by caller.
891 """
892
893 buffer = sprint_time() + "Issuing: " + cmd_buf + "\n"
894
895 return buffer
896
897###############################################################################
898
899
900###############################################################################
901def sprint_pgm_footer():
902
903 r"""
904 Return a standardized footer that programs should print at the end of the
905 program run. It includes useful information like total run time, etc.
906 """
907
908 buffer = "\n" + sprint_time() + "Finished running " + pgm_name + ".\n\n"
909
910 total_time = time.time() - start_time
911 total_time_string = "%0.6f" % total_time
912
Michael Walsh7423c012016-10-04 10:27:21 -0500913 buffer += sprint_varx(pgm_name_var_name + "_runtime", total_time_string)
914
915 return buffer
916
917###############################################################################
918
919
920###############################################################################
921def sprint(buffer=""):
922
923 r"""
924 Simply return the user's buffer. This function is used by the qprint and
925 dprint functions defined dynamically below, i.e. it would not normally be
926 called for general use.
927
928 Description of arguments.
929 buffer This will be returned to the caller.
930 """
Michael Walshde791732016-09-06 14:25:24 -0500931
932 return buffer
933
934###############################################################################
935
936
937###############################################################################
938# In the following section of code, we will dynamically create print versions
939# for each of the sprint functions defined above. So, for example, where we
940# have an sprint_time() function defined above that returns the time to the
Michael Walsh7423c012016-10-04 10:27:21 -0500941# caller in a string, we will create a corresponding print_time() function
942# that will print that string directly to stdout.
Michael Walshde791732016-09-06 14:25:24 -0500943
944# It can be complicated to follow what's being creaed by the exec statement
945# below. Here is an example of the print_time() function that will be created:
946
947# def print_time(*args):
948# s_funcname = "s" + sys._getframe().f_code.co_name
949# s_func = getattr(sys.modules[__name__], s_funcname)
950# sys.stdout.write(s_func(*args))
951
952# Here are comments describing the 3 lines in the body of the created function.
953# Calculate the "s" version of this function name (e.g. if this function name
954# is print_time, we want s_funcname to be "sprint_time".
955# Put a reference to the "s" version of this function in s_func.
Michael Walsh7423c012016-10-04 10:27:21 -0500956# Call the "s" version of this function passing it all of our arguments.
957# Write the result to stdout.
Michael Walshde791732016-09-06 14:25:24 -0500958
959# func_names contains a list of all print functions which should be created
960# from their sprint counterparts.
961func_names = ['print_time', 'print_timen', 'print_error', 'print_varx',
962 'print_var', 'print_dashes', 'print_call_stack',
963 'print_func_name', 'print_executing', 'print_pgm_header',
Michael Walsh7423c012016-10-04 10:27:21 -0500964 'issuing', 'print_pgm_footer', 'print_error_report', 'print']
Michael Walshde791732016-09-06 14:25:24 -0500965
966for func_name in func_names:
Michael Walsh7423c012016-10-04 10:27:21 -0500967 if func_name == "print":
968 continue
Michael Walshde791732016-09-06 14:25:24 -0500969 # Create abbreviated aliases (e.g. spvar is an alias for sprint_var).
970 alias = re.sub("print_", "p", func_name)
Michael Walsh7423c012016-10-04 10:27:21 -0500971 pgm_definition_string = "s" + alias + " = s" + func_name
972 if gen_print_debug:
973 print(pgm_definition_string)
974 exec(pgm_definition_string)
Michael Walshde791732016-09-06 14:25:24 -0500975
976for func_name in func_names:
Michael Walsh7423c012016-10-04 10:27:21 -0500977 if func_name == "print_error" or func_name == "print_error_report":
Michael Walshde791732016-09-06 14:25:24 -0500978 output_stream = "stderr"
979 else:
980 output_stream = "stdout"
981 func_def = \
982 [
983 "def " + func_name + "(*args):",
984 " s_func_name = \"s\" + sys._getframe().f_code.co_name",
985 " s_func = getattr(sys.modules[__name__], s_func_name)",
986 " sys." + output_stream + ".write(s_func(*args))",
987 " sys." + output_stream + ".flush()"
988 ]
Michael Walsh7423c012016-10-04 10:27:21 -0500989 if func_name != "print":
990 pgm_definition_string = '\n'.join(func_def)
991 if gen_print_debug:
992 print(pgm_definition_string)
993 exec(pgm_definition_string)
994
995 # Now define "q" versions of each print function.
996 func_def = \
997 [
998 "def q" + func_name + "(*args):",
999 " if __builtin__.quiet: return",
1000 " s_func_name = \"s" + func_name + "\"",
1001 " s_func = getattr(sys.modules[__name__], s_func_name)",
1002 " sys." + output_stream + ".write(s_func(*args))",
1003 " sys." + output_stream + ".flush()"
1004 ]
1005
Michael Walshde791732016-09-06 14:25:24 -05001006 pgm_definition_string = '\n'.join(func_def)
Michael Walsh7423c012016-10-04 10:27:21 -05001007 if gen_print_debug:
1008 print(pgm_definition_string)
Michael Walshde791732016-09-06 14:25:24 -05001009 exec(pgm_definition_string)
1010
Michael Walsh7423c012016-10-04 10:27:21 -05001011 # Now define "d" versions of each print function.
1012 func_def = \
1013 [
1014 "def d" + func_name + "(*args):",
1015 " if not __builtin__.debug: return",
1016 " s_func_name = \"s" + func_name + "\"",
1017 " s_func = getattr(sys.modules[__name__], s_func_name)",
1018 " sys." + output_stream + ".write(s_func(*args))",
1019 " sys." + output_stream + ".flush()"
1020 ]
1021
1022 pgm_definition_string = '\n'.join(func_def)
1023 if gen_print_debug:
1024 print(pgm_definition_string)
1025 exec(pgm_definition_string)
1026
1027 # Now define "l" versions of each print function.
1028 func_def = \
1029 [
1030 "def l" + func_name + "(*args):",
1031 " s_func_name = \"s" + func_name + "\"",
1032 " s_func = getattr(sys.modules[__name__], s_func_name)",
1033 " logging.log(getattr(logging, 'INFO'), s_func(*args))",
1034 ]
1035
1036 if func_name != "print_varx" and func_name != "print_var":
1037 pgm_definition_string = '\n'.join(func_def)
1038 if gen_print_debug:
1039 print(pgm_definition_string)
1040 exec(pgm_definition_string)
1041
1042 if func_name == "print":
1043 continue
1044
Michael Walshde791732016-09-06 14:25:24 -05001045 # Create abbreviated aliases (e.g. pvar is an alias for print_var).
1046 alias = re.sub("print_", "p", func_name)
Michael Walsh7423c012016-10-04 10:27:21 -05001047 pgm_definition_string = alias + " = " + func_name
1048 if gen_print_debug:
1049 print(pgm_definition_string)
1050 exec(pgm_definition_string)
1051
1052 # Create abbreviated aliases (e.g. qpvar is an alias for qprint_var).
1053 alias = re.sub("print_", "p", func_name)
1054 pgm_definition_string = "q" + alias + " = q" + func_name
1055 if gen_print_debug:
1056 print(pgm_definition_string)
1057 exec(pgm_definition_string)
1058
1059 # Create abbreviated aliases (e.g. dpvar is an alias for dprint_var).
1060 alias = re.sub("print_", "p", func_name)
1061 pgm_definition_string = "d" + alias + " = d" + func_name
1062 if gen_print_debug:
1063 print(pgm_definition_string)
1064 exec(pgm_definition_string)
1065
1066 # Create abbreviated aliases (e.g. lpvar is an alias for lprint_var).
1067 alias = re.sub("print_", "p", func_name)
1068 pgm_definition_string = "l" + alias + " = l" + func_name
1069 if gen_print_debug:
1070 print(pgm_definition_string)
1071 exec(pgm_definition_string)
Michael Walshde791732016-09-06 14:25:24 -05001072
1073###############################################################################