blob: 98ab72c1cc33e661fb420eb3350a3712c0f2058d [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
Michael Walshfd2733c2017-11-13 11:36:20 -060019from wrap_utils import *
Michael Walshbec416d2016-11-10 08:54:52 -060020
Michael Walshbec416d2016-11-10 08:54:52 -060021try:
Michael Walsh2ee77cd2017-03-08 11:50:17 -060022 robot_env = 1
Michael Walshbec416d2016-11-10 08:54:52 -060023 from robot.utils import DotDict
Michael Walsh8e6deb42017-01-27 14:22:41 -060024 from robot.utils import NormalizedDict
Michael Walsh2ee77cd2017-03-08 11:50:17 -060025 from robot.libraries.BuiltIn import BuiltIn
Michael Walshb1500152017-04-12 15:42:43 -050026 # Having access to the robot libraries alone does not indicate that we
27 # are in a robot environment. The following try block should confirm that.
28 try:
29 var_value = BuiltIn().get_variable_value("${SUITE_NAME}", "")
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -050030 except BaseException:
Michael Walshb1500152017-04-12 15:42:43 -050031 robot_env = 0
Michael Walshbec416d2016-11-10 08:54:52 -060032except ImportError:
Michael Walsh2ee77cd2017-03-08 11:50:17 -060033 robot_env = 0
Michael Walsh7423c012016-10-04 10:27:21 -050034
35import gen_arg as ga
Michael Walshde791732016-09-06 14:25:24 -050036
37# Setting these variables for use both inside this module and by programs
38# importing this module.
Michael Walshbf605652017-09-01 12:33:26 -050039pgm_file_path = sys.argv[0]
40pgm_name = os.path.basename(pgm_file_path)
Michael Walsh3ba8ecd2018-04-24 11:33:25 -050041pgm_dir_path = os.path.normpath(re.sub("/" + pgm_name, "", pgm_file_path)) +\
42 os.path.sep
Michael Walsh7423c012016-10-04 10:27:21 -050043
Michael Walshde791732016-09-06 14:25:24 -050044
45# Some functions (e.g. sprint_pgm_header) have need of a program name value
46# that looks more like a valid variable name. Therefore, we'll swap odd
47# characters like "." out for underscores.
48pgm_name_var_name = pgm_name.replace(".", "_")
49
50# Initialize global values used as defaults by print_time, print_var, etc.
51col1_indent = 0
52
53# Calculate default column width for print_var functions based on environment
54# variable settings. The objective is to make the variable values line up
55# nicely with the time stamps.
56col1_width = 29
Michael Walshb1500152017-04-12 15:42:43 -050057
58NANOSECONDS = os.environ.get('NANOSECONDS', '1')
59
Michael Walshde791732016-09-06 14:25:24 -050060
61if NANOSECONDS == "1":
62 col1_width = col1_width + 7
63
Michael Walshb1500152017-04-12 15:42:43 -050064SHOW_ELAPSED_TIME = os.environ.get('SHOW_ELAPSED_TIME', '1')
Michael Walshde791732016-09-06 14:25:24 -050065
66if SHOW_ELAPSED_TIME == "1":
67 if NANOSECONDS == "1":
68 col1_width = col1_width + 14
69 else:
70 col1_width = col1_width + 7
71
72# Initialize some time variables used in module functions.
73start_time = time.time()
74sprint_time_last_seconds = start_time
75
Michael Walsh2ee77cd2017-03-08 11:50:17 -060076# The user can set environment variable "GEN_PRINT_DEBUG" to get debug output
77# from this module.
78gen_print_debug = int(os.environ.get('GEN_PRINT_DEBUG', 0))
Michael Walsh7423c012016-10-04 10:27:21 -050079
Michael Walshde791732016-09-06 14:25:24 -050080
Michael Walshde791732016-09-06 14:25:24 -050081def sprint_func_name(stack_frame_ix=None):
Michael Walshde791732016-09-06 14:25:24 -050082 r"""
83 Return the function name associated with the indicated stack frame.
84
85 Description of arguments:
86 stack_frame_ix The index of the stack frame whose
87 function name should be returned. If the
Michael Walsh2ee77cd2017-03-08 11:50:17 -060088 caller does not specify a value, this
Michael Walshde791732016-09-06 14:25:24 -050089 function will set the value to 1 which is
90 the index of the caller's stack frame. If
91 the caller is the wrapper function
92 "print_func_name", this function will bump
93 it up by 1.
94 """
95
96 # If user specified no stack_frame_ix, we'll set it to a proper default
97 # value.
98 if stack_frame_ix is None:
99 func_name = sys._getframe().f_code.co_name
100 caller_func_name = sys._getframe(1).f_code.co_name
101 if func_name[1:] == caller_func_name:
102 stack_frame_ix = 2
103 else:
104 stack_frame_ix = 1
105
106 func_name = sys._getframe(stack_frame_ix).f_code.co_name
107
108 return func_name
109
Michael Walshde791732016-09-06 14:25:24 -0500110
Michael Walsh1173a522018-05-21 17:24:51 -0500111def get_line_indent(line):
112 r"""
113 Return the number of spaces at the beginning of the line.
114 """
115
116 return len(line) - len(line.lstrip(' '))
117
118
Michael Walshde791732016-09-06 14:25:24 -0500119# get_arg_name is not a print function per se. I have included it in this
120# module because it is used by sprint_var which is found in this module.
Michael Walshde791732016-09-06 14:25:24 -0500121def get_arg_name(var,
122 arg_num=1,
123 stack_frame_ix=1):
Michael Walshde791732016-09-06 14:25:24 -0500124 r"""
125 Return the "name" of an argument passed to a function. This could be a
126 literal or a variable name.
127
Michael Walsh2ee77cd2017-03-08 11:50:17 -0600128 Description of arguments:
Michael Walshde791732016-09-06 14:25:24 -0500129 var The variable whose name you want returned.
Michael Walsh1173a522018-05-21 17:24:51 -0500130 arg_num The arg number whose name is to be
131 returned. To illustrate how arg_num is
132 processed, suppose that a programmer codes
133 this line: "rc, outbuf = my_func(var1,
134 var2)" and suppose that my_func has this
135 line of code: "result = gp.get_arg_name(0,
136 arg_num, 2)". If arg_num is positive, the
137 indicated argument is returned. For
138 example, if arg_num is 1, "var1" would be
139 returned, If arg_num is 2, "var2" would be
140 returned. If arg_num exceeds the number
141 of arguments, get_arg_name will simply
142 return a complete list of the arguments.
143 If arg_num is 0, get_arg_name will return
144 the name of the target function as
145 specified in the calling line ("my_func"
146 in this case). To clarify, if the caller
147 of the target function uses an alias
148 function name, the alias name would be
149 returned. If arg_num is negative, an
150 lvalue variable name is returned.
151 Continuing with the given example, if
152 arg_num is -2 the 2nd parm to the left of
153 the "=" ("rc" in this case) should be
154 returned. If arg_num is -1, the 1st parm
155 to the left of the "=" ("out_buf" in this
156 case) should be returned. If arg_num is
157 less than -2, an entire dictionary is
158 returned. The keys to the dictionary for
159 this example would be -2 and -1.
Michael Walshde791732016-09-06 14:25:24 -0500160 stack_frame_ix The stack frame index of the target
161 function. This value must be 1 or
162 greater. 1 would indicate get_arg_name's
163 stack frame. 2 would be the caller of
164 get_arg_name's stack frame, etc.
165
166 Example 1:
167
168 my_var = "mike"
169 var_name = get_arg_name(my_var)
170
171 In this example, var_name will receive the value "my_var".
172
173 Example 2:
174
175 def test1(var):
176 # Getting the var name of the first arg to this function, test1.
177 # Note, in this case, it doesn't matter what you pass as the first arg
178 # to get_arg_name since it is the caller's variable name that matters.
179 dummy = 1
180 arg_num = 1
181 stack_frame = 2
182 var_name = get_arg_name(dummy, arg_num, stack_frame)
183
184 # Mainline...
185
186 another_var = "whatever"
187 test1(another_var)
188
189 In this example, var_name will be set to "another_var".
190
191 """
192
193 # Note: I wish to avoid recursion so I refrain from calling any function
194 # that calls this function (i.e. sprint_var, valid_value, etc.).
195
Michael Walsh23e7f492017-01-10 11:34:47 -0600196 # The user can set environment variable "GET_ARG_NAME_DEBUG" to get debug
197 # output from this function.
198 local_debug = int(os.environ.get('GET_ARG_NAME_DEBUG', 0))
199 # In addition to GET_ARG_NAME_DEBUG, the user can set environment
200 # variable "GET_ARG_NAME_SHOW_SOURCE" to have this function include source
201 # code in the debug output.
202 local_debug_show_source = int(
203 os.environ.get('GET_ARG_NAME_SHOW_SOURCE', 0))
Michael Walshde791732016-09-06 14:25:24 -0500204
Michael Walshde791732016-09-06 14:25:24 -0500205 if stack_frame_ix < 1:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500206 print_error("Programmer error - Variable \"stack_frame_ix\" has an"
207 + " invalid value of \"" + str(stack_frame_ix) + "\". The"
208 + " value must be an integer that is greater than or equal"
209 + " to 1.\n")
Michael Walshde791732016-09-06 14:25:24 -0500210 return
211
212 if local_debug:
213 debug_indent = 2
Michael Walsh23e7f492017-01-10 11:34:47 -0600214 print("")
215 print_dashes(0, 120)
Michael Walshde791732016-09-06 14:25:24 -0500216 print(sprint_func_name() + "() parms:")
217 print_varx("var", var, 0, debug_indent)
218 print_varx("arg_num", arg_num, 0, debug_indent)
219 print_varx("stack_frame_ix", stack_frame_ix, 0, debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600220 print("")
221 print_call_stack(debug_indent, 2)
Michael Walshde791732016-09-06 14:25:24 -0500222
Michael Walsh23e7f492017-01-10 11:34:47 -0600223 for count in range(0, 2):
224 try:
225 frame, filename, cur_line_no, function_name, lines, index = \
226 inspect.stack()[stack_frame_ix]
227 except IndexError:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500228 print_error("Programmer error - The caller has asked for"
229 + " information about the stack frame at index \""
230 + str(stack_frame_ix) + "\". However, the stack"
231 + " only contains " + str(len(inspect.stack()))
232 + " entries. Therefore the stack frame index is out"
233 + " of range.\n")
Michael Walsh23e7f492017-01-10 11:34:47 -0600234 return
235 if filename != "<string>":
236 break
237 # filename of "<string>" may mean that the function in question was
238 # defined dynamically and therefore its code stack is inaccessible.
239 # This may happen with functions like "rqprint_var". In this case,
240 # we'll increment the stack_frame_ix and try again.
241 stack_frame_ix += 1
242 if local_debug:
243 print("Adjusted stack_frame_ix...")
244 print_varx("stack_frame_ix", stack_frame_ix, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500245
Michael Walsh1173a522018-05-21 17:24:51 -0500246 real_called_func_name = sprint_func_name(stack_frame_ix)
Michael Walsh23e7f492017-01-10 11:34:47 -0600247
248 module = inspect.getmodule(frame)
249
250 # Though I would expect inspect.getsourcelines(frame) to get all module
251 # source lines if the frame is "<module>", it doesn't do that. Therefore,
252 # for this special case, I will do inspect.getsourcelines(module).
253 if function_name == "<module>":
254 source_lines, source_line_num =\
255 inspect.getsourcelines(module)
256 line_ix = cur_line_no - source_line_num - 1
257 else:
258 source_lines, source_line_num =\
259 inspect.getsourcelines(frame)
260 line_ix = cur_line_no - source_line_num
261
262 if local_debug:
263 print("\n Variables retrieved from inspect.stack() function:")
264 print_varx("frame", frame, 0, debug_indent + 2)
265 print_varx("filename", filename, 0, debug_indent + 2)
266 print_varx("cur_line_no", cur_line_no, 0, debug_indent + 2)
267 print_varx("function_name", function_name, 0, debug_indent + 2)
268 print_varx("lines", lines, 0, debug_indent + 2)
269 print_varx("index", index, 0, debug_indent + 2)
270 print_varx("source_line_num", source_line_num, 0, debug_indent)
271 print_varx("line_ix", line_ix, 0, debug_indent)
272 if local_debug_show_source:
273 print_varx("source_lines", source_lines, 0, debug_indent)
Michael Walsh1173a522018-05-21 17:24:51 -0500274 print_varx("real_called_func_name", real_called_func_name, 0,
275 debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600276
277 # Get a list of all functions defined for the module. Note that this
278 # doesn't work consistently when _run_exitfuncs is at the top of the stack
279 # (i.e. if we're running an exit function). I've coded a work-around
280 # below for this deficiency.
281 all_functions = inspect.getmembers(module, inspect.isfunction)
282
283 # Get called_func_id by searching for our function in the list of all
284 # functions.
285 called_func_id = None
286 for func_name, function in all_functions:
Michael Walsh1173a522018-05-21 17:24:51 -0500287 if func_name == real_called_func_name:
Michael Walsh23e7f492017-01-10 11:34:47 -0600288 called_func_id = id(function)
289 break
290 # NOTE: The only time I've found that called_func_id can't be found is
291 # when we're running from an exit function.
292
293 # Look for other functions in module with matching id.
Michael Walsh1173a522018-05-21 17:24:51 -0500294 aliases = set([real_called_func_name])
Michael Walsh23e7f492017-01-10 11:34:47 -0600295 for func_name, function in all_functions:
Michael Walsh1173a522018-05-21 17:24:51 -0500296 if func_name == real_called_func_name:
Michael Walsh23e7f492017-01-10 11:34:47 -0600297 continue
298 func_id = id(function)
299 if func_id == called_func_id:
300 aliases.add(func_name)
301
302 # In most cases, my general purpose code above will find all aliases.
303 # However, for the odd case (i.e. running from exit function), I've added
304 # code to handle pvar, qpvar, dpvar, etc. aliases explicitly since they
305 # are defined in this module and used frequently.
306 # pvar is an alias for print_var.
Michael Walsh1173a522018-05-21 17:24:51 -0500307 aliases.add(re.sub("print_var", "pvar", real_called_func_name))
Michael Walsh23e7f492017-01-10 11:34:47 -0600308
Michael Walsh3f248272018-06-01 13:59:35 -0500309 # The call to the function could be encased in a recast (e.g.
310 # int(func_name())).
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500311 recast_regex = "([^ ]+\\([ ]*)?"
312 import_name_regex = "([a-zA-Z0-9_]+\\.)?"
Michael Walsh3f248272018-06-01 13:59:35 -0500313 func_name_regex = recast_regex + import_name_regex + "(" +\
314 '|'.join(aliases) + ")"
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500315 pre_args_regex = ".*" + func_name_regex + "[ ]*\\("
Michael Walsh23e7f492017-01-10 11:34:47 -0600316
317 # Search backward through source lines looking for the calling function
318 # name.
319 found = False
320 for start_line_ix in range(line_ix, 0, -1):
321 # Skip comment lines.
322 if re.match(r"[ ]*#", source_lines[start_line_ix]):
323 continue
Michael Walsh1173a522018-05-21 17:24:51 -0500324 if re.match(pre_args_regex, source_lines[start_line_ix]):
Michael Walsh23e7f492017-01-10 11:34:47 -0600325 found = True
326 break
327 if not found:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500328 print_error("Programmer error - Could not find the source line with"
329 + " a reference to function \"" + real_called_func_name
330 + "\".\n")
Michael Walsh23e7f492017-01-10 11:34:47 -0600331 return
332
Michael Walsh82acf002017-05-04 14:33:05 -0500333 # Search forward through the source lines looking for a line whose
334 # indentation is the same or less than the start line. The end of our
335 # composite line should be the line preceding that line.
Michael Walsh1173a522018-05-21 17:24:51 -0500336 start_indent = get_line_indent(source_lines[start_line_ix])
Michael Walsh37cd29d2018-05-24 13:19:18 -0500337 end_line_ix = line_ix
Michael Walsh23e7f492017-01-10 11:34:47 -0600338 for end_line_ix in range(line_ix + 1, len(source_lines)):
339 if source_lines[end_line_ix].strip() == "":
340 continue
Michael Walsh1173a522018-05-21 17:24:51 -0500341 line_indent = get_line_indent(source_lines[end_line_ix])
Michael Walsh82acf002017-05-04 14:33:05 -0500342 if line_indent <= start_indent:
Michael Walsh23e7f492017-01-10 11:34:47 -0600343 end_line_ix -= 1
344 break
Michael Walsh1173a522018-05-21 17:24:51 -0500345 if start_line_ix != 0:
346 # Check to see whether the start line is a continuation of the prior
347 # line?
348 line_indent = get_line_indent(source_lines[start_line_ix - 1])
349 if line_indent < start_indent:
350 start_line_ix -= 1
351 # Remove the backslash (continuation char).
352 source_lines[start_line_ix] = re.sub(r"[ ]*\\([\r\n]$)",
353 " \\1",
354 source_lines[start_line_ix])
Michael Walsh23e7f492017-01-10 11:34:47 -0600355
356 # Join the start line through the end line into a composite line.
357 composite_line = ''.join(map(str.strip,
Gunnar Mills096cd562018-03-26 10:19:12 -0500358 source_lines[start_line_ix:end_line_ix + 1]))
Michael Walsh1173a522018-05-21 17:24:51 -0500359 # Insert one space after first "=" if there isn't one already.
360 composite_line = re.sub("=[ ]*([^ ])", "= \\1", composite_line, 1)
Michael Walsh7423c012016-10-04 10:27:21 -0500361
Michael Walsh3f248272018-06-01 13:59:35 -0500362 lvalue_regex = "[ ]*=[ ]+" + func_name_regex + ".*"
Michael Walsh1173a522018-05-21 17:24:51 -0500363 lvalue_string = re.sub(lvalue_regex, "", composite_line)
Michael Walsh3f248272018-06-01 13:59:35 -0500364 if lvalue_string == composite_line:
365 # i.e. the regex did not match so there are no lvalues.
366 lvalue_string = ""
367 lvalues_list = filter(None, map(str.strip, lvalue_string.split(",")))
368 try:
369 lvalues = collections.OrderedDict()
370 except AttributeError:
371 # A non-ordered dict doesn't look as nice when printed but it will do.
372 lvalues = {}
Michael Walsh1173a522018-05-21 17:24:51 -0500373 ix = len(lvalues_list) * -1
374 for lvalue in lvalues_list:
375 lvalues[ix] = lvalue
376 ix += 1
Michael Walsh3f248272018-06-01 13:59:35 -0500377 lvalue_prefix_regex = "(.*=[ ]+)?"
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500378 called_func_name_regex = lvalue_prefix_regex + func_name_regex +\
379 "[ ]*\\(.*"
Michael Walsh3f248272018-06-01 13:59:35 -0500380 called_func_name = re.sub(called_func_name_regex, "\\4", composite_line)
Michael Walsh1173a522018-05-21 17:24:51 -0500381 arg_list_etc = "(" + re.sub(pre_args_regex, "", composite_line)
Michael Walshde791732016-09-06 14:25:24 -0500382 if local_debug:
Michael Walsh23e7f492017-01-10 11:34:47 -0600383 print_varx("aliases", aliases, 0, debug_indent)
Michael Walsh3f248272018-06-01 13:59:35 -0500384 print_varx("import_name_regex", import_name_regex, 0, debug_indent)
385 print_varx("func_name_regex", func_name_regex, 0, debug_indent)
Michael Walsh1173a522018-05-21 17:24:51 -0500386 print_varx("pre_args_regex", pre_args_regex, 0, debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600387 print_varx("start_line_ix", start_line_ix, 0, debug_indent)
388 print_varx("end_line_ix", end_line_ix, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500389 print_varx("composite_line", composite_line, 0, debug_indent)
Michael Walsh1173a522018-05-21 17:24:51 -0500390 print_varx("lvalue_regex", lvalue_regex, 0, debug_indent)
391 print_varx("lvalue_string", lvalue_string, 0, debug_indent)
392 print_varx("lvalues", lvalues, 0, debug_indent)
Michael Walsh3f248272018-06-01 13:59:35 -0500393 print_varx("called_func_name_regex", called_func_name_regex, 0,
394 debug_indent)
Michael Walsh1173a522018-05-21 17:24:51 -0500395 print_varx("called_func_name", called_func_name, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500396 print_varx("arg_list_etc", arg_list_etc, 0, debug_indent)
397
398 # Parse arg list...
399 # Initialize...
400 nest_level = -1
401 arg_ix = 0
Michael Walsh7423c012016-10-04 10:27:21 -0500402 args_list = [""]
Michael Walshde791732016-09-06 14:25:24 -0500403 for ix in range(0, len(arg_list_etc)):
404 char = arg_list_etc[ix]
405 # Set the nest_level based on whether we've encounted a parenthesis.
406 if char == "(":
407 nest_level += 1
408 if nest_level == 0:
409 continue
410 elif char == ")":
411 nest_level -= 1
412 if nest_level < 0:
413 break
414
415 # If we reach a comma at base nest level, we are done processing an
Michael Walsh7423c012016-10-04 10:27:21 -0500416 # argument so we increment arg_ix and initialize a new args_list entry.
Michael Walshde791732016-09-06 14:25:24 -0500417 if char == "," and nest_level == 0:
418 arg_ix += 1
Michael Walsh7423c012016-10-04 10:27:21 -0500419 args_list.append("")
Michael Walshde791732016-09-06 14:25:24 -0500420 continue
421
Michael Walsh7423c012016-10-04 10:27:21 -0500422 # For any other character, we append it it to the current arg list
Michael Walshde791732016-09-06 14:25:24 -0500423 # entry.
Michael Walsh7423c012016-10-04 10:27:21 -0500424 args_list[arg_ix] += char
Michael Walshde791732016-09-06 14:25:24 -0500425
426 # Trim whitespace from each list entry.
Michael Walsh7423c012016-10-04 10:27:21 -0500427 args_list = [arg.strip() for arg in args_list]
Michael Walshde791732016-09-06 14:25:24 -0500428
Michael Walsh1173a522018-05-21 17:24:51 -0500429 if arg_num < 0:
430 if abs(arg_num) > len(lvalues):
431 argument = lvalues
432 else:
433 argument = lvalues[arg_num]
434 elif arg_num == 0:
435 argument = called_func_name
Michael Walsh2750b442018-05-18 14:49:11 -0500436 else:
Michael Walsh1173a522018-05-21 17:24:51 -0500437 if arg_num > len(args_list):
438 argument = args_list
439 else:
440 argument = args_list[arg_num - 1]
Michael Walshde791732016-09-06 14:25:24 -0500441
442 if local_debug:
Michael Walsh7423c012016-10-04 10:27:21 -0500443 print_varx("args_list", args_list, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500444 print_varx("argument", argument, 0, debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600445 print_dashes(0, 120)
Michael Walshde791732016-09-06 14:25:24 -0500446
447 return argument
448
Michael Walshde791732016-09-06 14:25:24 -0500449
Michael Walshde791732016-09-06 14:25:24 -0500450def sprint_time(buffer=""):
Michael Walshde791732016-09-06 14:25:24 -0500451 r"""
452 Return the time in the following format.
453
454 Example:
455
456 The following python code...
457
458 sys.stdout.write(sprint_time())
459 sys.stdout.write("Hi.\n")
460
461 Will result in the following type of output:
462
463 #(CDT) 2016/07/08 15:25:35 - Hi.
464
465 Example:
466
467 The following python code...
468
469 sys.stdout.write(sprint_time("Hi.\n"))
470
471 Will result in the following type of output:
472
473 #(CDT) 2016/08/03 17:12:05 - Hi.
474
475 The following environment variables will affect the formatting as
476 described:
477 NANOSECONDS This will cause the time stamps to be
478 precise to the microsecond (Yes, it
479 probably should have been named
480 MICROSECONDS but the convention was set
481 long ago so we're sticking with it).
482 Example of the output when environment
483 variable NANOSECONDS=1.
484
485 #(CDT) 2016/08/03 17:16:25.510469 - Hi.
486
487 SHOW_ELAPSED_TIME This will cause the elapsed time to be
488 included in the output. This is the
489 amount of time that has elapsed since the
490 last time this function was called. The
491 precision of the elapsed time field is
492 also affected by the value of the
493 NANOSECONDS environment variable. Example
494 of the output when environment variable
495 NANOSECONDS=0 and SHOW_ELAPSED_TIME=1.
496
497 #(CDT) 2016/08/03 17:17:40 - 0 - Hi.
498
499 Example of the output when environment variable NANOSECONDS=1 and
500 SHOW_ELAPSED_TIME=1.
501
502 #(CDT) 2016/08/03 17:18:47.317339 - 0.000046 - Hi.
503
504 Description of arguments.
505 buffer This will be appended to the formatted
506 time string.
507 """
508
509 global NANOSECONDS
510 global SHOW_ELAPSED_TIME
511 global sprint_time_last_seconds
512
513 seconds = time.time()
514 loc_time = time.localtime(seconds)
515 nanoseconds = "%0.6f" % seconds
516 pos = nanoseconds.find(".")
517 nanoseconds = nanoseconds[pos:]
518
519 time_string = time.strftime("#(%Z) %Y/%m/%d %H:%M:%S", loc_time)
520 if NANOSECONDS == "1":
521 time_string = time_string + nanoseconds
522
523 if SHOW_ELAPSED_TIME == "1":
524 cur_time_seconds = seconds
525 math_string = "%9.9f" % cur_time_seconds + " - " + "%9.9f" % \
526 sprint_time_last_seconds
527 elapsed_seconds = eval(math_string)
528 if NANOSECONDS == "1":
529 elapsed_seconds = "%11.6f" % elapsed_seconds
530 else:
531 elapsed_seconds = "%4i" % elapsed_seconds
532 sprint_time_last_seconds = cur_time_seconds
533 time_string = time_string + " - " + elapsed_seconds
534
535 return time_string + " - " + buffer
536
Michael Walshde791732016-09-06 14:25:24 -0500537
Michael Walshde791732016-09-06 14:25:24 -0500538def sprint_timen(buffer=""):
Michael Walshde791732016-09-06 14:25:24 -0500539 r"""
540 Append a line feed to the buffer, pass it to sprint_time and return the
541 result.
542 """
543
544 return sprint_time(buffer + "\n")
545
Michael Walshde791732016-09-06 14:25:24 -0500546
Michael Walshde791732016-09-06 14:25:24 -0500547def sprint_error(buffer=""):
Michael Walshde791732016-09-06 14:25:24 -0500548 r"""
549 Return a standardized error string. This includes:
550 - A time stamp
551 - The "**ERROR**" string
552 - The caller's buffer string.
553
554 Example:
555
556 The following python code...
557
558 print(sprint_error("Oops.\n"))
559
560 Will result in the following type of output:
561
562 #(CDT) 2016/08/03 17:12:05 - **ERROR** Oops.
563
564 Description of arguments.
565 buffer This will be appended to the formatted
566 error string.
567 """
568
569 return sprint_time() + "**ERROR** " + buffer
570
Michael Walshde791732016-09-06 14:25:24 -0500571
Michael Walsh3f248272018-06-01 13:59:35 -0500572# Implement "constants" with functions.
573def digit_length_in_bits():
574 r"""
575 Return the digit length in bits.
576 """
577
578 return 4
579
580
581def word_length_in_digits():
582 r"""
583 Return the word length in digits.
584 """
585
586 return 8
587
588
589def bit_length(number):
590 r"""
591 Return the bit length of the number.
592
593 Description of argument(s):
594 number The number to be analyzed.
595 """
596
597 if number < 0:
598 # Convert negative numbers to positive and subtract one. The
599 # following example illustrates the reason for this:
600 # Consider a single nibble whose signed values can range from -8 to 7
601 # (0x8 to 0x7). A value of 0x7 equals 0b0111. Therefore, its length
602 # in bits is 3. Since the negative bit (i.e. 0b1000) is not set, the
603 # value 7 clearly will fit in one nibble. With -8 = 0x8 = 0b1000, you
604 # have the smallest negative value that will fit. Note that it
605 # requires 3 bits of 0. So by converting a number value of -8 to a
606 # working_number of 7, this function can accurately calculate the
607 # number of bits and therefore nibbles required to represent the
608 # number in print.
609 working_number = abs(number) - 1
610 else:
611 working_number = number
612
613 # Handle the special case of the number 0.
614 if working_number == 0:
615 return 0
616
617 return len(bin(working_number)) - 2
618
619
620def get_req_num_hex_digits(number):
621 r"""
622 Return the required number of hex digits required to display the given
623 number.
624
625 The returned value will always be rounded up to the nearest multiple of 8.
626
627 Description of argument(s):
628 number The number to be analyzed.
629 """
630
631 if number < 0:
632 # Convert negative numbers to positive and subtract one. The
633 # following example illustrates the reason for this:
634 # Consider a single nibble whose signed values can range from -8 to 7
635 # (0x8 to 0x7). A value of 0x7 equals 0b0111. Therefore, its length
636 # in bits is 3. Since the negative bit (i.e. 0b1000) is not set, the
637 # value 7 clearly will fit in one nibble. With -8 = 0x8 = 0b1000, you
638 # have the smallest negative value that will fit. Note that it
639 # requires 3 bits of 0. So by converting a number value of -8 to a
640 # working_number of 7, this function can accurately calculate the
641 # number of bits and therefore nibbles required to represent the
642 # number in print.
643 working_number = abs(number) - 1
644 else:
645 working_number = number
646
647 # Handle the special case of the number 0.
648 if working_number == 0:
649 return word_length_in_digits()
650
651 num_length_in_bits = bit_length(working_number)
652 num_hex_digits, remainder = divmod(num_length_in_bits,
653 digit_length_in_bits())
654 if remainder > 0:
655 # Example: the number 7 requires 3 bits. The divmod above produces,
656 # 0 with remainder of 3. So because we have a remainder, we increment
657 # num_hex_digits from 0 to 1.
658 num_hex_digits += 1
659
660 # Check to see whether the negative bit is set. This is the left-most
661 # bit in the highest order digit.
662 negative_mask = 2 ** (num_hex_digits * 4 - 1)
663 if working_number & negative_mask:
664 # If a number that is intended to be positive has its negative bit
665 # on, an additional digit will be required to represent it correctly
666 # in print.
667 num_hex_digits += 1
668
669 num_words, remainder = divmod(num_hex_digits, word_length_in_digits())
670 if remainder > 0 or num_words == 0:
671 num_words += 1
672
673 # Round up to the next word length in digits.
674 return num_words * word_length_in_digits()
675
676
677def dft_num_hex_digits():
678 r"""
679 Return the default number of hex digits to be used to represent a hex
680 number in print.
681
682 The value returned is a function of sys.maxsize.
683 """
684
685 global _gen_print_dft_num_hex_digits_
686 try:
687 return _gen_print_dft_num_hex_digits_
688 except NameError:
689 _gen_print_dft_num_hex_digits_ = get_req_num_hex_digits(sys.maxsize)
690 return _gen_print_dft_num_hex_digits_
691
692
Michael Walshde791732016-09-06 14:25:24 -0500693def sprint_varx(var_name,
694 var_value,
695 hex=0,
696 loc_col1_indent=col1_indent,
Michael Walsh7423c012016-10-04 10:27:21 -0500697 loc_col1_width=col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500698 trailing_char="\n",
699 key_list=None):
Michael Walshde791732016-09-06 14:25:24 -0500700 r"""
701 Print the var name/value passed to it. If the caller lets loc_col1_width
702 default, the printing lines up nicely with output generated by the
703 print_time functions.
704
705 Note that the sprint_var function (defined below) can be used to call this
706 function so that the programmer does not need to pass the var_name.
707 sprint_var will figure out the var_name. The sprint_var function is the
708 one that would normally be used by the general user.
709
710 For example, the following python code:
711
712 first_name = "Mike"
713 print_time("Doing this...\n")
714 print_varx("first_name", first_name)
715 print_time("Doing that...\n")
716
717 Will generate output like this:
718
719 #(CDT) 2016/08/10 17:34:42.847374 - 0.001285 - Doing this...
720 first_name: Mike
721 #(CDT) 2016/08/10 17:34:42.847510 - 0.000136 - Doing that...
722
723 This function recognizes several complex types of data such as dict, list
724 or tuple.
725
726 For example, the following python code:
727
728 my_dict = dict(one=1, two=2, three=3)
729 print_var(my_dict)
730
731 Will generate the following output:
732
733 my_dict:
734 my_dict[three]: 3
735 my_dict[two]: 2
736 my_dict[one]: 1
737
738 Description of arguments.
739 var_name The name of the variable to be printed.
740 var_value The value of the variable to be printed.
741 hex This indicates that the value should be
742 printed in hex format. It is the user's
743 responsibility to ensure that a var_value
Michael Walshbec416d2016-11-10 08:54:52 -0600744 contains a valid hex number. For string
745 var_values, this will be interpreted as
746 show_blanks which means that blank values
Michael Walshd995cb02017-02-07 14:46:01 -0600747 will be printed as "<blank>". For dict
748 var_values, this will be interpreted as
749 terse format where keys are not repeated
750 in the output.
Michael Walshde791732016-09-06 14:25:24 -0500751 loc_col1_indent The number of spaces to indent the output.
752 loc_col1_width The width of the output column containing
753 the variable name. The default value of
754 this is adjusted so that the var_value
755 lines up with text printed via the
756 print_time function.
Michael Walsh7423c012016-10-04 10:27:21 -0500757 trailing_char The character to be used at the end of the
758 returned string. The default value is a
759 line feed.
Michael Walshd2869032018-03-22 16:12:11 -0500760 key_list A list of which dictionary keys should be
761 printed. All others keys will be skipped.
762 Each value in key_list will be regarded
763 as a regular expression and it will be
764 regarded as anchored to the beginning and
765 ends of the dictionary key being
766 referenced. For example if key_list is
767 ["one", "two"], the resulting regex used
768 will be "^one|two$", i.e. only keys "one"
769 and "two" from the var_value dictionary
770 will be printed. As another example, if
771 the caller were to specify a key_list of
772 ["one.*"], then only dictionary keys whose
773 names begin with "one" will be printed.
774 Note: This argument pertains only to
775 var_values which are dictionaries.
Michael Walsh7423c012016-10-04 10:27:21 -0500776 """
Michael Walshde791732016-09-06 14:25:24 -0500777
778 # Determine the type
Michael Walsh92ac3d02018-03-30 14:38:15 -0500779 if type(var_value) in (int, long, float, bool, str, unicode) \
Michael Walshde791732016-09-06 14:25:24 -0500780 or var_value is None:
781 # The data type is simple in the sense that it has no subordinate
782 # parts.
Michael Walsh7423c012016-10-04 10:27:21 -0500783 # Adjust loc_col1_width.
784 loc_col1_width = loc_col1_width - loc_col1_indent
Michael Walshde791732016-09-06 14:25:24 -0500785 # See if the user wants the output in hex format.
786 if hex:
Michael Walsh18176322016-11-15 15:11:21 -0600787 if type(var_value) not in (int, long):
Michael Walshbec416d2016-11-10 08:54:52 -0600788 value_format = "%s"
Michael Walsh2795edc2016-12-13 16:00:33 -0600789 if var_value == "":
Michael Walshbec416d2016-11-10 08:54:52 -0600790 var_value = "<blank>"
791 else:
Michael Walsh3f248272018-06-01 13:59:35 -0500792 num_hex_digits = max(dft_num_hex_digits(),
793 get_req_num_hex_digits(var_value))
794 # Convert a negative number to its positive twos complement
795 # for proper printing. For example, instead of printing -1 as
796 # "0x-000000000000001" it will be printed as
797 # "0xffffffffffffffff".
798 var_value = var_value & (2 ** (num_hex_digits * 4) - 1)
799 value_format = "0x%0" + str(num_hex_digits) + "x"
Michael Walshde791732016-09-06 14:25:24 -0500800 else:
801 value_format = "%s"
802 format_string = "%" + str(loc_col1_indent) + "s%-" \
Michael Walsh7423c012016-10-04 10:27:21 -0500803 + str(loc_col1_width) + "s" + value_format + trailing_char
Michael Walsh3383e652017-09-01 17:10:59 -0500804 if value_format == "0x%08x":
805 return format_string % ("", str(var_name) + ":",
806 var_value & 0xffffffff)
807 else:
808 return format_string % ("", str(var_name) + ":", var_value)
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500809 elif isinstance(var_value, type):
Michael Walsh20a87ab2017-06-30 17:00:30 -0500810 return sprint_varx(var_name, str(var_value).split("'")[1], hex,
Michael Walshd2869032018-03-22 16:12:11 -0500811 loc_col1_indent, loc_col1_width, trailing_char,
812 key_list)
Michael Walshde791732016-09-06 14:25:24 -0500813 else:
814 # The data type is complex in the sense that it has subordinate parts.
815 format_string = "%" + str(loc_col1_indent) + "s%s\n"
816 buffer = format_string % ("", var_name + ":")
817 loc_col1_indent += 2
Michael Walsh7423c012016-10-04 10:27:21 -0500818 try:
819 length = len(var_value)
820 except TypeError:
Michael Walsh23e7f492017-01-10 11:34:47 -0600821 length = 0
Michael Walsh7423c012016-10-04 10:27:21 -0500822 ix = 0
823 loc_trailing_char = "\n"
Michael Walshbec416d2016-11-10 08:54:52 -0600824 type_is_dict = 0
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500825 if isinstance(var_value, dict):
Michael Walsh23e7f492017-01-10 11:34:47 -0600826 type_is_dict = 1
Michael Walsh8e6deb42017-01-27 14:22:41 -0600827 try:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500828 if isinstance(var_value, collections.OrderedDict):
Michael Walsh8e6deb42017-01-27 14:22:41 -0600829 type_is_dict = 1
830 except AttributeError:
831 pass
832 try:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500833 if isinstance(var_value, DotDict):
Michael Walsh8e6deb42017-01-27 14:22:41 -0600834 type_is_dict = 1
835 except NameError:
836 pass
837 try:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500838 if isinstance(var_value, NormalizedDict):
Michael Walsh8e6deb42017-01-27 14:22:41 -0600839 type_is_dict = 1
840 except NameError:
841 pass
Michael Walshbec416d2016-11-10 08:54:52 -0600842 if type_is_dict:
Michael Walshde791732016-09-06 14:25:24 -0500843 for key, value in var_value.iteritems():
Michael Walshd2869032018-03-22 16:12:11 -0500844 if key_list is not None:
845 key_list_regex = "^" + "|".join(key_list) + "$"
846 if not re.match(key_list_regex, key):
847 continue
Michael Walsh7423c012016-10-04 10:27:21 -0500848 ix += 1
849 if ix == length:
850 loc_trailing_char = trailing_char
Michael Walshd995cb02017-02-07 14:46:01 -0600851 if hex:
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600852 # Since hex is being used as a format type, we want it
853 # turned off when processing integer dictionary values so
854 # it is not interpreted as a hex indicator.
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500855 loc_hex = not (isinstance(value, int))
Michael Walshf7b8a002017-08-29 10:38:39 -0500856 buffer += sprint_varx("[" + key + "]", value,
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600857 loc_hex, loc_col1_indent,
858 loc_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500859 loc_trailing_char,
860 key_list)
Michael Walshd995cb02017-02-07 14:46:01 -0600861 else:
Michael Walsh1173a522018-05-21 17:24:51 -0500862 buffer += sprint_varx(var_name + "[" + str(key) + "]",
863 value, hex, loc_col1_indent,
864 loc_col1_width, loc_trailing_char,
865 key_list)
Michael Walsh7423c012016-10-04 10:27:21 -0500866 elif type(var_value) in (list, tuple, set):
Michael Walshde791732016-09-06 14:25:24 -0500867 for key, value in enumerate(var_value):
Michael Walsh7423c012016-10-04 10:27:21 -0500868 ix += 1
869 if ix == length:
870 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500871 buffer += sprint_varx(var_name + "[" + str(key) + "]", value,
Michael Walsh7423c012016-10-04 10:27:21 -0500872 hex, loc_col1_indent, loc_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500873 loc_trailing_char, key_list)
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500874 elif isinstance(var_value, argparse.Namespace):
Michael Walshde791732016-09-06 14:25:24 -0500875 for key in var_value.__dict__:
Michael Walsh7423c012016-10-04 10:27:21 -0500876 ix += 1
877 if ix == length:
878 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500879 cmd_buf = "buffer += sprint_varx(var_name + \".\" + str(key)" \
Michael Walsh7423c012016-10-04 10:27:21 -0500880 + ", var_value." + key + ", hex, loc_col1_indent," \
Michael Walshd2869032018-03-22 16:12:11 -0500881 + " loc_col1_width, loc_trailing_char, key_list)"
Michael Walshde791732016-09-06 14:25:24 -0500882 exec(cmd_buf)
883 else:
884 var_type = type(var_value).__name__
885 func_name = sys._getframe().f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -0500886 var_value = "<" + var_type + " type not supported by " + \
887 func_name + "()>"
Michael Walshde791732016-09-06 14:25:24 -0500888 value_format = "%s"
889 loc_col1_indent -= 2
Michael Walsh7423c012016-10-04 10:27:21 -0500890 # Adjust loc_col1_width.
891 loc_col1_width = loc_col1_width - loc_col1_indent
Michael Walshde791732016-09-06 14:25:24 -0500892 format_string = "%" + str(loc_col1_indent) + "s%-" \
Michael Walsh7423c012016-10-04 10:27:21 -0500893 + str(loc_col1_width) + "s" + value_format + trailing_char
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600894 return format_string % ("", str(var_name) + ":", var_value)
Michael Walsh23e7f492017-01-10 11:34:47 -0600895
Michael Walshde791732016-09-06 14:25:24 -0500896 return buffer
897
898 return ""
899
Michael Walshde791732016-09-06 14:25:24 -0500900
Michael Walshfd2733c2017-11-13 11:36:20 -0600901def sprint_var(var_value,
902 hex=0,
903 loc_col1_indent=col1_indent,
904 loc_col1_width=col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500905 trailing_char="\n",
906 key_list=None):
Michael Walshde791732016-09-06 14:25:24 -0500907 r"""
908 Figure out the name of the first argument for you and then call
909 sprint_varx with it. Therefore, the following 2 calls are equivalent:
910 sprint_varx("var1", var1)
911 sprint_var(var1)
912 """
913
914 # Get the name of the first variable passed to this function.
915 stack_frame = 2
Michael Walsh7423c012016-10-04 10:27:21 -0500916 caller_func_name = sprint_func_name(2)
917 if caller_func_name.endswith("print_var"):
Michael Walshde791732016-09-06 14:25:24 -0500918 stack_frame += 1
919 var_name = get_arg_name(None, 1, stack_frame)
Michael Walshfd2733c2017-11-13 11:36:20 -0600920 return sprint_varx(var_name, var_value=var_value, hex=hex,
921 loc_col1_indent=loc_col1_indent,
922 loc_col1_width=loc_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500923 trailing_char=trailing_char,
924 key_list=key_list)
Michael Walshde791732016-09-06 14:25:24 -0500925
926
Michael Walsh18176322016-11-15 15:11:21 -0600927def sprint_vars(*args):
Michael Walsh18176322016-11-15 15:11:21 -0600928 r"""
929 Sprint the values of one or more variables.
930
931 Description of args:
932 args:
933 If the first argument is an integer, it will be interpreted to be the
934 "indent" value.
935 If the second argument is an integer, it will be interpreted to be the
936 "col1_width" value.
937 If the third argument is an integer, it will be interpreted to be the
938 "hex" value.
939 All remaining parms are considered variable names which are to be
940 sprinted.
941 """
942
943 if len(args) == 0:
944 return
945
946 # Get the name of the first variable passed to this function.
947 stack_frame = 2
948 caller_func_name = sprint_func_name(2)
949 if caller_func_name.endswith("print_vars"):
950 stack_frame += 1
951
952 parm_num = 1
953
954 # Create list from args (which is a tuple) so that it can be modified.
955 args_list = list(args)
956
957 var_name = get_arg_name(None, parm_num, stack_frame)
958 # See if parm 1 is to be interpreted as "indent".
959 try:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500960 if isinstance(int(var_name), int):
Michael Walsh18176322016-11-15 15:11:21 -0600961 indent = int(var_name)
962 args_list.pop(0)
963 parm_num += 1
964 except ValueError:
965 indent = 0
966
967 var_name = get_arg_name(None, parm_num, stack_frame)
968 # See if parm 1 is to be interpreted as "col1_width".
969 try:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500970 if isinstance(int(var_name), int):
Michael Walsh18176322016-11-15 15:11:21 -0600971 loc_col1_width = int(var_name)
972 args_list.pop(0)
973 parm_num += 1
974 except ValueError:
975 loc_col1_width = col1_width
976
977 var_name = get_arg_name(None, parm_num, stack_frame)
978 # See if parm 1 is to be interpreted as "hex".
979 try:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500980 if isinstance(int(var_name), int):
Michael Walsh18176322016-11-15 15:11:21 -0600981 hex = int(var_name)
982 args_list.pop(0)
983 parm_num += 1
984 except ValueError:
985 hex = 0
986
987 buffer = ""
988 for var_value in args_list:
989 var_name = get_arg_name(None, parm_num, stack_frame)
990 buffer += sprint_varx(var_name, var_value, hex, indent, loc_col1_width)
991 parm_num += 1
992
993 return buffer
994
Michael Walsh18176322016-11-15 15:11:21 -0600995
Michael Walsh7423c012016-10-04 10:27:21 -0500996def sprint_dashes(indent=col1_indent,
997 width=80,
998 line_feed=1,
999 char="-"):
Michael Walshde791732016-09-06 14:25:24 -05001000 r"""
1001 Return a string of dashes to the caller.
1002
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001003 Description of arguments:
Michael Walshde791732016-09-06 14:25:24 -05001004 indent The number of characters to indent the
1005 output.
1006 width The width of the string of dashes.
1007 line_feed Indicates whether the output should end
1008 with a line feed.
Michael Walsh7423c012016-10-04 10:27:21 -05001009 char The character to be repeated in the output
1010 string.
Michael Walshde791732016-09-06 14:25:24 -05001011 """
1012
Michael Walsh7423c012016-10-04 10:27:21 -05001013 width = int(width)
Michael Walsh23e7f492017-01-10 11:34:47 -06001014 buffer = " " * int(indent) + char * width
Michael Walshde791732016-09-06 14:25:24 -05001015 if line_feed:
1016 buffer += "\n"
1017
1018 return buffer
1019
Michael Walshde791732016-09-06 14:25:24 -05001020
Michael Walsh7423c012016-10-04 10:27:21 -05001021def sindent(text="",
1022 indent=0):
Michael Walsh7423c012016-10-04 10:27:21 -05001023 r"""
1024 Pre-pend the specified number of characters to the text string (i.e.
1025 indent it) and return it.
1026
1027 Description of arguments:
1028 text The string to be indented.
1029 indent The number of characters to indent the
1030 string.
1031 """
1032
1033 format_string = "%" + str(indent) + "s%s"
1034 buffer = format_string % ("", text)
1035
1036 return buffer
1037
Michael Walsh7423c012016-10-04 10:27:21 -05001038
Michael Walsh7423c012016-10-04 10:27:21 -05001039def sprint_call_stack(indent=0,
1040 stack_frame_ix=0):
Michael Walshde791732016-09-06 14:25:24 -05001041 r"""
1042 Return a call stack report for the given point in the program with line
1043 numbers, function names and function parameters and arguments.
1044
1045 Sample output:
1046
1047 -------------------------------------------------------------------------
1048 Python function call stack
1049
1050 Line # Function name and arguments
1051 ------ ------------------------------------------------------------------
1052 424 sprint_call_stack ()
1053 4 print_call_stack ()
1054 31 func1 (last_name = 'walsh', first_name = 'mikey')
1055 59 /tmp/scr5.py
1056 -------------------------------------------------------------------------
1057
1058 Description of arguments:
1059 indent The number of characters to indent each
1060 line of output.
1061 stack_frame_ix The index of the first stack frame which
1062 is to be returned.
1063 """
1064
1065 buffer = ""
Michael Walsh7423c012016-10-04 10:27:21 -05001066 buffer += sprint_dashes(indent)
1067 buffer += sindent("Python function call stack\n\n", indent)
1068 buffer += sindent("Line # Function name and arguments\n", indent)
1069 buffer += sprint_dashes(indent, 6, 0) + " " + sprint_dashes(0, 73)
Michael Walshde791732016-09-06 14:25:24 -05001070
1071 # Grab the current program stack.
1072 current_stack = inspect.stack()
1073
1074 # Process each frame in turn.
1075 format_string = "%6s %s\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001076 ix = 0
Michael Walshde791732016-09-06 14:25:24 -05001077 for stack_frame in current_stack:
Michael Walsh7423c012016-10-04 10:27:21 -05001078 if ix < stack_frame_ix:
1079 ix += 1
1080 continue
Michael Walsh23e7f492017-01-10 11:34:47 -06001081 # I want the line number shown to be the line where you find the line
1082 # shown.
1083 try:
1084 line_num = str(current_stack[ix + 1][2])
1085 except IndexError:
1086 line_num = ""
Michael Walshde791732016-09-06 14:25:24 -05001087 func_name = str(stack_frame[3])
1088 if func_name == "?":
1089 # "?" is the name used when code is not in a function.
1090 func_name = "(none)"
1091
1092 if func_name == "<module>":
Michael Walsh7423c012016-10-04 10:27:21 -05001093 # If the func_name is the "main" program, we simply get the
1094 # command line call string.
Michael Walshde791732016-09-06 14:25:24 -05001095 func_and_args = ' '.join(sys.argv)
1096 else:
1097 # Get the program arguments.
1098 arg_vals = inspect.getargvalues(stack_frame[0])
1099 function_parms = arg_vals[0]
1100 frame_locals = arg_vals[3]
1101
Michael Walsh7423c012016-10-04 10:27:21 -05001102 args_list = []
Michael Walshde791732016-09-06 14:25:24 -05001103 for arg_name in function_parms:
1104 # Get the arg value from frame locals.
1105 arg_value = frame_locals[arg_name]
Michael Walsh7423c012016-10-04 10:27:21 -05001106 args_list.append(arg_name + " = " + repr(arg_value))
1107 args_str = "(" + ', '.join(map(str, args_list)) + ")"
Michael Walshde791732016-09-06 14:25:24 -05001108
1109 # Now we need to print this in a nicely-wrapped way.
1110 func_and_args = func_name + " " + args_str
1111
Michael Walsh23e7f492017-01-10 11:34:47 -06001112 buffer += sindent(format_string % (line_num, func_and_args), indent)
Michael Walsh7423c012016-10-04 10:27:21 -05001113 ix += 1
Michael Walshde791732016-09-06 14:25:24 -05001114
Michael Walsh7423c012016-10-04 10:27:21 -05001115 buffer += sprint_dashes(indent)
Michael Walshde791732016-09-06 14:25:24 -05001116
1117 return buffer
1118
Michael Walshde791732016-09-06 14:25:24 -05001119
Michael Walshde791732016-09-06 14:25:24 -05001120def sprint_executing(stack_frame_ix=None):
Michael Walshde791732016-09-06 14:25:24 -05001121 r"""
1122 Print a line indicating what function is executing and with what parameter
1123 values. This is useful for debugging.
1124
1125 Sample output:
1126
1127 #(CDT) 2016/08/25 17:54:27 - Executing: func1 (x = 1)
1128
1129 Description of arguments:
1130 stack_frame_ix The index of the stack frame whose
1131 function info should be returned. If the
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001132 caller does not specify a value, this
Michael Walshde791732016-09-06 14:25:24 -05001133 function will set the value to 1 which is
1134 the index of the caller's stack frame. If
1135 the caller is the wrapper function
1136 "print_executing", this function will bump
1137 it up by 1.
1138 """
1139
1140 # If user wants default stack_frame_ix.
1141 if stack_frame_ix is None:
1142 func_name = sys._getframe().f_code.co_name
1143 caller_func_name = sys._getframe(1).f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -05001144 if caller_func_name.endswith(func_name[1:]):
Michael Walshde791732016-09-06 14:25:24 -05001145 stack_frame_ix = 2
1146 else:
1147 stack_frame_ix = 1
1148
1149 stack_frame = inspect.stack()[stack_frame_ix]
1150
1151 func_name = str(stack_frame[3])
1152 if func_name == "?":
1153 # "?" is the name used when code is not in a function.
1154 func_name = "(none)"
1155
1156 if func_name == "<module>":
1157 # If the func_name is the "main" program, we simply get the command
1158 # line call string.
1159 func_and_args = ' '.join(sys.argv)
1160 else:
1161 # Get the program arguments.
1162 arg_vals = inspect.getargvalues(stack_frame[0])
1163 function_parms = arg_vals[0]
1164 frame_locals = arg_vals[3]
1165
Michael Walsh7423c012016-10-04 10:27:21 -05001166 args_list = []
Michael Walshde791732016-09-06 14:25:24 -05001167 for arg_name in function_parms:
1168 # Get the arg value from frame locals.
1169 arg_value = frame_locals[arg_name]
Michael Walsh7423c012016-10-04 10:27:21 -05001170 args_list.append(arg_name + " = " + repr(arg_value))
1171 args_str = "(" + ', '.join(map(str, args_list)) + ")"
Michael Walshde791732016-09-06 14:25:24 -05001172
1173 # Now we need to print this in a nicely-wrapped way.
1174 func_and_args = func_name + " " + args_str
1175
1176 return sprint_time() + "Executing: " + func_and_args + "\n"
1177
Michael Walshde791732016-09-06 14:25:24 -05001178
Michael Walshbec416d2016-11-10 08:54:52 -06001179def sprint_pgm_header(indent=0,
1180 linefeed=1):
Michael Walshde791732016-09-06 14:25:24 -05001181 r"""
1182 Return a standardized header that programs should print at the beginning
1183 of the run. It includes useful information like command line, pid,
1184 userid, program parameters, etc.
1185
Michael Walsh7423c012016-10-04 10:27:21 -05001186 Description of arguments:
1187 indent The number of characters to indent each
1188 line of output.
Michael Walshbec416d2016-11-10 08:54:52 -06001189 linefeed Indicates whether a line feed be included
1190 at the beginning and end of the report.
Michael Walshde791732016-09-06 14:25:24 -05001191 """
1192
Michael Walshbec416d2016-11-10 08:54:52 -06001193 loc_col1_width = col1_width + indent
1194
1195 buffer = ""
1196 if linefeed:
1197 buffer = "\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001198
Michael Walshdb6e68a2017-05-23 17:55:31 -05001199 if robot_env:
1200 suite_name = BuiltIn().get_variable_value("${suite_name}")
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001201 buffer += sindent(sprint_time("Running test suite \"" + suite_name
1202 + "\".\n"), indent)
Michael Walshdb6e68a2017-05-23 17:55:31 -05001203
Michael Walsh7423c012016-10-04 10:27:21 -05001204 buffer += sindent(sprint_time() + "Running " + pgm_name + ".\n", indent)
1205 buffer += sindent(sprint_time() + "Program parameter values, etc.:\n\n",
1206 indent)
Michael Walshbec416d2016-11-10 08:54:52 -06001207 buffer += sprint_varx("command_line", ' '.join(sys.argv), 0, indent,
1208 loc_col1_width)
Michael Walsh7423c012016-10-04 10:27:21 -05001209 # We want the output to show a customized name for the pid and pgid but
1210 # we want it to look like a valid variable name. Therefore, we'll use
Michael Walshde791732016-09-06 14:25:24 -05001211 # pgm_name_var_name which was set when this module was imported.
Michael Walshbec416d2016-11-10 08:54:52 -06001212 buffer += sprint_varx(pgm_name_var_name + "_pid", os.getpid(), 0, indent,
1213 loc_col1_width)
1214 buffer += sprint_varx(pgm_name_var_name + "_pgid", os.getpgrp(), 0, indent,
1215 loc_col1_width)
Michael Walsh86de0d22016-12-05 10:13:15 -06001216 userid_num = str(os.geteuid())
1217 try:
1218 username = os.getlogin()
1219 except OSError:
1220 if userid_num == "0":
1221 username = "root"
1222 else:
1223 username = "?"
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001224 buffer += sprint_varx("uid", userid_num + " (" + username
1225 + ")", 0, indent, loc_col1_width)
1226 buffer += sprint_varx("gid", str(os.getgid()) + " ("
1227 + str(grp.getgrgid(os.getgid()).gr_name) + ")", 0,
Michael Walshbec416d2016-11-10 08:54:52 -06001228 indent, loc_col1_width)
1229 buffer += sprint_varx("host_name", socket.gethostname(), 0, indent,
1230 loc_col1_width)
Michael Walsh86de0d22016-12-05 10:13:15 -06001231 try:
1232 DISPLAY = os.environ['DISPLAY']
1233 except KeyError:
1234 DISPLAY = ""
1235 buffer += sprint_varx("DISPLAY", DISPLAY, 0, indent,
Michael Walshbec416d2016-11-10 08:54:52 -06001236 loc_col1_width)
Michael Walshde791732016-09-06 14:25:24 -05001237 # I want to add code to print caller's parms.
1238
Michael Walsh7423c012016-10-04 10:27:21 -05001239 # __builtin__.arg_obj is created by the get_arg module function,
1240 # gen_get_options.
1241 try:
1242 buffer += ga.sprint_args(__builtin__.arg_obj, indent)
1243 except AttributeError:
1244 pass
1245
Michael Walshdb6e68a2017-05-23 17:55:31 -05001246 if robot_env:
1247 # Get value of global parm_list.
1248 parm_list = BuiltIn().get_variable_value("${parm_list}")
1249
1250 for parm in parm_list:
1251 parm_value = BuiltIn().get_variable_value("${" + parm + "}")
1252 buffer += sprint_varx(parm, parm_value, 0, indent, loc_col1_width)
1253
1254 # Setting global program_pid.
1255 BuiltIn().set_global_variable("${program_pid}", os.getpid())
1256
Michael Walshbec416d2016-11-10 08:54:52 -06001257 if linefeed:
1258 buffer += "\n"
Michael Walshde791732016-09-06 14:25:24 -05001259
1260 return buffer
1261
Michael Walshde791732016-09-06 14:25:24 -05001262
Michael Walsh7423c012016-10-04 10:27:21 -05001263def sprint_error_report(error_text="\n",
Michael Walshdb6e68a2017-05-23 17:55:31 -05001264 indent=2,
1265 format=None):
Michael Walsh7423c012016-10-04 10:27:21 -05001266 r"""
1267 Return a string with a standardized report which includes the caller's
1268 error text, the call stack and the program header.
1269
1270 Description of args:
1271 error_text The error text to be included in the
1272 report. The caller should include any
1273 needed linefeeds.
1274 indent The number of characters to indent each
1275 line of output.
Michael Walshdb6e68a2017-05-23 17:55:31 -05001276 format Long or short format. Long includes
1277 extras like lines of dashes, call stack,
1278 etc.
Michael Walsh7423c012016-10-04 10:27:21 -05001279 """
1280
Michael Walshdb6e68a2017-05-23 17:55:31 -05001281 # Process input.
1282 indent = int(indent)
1283 if format is None:
1284 if robot_env:
1285 format = 'short'
1286 else:
1287 format = 'long'
1288 error_text = error_text.rstrip('\n') + '\n'
1289
1290 if format == 'short':
1291 return sprint_error(error_text)
1292
Michael Walsh7423c012016-10-04 10:27:21 -05001293 buffer = ""
1294 buffer += sprint_dashes(width=120, char="=")
1295 buffer += sprint_error(error_text)
1296 buffer += "\n"
1297 # Calling sprint_call_stack with stack_frame_ix of 0 causes it to show
1298 # itself and this function in the call stack. This is not helpful to a
1299 # debugger and is therefore clutter. We will adjust the stack_frame_ix to
1300 # hide that information.
Michael Walsh9c75f672017-09-12 17:11:35 -05001301 stack_frame_ix = 1
Michael Walsh7423c012016-10-04 10:27:21 -05001302 caller_func_name = sprint_func_name(2)
1303 if caller_func_name.endswith("print_error_report"):
1304 stack_frame_ix += 1
Michael Walshdb6e68a2017-05-23 17:55:31 -05001305 if not robot_env:
1306 buffer += sprint_call_stack(indent, stack_frame_ix)
Michael Walsh7423c012016-10-04 10:27:21 -05001307 buffer += sprint_pgm_header(indent)
1308 buffer += sprint_dashes(width=120, char="=")
1309
1310 return buffer
1311
Michael Walsh7423c012016-10-04 10:27:21 -05001312
Michael Walsh18176322016-11-15 15:11:21 -06001313def sprint_issuing(cmd_buf,
1314 test_mode=0):
Michael Walshde791732016-09-06 14:25:24 -05001315 r"""
1316 Return a line indicating a command that the program is about to execute.
1317
1318 Sample output for a cmd_buf of "ls"
1319
1320 #(CDT) 2016/08/25 17:57:36 - Issuing: ls
Michael Walshbec416d2016-11-10 08:54:52 -06001321
Michael Walshde791732016-09-06 14:25:24 -05001322 Description of args:
1323 cmd_buf The command to be executed by caller.
Michael Walshbec416d2016-11-10 08:54:52 -06001324 test_mode With test_mode set, your output will look
1325 like this:
1326
1327 #(CDT) 2016/08/25 17:57:36 - (test_mode) Issuing: ls
1328
Michael Walshde791732016-09-06 14:25:24 -05001329 """
1330
Michael Walshbec416d2016-11-10 08:54:52 -06001331 buffer = sprint_time()
1332 if test_mode:
1333 buffer += "(test_mode) "
1334 buffer += "Issuing: " + cmd_buf + "\n"
Michael Walshde791732016-09-06 14:25:24 -05001335
1336 return buffer
1337
Michael Walshde791732016-09-06 14:25:24 -05001338
Michael Walshde791732016-09-06 14:25:24 -05001339def sprint_pgm_footer():
Michael Walshde791732016-09-06 14:25:24 -05001340 r"""
1341 Return a standardized footer that programs should print at the end of the
1342 program run. It includes useful information like total run time, etc.
1343 """
1344
1345 buffer = "\n" + sprint_time() + "Finished running " + pgm_name + ".\n\n"
1346
1347 total_time = time.time() - start_time
1348 total_time_string = "%0.6f" % total_time
1349
Michael Walsh7423c012016-10-04 10:27:21 -05001350 buffer += sprint_varx(pgm_name_var_name + "_runtime", total_time_string)
Michael Walshbec416d2016-11-10 08:54:52 -06001351 buffer += "\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001352
1353 return buffer
1354
Michael Walsh7423c012016-10-04 10:27:21 -05001355
Michael Walsh7423c012016-10-04 10:27:21 -05001356def sprint(buffer=""):
Michael Walsh7423c012016-10-04 10:27:21 -05001357 r"""
1358 Simply return the user's buffer. This function is used by the qprint and
1359 dprint functions defined dynamically below, i.e. it would not normally be
1360 called for general use.
1361
1362 Description of arguments.
1363 buffer This will be returned to the caller.
1364 """
Michael Walshde791732016-09-06 14:25:24 -05001365
Michael Walsh95e45102018-02-09 12:44:43 -06001366 try:
1367 return str(buffer)
1368 except UnicodeEncodeError:
1369 return buffer
Michael Walshbec416d2016-11-10 08:54:52 -06001370
Michael Walshbec416d2016-11-10 08:54:52 -06001371
Michael Walshbec416d2016-11-10 08:54:52 -06001372def sprintn(buffer=""):
Michael Walshbec416d2016-11-10 08:54:52 -06001373 r"""
1374 Simply return the user's buffer with a line feed. This function is used
1375 by the qprint and dprint functions defined dynamically below, i.e. it
1376 would not normally be called for general use.
1377
1378 Description of arguments.
1379 buffer This will be returned to the caller.
1380 """
1381
Michael Walsh95e45102018-02-09 12:44:43 -06001382 try:
1383 buffer = str(buffer) + "\n"
1384 except UnicodeEncodeError:
1385 buffer = buffer + "\n"
Michael Walshbec416d2016-11-10 08:54:52 -06001386
Michael Walshde791732016-09-06 14:25:24 -05001387 return buffer
1388
Michael Walsh168eb0f2017-12-01 15:35:32 -06001389
Michael Walshfd2733c2017-11-13 11:36:20 -06001390def gp_print(buffer,
1391 stream='stdout'):
Michael Walshfd2733c2017-11-13 11:36:20 -06001392 r"""
1393 Print the buffer using either sys.stdout.write or BuiltIn().log_to_console
1394 depending on whether we are running in a robot environment.
1395
1396 This function is intended for use only by other functions in this module.
1397
1398 Description of arguments:
1399 buffer The string to be printed.
1400 stream Either "stdout" or "stderr".
1401 """
1402
1403 if robot_env:
1404 BuiltIn().log_to_console(buffer, stream=stream, no_newline=True)
1405 else:
1406 if stream == "stdout":
1407 sys.stdout.write(buffer)
1408 sys.stdout.flush()
1409 else:
1410 sys.stderr.write(buffer)
1411 sys.stderr.flush()
Michael Walshde791732016-09-06 14:25:24 -05001412
1413
Michael Walsh168eb0f2017-12-01 15:35:32 -06001414def gp_log(buffer):
Michael Walsh168eb0f2017-12-01 15:35:32 -06001415 r"""
1416 Log the buffer using either python logging or BuiltIn().log depending on
1417 whether we are running in a robot environment.
1418
1419 This function is intended for use only by other functions in this module.
1420
1421 Description of arguments:
1422 buffer The string to be logged.
1423 """
1424
1425 if robot_env:
1426 BuiltIn().log(buffer)
1427 else:
1428 logging.warning(buffer)
1429
1430
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001431def gp_debug_print(buffer):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001432 r"""
Michael Walshfd2733c2017-11-13 11:36:20 -06001433 Print with gp_print only if gen_print_debug is set.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001434
1435 This function is intended for use only by other functions in this module.
1436
1437 Description of arguments:
1438 buffer The string to be printed.
1439 """
1440
1441 if not gen_print_debug:
1442 return
1443
Michael Walshfd2733c2017-11-13 11:36:20 -06001444 gp_print(buffer)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001445
1446
Michael Walshb1500152017-04-12 15:42:43 -05001447def get_var_value(var_value=None,
1448 default=1,
1449 var_name=None):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001450 r"""
Michael Walshb1500152017-04-12 15:42:43 -05001451 Return either var_value, the corresponding global value or default.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001452
Michael Walshb1500152017-04-12 15:42:43 -05001453 If var_value is not None, it will simply be returned.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001454
Michael Walshb1500152017-04-12 15:42:43 -05001455 If var_value is None, this function will return the corresponding global
1456 value of the variable in question.
1457
1458 Note: For global values, if we are in a robot environment,
1459 get_variable_value will be used. Otherwise, the __builtin__ version of
1460 the variable is returned (which are set by gen_arg.py functions).
1461
1462 If there is no global value associated with the variable, default is
1463 returned.
1464
1465 This function is useful for other functions in setting default values for
1466 parameters.
1467
1468 Example use:
1469
1470 def my_func(quiet=None):
1471
1472 quiet = int(get_var_value(quiet, 0))
1473
1474 Example calls to my_func():
1475
1476 In the following example, the caller is explicitly asking to have quiet be
1477 set to 1.
1478
1479 my_func(quiet=1)
1480
1481 In the following example, quiet will be set to the global value of quiet,
1482 if defined, or to 0 (the default).
1483
1484 my_func()
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001485
1486 Description of arguments:
Michael Walshb1500152017-04-12 15:42:43 -05001487 var_value The value to be returned (if not equal to
1488 None).
1489 default The value that is returned if var_value is
1490 None and there is no corresponding global
1491 value defined.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001492 var_name The name of the variable whose value is to
Michael Walshb1500152017-04-12 15:42:43 -05001493 be returned. Under most circumstances,
1494 this value need not be provided. This
1495 function can figure out the name of the
1496 variable passed as var_value. One
1497 exception to this would be if this
1498 function is called directly from a .robot
1499 file.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001500 """
1501
Michael Walshb1500152017-04-12 15:42:43 -05001502 if var_value is not None:
1503 return var_value
1504
1505 if var_name is None:
1506 var_name = get_arg_name(None, 1, 2)
1507
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001508 if robot_env:
Michael Walshc6537442017-06-06 15:33:52 -05001509 var_value = BuiltIn().get_variable_value("${" + var_name + "}",
1510 default)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001511 else:
1512 var_value = getattr(__builtin__, var_name, default)
1513
1514 return var_value
1515
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001516
Michael Walsh052ff812018-05-18 16:09:09 -05001517def get_stack_var(var_name,
1518 default="",
1519 init_stack_ix=2):
Michael Walsh052ff812018-05-18 16:09:09 -05001520 r"""
1521 Starting with the caller's stack level, search upward in the call stack,
1522 for a variable named var_name and return its value. If the variable
1523 cannot be found, return default.
1524
1525 Example code:
1526
1527 def func12():
1528 my_loc_var1 = get_stack_var('my_var1', "default value")
1529
1530 def func11():
1531 my_var1 = 11
1532 func12()
1533
1534 In this example, get_stack_var will find the value of my_var1 in func11's
1535 stack and will therefore return the value 11. Therefore, my_loc_var1
1536 would get set to 11.
1537
1538 Description of argument(s):
1539 var_name The name of the variable to be searched
1540 for.
1541 default The value to return if the the variable
1542 cannot be found.
1543 init_stack_ix The initial stack index from which to
1544 begin the search. 0 would be the index of
1545 this func1tion ("get_stack_var"), 1 would
1546 be the index of the function calling this
1547 function, etc.
1548 """
1549
1550 return next((frame[0].f_locals[var_name]
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001551 for frame in inspect.stack()[init_stack_ix:]
1552 if var_name in frame[0].f_locals), default)
Michael Walsh052ff812018-05-18 16:09:09 -05001553
1554
Michael Walsh82acf002017-05-04 14:33:05 -05001555# hidden_text is a list of passwords which are to be replaced with asterisks
1556# by print functions defined in this module.
1557hidden_text = []
1558# password_regex is created based on the contents of hidden_text.
1559password_regex = ""
1560
1561
Michael Walsh82acf002017-05-04 14:33:05 -05001562def register_passwords(*args):
Michael Walsh82acf002017-05-04 14:33:05 -05001563 r"""
1564 Register one or more passwords which are to be hidden in output produced
1565 by the print functions in this module.
1566
1567 Note: Blank password values are NOT registered. They are simply ignored.
1568
1569 Description of argument(s):
1570 args One or more password values. If a given
1571 password value is already registered, this
1572 function will simply do nothing.
1573 """
1574
1575 global hidden_text
1576 global password_regex
1577
1578 for password in args:
1579 if password == "":
1580 break
1581 if password in hidden_text:
1582 break
1583
1584 # Place the password into the hidden_text list.
1585 hidden_text.append(password)
1586 # Create a corresponding password regular expression. Escape regex
1587 # special characters too.
1588 password_regex = '(' +\
1589 '|'.join([re.escape(x) for x in hidden_text]) + ')'
1590
Michael Walsh82acf002017-05-04 14:33:05 -05001591
Michael Walsh82acf002017-05-04 14:33:05 -05001592def replace_passwords(buffer):
Michael Walsh82acf002017-05-04 14:33:05 -05001593 r"""
1594 Return the buffer but with all registered passwords replaced by a string
1595 of asterisks.
1596
1597
1598 Description of argument(s):
1599 buffer The string to be returned but with
1600 passwords replaced.
1601 """
1602
1603 global password_regex
1604
1605 if int(os.environ.get("DEBUG_SHOW_PASSWORDS", "0")):
1606 return buffer
1607
1608 if password_regex == "":
1609 # No passwords to replace.
1610 return buffer
1611
1612 return re.sub(password_regex, "********", buffer)
1613
Michael Walshfd2733c2017-11-13 11:36:20 -06001614
1615def create_print_wrapper_funcs(func_names,
1616 stderr_func_names,
1617 replace_dict):
Michael Walshfd2733c2017-11-13 11:36:20 -06001618 r"""
1619 Generate code for print wrapper functions and return the generated code as
1620 a string.
1621
1622 To illustrate, suppose there is a "print_foo_bar" function in the
1623 func_names list.
1624 This function will...
1625 - Expect that there is an sprint_foo_bar function already in existence.
1626 - Create a print_foo_bar function which calls sprint_foo_bar and prints
Michael Walshfaafa9c2018-06-27 16:39:31 -05001627 the result.
Michael Walshfd2733c2017-11-13 11:36:20 -06001628 - Create a qprint_foo_bar function which calls upon sprint_foo_bar only if
Michael Walshfaafa9c2018-06-27 16:39:31 -05001629 global value quiet is 0.
Michael Walshfd2733c2017-11-13 11:36:20 -06001630 - Create a dprint_foo_bar function which calls upon sprint_foo_bar only if
Michael Walshfaafa9c2018-06-27 16:39:31 -05001631 global value debug is 1.
Michael Walshfd2733c2017-11-13 11:36:20 -06001632
1633 Also, code will be generated to define aliases for each function as well.
1634 Each alias will be created by replacing "print_" in the function name with
1635 "p" For example, the alias for print_foo_bar will be pfoo_bar.
1636
1637 Description of argument(s):
1638 func_names A list of functions for which print
1639 wrapper function code is to be generated.
1640 stderr_func_names A list of functions whose generated code
1641 should print to stderr rather than to
1642 stdout.
1643 replace_dict Please see the create_func_def_string
1644 function in wrap_utils.py for details on
1645 this parameter. This parameter will be
1646 passed directly to create_func_def_string.
1647 """
1648
1649 buffer = ""
1650
1651 for func_name in func_names:
1652 if func_name in stderr_func_names:
1653 replace_dict['output_stream'] = "stderr"
1654 else:
1655 replace_dict['output_stream'] = "stdout"
1656
1657 s_func_name = "s" + func_name
1658 q_func_name = "q" + func_name
1659 d_func_name = "d" + func_name
1660
1661 # We don't want to try to redefine the "print" function, thus the
1662 # following if statement.
1663 if func_name != "print":
1664 func_def = create_func_def_string(s_func_name, func_name,
1665 print_func_template,
1666 replace_dict)
1667 buffer += func_def
1668
1669 func_def = create_func_def_string(s_func_name, "q" + func_name,
1670 qprint_func_template, replace_dict)
1671 buffer += func_def
1672
1673 func_def = create_func_def_string(s_func_name, "d" + func_name,
1674 dprint_func_template, replace_dict)
1675 buffer += func_def
1676
Michael Walsh168eb0f2017-12-01 15:35:32 -06001677 func_def = create_func_def_string(s_func_name, "l" + func_name,
1678 lprint_func_template, replace_dict)
1679 buffer += func_def
1680
Michael Walshfd2733c2017-11-13 11:36:20 -06001681 # Create abbreviated aliases (e.g. spvar is an alias for sprint_var).
1682 alias = re.sub("print_", "p", func_name)
1683 alias = re.sub("print", "p", alias)
Michael Walsh168eb0f2017-12-01 15:35:32 -06001684 prefixes = ["", "s", "q", "d", "l"]
Michael Walshfd2733c2017-11-13 11:36:20 -06001685 for prefix in prefixes:
1686 if alias == "p":
1687 continue
1688 func_def = prefix + alias + " = " + prefix + func_name
1689 buffer += func_def + "\n"
1690
1691 return buffer
Michael Walsh82acf002017-05-04 14:33:05 -05001692
1693
Michael Walshde791732016-09-06 14:25:24 -05001694# In the following section of code, we will dynamically create print versions
1695# for each of the sprint functions defined above. So, for example, where we
1696# have an sprint_time() function defined above that returns the time to the
Michael Walsh7423c012016-10-04 10:27:21 -05001697# caller in a string, we will create a corresponding print_time() function
1698# that will print that string directly to stdout.
Michael Walshde791732016-09-06 14:25:24 -05001699
Michael Walshfd2733c2017-11-13 11:36:20 -06001700# It can be complicated to follow what's being created by below. Here is an
1701# example of the print_time() function that will be created:
Michael Walshde791732016-09-06 14:25:24 -05001702
Michael Walshfd2733c2017-11-13 11:36:20 -06001703# def print_time(buffer=''):
1704# sys.stdout.write(replace_passwords(sprint_time(buffer=buffer)))
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001705# sys.stdout.flush()
Michael Walshde791732016-09-06 14:25:24 -05001706
Michael Walshfd2733c2017-11-13 11:36:20 -06001707# Templates for the various print wrapper functions.
1708print_func_template = \
1709 [
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001710 " <mod_qualifier>gp_print(<mod_qualifier>replace_passwords("
1711 + "<call_line>), stream='<output_stream>')"
Michael Walshfd2733c2017-11-13 11:36:20 -06001712 ]
1713
1714qprint_func_template = \
1715 [
Michael Walsh81c02342018-01-05 15:43:28 -06001716 " if int(<mod_qualifier>get_var_value(None, 0, \"quiet\")): return"
Michael Walshfd2733c2017-11-13 11:36:20 -06001717 ] + print_func_template
1718
1719dprint_func_template = \
1720 [
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001721 " if not int(<mod_qualifier>get_var_value(None, 0, \"debug\")):"
1722 + " return"
Michael Walshfd2733c2017-11-13 11:36:20 -06001723 ] + print_func_template
1724
Michael Walsh168eb0f2017-12-01 15:35:32 -06001725lprint_func_template = \
1726 [
Michael Walsh81c02342018-01-05 15:43:28 -06001727 " gp_log(<mod_qualifier>replace_passwords(<call_line>))"
Michael Walsh168eb0f2017-12-01 15:35:32 -06001728 ]
1729
Michael Walsh81c02342018-01-05 15:43:28 -06001730replace_dict = {'output_stream': 'stdout', 'mod_qualifier': ''}
Michael Walshfd2733c2017-11-13 11:36:20 -06001731
1732
1733gp_debug_print("robot_env: " + str(robot_env))
Michael Walshde791732016-09-06 14:25:24 -05001734
1735# func_names contains a list of all print functions which should be created
1736# from their sprint counterparts.
1737func_names = ['print_time', 'print_timen', 'print_error', 'print_varx',
Michael Walsh18176322016-11-15 15:11:21 -06001738 'print_var', 'print_vars', 'print_dashes', 'indent',
1739 'print_call_stack', 'print_func_name', 'print_executing',
1740 'print_pgm_header', 'print_issuing', 'print_pgm_footer',
1741 'print_error_report', 'print', 'printn']
Michael Walshde791732016-09-06 14:25:24 -05001742
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001743# stderr_func_names is a list of functions whose output should go to stderr
1744# rather than stdout.
1745stderr_func_names = ['print_error', 'print_error_report']
Michael Walshde791732016-09-06 14:25:24 -05001746
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001747
Michael Walshfd2733c2017-11-13 11:36:20 -06001748func_defs = create_print_wrapper_funcs(func_names, stderr_func_names,
1749 replace_dict)
1750gp_debug_print(func_defs)
1751exec(func_defs)