blob: 6b405d78df7edce872e19e3831e8f6938a4a04b6 [file] [log] [blame]
Michael Walshde791732016-09-06 14:25:24 -05001#!/usr/bin/env python
2
3r"""
4This module provides many valuable print functions such as sprint_var,
5sprint_time, sprint_error, sprint_call_stack.
6"""
7
8import sys
9import os
10import time
11import inspect
12import re
13import grp
14import socket
15import argparse
Michael Walsh7423c012016-10-04 10:27:21 -050016import __builtin__
17import logging
Michael Walshbec416d2016-11-10 08:54:52 -060018import collections
19
Michael Walshbec416d2016-11-10 08:54:52 -060020try:
21 from robot.utils import DotDict
22except ImportError:
Michael Walsha6723f22016-11-22 11:12:01 -060023 pass
Michael Walsh7423c012016-10-04 10:27:21 -050024
25import gen_arg as ga
Michael Walshde791732016-09-06 14:25:24 -050026
27# Setting these variables for use both inside this module and by programs
28# importing this module.
29pgm_dir_path = sys.argv[0]
30pgm_name = os.path.basename(pgm_dir_path)
Michael Walsh7423c012016-10-04 10:27:21 -050031pgm_dir_name = re.sub("/" + pgm_name, "", pgm_dir_path) + "/"
32
Michael Walshde791732016-09-06 14:25:24 -050033
34# Some functions (e.g. sprint_pgm_header) have need of a program name value
35# that looks more like a valid variable name. Therefore, we'll swap odd
36# characters like "." out for underscores.
37pgm_name_var_name = pgm_name.replace(".", "_")
38
39# Initialize global values used as defaults by print_time, print_var, etc.
40col1_indent = 0
41
42# Calculate default column width for print_var functions based on environment
43# variable settings. The objective is to make the variable values line up
44# nicely with the time stamps.
45col1_width = 29
46if 'NANOSECONDS' in os.environ:
47 NANOSECONDS = os.environ['NANOSECONDS']
48else:
49 NANOSECONDS = 0
50
51if NANOSECONDS == "1":
52 col1_width = col1_width + 7
53
54if 'SHOW_ELAPSED_TIME' in os.environ:
55 SHOW_ELAPSED_TIME = os.environ['SHOW_ELAPSED_TIME']
56else:
57 SHOW_ELAPSED_TIME = 0
58
59if SHOW_ELAPSED_TIME == "1":
60 if NANOSECONDS == "1":
61 col1_width = col1_width + 14
62 else:
63 col1_width = col1_width + 7
64
65# Initialize some time variables used in module functions.
66start_time = time.time()
67sprint_time_last_seconds = start_time
68
Michael Walsh7423c012016-10-04 10:27:21 -050069try:
70 # The user can set environment variable "GEN_PRINT_DEBUG" to get debug
71 # output from this module.
Michael Walsha6723f22016-11-22 11:12:01 -060072 gen_print_debug = int(os.environ['GEN_PRINT_DEBUG'])
Michael Walsh7423c012016-10-04 10:27:21 -050073except KeyError:
74 gen_print_debug = 0
75
Michael Walshde791732016-09-06 14:25:24 -050076
77###############################################################################
78def sprint_func_name(stack_frame_ix=None):
79
80 r"""
81 Return the function name associated with the indicated stack frame.
82
83 Description of arguments:
84 stack_frame_ix The index of the stack frame whose
85 function name should be returned. If the
86 caller does not specifiy a value, this
87 function will set the value to 1 which is
88 the index of the caller's stack frame. If
89 the caller is the wrapper function
90 "print_func_name", this function will bump
91 it up by 1.
92 """
93
94 # If user specified no stack_frame_ix, we'll set it to a proper default
95 # value.
96 if stack_frame_ix is None:
97 func_name = sys._getframe().f_code.co_name
98 caller_func_name = sys._getframe(1).f_code.co_name
99 if func_name[1:] == caller_func_name:
100 stack_frame_ix = 2
101 else:
102 stack_frame_ix = 1
103
104 func_name = sys._getframe(stack_frame_ix).f_code.co_name
105
106 return func_name
107
108###############################################################################
109
110
111# get_arg_name is not a print function per se. I have included it in this
112# module because it is used by sprint_var which is found in this module.
113###############################################################################
114def get_arg_name(var,
115 arg_num=1,
116 stack_frame_ix=1):
117
118 r"""
119 Return the "name" of an argument passed to a function. This could be a
120 literal or a variable name.
121
122 Description of arguements:
123 var The variable whose name you want returned.
124 arg_num The arg number (1 through n) whose name
125 you wish to have returned. This value
126 should not exceed the number of arguments
127 allowed by the target function.
128 stack_frame_ix The stack frame index of the target
129 function. This value must be 1 or
130 greater. 1 would indicate get_arg_name's
131 stack frame. 2 would be the caller of
132 get_arg_name's stack frame, etc.
133
134 Example 1:
135
136 my_var = "mike"
137 var_name = get_arg_name(my_var)
138
139 In this example, var_name will receive the value "my_var".
140
141 Example 2:
142
143 def test1(var):
144 # Getting the var name of the first arg to this function, test1.
145 # Note, in this case, it doesn't matter what you pass as the first arg
146 # to get_arg_name since it is the caller's variable name that matters.
147 dummy = 1
148 arg_num = 1
149 stack_frame = 2
150 var_name = get_arg_name(dummy, arg_num, stack_frame)
151
152 # Mainline...
153
154 another_var = "whatever"
155 test1(another_var)
156
157 In this example, var_name will be set to "another_var".
158
159 """
160
161 # Note: I wish to avoid recursion so I refrain from calling any function
162 # that calls this function (i.e. sprint_var, valid_value, etc.).
163
164 try:
165 # The user can set environment variable "GET_ARG_NAME_DEBUG" to get
166 # debug output from this function.
167 local_debug = os.environ['GET_ARG_NAME_DEBUG']
168 except KeyError:
169 local_debug = 0
170
171 if arg_num < 1:
172 print_error("Programmer error - Variable \"arg_num\" has an invalid" +
173 " value of \"" + str(arg_num) + "\". The value must be" +
174 " an integer that is greater than 0.\n")
175 # What is the best way to handle errors? Raise exception? I'll
176 # revisit later.
177 return
178 if stack_frame_ix < 1:
179 print_error("Programmer error - Variable \"stack_frame_ix\" has an" +
180 " invalid value of \"" + str(stack_frame_ix) + "\". The" +
181 " value must be an integer that is greater than or equal" +
182 " to 1.\n")
183 return
184
185 if local_debug:
186 debug_indent = 2
187 print(sprint_func_name() + "() parms:")
188 print_varx("var", var, 0, debug_indent)
189 print_varx("arg_num", arg_num, 0, debug_indent)
190 print_varx("stack_frame_ix", stack_frame_ix, 0, debug_indent)
191
192 try:
193 frame, filename, cur_line_no, function_name, lines, index = \
194 inspect.stack()[stack_frame_ix]
195 except IndexError:
196 print_error("Programmer error - The caller has asked for information" +
197 " about the stack frame at index \"" +
198 str(stack_frame_ix) + "\". However, the stack only" +
199 " contains " + str(len(inspect.stack())) + " entries." +
200 " Therefore the stack frame index is out of range.\n")
201 return
202
203 if local_debug:
204 print("\nVariables retrieved from inspect.stack() function:")
205 print_varx("frame", frame, 0, debug_indent)
206 print_varx("filename", filename, 0, debug_indent)
207 print_varx("cur_line_no", cur_line_no, 0, debug_indent)
208 print_varx("function_name", function_name, 0, debug_indent)
209 print_varx("lines", lines, 0, debug_indent)
210 print_varx("index", index, 0, debug_indent)
211
212 composite_line = lines[0].strip()
213
214 called_func_name = sprint_func_name(stack_frame_ix)
Michael Walshbec416d2016-11-10 08:54:52 -0600215 # Needed to add a right anchor to func_regex for cases like this where
216 # there is an arg whose name is a substring of the function name. So the
217 # function name needs to be bounded on the right by zero or more spaces
218 # and a left parenthesis.
219 # if not valid_value(whatever, valid_values=["one", "two"]):
220 func_regex = ".*" + called_func_name + "[ ]*\("
221 # if not re.match(r".*" + called_func_name, composite_line):
222 if not re.match(func_regex, composite_line):
Michael Walsh7423c012016-10-04 10:27:21 -0500223 # The called function name was not found in the composite line. The
224 # caller may be using a function alias.
225 # I added code to handle pvar, qpvar, dpvar, etc. aliases.
226 # pvar is an alias for print_var. However, when it is used,
227 # sprint_func_name() returns the non-alias version, i.e. "print_var".
228 # Adjusting for that here.
229 alias = re.sub("print_var", "pvar", called_func_name)
Michael Walshbec416d2016-11-10 08:54:52 -0600230 if local_debug:
231 print_varx("alias", alias, 0, debug_indent)
Michael Walsh7423c012016-10-04 10:27:21 -0500232 called_func_name = alias
Michael Walshbec416d2016-11-10 08:54:52 -0600233 func_regex = ".*" + called_func_name + "[ ]*\("
Michael Walsh7423c012016-10-04 10:27:21 -0500234
Michael Walshbec416d2016-11-10 08:54:52 -0600235 # arg_list_etc = re.sub(".*" + called_func_name, "", composite_line)
236 arg_list_etc = "(" + re.sub(func_regex, "", composite_line)
Michael Walshde791732016-09-06 14:25:24 -0500237 if local_debug:
Michael Walshbec416d2016-11-10 08:54:52 -0600238 print_varx("func_regex", func_regex, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500239 print_varx("called_func_name", called_func_name, 0, debug_indent)
240 print_varx("composite_line", composite_line, 0, debug_indent)
241 print_varx("arg_list_etc", arg_list_etc, 0, debug_indent)
242
243 # Parse arg list...
244 # Initialize...
245 nest_level = -1
246 arg_ix = 0
Michael Walsh7423c012016-10-04 10:27:21 -0500247 args_list = [""]
Michael Walshde791732016-09-06 14:25:24 -0500248 for ix in range(0, len(arg_list_etc)):
249 char = arg_list_etc[ix]
250 # Set the nest_level based on whether we've encounted a parenthesis.
251 if char == "(":
252 nest_level += 1
253 if nest_level == 0:
254 continue
255 elif char == ")":
256 nest_level -= 1
257 if nest_level < 0:
258 break
259
260 # If we reach a comma at base nest level, we are done processing an
Michael Walsh7423c012016-10-04 10:27:21 -0500261 # argument so we increment arg_ix and initialize a new args_list entry.
Michael Walshde791732016-09-06 14:25:24 -0500262 if char == "," and nest_level == 0:
263 arg_ix += 1
Michael Walsh7423c012016-10-04 10:27:21 -0500264 args_list.append("")
Michael Walshde791732016-09-06 14:25:24 -0500265 continue
266
Michael Walsh7423c012016-10-04 10:27:21 -0500267 # For any other character, we append it it to the current arg list
Michael Walshde791732016-09-06 14:25:24 -0500268 # entry.
Michael Walsh7423c012016-10-04 10:27:21 -0500269 args_list[arg_ix] += char
Michael Walshde791732016-09-06 14:25:24 -0500270
271 # Trim whitespace from each list entry.
Michael Walsh7423c012016-10-04 10:27:21 -0500272 args_list = [arg.strip() for arg in args_list]
Michael Walshde791732016-09-06 14:25:24 -0500273
Michael Walsh7423c012016-10-04 10:27:21 -0500274 if arg_num > len(args_list):
Michael Walshde791732016-09-06 14:25:24 -0500275 print_error("Programmer error - The caller has asked for the name of" +
276 " argument number \"" + str(arg_num) + "\" but there " +
Michael Walsh7423c012016-10-04 10:27:21 -0500277 "were only \"" + str(len(args_list)) + "\" args used:\n" +
278 sprint_varx("args_list", args_list))
Michael Walshde791732016-09-06 14:25:24 -0500279 return
280
Michael Walsh7423c012016-10-04 10:27:21 -0500281 argument = args_list[arg_num - 1]
Michael Walshde791732016-09-06 14:25:24 -0500282
283 if local_debug:
Michael Walsh7423c012016-10-04 10:27:21 -0500284 print_varx("args_list", args_list, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500285 print_varx("argument", argument, 0, debug_indent)
286
287 return argument
288
289###############################################################################
290
291
292###############################################################################
293def sprint_time(buffer=""):
294
295 r"""
296 Return the time in the following format.
297
298 Example:
299
300 The following python code...
301
302 sys.stdout.write(sprint_time())
303 sys.stdout.write("Hi.\n")
304
305 Will result in the following type of output:
306
307 #(CDT) 2016/07/08 15:25:35 - Hi.
308
309 Example:
310
311 The following python code...
312
313 sys.stdout.write(sprint_time("Hi.\n"))
314
315 Will result in the following type of output:
316
317 #(CDT) 2016/08/03 17:12:05 - Hi.
318
319 The following environment variables will affect the formatting as
320 described:
321 NANOSECONDS This will cause the time stamps to be
322 precise to the microsecond (Yes, it
323 probably should have been named
324 MICROSECONDS but the convention was set
325 long ago so we're sticking with it).
326 Example of the output when environment
327 variable NANOSECONDS=1.
328
329 #(CDT) 2016/08/03 17:16:25.510469 - Hi.
330
331 SHOW_ELAPSED_TIME This will cause the elapsed time to be
332 included in the output. This is the
333 amount of time that has elapsed since the
334 last time this function was called. The
335 precision of the elapsed time field is
336 also affected by the value of the
337 NANOSECONDS environment variable. Example
338 of the output when environment variable
339 NANOSECONDS=0 and SHOW_ELAPSED_TIME=1.
340
341 #(CDT) 2016/08/03 17:17:40 - 0 - Hi.
342
343 Example of the output when environment variable NANOSECONDS=1 and
344 SHOW_ELAPSED_TIME=1.
345
346 #(CDT) 2016/08/03 17:18:47.317339 - 0.000046 - Hi.
347
348 Description of arguments.
349 buffer This will be appended to the formatted
350 time string.
351 """
352
353 global NANOSECONDS
354 global SHOW_ELAPSED_TIME
355 global sprint_time_last_seconds
356
357 seconds = time.time()
358 loc_time = time.localtime(seconds)
359 nanoseconds = "%0.6f" % seconds
360 pos = nanoseconds.find(".")
361 nanoseconds = nanoseconds[pos:]
362
363 time_string = time.strftime("#(%Z) %Y/%m/%d %H:%M:%S", loc_time)
364 if NANOSECONDS == "1":
365 time_string = time_string + nanoseconds
366
367 if SHOW_ELAPSED_TIME == "1":
368 cur_time_seconds = seconds
369 math_string = "%9.9f" % cur_time_seconds + " - " + "%9.9f" % \
370 sprint_time_last_seconds
371 elapsed_seconds = eval(math_string)
372 if NANOSECONDS == "1":
373 elapsed_seconds = "%11.6f" % elapsed_seconds
374 else:
375 elapsed_seconds = "%4i" % elapsed_seconds
376 sprint_time_last_seconds = cur_time_seconds
377 time_string = time_string + " - " + elapsed_seconds
378
379 return time_string + " - " + buffer
380
381###############################################################################
382
383
384###############################################################################
385def sprint_timen(buffer=""):
386
387 r"""
388 Append a line feed to the buffer, pass it to sprint_time and return the
389 result.
390 """
391
392 return sprint_time(buffer + "\n")
393
394###############################################################################
395
396
397###############################################################################
398def sprint_error(buffer=""):
399
400 r"""
401 Return a standardized error string. This includes:
402 - A time stamp
403 - The "**ERROR**" string
404 - The caller's buffer string.
405
406 Example:
407
408 The following python code...
409
410 print(sprint_error("Oops.\n"))
411
412 Will result in the following type of output:
413
414 #(CDT) 2016/08/03 17:12:05 - **ERROR** Oops.
415
416 Description of arguments.
417 buffer This will be appended to the formatted
418 error string.
419 """
420
421 return sprint_time() + "**ERROR** " + buffer
422
423###############################################################################
424
425
426###############################################################################
427def sprint_varx(var_name,
428 var_value,
429 hex=0,
430 loc_col1_indent=col1_indent,
Michael Walsh7423c012016-10-04 10:27:21 -0500431 loc_col1_width=col1_width,
432 trailing_char="\n"):
Michael Walshde791732016-09-06 14:25:24 -0500433
434 r"""
435 Print the var name/value passed to it. If the caller lets loc_col1_width
436 default, the printing lines up nicely with output generated by the
437 print_time functions.
438
439 Note that the sprint_var function (defined below) can be used to call this
440 function so that the programmer does not need to pass the var_name.
441 sprint_var will figure out the var_name. The sprint_var function is the
442 one that would normally be used by the general user.
443
444 For example, the following python code:
445
446 first_name = "Mike"
447 print_time("Doing this...\n")
448 print_varx("first_name", first_name)
449 print_time("Doing that...\n")
450
451 Will generate output like this:
452
453 #(CDT) 2016/08/10 17:34:42.847374 - 0.001285 - Doing this...
454 first_name: Mike
455 #(CDT) 2016/08/10 17:34:42.847510 - 0.000136 - Doing that...
456
457 This function recognizes several complex types of data such as dict, list
458 or tuple.
459
460 For example, the following python code:
461
462 my_dict = dict(one=1, two=2, three=3)
463 print_var(my_dict)
464
465 Will generate the following output:
466
467 my_dict:
468 my_dict[three]: 3
469 my_dict[two]: 2
470 my_dict[one]: 1
471
472 Description of arguments.
473 var_name The name of the variable to be printed.
474 var_value The value of the variable to be printed.
475 hex This indicates that the value should be
476 printed in hex format. It is the user's
477 responsibility to ensure that a var_value
Michael Walshbec416d2016-11-10 08:54:52 -0600478 contains a valid hex number. For string
479 var_values, this will be interpreted as
480 show_blanks which means that blank values
481 will be printed as "<blank>".
Michael Walshde791732016-09-06 14:25:24 -0500482 loc_col1_indent The number of spaces to indent the output.
483 loc_col1_width The width of the output column containing
484 the variable name. The default value of
485 this is adjusted so that the var_value
486 lines up with text printed via the
487 print_time function.
Michael Walsh7423c012016-10-04 10:27:21 -0500488 trailing_char The character to be used at the end of the
489 returned string. The default value is a
490 line feed.
491 """
Michael Walshde791732016-09-06 14:25:24 -0500492
493 # Determine the type
494 if type(var_value) in (int, float, bool, str, unicode) \
495 or var_value is None:
496 # The data type is simple in the sense that it has no subordinate
497 # parts.
Michael Walsh7423c012016-10-04 10:27:21 -0500498 # Adjust loc_col1_width.
499 loc_col1_width = loc_col1_width - loc_col1_indent
Michael Walshde791732016-09-06 14:25:24 -0500500 # See if the user wants the output in hex format.
501 if hex:
Michael Walsh18176322016-11-15 15:11:21 -0600502 if type(var_value) not in (int, long):
Michael Walshbec416d2016-11-10 08:54:52 -0600503 value_format = "%s"
504 if var_value is "":
505 var_value = "<blank>"
506 else:
507 value_format = "0x%08x"
Michael Walshde791732016-09-06 14:25:24 -0500508 else:
509 value_format = "%s"
510 format_string = "%" + str(loc_col1_indent) + "s%-" \
Michael Walsh7423c012016-10-04 10:27:21 -0500511 + str(loc_col1_width) + "s" + value_format + trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500512 return format_string % ("", var_name + ":", var_value)
513 else:
514 # The data type is complex in the sense that it has subordinate parts.
515 format_string = "%" + str(loc_col1_indent) + "s%s\n"
516 buffer = format_string % ("", var_name + ":")
517 loc_col1_indent += 2
Michael Walsh7423c012016-10-04 10:27:21 -0500518 try:
519 length = len(var_value)
520 except TypeError:
521 pass
522 ix = 0
523 loc_trailing_char = "\n"
Michael Walshbec416d2016-11-10 08:54:52 -0600524 type_is_dict = 0
525 try:
526 if type(var_value) in (dict, collections.OrderedDict):
527 type_is_dict = 1
528 except AttributeError:
Michael Walsha6723f22016-11-22 11:12:01 -0600529 try:
530 if type(var_value) is DotDict:
531 type_is_dict = 1
532 except NameError:
533 pass
Michael Walshbec416d2016-11-10 08:54:52 -0600534 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###############################################################################