blob: 996a28959297921d10d6242e98941cc865866b7e [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
20running_from_robot = 1
21try:
22 from robot.utils import DotDict
23except ImportError:
24 running_from_robot = 0
Michael Walsh7423c012016-10-04 10:27:21 -050025
26import gen_arg as ga
Michael Walshde791732016-09-06 14:25:24 -050027
28# Setting these variables for use both inside this module and by programs
29# importing this module.
30pgm_dir_path = sys.argv[0]
31pgm_name = os.path.basename(pgm_dir_path)
Michael Walsh7423c012016-10-04 10:27:21 -050032pgm_dir_name = re.sub("/" + pgm_name, "", pgm_dir_path) + "/"
33
Michael Walshde791732016-09-06 14:25:24 -050034
35# Some functions (e.g. sprint_pgm_header) have need of a program name value
36# that looks more like a valid variable name. Therefore, we'll swap odd
37# characters like "." out for underscores.
38pgm_name_var_name = pgm_name.replace(".", "_")
39
40# Initialize global values used as defaults by print_time, print_var, etc.
41col1_indent = 0
42
43# Calculate default column width for print_var functions based on environment
44# variable settings. The objective is to make the variable values line up
45# nicely with the time stamps.
46col1_width = 29
47if 'NANOSECONDS' in os.environ:
48 NANOSECONDS = os.environ['NANOSECONDS']
49else:
50 NANOSECONDS = 0
51
52if NANOSECONDS == "1":
53 col1_width = col1_width + 7
54
55if 'SHOW_ELAPSED_TIME' in os.environ:
56 SHOW_ELAPSED_TIME = os.environ['SHOW_ELAPSED_TIME']
57else:
58 SHOW_ELAPSED_TIME = 0
59
60if SHOW_ELAPSED_TIME == "1":
61 if NANOSECONDS == "1":
62 col1_width = col1_width + 14
63 else:
64 col1_width = col1_width + 7
65
66# Initialize some time variables used in module functions.
67start_time = time.time()
68sprint_time_last_seconds = start_time
69
Michael Walsh7423c012016-10-04 10:27:21 -050070try:
71 # The user can set environment variable "GEN_PRINT_DEBUG" to get debug
72 # output from this module.
73 gen_print_debug = os.environ['GEN_PRINT_DEBUG']
74except KeyError:
75 gen_print_debug = 0
76
Michael Walshde791732016-09-06 14:25:24 -050077
78###############################################################################
79def sprint_func_name(stack_frame_ix=None):
80
81 r"""
82 Return the function name associated with the indicated stack frame.
83
84 Description of arguments:
85 stack_frame_ix The index of the stack frame whose
86 function name should be returned. If the
87 caller does not specifiy a value, this
88 function will set the value to 1 which is
89 the index of the caller's stack frame. If
90 the caller is the wrapper function
91 "print_func_name", this function will bump
92 it up by 1.
93 """
94
95 # If user specified no stack_frame_ix, we'll set it to a proper default
96 # value.
97 if stack_frame_ix is None:
98 func_name = sys._getframe().f_code.co_name
99 caller_func_name = sys._getframe(1).f_code.co_name
100 if func_name[1:] == caller_func_name:
101 stack_frame_ix = 2
102 else:
103 stack_frame_ix = 1
104
105 func_name = sys._getframe(stack_frame_ix).f_code.co_name
106
107 return func_name
108
109###############################################################################
110
111
112# get_arg_name is not a print function per se. I have included it in this
113# module because it is used by sprint_var which is found in this module.
114###############################################################################
115def get_arg_name(var,
116 arg_num=1,
117 stack_frame_ix=1):
118
119 r"""
120 Return the "name" of an argument passed to a function. This could be a
121 literal or a variable name.
122
123 Description of arguements:
124 var The variable whose name you want returned.
125 arg_num The arg number (1 through n) whose name
126 you wish to have returned. This value
127 should not exceed the number of arguments
128 allowed by the target function.
129 stack_frame_ix The stack frame index of the target
130 function. This value must be 1 or
131 greater. 1 would indicate get_arg_name's
132 stack frame. 2 would be the caller of
133 get_arg_name's stack frame, etc.
134
135 Example 1:
136
137 my_var = "mike"
138 var_name = get_arg_name(my_var)
139
140 In this example, var_name will receive the value "my_var".
141
142 Example 2:
143
144 def test1(var):
145 # Getting the var name of the first arg to this function, test1.
146 # Note, in this case, it doesn't matter what you pass as the first arg
147 # to get_arg_name since it is the caller's variable name that matters.
148 dummy = 1
149 arg_num = 1
150 stack_frame = 2
151 var_name = get_arg_name(dummy, arg_num, stack_frame)
152
153 # Mainline...
154
155 another_var = "whatever"
156 test1(another_var)
157
158 In this example, var_name will be set to "another_var".
159
160 """
161
162 # Note: I wish to avoid recursion so I refrain from calling any function
163 # that calls this function (i.e. sprint_var, valid_value, etc.).
164
165 try:
166 # The user can set environment variable "GET_ARG_NAME_DEBUG" to get
167 # debug output from this function.
168 local_debug = os.environ['GET_ARG_NAME_DEBUG']
169 except KeyError:
170 local_debug = 0
171
172 if arg_num < 1:
173 print_error("Programmer error - Variable \"arg_num\" has an invalid" +
174 " value of \"" + str(arg_num) + "\". The value must be" +
175 " an integer that is greater than 0.\n")
176 # What is the best way to handle errors? Raise exception? I'll
177 # revisit later.
178 return
179 if stack_frame_ix < 1:
180 print_error("Programmer error - Variable \"stack_frame_ix\" has an" +
181 " invalid value of \"" + str(stack_frame_ix) + "\". The" +
182 " value must be an integer that is greater than or equal" +
183 " to 1.\n")
184 return
185
186 if local_debug:
187 debug_indent = 2
188 print(sprint_func_name() + "() parms:")
189 print_varx("var", var, 0, debug_indent)
190 print_varx("arg_num", arg_num, 0, debug_indent)
191 print_varx("stack_frame_ix", stack_frame_ix, 0, debug_indent)
192
193 try:
194 frame, filename, cur_line_no, function_name, lines, index = \
195 inspect.stack()[stack_frame_ix]
196 except IndexError:
197 print_error("Programmer error - The caller has asked for information" +
198 " about the stack frame at index \"" +
199 str(stack_frame_ix) + "\". However, the stack only" +
200 " contains " + str(len(inspect.stack())) + " entries." +
201 " Therefore the stack frame index is out of range.\n")
202 return
203
204 if local_debug:
205 print("\nVariables retrieved from inspect.stack() function:")
206 print_varx("frame", frame, 0, debug_indent)
207 print_varx("filename", filename, 0, debug_indent)
208 print_varx("cur_line_no", cur_line_no, 0, debug_indent)
209 print_varx("function_name", function_name, 0, debug_indent)
210 print_varx("lines", lines, 0, debug_indent)
211 print_varx("index", index, 0, debug_indent)
212
213 composite_line = lines[0].strip()
214
215 called_func_name = sprint_func_name(stack_frame_ix)
Michael Walshbec416d2016-11-10 08:54:52 -0600216 # Needed to add a right anchor to func_regex for cases like this where
217 # there is an arg whose name is a substring of the function name. So the
218 # function name needs to be bounded on the right by zero or more spaces
219 # and a left parenthesis.
220 # if not valid_value(whatever, valid_values=["one", "two"]):
221 func_regex = ".*" + called_func_name + "[ ]*\("
222 # if not re.match(r".*" + called_func_name, composite_line):
223 if not re.match(func_regex, composite_line):
Michael Walsh7423c012016-10-04 10:27:21 -0500224 # The called function name was not found in the composite line. The
225 # caller may be using a function alias.
226 # I added code to handle pvar, qpvar, dpvar, etc. aliases.
227 # pvar is an alias for print_var. However, when it is used,
228 # sprint_func_name() returns the non-alias version, i.e. "print_var".
229 # Adjusting for that here.
230 alias = re.sub("print_var", "pvar", called_func_name)
Michael Walshbec416d2016-11-10 08:54:52 -0600231 if local_debug:
232 print_varx("alias", alias, 0, debug_indent)
Michael Walsh7423c012016-10-04 10:27:21 -0500233 called_func_name = alias
Michael Walshbec416d2016-11-10 08:54:52 -0600234 func_regex = ".*" + called_func_name + "[ ]*\("
Michael Walsh7423c012016-10-04 10:27:21 -0500235
Michael Walshbec416d2016-11-10 08:54:52 -0600236 # arg_list_etc = re.sub(".*" + called_func_name, "", composite_line)
237 arg_list_etc = "(" + re.sub(func_regex, "", composite_line)
Michael Walshde791732016-09-06 14:25:24 -0500238 if local_debug:
Michael Walshbec416d2016-11-10 08:54:52 -0600239 print_varx("func_regex", func_regex, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500240 print_varx("called_func_name", called_func_name, 0, debug_indent)
241 print_varx("composite_line", composite_line, 0, debug_indent)
242 print_varx("arg_list_etc", arg_list_etc, 0, debug_indent)
243
244 # Parse arg list...
245 # Initialize...
246 nest_level = -1
247 arg_ix = 0
Michael Walsh7423c012016-10-04 10:27:21 -0500248 args_list = [""]
Michael Walshde791732016-09-06 14:25:24 -0500249 for ix in range(0, len(arg_list_etc)):
250 char = arg_list_etc[ix]
251 # Set the nest_level based on whether we've encounted a parenthesis.
252 if char == "(":
253 nest_level += 1
254 if nest_level == 0:
255 continue
256 elif char == ")":
257 nest_level -= 1
258 if nest_level < 0:
259 break
260
261 # If we reach a comma at base nest level, we are done processing an
Michael Walsh7423c012016-10-04 10:27:21 -0500262 # argument so we increment arg_ix and initialize a new args_list entry.
Michael Walshde791732016-09-06 14:25:24 -0500263 if char == "," and nest_level == 0:
264 arg_ix += 1
Michael Walsh7423c012016-10-04 10:27:21 -0500265 args_list.append("")
Michael Walshde791732016-09-06 14:25:24 -0500266 continue
267
Michael Walsh7423c012016-10-04 10:27:21 -0500268 # For any other character, we append it it to the current arg list
Michael Walshde791732016-09-06 14:25:24 -0500269 # entry.
Michael Walsh7423c012016-10-04 10:27:21 -0500270 args_list[arg_ix] += char
Michael Walshde791732016-09-06 14:25:24 -0500271
272 # Trim whitespace from each list entry.
Michael Walsh7423c012016-10-04 10:27:21 -0500273 args_list = [arg.strip() for arg in args_list]
Michael Walshde791732016-09-06 14:25:24 -0500274
Michael Walsh7423c012016-10-04 10:27:21 -0500275 if arg_num > len(args_list):
Michael Walshde791732016-09-06 14:25:24 -0500276 print_error("Programmer error - The caller has asked for the name of" +
277 " argument number \"" + str(arg_num) + "\" but there " +
Michael Walsh7423c012016-10-04 10:27:21 -0500278 "were only \"" + str(len(args_list)) + "\" args used:\n" +
279 sprint_varx("args_list", args_list))
Michael Walshde791732016-09-06 14:25:24 -0500280 return
281
Michael Walsh7423c012016-10-04 10:27:21 -0500282 argument = args_list[arg_num - 1]
Michael Walshde791732016-09-06 14:25:24 -0500283
284 if local_debug:
Michael Walsh7423c012016-10-04 10:27:21 -0500285 print_varx("args_list", args_list, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500286 print_varx("argument", argument, 0, debug_indent)
287
288 return argument
289
290###############################################################################
291
292
293###############################################################################
294def sprint_time(buffer=""):
295
296 r"""
297 Return the time in the following format.
298
299 Example:
300
301 The following python code...
302
303 sys.stdout.write(sprint_time())
304 sys.stdout.write("Hi.\n")
305
306 Will result in the following type of output:
307
308 #(CDT) 2016/07/08 15:25:35 - Hi.
309
310 Example:
311
312 The following python code...
313
314 sys.stdout.write(sprint_time("Hi.\n"))
315
316 Will result in the following type of output:
317
318 #(CDT) 2016/08/03 17:12:05 - Hi.
319
320 The following environment variables will affect the formatting as
321 described:
322 NANOSECONDS This will cause the time stamps to be
323 precise to the microsecond (Yes, it
324 probably should have been named
325 MICROSECONDS but the convention was set
326 long ago so we're sticking with it).
327 Example of the output when environment
328 variable NANOSECONDS=1.
329
330 #(CDT) 2016/08/03 17:16:25.510469 - Hi.
331
332 SHOW_ELAPSED_TIME This will cause the elapsed time to be
333 included in the output. This is the
334 amount of time that has elapsed since the
335 last time this function was called. The
336 precision of the elapsed time field is
337 also affected by the value of the
338 NANOSECONDS environment variable. Example
339 of the output when environment variable
340 NANOSECONDS=0 and SHOW_ELAPSED_TIME=1.
341
342 #(CDT) 2016/08/03 17:17:40 - 0 - Hi.
343
344 Example of the output when environment variable NANOSECONDS=1 and
345 SHOW_ELAPSED_TIME=1.
346
347 #(CDT) 2016/08/03 17:18:47.317339 - 0.000046 - Hi.
348
349 Description of arguments.
350 buffer This will be appended to the formatted
351 time string.
352 """
353
354 global NANOSECONDS
355 global SHOW_ELAPSED_TIME
356 global sprint_time_last_seconds
357
358 seconds = time.time()
359 loc_time = time.localtime(seconds)
360 nanoseconds = "%0.6f" % seconds
361 pos = nanoseconds.find(".")
362 nanoseconds = nanoseconds[pos:]
363
364 time_string = time.strftime("#(%Z) %Y/%m/%d %H:%M:%S", loc_time)
365 if NANOSECONDS == "1":
366 time_string = time_string + nanoseconds
367
368 if SHOW_ELAPSED_TIME == "1":
369 cur_time_seconds = seconds
370 math_string = "%9.9f" % cur_time_seconds + " - " + "%9.9f" % \
371 sprint_time_last_seconds
372 elapsed_seconds = eval(math_string)
373 if NANOSECONDS == "1":
374 elapsed_seconds = "%11.6f" % elapsed_seconds
375 else:
376 elapsed_seconds = "%4i" % elapsed_seconds
377 sprint_time_last_seconds = cur_time_seconds
378 time_string = time_string + " - " + elapsed_seconds
379
380 return time_string + " - " + buffer
381
382###############################################################################
383
384
385###############################################################################
386def sprint_timen(buffer=""):
387
388 r"""
389 Append a line feed to the buffer, pass it to sprint_time and return the
390 result.
391 """
392
393 return sprint_time(buffer + "\n")
394
395###############################################################################
396
397
398###############################################################################
399def sprint_error(buffer=""):
400
401 r"""
402 Return a standardized error string. This includes:
403 - A time stamp
404 - The "**ERROR**" string
405 - The caller's buffer string.
406
407 Example:
408
409 The following python code...
410
411 print(sprint_error("Oops.\n"))
412
413 Will result in the following type of output:
414
415 #(CDT) 2016/08/03 17:12:05 - **ERROR** Oops.
416
417 Description of arguments.
418 buffer This will be appended to the formatted
419 error string.
420 """
421
422 return sprint_time() + "**ERROR** " + buffer
423
424###############################################################################
425
426
427###############################################################################
428def sprint_varx(var_name,
429 var_value,
430 hex=0,
431 loc_col1_indent=col1_indent,
Michael Walsh7423c012016-10-04 10:27:21 -0500432 loc_col1_width=col1_width,
433 trailing_char="\n"):
Michael Walshde791732016-09-06 14:25:24 -0500434
435 r"""
436 Print the var name/value passed to it. If the caller lets loc_col1_width
437 default, the printing lines up nicely with output generated by the
438 print_time functions.
439
440 Note that the sprint_var function (defined below) can be used to call this
441 function so that the programmer does not need to pass the var_name.
442 sprint_var will figure out the var_name. The sprint_var function is the
443 one that would normally be used by the general user.
444
445 For example, the following python code:
446
447 first_name = "Mike"
448 print_time("Doing this...\n")
449 print_varx("first_name", first_name)
450 print_time("Doing that...\n")
451
452 Will generate output like this:
453
454 #(CDT) 2016/08/10 17:34:42.847374 - 0.001285 - Doing this...
455 first_name: Mike
456 #(CDT) 2016/08/10 17:34:42.847510 - 0.000136 - Doing that...
457
458 This function recognizes several complex types of data such as dict, list
459 or tuple.
460
461 For example, the following python code:
462
463 my_dict = dict(one=1, two=2, three=3)
464 print_var(my_dict)
465
466 Will generate the following output:
467
468 my_dict:
469 my_dict[three]: 3
470 my_dict[two]: 2
471 my_dict[one]: 1
472
473 Description of arguments.
474 var_name The name of the variable to be printed.
475 var_value The value of the variable to be printed.
476 hex This indicates that the value should be
477 printed in hex format. It is the user's
478 responsibility to ensure that a var_value
Michael Walshbec416d2016-11-10 08:54:52 -0600479 contains a valid hex number. For string
480 var_values, this will be interpreted as
481 show_blanks which means that blank values
482 will be printed as "<blank>".
Michael Walshde791732016-09-06 14:25:24 -0500483 loc_col1_indent The number of spaces to indent the output.
484 loc_col1_width The width of the output column containing
485 the variable name. The default value of
486 this is adjusted so that the var_value
487 lines up with text printed via the
488 print_time function.
Michael Walsh7423c012016-10-04 10:27:21 -0500489 trailing_char The character to be used at the end of the
490 returned string. The default value is a
491 line feed.
492 """
Michael Walshde791732016-09-06 14:25:24 -0500493
494 # Determine the type
495 if type(var_value) in (int, float, bool, str, unicode) \
496 or var_value is None:
497 # The data type is simple in the sense that it has no subordinate
498 # parts.
Michael Walsh7423c012016-10-04 10:27:21 -0500499 # Adjust loc_col1_width.
500 loc_col1_width = loc_col1_width - loc_col1_indent
Michael Walshde791732016-09-06 14:25:24 -0500501 # See if the user wants the output in hex format.
502 if hex:
Michael Walsh18176322016-11-15 15:11:21 -0600503 if type(var_value) not in (int, long):
Michael Walshbec416d2016-11-10 08:54:52 -0600504 value_format = "%s"
505 if var_value is "":
506 var_value = "<blank>"
507 else:
508 value_format = "0x%08x"
Michael Walshde791732016-09-06 14:25:24 -0500509 else:
510 value_format = "%s"
511 format_string = "%" + str(loc_col1_indent) + "s%-" \
Michael Walsh7423c012016-10-04 10:27:21 -0500512 + str(loc_col1_width) + "s" + value_format + trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500513 return format_string % ("", var_name + ":", var_value)
514 else:
515 # The data type is complex in the sense that it has subordinate parts.
516 format_string = "%" + str(loc_col1_indent) + "s%s\n"
517 buffer = format_string % ("", var_name + ":")
518 loc_col1_indent += 2
Michael Walsh7423c012016-10-04 10:27:21 -0500519 try:
520 length = len(var_value)
521 except TypeError:
522 pass
523 ix = 0
524 loc_trailing_char = "\n"
Michael Walshbec416d2016-11-10 08:54:52 -0600525 type_is_dict = 0
526 try:
527 if type(var_value) in (dict, collections.OrderedDict):
528 type_is_dict = 1
529 except AttributeError:
530 type_is_dict = 0
531 if running_from_robot:
532 if type(var_value) is DotDict:
533 type_is_dict = 1
534 if type_is_dict:
Michael Walshde791732016-09-06 14:25:24 -0500535 for key, value in var_value.iteritems():
Michael Walsh7423c012016-10-04 10:27:21 -0500536 ix += 1
537 if ix == length:
538 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500539 buffer += sprint_varx(var_name + "[" + key + "]", value, hex,
Michael Walsh7423c012016-10-04 10:27:21 -0500540 loc_col1_indent, loc_col1_width,
541 loc_trailing_char)
542 elif type(var_value) in (list, tuple, set):
Michael Walshde791732016-09-06 14:25:24 -0500543 for key, value in enumerate(var_value):
Michael Walsh7423c012016-10-04 10:27:21 -0500544 ix += 1
545 if ix == length:
546 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500547 buffer += sprint_varx(var_name + "[" + str(key) + "]", value,
Michael Walsh7423c012016-10-04 10:27:21 -0500548 hex, loc_col1_indent, loc_col1_width,
549 loc_trailing_char)
Michael Walshde791732016-09-06 14:25:24 -0500550 elif type(var_value) is argparse.Namespace:
551 for key in var_value.__dict__:
Michael Walsh7423c012016-10-04 10:27:21 -0500552 ix += 1
553 if ix == length:
554 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500555 cmd_buf = "buffer += sprint_varx(var_name + \".\" + str(key)" \
Michael Walsh7423c012016-10-04 10:27:21 -0500556 + ", var_value." + key + ", hex, loc_col1_indent," \
557 + " loc_col1_width, loc_trailing_char)"
Michael Walshde791732016-09-06 14:25:24 -0500558 exec(cmd_buf)
559 else:
560 var_type = type(var_value).__name__
561 func_name = sys._getframe().f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -0500562 var_value = "<" + var_type + " type not supported by " + \
563 func_name + "()>"
Michael Walshde791732016-09-06 14:25:24 -0500564 value_format = "%s"
565 loc_col1_indent -= 2
Michael Walsh7423c012016-10-04 10:27:21 -0500566 # Adjust loc_col1_width.
567 loc_col1_width = loc_col1_width - loc_col1_indent
Michael Walshde791732016-09-06 14:25:24 -0500568 format_string = "%" + str(loc_col1_indent) + "s%-" \
Michael Walsh7423c012016-10-04 10:27:21 -0500569 + str(loc_col1_width) + "s" + value_format + trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500570 return format_string % ("", var_name + ":", var_value)
571 return buffer
572
573 return ""
574
575###############################################################################
576
577
578###############################################################################
579def sprint_var(*args):
580
581 r"""
582 Figure out the name of the first argument for you and then call
583 sprint_varx with it. Therefore, the following 2 calls are equivalent:
584 sprint_varx("var1", var1)
585 sprint_var(var1)
586 """
587
588 # Get the name of the first variable passed to this function.
589 stack_frame = 2
Michael Walsh7423c012016-10-04 10:27:21 -0500590 caller_func_name = sprint_func_name(2)
591 if caller_func_name.endswith("print_var"):
Michael Walshde791732016-09-06 14:25:24 -0500592 stack_frame += 1
593 var_name = get_arg_name(None, 1, stack_frame)
594 return sprint_varx(var_name, *args)
595
596###############################################################################
597
598
599###############################################################################
Michael Walsh18176322016-11-15 15:11:21 -0600600def sprint_vars(*args):
601
602 r"""
603 Sprint the values of one or more variables.
604
605 Description of args:
606 args:
607 If the first argument is an integer, it will be interpreted to be the
608 "indent" value.
609 If the second argument is an integer, it will be interpreted to be the
610 "col1_width" value.
611 If the third argument is an integer, it will be interpreted to be the
612 "hex" value.
613 All remaining parms are considered variable names which are to be
614 sprinted.
615 """
616
617 if len(args) == 0:
618 return
619
620 # Get the name of the first variable passed to this function.
621 stack_frame = 2
622 caller_func_name = sprint_func_name(2)
623 if caller_func_name.endswith("print_vars"):
624 stack_frame += 1
625
626 parm_num = 1
627
628 # Create list from args (which is a tuple) so that it can be modified.
629 args_list = list(args)
630
631 var_name = get_arg_name(None, parm_num, stack_frame)
632 # See if parm 1 is to be interpreted as "indent".
633 try:
634 if type(int(var_name)) is int:
635 indent = int(var_name)
636 args_list.pop(0)
637 parm_num += 1
638 except ValueError:
639 indent = 0
640
641 var_name = get_arg_name(None, parm_num, stack_frame)
642 # See if parm 1 is to be interpreted as "col1_width".
643 try:
644 if type(int(var_name)) is int:
645 loc_col1_width = int(var_name)
646 args_list.pop(0)
647 parm_num += 1
648 except ValueError:
649 loc_col1_width = col1_width
650
651 var_name = get_arg_name(None, parm_num, stack_frame)
652 # See if parm 1 is to be interpreted as "hex".
653 try:
654 if type(int(var_name)) is int:
655 hex = int(var_name)
656 args_list.pop(0)
657 parm_num += 1
658 except ValueError:
659 hex = 0
660
661 buffer = ""
662 for var_value in args_list:
663 var_name = get_arg_name(None, parm_num, stack_frame)
664 buffer += sprint_varx(var_name, var_value, hex, indent, loc_col1_width)
665 parm_num += 1
666
667 return buffer
668
669###############################################################################
670
671
672###############################################################################
Michael Walsh7423c012016-10-04 10:27:21 -0500673def lprint_varx(var_name,
674 var_value,
675 hex=0,
676 loc_col1_indent=col1_indent,
677 loc_col1_width=col1_width,
678 log_level=getattr(logging, 'INFO')):
679
680 r"""
681 Send sprint_varx output to logging.
682 """
683
684 logging.log(log_level, sprint_varx(var_name, var_value, hex,
685 loc_col1_indent, loc_col1_width, ""))
686
687###############################################################################
688
689
690###############################################################################
691def lprint_var(*args):
692
693 r"""
694 Figure out the name of the first argument for you and then call
695 lprint_varx with it. Therefore, the following 2 calls are equivalent:
696 lprint_varx("var1", var1)
697 lprint_var(var1)
698 """
699
700 # Get the name of the first variable passed to this function.
701 stack_frame = 2
702 caller_func_name = sprint_func_name(2)
703 if caller_func_name.endswith("print_var"):
704 stack_frame += 1
705 var_name = get_arg_name(None, 1, stack_frame)
706 lprint_varx(var_name, *args)
707
708###############################################################################
709
710
711###############################################################################
712def sprint_dashes(indent=col1_indent,
713 width=80,
714 line_feed=1,
715 char="-"):
Michael Walshde791732016-09-06 14:25:24 -0500716
717 r"""
718 Return a string of dashes to the caller.
719
720 Description of arguements:
721 indent The number of characters to indent the
722 output.
723 width The width of the string of dashes.
724 line_feed Indicates whether the output should end
725 with a line feed.
Michael Walsh7423c012016-10-04 10:27:21 -0500726 char The character to be repeated in the output
727 string.
Michael Walshde791732016-09-06 14:25:24 -0500728 """
729
Michael Walsh7423c012016-10-04 10:27:21 -0500730 width = int(width)
731 buffer = " "*int(indent) + char*width
Michael Walshde791732016-09-06 14:25:24 -0500732 if line_feed:
733 buffer += "\n"
734
735 return buffer
736
737###############################################################################
738
739
740###############################################################################
Michael Walsh7423c012016-10-04 10:27:21 -0500741def sindent(text="",
742 indent=0):
743
744 r"""
745 Pre-pend the specified number of characters to the text string (i.e.
746 indent it) and return it.
747
748 Description of arguments:
749 text The string to be indented.
750 indent The number of characters to indent the
751 string.
752 """
753
754 format_string = "%" + str(indent) + "s%s"
755 buffer = format_string % ("", text)
756
757 return buffer
758
759###############################################################################
760
761
762###############################################################################
763def sprint_call_stack(indent=0,
764 stack_frame_ix=0):
Michael Walshde791732016-09-06 14:25:24 -0500765
766 r"""
767 Return a call stack report for the given point in the program with line
768 numbers, function names and function parameters and arguments.
769
770 Sample output:
771
772 -------------------------------------------------------------------------
773 Python function call stack
774
775 Line # Function name and arguments
776 ------ ------------------------------------------------------------------
777 424 sprint_call_stack ()
778 4 print_call_stack ()
779 31 func1 (last_name = 'walsh', first_name = 'mikey')
780 59 /tmp/scr5.py
781 -------------------------------------------------------------------------
782
783 Description of arguments:
784 indent The number of characters to indent each
785 line of output.
786 stack_frame_ix The index of the first stack frame which
787 is to be returned.
788 """
789
790 buffer = ""
Michael Walsh7423c012016-10-04 10:27:21 -0500791 buffer += sprint_dashes(indent)
792 buffer += sindent("Python function call stack\n\n", indent)
793 buffer += sindent("Line # Function name and arguments\n", indent)
794 buffer += sprint_dashes(indent, 6, 0) + " " + sprint_dashes(0, 73)
Michael Walshde791732016-09-06 14:25:24 -0500795
796 # Grab the current program stack.
797 current_stack = inspect.stack()
798
799 # Process each frame in turn.
800 format_string = "%6s %s\n"
Michael Walsh7423c012016-10-04 10:27:21 -0500801 ix = 0
Michael Walshde791732016-09-06 14:25:24 -0500802 for stack_frame in current_stack:
Michael Walsh7423c012016-10-04 10:27:21 -0500803 if ix < stack_frame_ix:
804 ix += 1
805 continue
Michael Walshde791732016-09-06 14:25:24 -0500806 lineno = str(stack_frame[2])
807 func_name = str(stack_frame[3])
808 if func_name == "?":
809 # "?" is the name used when code is not in a function.
810 func_name = "(none)"
811
812 if func_name == "<module>":
Michael Walsh7423c012016-10-04 10:27:21 -0500813 # If the func_name is the "main" program, we simply get the
814 # command line call string.
Michael Walshde791732016-09-06 14:25:24 -0500815 func_and_args = ' '.join(sys.argv)
816 else:
817 # Get the program arguments.
818 arg_vals = inspect.getargvalues(stack_frame[0])
819 function_parms = arg_vals[0]
820 frame_locals = arg_vals[3]
821
Michael Walsh7423c012016-10-04 10:27:21 -0500822 args_list = []
Michael Walshde791732016-09-06 14:25:24 -0500823 for arg_name in function_parms:
824 # Get the arg value from frame locals.
825 arg_value = frame_locals[arg_name]
Michael Walsh7423c012016-10-04 10:27:21 -0500826 args_list.append(arg_name + " = " + repr(arg_value))
827 args_str = "(" + ', '.join(map(str, args_list)) + ")"
Michael Walshde791732016-09-06 14:25:24 -0500828
829 # Now we need to print this in a nicely-wrapped way.
830 func_and_args = func_name + " " + args_str
831
Michael Walsh7423c012016-10-04 10:27:21 -0500832 buffer += sindent(format_string % (lineno, func_and_args), indent)
833 ix += 1
Michael Walshde791732016-09-06 14:25:24 -0500834
Michael Walsh7423c012016-10-04 10:27:21 -0500835 buffer += sprint_dashes(indent)
Michael Walshde791732016-09-06 14:25:24 -0500836
837 return buffer
838
839###############################################################################
840
841
842###############################################################################
843def sprint_executing(stack_frame_ix=None):
844
845 r"""
846 Print a line indicating what function is executing and with what parameter
847 values. This is useful for debugging.
848
849 Sample output:
850
851 #(CDT) 2016/08/25 17:54:27 - Executing: func1 (x = 1)
852
853 Description of arguments:
854 stack_frame_ix The index of the stack frame whose
855 function info should be returned. If the
856 caller does not specifiy a value, this
857 function will set the value to 1 which is
858 the index of the caller's stack frame. If
859 the caller is the wrapper function
860 "print_executing", this function will bump
861 it up by 1.
862 """
863
864 # If user wants default stack_frame_ix.
865 if stack_frame_ix is None:
866 func_name = sys._getframe().f_code.co_name
867 caller_func_name = sys._getframe(1).f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -0500868 if caller_func_name.endswith(func_name[1:]):
Michael Walshde791732016-09-06 14:25:24 -0500869 stack_frame_ix = 2
870 else:
871 stack_frame_ix = 1
872
873 stack_frame = inspect.stack()[stack_frame_ix]
874
875 func_name = str(stack_frame[3])
876 if func_name == "?":
877 # "?" is the name used when code is not in a function.
878 func_name = "(none)"
879
880 if func_name == "<module>":
881 # If the func_name is the "main" program, we simply get the command
882 # line call string.
883 func_and_args = ' '.join(sys.argv)
884 else:
885 # Get the program arguments.
886 arg_vals = inspect.getargvalues(stack_frame[0])
887 function_parms = arg_vals[0]
888 frame_locals = arg_vals[3]
889
Michael Walsh7423c012016-10-04 10:27:21 -0500890 args_list = []
Michael Walshde791732016-09-06 14:25:24 -0500891 for arg_name in function_parms:
892 # Get the arg value from frame locals.
893 arg_value = frame_locals[arg_name]
Michael Walsh7423c012016-10-04 10:27:21 -0500894 args_list.append(arg_name + " = " + repr(arg_value))
895 args_str = "(" + ', '.join(map(str, args_list)) + ")"
Michael Walshde791732016-09-06 14:25:24 -0500896
897 # Now we need to print this in a nicely-wrapped way.
898 func_and_args = func_name + " " + args_str
899
900 return sprint_time() + "Executing: " + func_and_args + "\n"
901
902###############################################################################
903
904
905###############################################################################
Michael Walshbec416d2016-11-10 08:54:52 -0600906def sprint_pgm_header(indent=0,
907 linefeed=1):
Michael Walshde791732016-09-06 14:25:24 -0500908
909 r"""
910 Return a standardized header that programs should print at the beginning
911 of the run. It includes useful information like command line, pid,
912 userid, program parameters, etc.
913
Michael Walsh7423c012016-10-04 10:27:21 -0500914 Description of arguments:
915 indent The number of characters to indent each
916 line of output.
Michael Walshbec416d2016-11-10 08:54:52 -0600917 linefeed Indicates whether a line feed be included
918 at the beginning and end of the report.
Michael Walshde791732016-09-06 14:25:24 -0500919 """
920
Michael Walshbec416d2016-11-10 08:54:52 -0600921 loc_col1_width = col1_width + indent
922
923 buffer = ""
924 if linefeed:
925 buffer = "\n"
Michael Walsh7423c012016-10-04 10:27:21 -0500926
927 buffer += sindent(sprint_time() + "Running " + pgm_name + ".\n", indent)
928 buffer += sindent(sprint_time() + "Program parameter values, etc.:\n\n",
929 indent)
Michael Walshbec416d2016-11-10 08:54:52 -0600930 buffer += sprint_varx("command_line", ' '.join(sys.argv), 0, indent,
931 loc_col1_width)
Michael Walsh7423c012016-10-04 10:27:21 -0500932 # We want the output to show a customized name for the pid and pgid but
933 # we want it to look like a valid variable name. Therefore, we'll use
Michael Walshde791732016-09-06 14:25:24 -0500934 # pgm_name_var_name which was set when this module was imported.
Michael Walshbec416d2016-11-10 08:54:52 -0600935 buffer += sprint_varx(pgm_name_var_name + "_pid", os.getpid(), 0, indent,
936 loc_col1_width)
937 buffer += sprint_varx(pgm_name_var_name + "_pgid", os.getpgrp(), 0, indent,
938 loc_col1_width)
Michael Walshde791732016-09-06 14:25:24 -0500939 buffer += sprint_varx("uid", str(os.geteuid()) + " (" + os.getlogin() +
Michael Walshbec416d2016-11-10 08:54:52 -0600940 ")", 0, indent, loc_col1_width)
Michael Walshde791732016-09-06 14:25:24 -0500941 buffer += sprint_varx("gid", str(os.getgid()) + " (" +
Michael Walsh7423c012016-10-04 10:27:21 -0500942 str(grp.getgrgid(os.getgid()).gr_name) + ")", 0,
Michael Walshbec416d2016-11-10 08:54:52 -0600943 indent, loc_col1_width)
944 buffer += sprint_varx("host_name", socket.gethostname(), 0, indent,
945 loc_col1_width)
946 buffer += sprint_varx("DISPLAY", os.environ['DISPLAY'], 0, indent,
947 loc_col1_width)
Michael Walshde791732016-09-06 14:25:24 -0500948 # I want to add code to print caller's parms.
949
Michael Walsh7423c012016-10-04 10:27:21 -0500950 # __builtin__.arg_obj is created by the get_arg module function,
951 # gen_get_options.
952 try:
953 buffer += ga.sprint_args(__builtin__.arg_obj, indent)
954 except AttributeError:
955 pass
956
Michael Walshbec416d2016-11-10 08:54:52 -0600957 if linefeed:
958 buffer += "\n"
Michael Walshde791732016-09-06 14:25:24 -0500959
960 return buffer
961
962###############################################################################
963
964
965###############################################################################
Michael Walsh7423c012016-10-04 10:27:21 -0500966def sprint_error_report(error_text="\n",
967 indent=2):
968
969 r"""
970 Return a string with a standardized report which includes the caller's
971 error text, the call stack and the program header.
972
973 Description of args:
974 error_text The error text to be included in the
975 report. The caller should include any
976 needed linefeeds.
977 indent The number of characters to indent each
978 line of output.
979 """
980
981 buffer = ""
982 buffer += sprint_dashes(width=120, char="=")
983 buffer += sprint_error(error_text)
984 buffer += "\n"
985 # Calling sprint_call_stack with stack_frame_ix of 0 causes it to show
986 # itself and this function in the call stack. This is not helpful to a
987 # debugger and is therefore clutter. We will adjust the stack_frame_ix to
988 # hide that information.
989 stack_frame_ix = 2
990 caller_func_name = sprint_func_name(2)
991 if caller_func_name.endswith("print_error_report"):
992 stack_frame_ix += 1
993 buffer += sprint_call_stack(indent, stack_frame_ix)
994 buffer += sprint_pgm_header(indent)
995 buffer += sprint_dashes(width=120, char="=")
996
997 return buffer
998
999###############################################################################
1000
1001
1002###############################################################################
Michael Walsh18176322016-11-15 15:11:21 -06001003def sprint_issuing(cmd_buf,
1004 test_mode=0):
Michael Walshde791732016-09-06 14:25:24 -05001005
1006 r"""
1007 Return a line indicating a command that the program is about to execute.
1008
1009 Sample output for a cmd_buf of "ls"
1010
1011 #(CDT) 2016/08/25 17:57:36 - Issuing: ls
Michael Walshbec416d2016-11-10 08:54:52 -06001012
Michael Walshde791732016-09-06 14:25:24 -05001013 Description of args:
1014 cmd_buf The command to be executed by caller.
Michael Walshbec416d2016-11-10 08:54:52 -06001015 test_mode With test_mode set, your output will look
1016 like this:
1017
1018 #(CDT) 2016/08/25 17:57:36 - (test_mode) Issuing: ls
1019
Michael Walshde791732016-09-06 14:25:24 -05001020 """
1021
Michael Walshbec416d2016-11-10 08:54:52 -06001022 buffer = sprint_time()
1023 if test_mode:
1024 buffer += "(test_mode) "
1025 buffer += "Issuing: " + cmd_buf + "\n"
Michael Walshde791732016-09-06 14:25:24 -05001026
1027 return buffer
1028
1029###############################################################################
1030
1031
1032###############################################################################
1033def sprint_pgm_footer():
1034
1035 r"""
1036 Return a standardized footer that programs should print at the end of the
1037 program run. It includes useful information like total run time, etc.
1038 """
1039
1040 buffer = "\n" + sprint_time() + "Finished running " + pgm_name + ".\n\n"
1041
1042 total_time = time.time() - start_time
1043 total_time_string = "%0.6f" % total_time
1044
Michael Walsh7423c012016-10-04 10:27:21 -05001045 buffer += sprint_varx(pgm_name_var_name + "_runtime", total_time_string)
Michael Walshbec416d2016-11-10 08:54:52 -06001046 buffer += "\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001047
1048 return buffer
1049
1050###############################################################################
1051
1052
1053###############################################################################
1054def sprint(buffer=""):
1055
1056 r"""
1057 Simply return the user's buffer. This function is used by the qprint and
1058 dprint functions defined dynamically below, i.e. it would not normally be
1059 called for general use.
1060
1061 Description of arguments.
1062 buffer This will be returned to the caller.
1063 """
Michael Walshde791732016-09-06 14:25:24 -05001064
Michael Walshbec416d2016-11-10 08:54:52 -06001065 return str(buffer)
1066
1067###############################################################################
1068
1069
1070###############################################################################
1071def sprintn(buffer=""):
1072
1073 r"""
1074 Simply return the user's buffer with a line feed. This function is used
1075 by the qprint and dprint functions defined dynamically below, i.e. it
1076 would not normally be called for general use.
1077
1078 Description of arguments.
1079 buffer This will be returned to the caller.
1080 """
1081
1082 buffer = str(buffer) + "\n"
1083
Michael Walshde791732016-09-06 14:25:24 -05001084 return buffer
1085
1086###############################################################################
1087
1088
1089###############################################################################
1090# In the following section of code, we will dynamically create print versions
1091# for each of the sprint functions defined above. So, for example, where we
1092# have an sprint_time() function defined above that returns the time to the
Michael Walsh7423c012016-10-04 10:27:21 -05001093# caller in a string, we will create a corresponding print_time() function
1094# that will print that string directly to stdout.
Michael Walshde791732016-09-06 14:25:24 -05001095
1096# It can be complicated to follow what's being creaed by the exec statement
1097# below. Here is an example of the print_time() function that will be created:
1098
1099# def print_time(*args):
1100# s_funcname = "s" + sys._getframe().f_code.co_name
1101# s_func = getattr(sys.modules[__name__], s_funcname)
1102# sys.stdout.write(s_func(*args))
1103
1104# Here are comments describing the 3 lines in the body of the created function.
1105# Calculate the "s" version of this function name (e.g. if this function name
1106# is print_time, we want s_funcname to be "sprint_time".
1107# Put a reference to the "s" version of this function in s_func.
Michael Walsh7423c012016-10-04 10:27:21 -05001108# Call the "s" version of this function passing it all of our arguments.
1109# Write the result to stdout.
Michael Walshde791732016-09-06 14:25:24 -05001110
1111# func_names contains a list of all print functions which should be created
1112# from their sprint counterparts.
1113func_names = ['print_time', 'print_timen', 'print_error', 'print_varx',
Michael Walsh18176322016-11-15 15:11:21 -06001114 'print_var', 'print_vars', 'print_dashes', 'indent',
1115 'print_call_stack', 'print_func_name', 'print_executing',
1116 'print_pgm_header', 'print_issuing', 'print_pgm_footer',
1117 'print_error_report', 'print', 'printn']
Michael Walshde791732016-09-06 14:25:24 -05001118
1119for func_name in func_names:
Michael Walsh7423c012016-10-04 10:27:21 -05001120 if func_name == "print":
1121 continue
Michael Walshde791732016-09-06 14:25:24 -05001122 # Create abbreviated aliases (e.g. spvar is an alias for sprint_var).
1123 alias = re.sub("print_", "p", func_name)
Michael Walsh7423c012016-10-04 10:27:21 -05001124 pgm_definition_string = "s" + alias + " = s" + func_name
1125 if gen_print_debug:
1126 print(pgm_definition_string)
1127 exec(pgm_definition_string)
Michael Walshde791732016-09-06 14:25:24 -05001128
1129for func_name in func_names:
Michael Walsh7423c012016-10-04 10:27:21 -05001130 if func_name == "print_error" or func_name == "print_error_report":
Michael Walshde791732016-09-06 14:25:24 -05001131 output_stream = "stderr"
1132 else:
1133 output_stream = "stdout"
1134 func_def = \
1135 [
1136 "def " + func_name + "(*args):",
1137 " s_func_name = \"s\" + sys._getframe().f_code.co_name",
1138 " s_func = getattr(sys.modules[__name__], s_func_name)",
1139 " sys." + output_stream + ".write(s_func(*args))",
1140 " sys." + output_stream + ".flush()"
1141 ]
Michael Walsh7423c012016-10-04 10:27:21 -05001142 if func_name != "print":
1143 pgm_definition_string = '\n'.join(func_def)
1144 if gen_print_debug:
1145 print(pgm_definition_string)
1146 exec(pgm_definition_string)
1147
1148 # Now define "q" versions of each print function.
1149 func_def = \
1150 [
1151 "def q" + func_name + "(*args):",
1152 " if __builtin__.quiet: return",
1153 " s_func_name = \"s" + func_name + "\"",
1154 " s_func = getattr(sys.modules[__name__], s_func_name)",
1155 " sys." + output_stream + ".write(s_func(*args))",
1156 " sys." + output_stream + ".flush()"
1157 ]
1158
Michael Walshde791732016-09-06 14:25:24 -05001159 pgm_definition_string = '\n'.join(func_def)
Michael Walsh7423c012016-10-04 10:27:21 -05001160 if gen_print_debug:
1161 print(pgm_definition_string)
Michael Walshde791732016-09-06 14:25:24 -05001162 exec(pgm_definition_string)
1163
Michael Walsh7423c012016-10-04 10:27:21 -05001164 # Now define "d" versions of each print function.
1165 func_def = \
1166 [
1167 "def d" + func_name + "(*args):",
1168 " if not __builtin__.debug: return",
1169 " s_func_name = \"s" + func_name + "\"",
1170 " s_func = getattr(sys.modules[__name__], s_func_name)",
1171 " sys." + output_stream + ".write(s_func(*args))",
1172 " sys." + output_stream + ".flush()"
1173 ]
1174
1175 pgm_definition_string = '\n'.join(func_def)
1176 if gen_print_debug:
1177 print(pgm_definition_string)
1178 exec(pgm_definition_string)
1179
1180 # Now define "l" versions of each print function.
1181 func_def = \
1182 [
1183 "def l" + func_name + "(*args):",
1184 " s_func_name = \"s" + func_name + "\"",
1185 " s_func = getattr(sys.modules[__name__], s_func_name)",
1186 " logging.log(getattr(logging, 'INFO'), s_func(*args))",
1187 ]
1188
1189 if func_name != "print_varx" and func_name != "print_var":
1190 pgm_definition_string = '\n'.join(func_def)
1191 if gen_print_debug:
1192 print(pgm_definition_string)
1193 exec(pgm_definition_string)
1194
1195 if func_name == "print":
1196 continue
1197
Michael Walshde791732016-09-06 14:25:24 -05001198 # Create abbreviated aliases (e.g. pvar is an alias for print_var).
1199 alias = re.sub("print_", "p", func_name)
Michael Walsh7423c012016-10-04 10:27:21 -05001200 pgm_definition_string = alias + " = " + func_name
1201 if gen_print_debug:
1202 print(pgm_definition_string)
1203 exec(pgm_definition_string)
1204
1205 # Create abbreviated aliases (e.g. qpvar is an alias for qprint_var).
1206 alias = re.sub("print_", "p", func_name)
1207 pgm_definition_string = "q" + alias + " = q" + func_name
1208 if gen_print_debug:
1209 print(pgm_definition_string)
1210 exec(pgm_definition_string)
1211
1212 # Create abbreviated aliases (e.g. dpvar is an alias for dprint_var).
1213 alias = re.sub("print_", "p", func_name)
1214 pgm_definition_string = "d" + alias + " = d" + func_name
1215 if gen_print_debug:
1216 print(pgm_definition_string)
1217 exec(pgm_definition_string)
1218
1219 # Create abbreviated aliases (e.g. lpvar is an alias for lprint_var).
1220 alias = re.sub("print_", "p", func_name)
1221 pgm_definition_string = "l" + alias + " = l" + func_name
1222 if gen_print_debug:
1223 print(pgm_definition_string)
1224 exec(pgm_definition_string)
Michael Walshde791732016-09-06 14:25:24 -05001225
1226###############################################################################