blob: e7efb16015bcac5c2f2d3bb024abeefba7c6ccba [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 Walshbec416d2016-11-10 08:54:52 -0600503 if type(var_value) in (str, unicode):
504 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 Walsh7423c012016-10-04 10:27:21 -0500600def lprint_varx(var_name,
601 var_value,
602 hex=0,
603 loc_col1_indent=col1_indent,
604 loc_col1_width=col1_width,
605 log_level=getattr(logging, 'INFO')):
606
607 r"""
608 Send sprint_varx output to logging.
609 """
610
611 logging.log(log_level, sprint_varx(var_name, var_value, hex,
612 loc_col1_indent, loc_col1_width, ""))
613
614###############################################################################
615
616
617###############################################################################
618def lprint_var(*args):
619
620 r"""
621 Figure out the name of the first argument for you and then call
622 lprint_varx with it. Therefore, the following 2 calls are equivalent:
623 lprint_varx("var1", var1)
624 lprint_var(var1)
625 """
626
627 # Get the name of the first variable passed to this function.
628 stack_frame = 2
629 caller_func_name = sprint_func_name(2)
630 if caller_func_name.endswith("print_var"):
631 stack_frame += 1
632 var_name = get_arg_name(None, 1, stack_frame)
633 lprint_varx(var_name, *args)
634
635###############################################################################
636
637
638###############################################################################
639def sprint_dashes(indent=col1_indent,
640 width=80,
641 line_feed=1,
642 char="-"):
Michael Walshde791732016-09-06 14:25:24 -0500643
644 r"""
645 Return a string of dashes to the caller.
646
647 Description of arguements:
648 indent The number of characters to indent the
649 output.
650 width The width of the string of dashes.
651 line_feed Indicates whether the output should end
652 with a line feed.
Michael Walsh7423c012016-10-04 10:27:21 -0500653 char The character to be repeated in the output
654 string.
Michael Walshde791732016-09-06 14:25:24 -0500655 """
656
Michael Walsh7423c012016-10-04 10:27:21 -0500657 width = int(width)
658 buffer = " "*int(indent) + char*width
Michael Walshde791732016-09-06 14:25:24 -0500659 if line_feed:
660 buffer += "\n"
661
662 return buffer
663
664###############################################################################
665
666
667###############################################################################
Michael Walsh7423c012016-10-04 10:27:21 -0500668def sindent(text="",
669 indent=0):
670
671 r"""
672 Pre-pend the specified number of characters to the text string (i.e.
673 indent it) and return it.
674
675 Description of arguments:
676 text The string to be indented.
677 indent The number of characters to indent the
678 string.
679 """
680
681 format_string = "%" + str(indent) + "s%s"
682 buffer = format_string % ("", text)
683
684 return buffer
685
686###############################################################################
687
688
689###############################################################################
690def sprint_call_stack(indent=0,
691 stack_frame_ix=0):
Michael Walshde791732016-09-06 14:25:24 -0500692
693 r"""
694 Return a call stack report for the given point in the program with line
695 numbers, function names and function parameters and arguments.
696
697 Sample output:
698
699 -------------------------------------------------------------------------
700 Python function call stack
701
702 Line # Function name and arguments
703 ------ ------------------------------------------------------------------
704 424 sprint_call_stack ()
705 4 print_call_stack ()
706 31 func1 (last_name = 'walsh', first_name = 'mikey')
707 59 /tmp/scr5.py
708 -------------------------------------------------------------------------
709
710 Description of arguments:
711 indent The number of characters to indent each
712 line of output.
713 stack_frame_ix The index of the first stack frame which
714 is to be returned.
715 """
716
717 buffer = ""
Michael Walsh7423c012016-10-04 10:27:21 -0500718 buffer += sprint_dashes(indent)
719 buffer += sindent("Python function call stack\n\n", indent)
720 buffer += sindent("Line # Function name and arguments\n", indent)
721 buffer += sprint_dashes(indent, 6, 0) + " " + sprint_dashes(0, 73)
Michael Walshde791732016-09-06 14:25:24 -0500722
723 # Grab the current program stack.
724 current_stack = inspect.stack()
725
726 # Process each frame in turn.
727 format_string = "%6s %s\n"
Michael Walsh7423c012016-10-04 10:27:21 -0500728 ix = 0
Michael Walshde791732016-09-06 14:25:24 -0500729 for stack_frame in current_stack:
Michael Walsh7423c012016-10-04 10:27:21 -0500730 if ix < stack_frame_ix:
731 ix += 1
732 continue
Michael Walshde791732016-09-06 14:25:24 -0500733 lineno = str(stack_frame[2])
734 func_name = str(stack_frame[3])
735 if func_name == "?":
736 # "?" is the name used when code is not in a function.
737 func_name = "(none)"
738
739 if func_name == "<module>":
Michael Walsh7423c012016-10-04 10:27:21 -0500740 # If the func_name is the "main" program, we simply get the
741 # command line call string.
Michael Walshde791732016-09-06 14:25:24 -0500742 func_and_args = ' '.join(sys.argv)
743 else:
744 # Get the program arguments.
745 arg_vals = inspect.getargvalues(stack_frame[0])
746 function_parms = arg_vals[0]
747 frame_locals = arg_vals[3]
748
Michael Walsh7423c012016-10-04 10:27:21 -0500749 args_list = []
Michael Walshde791732016-09-06 14:25:24 -0500750 for arg_name in function_parms:
751 # Get the arg value from frame locals.
752 arg_value = frame_locals[arg_name]
Michael Walsh7423c012016-10-04 10:27:21 -0500753 args_list.append(arg_name + " = " + repr(arg_value))
754 args_str = "(" + ', '.join(map(str, args_list)) + ")"
Michael Walshde791732016-09-06 14:25:24 -0500755
756 # Now we need to print this in a nicely-wrapped way.
757 func_and_args = func_name + " " + args_str
758
Michael Walsh7423c012016-10-04 10:27:21 -0500759 buffer += sindent(format_string % (lineno, func_and_args), indent)
760 ix += 1
Michael Walshde791732016-09-06 14:25:24 -0500761
Michael Walsh7423c012016-10-04 10:27:21 -0500762 buffer += sprint_dashes(indent)
Michael Walshde791732016-09-06 14:25:24 -0500763
764 return buffer
765
766###############################################################################
767
768
769###############################################################################
770def sprint_executing(stack_frame_ix=None):
771
772 r"""
773 Print a line indicating what function is executing and with what parameter
774 values. This is useful for debugging.
775
776 Sample output:
777
778 #(CDT) 2016/08/25 17:54:27 - Executing: func1 (x = 1)
779
780 Description of arguments:
781 stack_frame_ix The index of the stack frame whose
782 function info should be returned. If the
783 caller does not specifiy a value, this
784 function will set the value to 1 which is
785 the index of the caller's stack frame. If
786 the caller is the wrapper function
787 "print_executing", this function will bump
788 it up by 1.
789 """
790
791 # If user wants default stack_frame_ix.
792 if stack_frame_ix is None:
793 func_name = sys._getframe().f_code.co_name
794 caller_func_name = sys._getframe(1).f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -0500795 if caller_func_name.endswith(func_name[1:]):
Michael Walshde791732016-09-06 14:25:24 -0500796 stack_frame_ix = 2
797 else:
798 stack_frame_ix = 1
799
800 stack_frame = inspect.stack()[stack_frame_ix]
801
802 func_name = str(stack_frame[3])
803 if func_name == "?":
804 # "?" is the name used when code is not in a function.
805 func_name = "(none)"
806
807 if func_name == "<module>":
808 # If the func_name is the "main" program, we simply get the command
809 # line call string.
810 func_and_args = ' '.join(sys.argv)
811 else:
812 # Get the program arguments.
813 arg_vals = inspect.getargvalues(stack_frame[0])
814 function_parms = arg_vals[0]
815 frame_locals = arg_vals[3]
816
Michael Walsh7423c012016-10-04 10:27:21 -0500817 args_list = []
Michael Walshde791732016-09-06 14:25:24 -0500818 for arg_name in function_parms:
819 # Get the arg value from frame locals.
820 arg_value = frame_locals[arg_name]
Michael Walsh7423c012016-10-04 10:27:21 -0500821 args_list.append(arg_name + " = " + repr(arg_value))
822 args_str = "(" + ', '.join(map(str, args_list)) + ")"
Michael Walshde791732016-09-06 14:25:24 -0500823
824 # Now we need to print this in a nicely-wrapped way.
825 func_and_args = func_name + " " + args_str
826
827 return sprint_time() + "Executing: " + func_and_args + "\n"
828
829###############################################################################
830
831
832###############################################################################
Michael Walshbec416d2016-11-10 08:54:52 -0600833def sprint_pgm_header(indent=0,
834 linefeed=1):
Michael Walshde791732016-09-06 14:25:24 -0500835
836 r"""
837 Return a standardized header that programs should print at the beginning
838 of the run. It includes useful information like command line, pid,
839 userid, program parameters, etc.
840
Michael Walsh7423c012016-10-04 10:27:21 -0500841 Description of arguments:
842 indent The number of characters to indent each
843 line of output.
Michael Walshbec416d2016-11-10 08:54:52 -0600844 linefeed Indicates whether a line feed be included
845 at the beginning and end of the report.
Michael Walshde791732016-09-06 14:25:24 -0500846 """
847
Michael Walshbec416d2016-11-10 08:54:52 -0600848 loc_col1_width = col1_width + indent
849
850 buffer = ""
851 if linefeed:
852 buffer = "\n"
Michael Walsh7423c012016-10-04 10:27:21 -0500853
854 buffer += sindent(sprint_time() + "Running " + pgm_name + ".\n", indent)
855 buffer += sindent(sprint_time() + "Program parameter values, etc.:\n\n",
856 indent)
Michael Walshbec416d2016-11-10 08:54:52 -0600857 buffer += sprint_varx("command_line", ' '.join(sys.argv), 0, indent,
858 loc_col1_width)
Michael Walsh7423c012016-10-04 10:27:21 -0500859 # We want the output to show a customized name for the pid and pgid but
860 # we want it to look like a valid variable name. Therefore, we'll use
Michael Walshde791732016-09-06 14:25:24 -0500861 # pgm_name_var_name which was set when this module was imported.
Michael Walshbec416d2016-11-10 08:54:52 -0600862 buffer += sprint_varx(pgm_name_var_name + "_pid", os.getpid(), 0, indent,
863 loc_col1_width)
864 buffer += sprint_varx(pgm_name_var_name + "_pgid", os.getpgrp(), 0, indent,
865 loc_col1_width)
Michael Walshde791732016-09-06 14:25:24 -0500866 buffer += sprint_varx("uid", str(os.geteuid()) + " (" + os.getlogin() +
Michael Walshbec416d2016-11-10 08:54:52 -0600867 ")", 0, indent, loc_col1_width)
Michael Walshde791732016-09-06 14:25:24 -0500868 buffer += sprint_varx("gid", str(os.getgid()) + " (" +
Michael Walsh7423c012016-10-04 10:27:21 -0500869 str(grp.getgrgid(os.getgid()).gr_name) + ")", 0,
Michael Walshbec416d2016-11-10 08:54:52 -0600870 indent, loc_col1_width)
871 buffer += sprint_varx("host_name", socket.gethostname(), 0, indent,
872 loc_col1_width)
873 buffer += sprint_varx("DISPLAY", os.environ['DISPLAY'], 0, indent,
874 loc_col1_width)
Michael Walshde791732016-09-06 14:25:24 -0500875 # I want to add code to print caller's parms.
876
Michael Walsh7423c012016-10-04 10:27:21 -0500877 # __builtin__.arg_obj is created by the get_arg module function,
878 # gen_get_options.
879 try:
880 buffer += ga.sprint_args(__builtin__.arg_obj, indent)
881 except AttributeError:
882 pass
883
Michael Walshbec416d2016-11-10 08:54:52 -0600884 if linefeed:
885 buffer += "\n"
Michael Walshde791732016-09-06 14:25:24 -0500886
887 return buffer
888
889###############################################################################
890
891
892###############################################################################
Michael Walsh7423c012016-10-04 10:27:21 -0500893def sprint_error_report(error_text="\n",
894 indent=2):
895
896 r"""
897 Return a string with a standardized report which includes the caller's
898 error text, the call stack and the program header.
899
900 Description of args:
901 error_text The error text to be included in the
902 report. The caller should include any
903 needed linefeeds.
904 indent The number of characters to indent each
905 line of output.
906 """
907
908 buffer = ""
909 buffer += sprint_dashes(width=120, char="=")
910 buffer += sprint_error(error_text)
911 buffer += "\n"
912 # Calling sprint_call_stack with stack_frame_ix of 0 causes it to show
913 # itself and this function in the call stack. This is not helpful to a
914 # debugger and is therefore clutter. We will adjust the stack_frame_ix to
915 # hide that information.
916 stack_frame_ix = 2
917 caller_func_name = sprint_func_name(2)
918 if caller_func_name.endswith("print_error_report"):
919 stack_frame_ix += 1
920 buffer += sprint_call_stack(indent, stack_frame_ix)
921 buffer += sprint_pgm_header(indent)
922 buffer += sprint_dashes(width=120, char="=")
923
924 return buffer
925
926###############################################################################
927
928
929###############################################################################
Michael Walshbec416d2016-11-10 08:54:52 -0600930def sissuing(cmd_buf,
931 test_mode=0):
Michael Walshde791732016-09-06 14:25:24 -0500932
933 r"""
934 Return a line indicating a command that the program is about to execute.
935
936 Sample output for a cmd_buf of "ls"
937
938 #(CDT) 2016/08/25 17:57:36 - Issuing: ls
Michael Walshbec416d2016-11-10 08:54:52 -0600939
Michael Walshde791732016-09-06 14:25:24 -0500940 Description of args:
941 cmd_buf The command to be executed by caller.
Michael Walshbec416d2016-11-10 08:54:52 -0600942 test_mode With test_mode set, your output will look
943 like this:
944
945 #(CDT) 2016/08/25 17:57:36 - (test_mode) Issuing: ls
946
Michael Walshde791732016-09-06 14:25:24 -0500947 """
948
Michael Walshbec416d2016-11-10 08:54:52 -0600949 buffer = sprint_time()
950 if test_mode:
951 buffer += "(test_mode) "
952 buffer += "Issuing: " + cmd_buf + "\n"
Michael Walshde791732016-09-06 14:25:24 -0500953
954 return buffer
955
956###############################################################################
957
958
959###############################################################################
960def sprint_pgm_footer():
961
962 r"""
963 Return a standardized footer that programs should print at the end of the
964 program run. It includes useful information like total run time, etc.
965 """
966
967 buffer = "\n" + sprint_time() + "Finished running " + pgm_name + ".\n\n"
968
969 total_time = time.time() - start_time
970 total_time_string = "%0.6f" % total_time
971
Michael Walsh7423c012016-10-04 10:27:21 -0500972 buffer += sprint_varx(pgm_name_var_name + "_runtime", total_time_string)
Michael Walshbec416d2016-11-10 08:54:52 -0600973 buffer += "\n"
Michael Walsh7423c012016-10-04 10:27:21 -0500974
975 return buffer
976
977###############################################################################
978
979
980###############################################################################
981def sprint(buffer=""):
982
983 r"""
984 Simply return the user's buffer. This function is used by the qprint and
985 dprint functions defined dynamically below, i.e. it would not normally be
986 called for general use.
987
988 Description of arguments.
989 buffer This will be returned to the caller.
990 """
Michael Walshde791732016-09-06 14:25:24 -0500991
Michael Walshbec416d2016-11-10 08:54:52 -0600992 return str(buffer)
993
994###############################################################################
995
996
997###############################################################################
998def sprintn(buffer=""):
999
1000 r"""
1001 Simply return the user's buffer with a line feed. This function is used
1002 by the qprint and dprint functions defined dynamically below, i.e. it
1003 would not normally be called for general use.
1004
1005 Description of arguments.
1006 buffer This will be returned to the caller.
1007 """
1008
1009 buffer = str(buffer) + "\n"
1010
Michael Walshde791732016-09-06 14:25:24 -05001011 return buffer
1012
1013###############################################################################
1014
1015
1016###############################################################################
1017# In the following section of code, we will dynamically create print versions
1018# for each of the sprint functions defined above. So, for example, where we
1019# have an sprint_time() function defined above that returns the time to the
Michael Walsh7423c012016-10-04 10:27:21 -05001020# caller in a string, we will create a corresponding print_time() function
1021# that will print that string directly to stdout.
Michael Walshde791732016-09-06 14:25:24 -05001022
1023# It can be complicated to follow what's being creaed by the exec statement
1024# below. Here is an example of the print_time() function that will be created:
1025
1026# def print_time(*args):
1027# s_funcname = "s" + sys._getframe().f_code.co_name
1028# s_func = getattr(sys.modules[__name__], s_funcname)
1029# sys.stdout.write(s_func(*args))
1030
1031# Here are comments describing the 3 lines in the body of the created function.
1032# Calculate the "s" version of this function name (e.g. if this function name
1033# is print_time, we want s_funcname to be "sprint_time".
1034# Put a reference to the "s" version of this function in s_func.
Michael Walsh7423c012016-10-04 10:27:21 -05001035# Call the "s" version of this function passing it all of our arguments.
1036# Write the result to stdout.
Michael Walshde791732016-09-06 14:25:24 -05001037
1038# func_names contains a list of all print functions which should be created
1039# from their sprint counterparts.
1040func_names = ['print_time', 'print_timen', 'print_error', 'print_varx',
Michael Walshbec416d2016-11-10 08:54:52 -06001041 'print_var', 'print_dashes', 'indent', 'print_call_stack',
Michael Walshde791732016-09-06 14:25:24 -05001042 'print_func_name', 'print_executing', 'print_pgm_header',
Michael Walshbec416d2016-11-10 08:54:52 -06001043 'issuing', 'print_pgm_footer', 'print_error_report', 'print',
1044 'printn']
Michael Walshde791732016-09-06 14:25:24 -05001045
1046for func_name in func_names:
Michael Walsh7423c012016-10-04 10:27:21 -05001047 if func_name == "print":
1048 continue
Michael Walshde791732016-09-06 14:25:24 -05001049 # Create abbreviated aliases (e.g. spvar is an alias for sprint_var).
1050 alias = re.sub("print_", "p", func_name)
Michael Walsh7423c012016-10-04 10:27:21 -05001051 pgm_definition_string = "s" + alias + " = s" + func_name
1052 if gen_print_debug:
1053 print(pgm_definition_string)
1054 exec(pgm_definition_string)
Michael Walshde791732016-09-06 14:25:24 -05001055
1056for func_name in func_names:
Michael Walsh7423c012016-10-04 10:27:21 -05001057 if func_name == "print_error" or func_name == "print_error_report":
Michael Walshde791732016-09-06 14:25:24 -05001058 output_stream = "stderr"
1059 else:
1060 output_stream = "stdout"
1061 func_def = \
1062 [
1063 "def " + func_name + "(*args):",
1064 " s_func_name = \"s\" + sys._getframe().f_code.co_name",
1065 " s_func = getattr(sys.modules[__name__], s_func_name)",
1066 " sys." + output_stream + ".write(s_func(*args))",
1067 " sys." + output_stream + ".flush()"
1068 ]
Michael Walsh7423c012016-10-04 10:27:21 -05001069 if func_name != "print":
1070 pgm_definition_string = '\n'.join(func_def)
1071 if gen_print_debug:
1072 print(pgm_definition_string)
1073 exec(pgm_definition_string)
1074
1075 # Now define "q" versions of each print function.
1076 func_def = \
1077 [
1078 "def q" + func_name + "(*args):",
1079 " if __builtin__.quiet: return",
1080 " s_func_name = \"s" + func_name + "\"",
1081 " s_func = getattr(sys.modules[__name__], s_func_name)",
1082 " sys." + output_stream + ".write(s_func(*args))",
1083 " sys." + output_stream + ".flush()"
1084 ]
1085
Michael Walshde791732016-09-06 14:25:24 -05001086 pgm_definition_string = '\n'.join(func_def)
Michael Walsh7423c012016-10-04 10:27:21 -05001087 if gen_print_debug:
1088 print(pgm_definition_string)
Michael Walshde791732016-09-06 14:25:24 -05001089 exec(pgm_definition_string)
1090
Michael Walsh7423c012016-10-04 10:27:21 -05001091 # Now define "d" versions of each print function.
1092 func_def = \
1093 [
1094 "def d" + func_name + "(*args):",
1095 " if not __builtin__.debug: return",
1096 " s_func_name = \"s" + func_name + "\"",
1097 " s_func = getattr(sys.modules[__name__], s_func_name)",
1098 " sys." + output_stream + ".write(s_func(*args))",
1099 " sys." + output_stream + ".flush()"
1100 ]
1101
1102 pgm_definition_string = '\n'.join(func_def)
1103 if gen_print_debug:
1104 print(pgm_definition_string)
1105 exec(pgm_definition_string)
1106
1107 # Now define "l" versions of each print function.
1108 func_def = \
1109 [
1110 "def l" + func_name + "(*args):",
1111 " s_func_name = \"s" + func_name + "\"",
1112 " s_func = getattr(sys.modules[__name__], s_func_name)",
1113 " logging.log(getattr(logging, 'INFO'), s_func(*args))",
1114 ]
1115
1116 if func_name != "print_varx" and func_name != "print_var":
1117 pgm_definition_string = '\n'.join(func_def)
1118 if gen_print_debug:
1119 print(pgm_definition_string)
1120 exec(pgm_definition_string)
1121
1122 if func_name == "print":
1123 continue
1124
Michael Walshde791732016-09-06 14:25:24 -05001125 # Create abbreviated aliases (e.g. pvar is an alias for print_var).
1126 alias = re.sub("print_", "p", func_name)
Michael Walsh7423c012016-10-04 10:27:21 -05001127 pgm_definition_string = alias + " = " + func_name
1128 if gen_print_debug:
1129 print(pgm_definition_string)
1130 exec(pgm_definition_string)
1131
1132 # Create abbreviated aliases (e.g. qpvar is an alias for qprint_var).
1133 alias = re.sub("print_", "p", func_name)
1134 pgm_definition_string = "q" + alias + " = q" + func_name
1135 if gen_print_debug:
1136 print(pgm_definition_string)
1137 exec(pgm_definition_string)
1138
1139 # Create abbreviated aliases (e.g. dpvar is an alias for dprint_var).
1140 alias = re.sub("print_", "p", func_name)
1141 pgm_definition_string = "d" + alias + " = d" + func_name
1142 if gen_print_debug:
1143 print(pgm_definition_string)
1144 exec(pgm_definition_string)
1145
1146 # Create abbreviated aliases (e.g. lpvar is an alias for lprint_var).
1147 alias = re.sub("print_", "p", func_name)
1148 pgm_definition_string = "l" + alias + " = l" + func_name
1149 if gen_print_debug:
1150 print(pgm_definition_string)
1151 exec(pgm_definition_string)
Michael Walshde791732016-09-06 14:25:24 -05001152
1153###############################################################################