blob: 70ccb8bea6c3cc2f3f99a72f21b04ca50a7a559d [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
George Keishing3b7115a2018-08-02 10:48:17 -050016try:
17 import __builtin__
18except ImportError:
19 import builtins as __builtin__
Michael Walsh7423c012016-10-04 10:27:21 -050020import logging
Michael Walshbec416d2016-11-10 08:54:52 -060021import collections
Michael Walshfd2733c2017-11-13 11:36:20 -060022from wrap_utils import *
Michael Walshbec416d2016-11-10 08:54:52 -060023
Michael Walshbec416d2016-11-10 08:54:52 -060024try:
Michael Walsh2ee77cd2017-03-08 11:50:17 -060025 robot_env = 1
Michael Walshbec416d2016-11-10 08:54:52 -060026 from robot.utils import DotDict
Michael Walsh8e6deb42017-01-27 14:22:41 -060027 from robot.utils import NormalizedDict
Michael Walsh2ee77cd2017-03-08 11:50:17 -060028 from robot.libraries.BuiltIn import BuiltIn
Michael Walshb1500152017-04-12 15:42:43 -050029 # Having access to the robot libraries alone does not indicate that we
30 # are in a robot environment. The following try block should confirm that.
31 try:
32 var_value = BuiltIn().get_variable_value("${SUITE_NAME}", "")
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -050033 except BaseException:
Michael Walshb1500152017-04-12 15:42:43 -050034 robot_env = 0
Michael Walshbec416d2016-11-10 08:54:52 -060035except ImportError:
Michael Walsh2ee77cd2017-03-08 11:50:17 -060036 robot_env = 0
Michael Walsh7423c012016-10-04 10:27:21 -050037
38import gen_arg as ga
Michael Walshde791732016-09-06 14:25:24 -050039
40# Setting these variables for use both inside this module and by programs
41# importing this module.
Michael Walshbf605652017-09-01 12:33:26 -050042pgm_file_path = sys.argv[0]
43pgm_name = os.path.basename(pgm_file_path)
Michael Walsh3ba8ecd2018-04-24 11:33:25 -050044pgm_dir_path = os.path.normpath(re.sub("/" + pgm_name, "", pgm_file_path)) +\
45 os.path.sep
Michael Walsh7423c012016-10-04 10:27:21 -050046
Michael Walshde791732016-09-06 14:25:24 -050047
48# Some functions (e.g. sprint_pgm_header) have need of a program name value
49# that looks more like a valid variable name. Therefore, we'll swap odd
50# characters like "." out for underscores.
51pgm_name_var_name = pgm_name.replace(".", "_")
52
53# Initialize global values used as defaults by print_time, print_var, etc.
54col1_indent = 0
55
56# Calculate default column width for print_var functions based on environment
57# variable settings. The objective is to make the variable values line up
58# nicely with the time stamps.
59col1_width = 29
Michael Walshb1500152017-04-12 15:42:43 -050060
61NANOSECONDS = os.environ.get('NANOSECONDS', '1')
62
Michael Walshde791732016-09-06 14:25:24 -050063
64if NANOSECONDS == "1":
65 col1_width = col1_width + 7
66
Michael Walshb1500152017-04-12 15:42:43 -050067SHOW_ELAPSED_TIME = os.environ.get('SHOW_ELAPSED_TIME', '1')
Michael Walshde791732016-09-06 14:25:24 -050068
69if SHOW_ELAPSED_TIME == "1":
70 if NANOSECONDS == "1":
71 col1_width = col1_width + 14
72 else:
73 col1_width = col1_width + 7
74
75# Initialize some time variables used in module functions.
76start_time = time.time()
77sprint_time_last_seconds = start_time
78
Michael Walsh2ee77cd2017-03-08 11:50:17 -060079# The user can set environment variable "GEN_PRINT_DEBUG" to get debug output
80# from this module.
81gen_print_debug = int(os.environ.get('GEN_PRINT_DEBUG', 0))
Michael Walsh7423c012016-10-04 10:27:21 -050082
Michael Walshde791732016-09-06 14:25:24 -050083
Michael Walshde791732016-09-06 14:25:24 -050084def sprint_func_name(stack_frame_ix=None):
Michael Walshde791732016-09-06 14:25:24 -050085 r"""
86 Return the function name associated with the indicated stack frame.
87
88 Description of arguments:
89 stack_frame_ix The index of the stack frame whose
90 function name should be returned. If the
Michael Walsh2ee77cd2017-03-08 11:50:17 -060091 caller does not specify a value, this
Michael Walshde791732016-09-06 14:25:24 -050092 function will set the value to 1 which is
93 the index of the caller's stack frame. If
94 the caller is the wrapper function
95 "print_func_name", this function will bump
96 it up by 1.
97 """
98
99 # If user specified no stack_frame_ix, we'll set it to a proper default
100 # value.
101 if stack_frame_ix is None:
102 func_name = sys._getframe().f_code.co_name
103 caller_func_name = sys._getframe(1).f_code.co_name
104 if func_name[1:] == caller_func_name:
105 stack_frame_ix = 2
106 else:
107 stack_frame_ix = 1
108
109 func_name = sys._getframe(stack_frame_ix).f_code.co_name
110
111 return func_name
112
Michael Walshde791732016-09-06 14:25:24 -0500113
Michael Walsh1173a522018-05-21 17:24:51 -0500114def get_line_indent(line):
115 r"""
116 Return the number of spaces at the beginning of the line.
117 """
118
119 return len(line) - len(line.lstrip(' '))
120
121
Michael Walshde791732016-09-06 14:25:24 -0500122# get_arg_name is not a print function per se. I have included it in this
123# module because it is used by sprint_var which is found in this module.
Michael Walshde791732016-09-06 14:25:24 -0500124def get_arg_name(var,
125 arg_num=1,
126 stack_frame_ix=1):
Michael Walshde791732016-09-06 14:25:24 -0500127 r"""
128 Return the "name" of an argument passed to a function. This could be a
129 literal or a variable name.
130
Michael Walsh2ee77cd2017-03-08 11:50:17 -0600131 Description of arguments:
Michael Walshde791732016-09-06 14:25:24 -0500132 var The variable whose name you want returned.
Michael Walsh1173a522018-05-21 17:24:51 -0500133 arg_num The arg number whose name is to be
134 returned. To illustrate how arg_num is
135 processed, suppose that a programmer codes
136 this line: "rc, outbuf = my_func(var1,
137 var2)" and suppose that my_func has this
138 line of code: "result = gp.get_arg_name(0,
139 arg_num, 2)". If arg_num is positive, the
140 indicated argument is returned. For
141 example, if arg_num is 1, "var1" would be
142 returned, If arg_num is 2, "var2" would be
143 returned. If arg_num exceeds the number
144 of arguments, get_arg_name will simply
145 return a complete list of the arguments.
146 If arg_num is 0, get_arg_name will return
147 the name of the target function as
148 specified in the calling line ("my_func"
149 in this case). To clarify, if the caller
150 of the target function uses an alias
151 function name, the alias name would be
152 returned. If arg_num is negative, an
153 lvalue variable name is returned.
154 Continuing with the given example, if
155 arg_num is -2 the 2nd parm to the left of
156 the "=" ("rc" in this case) should be
157 returned. If arg_num is -1, the 1st parm
158 to the left of the "=" ("out_buf" in this
159 case) should be returned. If arg_num is
160 less than -2, an entire dictionary is
161 returned. The keys to the dictionary for
162 this example would be -2 and -1.
Michael Walshde791732016-09-06 14:25:24 -0500163 stack_frame_ix The stack frame index of the target
164 function. This value must be 1 or
165 greater. 1 would indicate get_arg_name's
166 stack frame. 2 would be the caller of
167 get_arg_name's stack frame, etc.
168
169 Example 1:
170
171 my_var = "mike"
172 var_name = get_arg_name(my_var)
173
174 In this example, var_name will receive the value "my_var".
175
176 Example 2:
177
178 def test1(var):
179 # Getting the var name of the first arg to this function, test1.
180 # Note, in this case, it doesn't matter what you pass as the first arg
181 # to get_arg_name since it is the caller's variable name that matters.
182 dummy = 1
183 arg_num = 1
184 stack_frame = 2
185 var_name = get_arg_name(dummy, arg_num, stack_frame)
186
187 # Mainline...
188
189 another_var = "whatever"
190 test1(another_var)
191
192 In this example, var_name will be set to "another_var".
193
194 """
195
196 # Note: I wish to avoid recursion so I refrain from calling any function
197 # that calls this function (i.e. sprint_var, valid_value, etc.).
198
Michael Walsh23e7f492017-01-10 11:34:47 -0600199 # The user can set environment variable "GET_ARG_NAME_DEBUG" to get debug
200 # output from this function.
201 local_debug = int(os.environ.get('GET_ARG_NAME_DEBUG', 0))
202 # In addition to GET_ARG_NAME_DEBUG, the user can set environment
203 # variable "GET_ARG_NAME_SHOW_SOURCE" to have this function include source
204 # code in the debug output.
205 local_debug_show_source = int(
206 os.environ.get('GET_ARG_NAME_SHOW_SOURCE', 0))
Michael Walshde791732016-09-06 14:25:24 -0500207
Michael Walshde791732016-09-06 14:25:24 -0500208 if stack_frame_ix < 1:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500209 print_error("Programmer error - Variable \"stack_frame_ix\" has an"
210 + " invalid value of \"" + str(stack_frame_ix) + "\". The"
211 + " value must be an integer that is greater than or equal"
212 + " to 1.\n")
Michael Walshde791732016-09-06 14:25:24 -0500213 return
214
215 if local_debug:
216 debug_indent = 2
Michael Walsh23e7f492017-01-10 11:34:47 -0600217 print("")
218 print_dashes(0, 120)
Michael Walshde791732016-09-06 14:25:24 -0500219 print(sprint_func_name() + "() parms:")
220 print_varx("var", var, 0, debug_indent)
221 print_varx("arg_num", arg_num, 0, debug_indent)
222 print_varx("stack_frame_ix", stack_frame_ix, 0, debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600223 print("")
224 print_call_stack(debug_indent, 2)
Michael Walshde791732016-09-06 14:25:24 -0500225
Michael Walsh23e7f492017-01-10 11:34:47 -0600226 for count in range(0, 2):
227 try:
228 frame, filename, cur_line_no, function_name, lines, index = \
229 inspect.stack()[stack_frame_ix]
230 except IndexError:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500231 print_error("Programmer error - The caller has asked for"
232 + " information about the stack frame at index \""
233 + str(stack_frame_ix) + "\". However, the stack"
234 + " only contains " + str(len(inspect.stack()))
235 + " entries. Therefore the stack frame index is out"
236 + " of range.\n")
Michael Walsh23e7f492017-01-10 11:34:47 -0600237 return
238 if filename != "<string>":
239 break
240 # filename of "<string>" may mean that the function in question was
241 # defined dynamically and therefore its code stack is inaccessible.
242 # This may happen with functions like "rqprint_var". In this case,
243 # we'll increment the stack_frame_ix and try again.
244 stack_frame_ix += 1
245 if local_debug:
246 print("Adjusted stack_frame_ix...")
247 print_varx("stack_frame_ix", stack_frame_ix, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500248
Michael Walsh1173a522018-05-21 17:24:51 -0500249 real_called_func_name = sprint_func_name(stack_frame_ix)
Michael Walsh23e7f492017-01-10 11:34:47 -0600250
251 module = inspect.getmodule(frame)
252
253 # Though I would expect inspect.getsourcelines(frame) to get all module
254 # source lines if the frame is "<module>", it doesn't do that. Therefore,
255 # for this special case, I will do inspect.getsourcelines(module).
256 if function_name == "<module>":
257 source_lines, source_line_num =\
258 inspect.getsourcelines(module)
259 line_ix = cur_line_no - source_line_num - 1
260 else:
261 source_lines, source_line_num =\
262 inspect.getsourcelines(frame)
263 line_ix = cur_line_no - source_line_num
264
265 if local_debug:
266 print("\n Variables retrieved from inspect.stack() function:")
267 print_varx("frame", frame, 0, debug_indent + 2)
268 print_varx("filename", filename, 0, debug_indent + 2)
269 print_varx("cur_line_no", cur_line_no, 0, debug_indent + 2)
270 print_varx("function_name", function_name, 0, debug_indent + 2)
271 print_varx("lines", lines, 0, debug_indent + 2)
272 print_varx("index", index, 0, debug_indent + 2)
273 print_varx("source_line_num", source_line_num, 0, debug_indent)
274 print_varx("line_ix", line_ix, 0, debug_indent)
275 if local_debug_show_source:
276 print_varx("source_lines", source_lines, 0, debug_indent)
Michael Walsh1173a522018-05-21 17:24:51 -0500277 print_varx("real_called_func_name", real_called_func_name, 0,
278 debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600279
280 # Get a list of all functions defined for the module. Note that this
281 # doesn't work consistently when _run_exitfuncs is at the top of the stack
282 # (i.e. if we're running an exit function). I've coded a work-around
283 # below for this deficiency.
284 all_functions = inspect.getmembers(module, inspect.isfunction)
285
286 # Get called_func_id by searching for our function in the list of all
287 # functions.
288 called_func_id = None
289 for func_name, function in all_functions:
Michael Walsh1173a522018-05-21 17:24:51 -0500290 if func_name == real_called_func_name:
Michael Walsh23e7f492017-01-10 11:34:47 -0600291 called_func_id = id(function)
292 break
293 # NOTE: The only time I've found that called_func_id can't be found is
294 # when we're running from an exit function.
295
296 # Look for other functions in module with matching id.
Michael Walsh1173a522018-05-21 17:24:51 -0500297 aliases = set([real_called_func_name])
Michael Walsh23e7f492017-01-10 11:34:47 -0600298 for func_name, function in all_functions:
Michael Walsh1173a522018-05-21 17:24:51 -0500299 if func_name == real_called_func_name:
Michael Walsh23e7f492017-01-10 11:34:47 -0600300 continue
301 func_id = id(function)
302 if func_id == called_func_id:
303 aliases.add(func_name)
304
305 # In most cases, my general purpose code above will find all aliases.
306 # However, for the odd case (i.e. running from exit function), I've added
307 # code to handle pvar, qpvar, dpvar, etc. aliases explicitly since they
308 # are defined in this module and used frequently.
309 # pvar is an alias for print_var.
Michael Walsh1173a522018-05-21 17:24:51 -0500310 aliases.add(re.sub("print_var", "pvar", real_called_func_name))
Michael Walsh23e7f492017-01-10 11:34:47 -0600311
Michael Walsh3f248272018-06-01 13:59:35 -0500312 # The call to the function could be encased in a recast (e.g.
313 # int(func_name())).
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500314 recast_regex = "([^ ]+\\([ ]*)?"
315 import_name_regex = "([a-zA-Z0-9_]+\\.)?"
Michael Walsh3f248272018-06-01 13:59:35 -0500316 func_name_regex = recast_regex + import_name_regex + "(" +\
317 '|'.join(aliases) + ")"
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500318 pre_args_regex = ".*" + func_name_regex + "[ ]*\\("
Michael Walsh23e7f492017-01-10 11:34:47 -0600319
320 # Search backward through source lines looking for the calling function
321 # name.
322 found = False
323 for start_line_ix in range(line_ix, 0, -1):
324 # Skip comment lines.
325 if re.match(r"[ ]*#", source_lines[start_line_ix]):
326 continue
Michael Walsh1173a522018-05-21 17:24:51 -0500327 if re.match(pre_args_regex, source_lines[start_line_ix]):
Michael Walsh23e7f492017-01-10 11:34:47 -0600328 found = True
329 break
330 if not found:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500331 print_error("Programmer error - Could not find the source line with"
332 + " a reference to function \"" + real_called_func_name
333 + "\".\n")
Michael Walsh23e7f492017-01-10 11:34:47 -0600334 return
335
Michael Walsh82acf002017-05-04 14:33:05 -0500336 # Search forward through the source lines looking for a line whose
337 # indentation is the same or less than the start line. The end of our
338 # composite line should be the line preceding that line.
Michael Walsh1173a522018-05-21 17:24:51 -0500339 start_indent = get_line_indent(source_lines[start_line_ix])
Michael Walsh37cd29d2018-05-24 13:19:18 -0500340 end_line_ix = line_ix
Michael Walsh23e7f492017-01-10 11:34:47 -0600341 for end_line_ix in range(line_ix + 1, len(source_lines)):
342 if source_lines[end_line_ix].strip() == "":
343 continue
Michael Walsh1173a522018-05-21 17:24:51 -0500344 line_indent = get_line_indent(source_lines[end_line_ix])
Michael Walsh82acf002017-05-04 14:33:05 -0500345 if line_indent <= start_indent:
Michael Walsh23e7f492017-01-10 11:34:47 -0600346 end_line_ix -= 1
347 break
Michael Walsh1173a522018-05-21 17:24:51 -0500348 if start_line_ix != 0:
349 # Check to see whether the start line is a continuation of the prior
350 # line?
351 line_indent = get_line_indent(source_lines[start_line_ix - 1])
352 if line_indent < start_indent:
353 start_line_ix -= 1
354 # Remove the backslash (continuation char).
355 source_lines[start_line_ix] = re.sub(r"[ ]*\\([\r\n]$)",
356 " \\1",
357 source_lines[start_line_ix])
Michael Walsh23e7f492017-01-10 11:34:47 -0600358
359 # Join the start line through the end line into a composite line.
360 composite_line = ''.join(map(str.strip,
Gunnar Mills096cd562018-03-26 10:19:12 -0500361 source_lines[start_line_ix:end_line_ix + 1]))
Michael Walsh1173a522018-05-21 17:24:51 -0500362 # Insert one space after first "=" if there isn't one already.
363 composite_line = re.sub("=[ ]*([^ ])", "= \\1", composite_line, 1)
Michael Walsh7423c012016-10-04 10:27:21 -0500364
Michael Walsh3f248272018-06-01 13:59:35 -0500365 lvalue_regex = "[ ]*=[ ]+" + func_name_regex + ".*"
Michael Walsh1173a522018-05-21 17:24:51 -0500366 lvalue_string = re.sub(lvalue_regex, "", composite_line)
Michael Walsh3f248272018-06-01 13:59:35 -0500367 if lvalue_string == composite_line:
368 # i.e. the regex did not match so there are no lvalues.
369 lvalue_string = ""
Michael Walsh37762f92018-08-07 14:59:18 -0500370 lvalues_list = list(filter(None, map(str.strip, lvalue_string.split(","))))
Michael Walsh3f248272018-06-01 13:59:35 -0500371 try:
372 lvalues = collections.OrderedDict()
373 except AttributeError:
374 # A non-ordered dict doesn't look as nice when printed but it will do.
375 lvalues = {}
Michael Walsh1173a522018-05-21 17:24:51 -0500376 ix = len(lvalues_list) * -1
377 for lvalue in lvalues_list:
378 lvalues[ix] = lvalue
379 ix += 1
Michael Walsh3f248272018-06-01 13:59:35 -0500380 lvalue_prefix_regex = "(.*=[ ]+)?"
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500381 called_func_name_regex = lvalue_prefix_regex + func_name_regex +\
382 "[ ]*\\(.*"
Michael Walsh3f248272018-06-01 13:59:35 -0500383 called_func_name = re.sub(called_func_name_regex, "\\4", composite_line)
Michael Walsh1173a522018-05-21 17:24:51 -0500384 arg_list_etc = "(" + re.sub(pre_args_regex, "", composite_line)
Michael Walshde791732016-09-06 14:25:24 -0500385 if local_debug:
Michael Walsh23e7f492017-01-10 11:34:47 -0600386 print_varx("aliases", aliases, 0, debug_indent)
Michael Walsh3f248272018-06-01 13:59:35 -0500387 print_varx("import_name_regex", import_name_regex, 0, debug_indent)
388 print_varx("func_name_regex", func_name_regex, 0, debug_indent)
Michael Walsh1173a522018-05-21 17:24:51 -0500389 print_varx("pre_args_regex", pre_args_regex, 0, debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600390 print_varx("start_line_ix", start_line_ix, 0, debug_indent)
391 print_varx("end_line_ix", end_line_ix, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500392 print_varx("composite_line", composite_line, 0, debug_indent)
Michael Walsh1173a522018-05-21 17:24:51 -0500393 print_varx("lvalue_regex", lvalue_regex, 0, debug_indent)
394 print_varx("lvalue_string", lvalue_string, 0, debug_indent)
395 print_varx("lvalues", lvalues, 0, debug_indent)
Michael Walsh3f248272018-06-01 13:59:35 -0500396 print_varx("called_func_name_regex", called_func_name_regex, 0,
397 debug_indent)
Michael Walsh1173a522018-05-21 17:24:51 -0500398 print_varx("called_func_name", called_func_name, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500399 print_varx("arg_list_etc", arg_list_etc, 0, debug_indent)
400
401 # Parse arg list...
402 # Initialize...
403 nest_level = -1
404 arg_ix = 0
Michael Walsh7423c012016-10-04 10:27:21 -0500405 args_list = [""]
Michael Walshde791732016-09-06 14:25:24 -0500406 for ix in range(0, len(arg_list_etc)):
407 char = arg_list_etc[ix]
408 # Set the nest_level based on whether we've encounted a parenthesis.
409 if char == "(":
410 nest_level += 1
411 if nest_level == 0:
412 continue
413 elif char == ")":
414 nest_level -= 1
415 if nest_level < 0:
416 break
417
418 # If we reach a comma at base nest level, we are done processing an
Michael Walsh7423c012016-10-04 10:27:21 -0500419 # argument so we increment arg_ix and initialize a new args_list entry.
Michael Walshde791732016-09-06 14:25:24 -0500420 if char == "," and nest_level == 0:
421 arg_ix += 1
Michael Walsh7423c012016-10-04 10:27:21 -0500422 args_list.append("")
Michael Walshde791732016-09-06 14:25:24 -0500423 continue
424
Michael Walsh7423c012016-10-04 10:27:21 -0500425 # For any other character, we append it it to the current arg list
Michael Walshde791732016-09-06 14:25:24 -0500426 # entry.
Michael Walsh7423c012016-10-04 10:27:21 -0500427 args_list[arg_ix] += char
Michael Walshde791732016-09-06 14:25:24 -0500428
429 # Trim whitespace from each list entry.
Michael Walsh7423c012016-10-04 10:27:21 -0500430 args_list = [arg.strip() for arg in args_list]
Michael Walshde791732016-09-06 14:25:24 -0500431
Michael Walsh1173a522018-05-21 17:24:51 -0500432 if arg_num < 0:
433 if abs(arg_num) > len(lvalues):
434 argument = lvalues
435 else:
436 argument = lvalues[arg_num]
437 elif arg_num == 0:
438 argument = called_func_name
Michael Walsh2750b442018-05-18 14:49:11 -0500439 else:
Michael Walsh1173a522018-05-21 17:24:51 -0500440 if arg_num > len(args_list):
441 argument = args_list
442 else:
443 argument = args_list[arg_num - 1]
Michael Walshde791732016-09-06 14:25:24 -0500444
445 if local_debug:
Michael Walsh7423c012016-10-04 10:27:21 -0500446 print_varx("args_list", args_list, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500447 print_varx("argument", argument, 0, debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600448 print_dashes(0, 120)
Michael Walshde791732016-09-06 14:25:24 -0500449
450 return argument
451
Michael Walshde791732016-09-06 14:25:24 -0500452
Michael Walshde791732016-09-06 14:25:24 -0500453def sprint_time(buffer=""):
Michael Walshde791732016-09-06 14:25:24 -0500454 r"""
455 Return the time in the following format.
456
457 Example:
458
459 The following python code...
460
461 sys.stdout.write(sprint_time())
462 sys.stdout.write("Hi.\n")
463
464 Will result in the following type of output:
465
466 #(CDT) 2016/07/08 15:25:35 - Hi.
467
468 Example:
469
470 The following python code...
471
472 sys.stdout.write(sprint_time("Hi.\n"))
473
474 Will result in the following type of output:
475
476 #(CDT) 2016/08/03 17:12:05 - Hi.
477
478 The following environment variables will affect the formatting as
479 described:
480 NANOSECONDS This will cause the time stamps to be
481 precise to the microsecond (Yes, it
482 probably should have been named
483 MICROSECONDS but the convention was set
484 long ago so we're sticking with it).
485 Example of the output when environment
486 variable NANOSECONDS=1.
487
488 #(CDT) 2016/08/03 17:16:25.510469 - Hi.
489
490 SHOW_ELAPSED_TIME This will cause the elapsed time to be
491 included in the output. This is the
492 amount of time that has elapsed since the
493 last time this function was called. The
494 precision of the elapsed time field is
495 also affected by the value of the
496 NANOSECONDS environment variable. Example
497 of the output when environment variable
498 NANOSECONDS=0 and SHOW_ELAPSED_TIME=1.
499
500 #(CDT) 2016/08/03 17:17:40 - 0 - Hi.
501
502 Example of the output when environment variable NANOSECONDS=1 and
503 SHOW_ELAPSED_TIME=1.
504
505 #(CDT) 2016/08/03 17:18:47.317339 - 0.000046 - Hi.
506
507 Description of arguments.
508 buffer This will be appended to the formatted
509 time string.
510 """
511
512 global NANOSECONDS
513 global SHOW_ELAPSED_TIME
514 global sprint_time_last_seconds
515
516 seconds = time.time()
517 loc_time = time.localtime(seconds)
518 nanoseconds = "%0.6f" % seconds
519 pos = nanoseconds.find(".")
520 nanoseconds = nanoseconds[pos:]
521
522 time_string = time.strftime("#(%Z) %Y/%m/%d %H:%M:%S", loc_time)
523 if NANOSECONDS == "1":
524 time_string = time_string + nanoseconds
525
526 if SHOW_ELAPSED_TIME == "1":
527 cur_time_seconds = seconds
528 math_string = "%9.9f" % cur_time_seconds + " - " + "%9.9f" % \
529 sprint_time_last_seconds
530 elapsed_seconds = eval(math_string)
531 if NANOSECONDS == "1":
532 elapsed_seconds = "%11.6f" % elapsed_seconds
533 else:
534 elapsed_seconds = "%4i" % elapsed_seconds
535 sprint_time_last_seconds = cur_time_seconds
536 time_string = time_string + " - " + elapsed_seconds
537
538 return time_string + " - " + buffer
539
Michael Walshde791732016-09-06 14:25:24 -0500540
Michael Walshde791732016-09-06 14:25:24 -0500541def sprint_timen(buffer=""):
Michael Walshde791732016-09-06 14:25:24 -0500542 r"""
543 Append a line feed to the buffer, pass it to sprint_time and return the
544 result.
545 """
546
547 return sprint_time(buffer + "\n")
548
Michael Walshde791732016-09-06 14:25:24 -0500549
Michael Walshde791732016-09-06 14:25:24 -0500550def sprint_error(buffer=""):
Michael Walshde791732016-09-06 14:25:24 -0500551 r"""
552 Return a standardized error string. This includes:
553 - A time stamp
554 - The "**ERROR**" string
555 - The caller's buffer string.
556
557 Example:
558
559 The following python code...
560
561 print(sprint_error("Oops.\n"))
562
563 Will result in the following type of output:
564
565 #(CDT) 2016/08/03 17:12:05 - **ERROR** Oops.
566
567 Description of arguments.
568 buffer This will be appended to the formatted
569 error string.
570 """
571
572 return sprint_time() + "**ERROR** " + buffer
573
Michael Walshde791732016-09-06 14:25:24 -0500574
Michael Walsh3f248272018-06-01 13:59:35 -0500575# Implement "constants" with functions.
576def digit_length_in_bits():
577 r"""
578 Return the digit length in bits.
579 """
580
581 return 4
582
583
584def word_length_in_digits():
585 r"""
586 Return the word length in digits.
587 """
588
589 return 8
590
591
592def bit_length(number):
593 r"""
594 Return the bit length of the number.
595
596 Description of argument(s):
597 number The number to be analyzed.
598 """
599
600 if number < 0:
601 # Convert negative numbers to positive and subtract one. The
602 # following example illustrates the reason for this:
603 # Consider a single nibble whose signed values can range from -8 to 7
604 # (0x8 to 0x7). A value of 0x7 equals 0b0111. Therefore, its length
605 # in bits is 3. Since the negative bit (i.e. 0b1000) is not set, the
606 # value 7 clearly will fit in one nibble. With -8 = 0x8 = 0b1000, you
607 # have the smallest negative value that will fit. Note that it
608 # requires 3 bits of 0. So by converting a number value of -8 to a
609 # working_number of 7, this function can accurately calculate the
610 # number of bits and therefore nibbles required to represent the
611 # number in print.
612 working_number = abs(number) - 1
613 else:
614 working_number = number
615
616 # Handle the special case of the number 0.
617 if working_number == 0:
618 return 0
619
620 return len(bin(working_number)) - 2
621
622
623def get_req_num_hex_digits(number):
624 r"""
625 Return the required number of hex digits required to display the given
626 number.
627
628 The returned value will always be rounded up to the nearest multiple of 8.
629
630 Description of argument(s):
631 number The number to be analyzed.
632 """
633
634 if number < 0:
635 # Convert negative numbers to positive and subtract one. The
636 # following example illustrates the reason for this:
637 # Consider a single nibble whose signed values can range from -8 to 7
638 # (0x8 to 0x7). A value of 0x7 equals 0b0111. Therefore, its length
639 # in bits is 3. Since the negative bit (i.e. 0b1000) is not set, the
640 # value 7 clearly will fit in one nibble. With -8 = 0x8 = 0b1000, you
641 # have the smallest negative value that will fit. Note that it
642 # requires 3 bits of 0. So by converting a number value of -8 to a
643 # working_number of 7, this function can accurately calculate the
644 # number of bits and therefore nibbles required to represent the
645 # number in print.
646 working_number = abs(number) - 1
647 else:
648 working_number = number
649
650 # Handle the special case of the number 0.
651 if working_number == 0:
652 return word_length_in_digits()
653
654 num_length_in_bits = bit_length(working_number)
655 num_hex_digits, remainder = divmod(num_length_in_bits,
656 digit_length_in_bits())
657 if remainder > 0:
658 # Example: the number 7 requires 3 bits. The divmod above produces,
659 # 0 with remainder of 3. So because we have a remainder, we increment
660 # num_hex_digits from 0 to 1.
661 num_hex_digits += 1
662
663 # Check to see whether the negative bit is set. This is the left-most
664 # bit in the highest order digit.
665 negative_mask = 2 ** (num_hex_digits * 4 - 1)
666 if working_number & negative_mask:
667 # If a number that is intended to be positive has its negative bit
668 # on, an additional digit will be required to represent it correctly
669 # in print.
670 num_hex_digits += 1
671
672 num_words, remainder = divmod(num_hex_digits, word_length_in_digits())
673 if remainder > 0 or num_words == 0:
674 num_words += 1
675
676 # Round up to the next word length in digits.
677 return num_words * word_length_in_digits()
678
679
680def dft_num_hex_digits():
681 r"""
682 Return the default number of hex digits to be used to represent a hex
683 number in print.
684
685 The value returned is a function of sys.maxsize.
686 """
687
688 global _gen_print_dft_num_hex_digits_
689 try:
690 return _gen_print_dft_num_hex_digits_
691 except NameError:
692 _gen_print_dft_num_hex_digits_ = get_req_num_hex_digits(sys.maxsize)
693 return _gen_print_dft_num_hex_digits_
694
695
Michael Walshde791732016-09-06 14:25:24 -0500696def sprint_varx(var_name,
697 var_value,
698 hex=0,
699 loc_col1_indent=col1_indent,
Michael Walsh7423c012016-10-04 10:27:21 -0500700 loc_col1_width=col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500701 trailing_char="\n",
702 key_list=None):
Michael Walshde791732016-09-06 14:25:24 -0500703 r"""
704 Print the var name/value passed to it. If the caller lets loc_col1_width
705 default, the printing lines up nicely with output generated by the
706 print_time functions.
707
708 Note that the sprint_var function (defined below) can be used to call this
709 function so that the programmer does not need to pass the var_name.
710 sprint_var will figure out the var_name. The sprint_var function is the
711 one that would normally be used by the general user.
712
713 For example, the following python code:
714
715 first_name = "Mike"
716 print_time("Doing this...\n")
717 print_varx("first_name", first_name)
718 print_time("Doing that...\n")
719
720 Will generate output like this:
721
722 #(CDT) 2016/08/10 17:34:42.847374 - 0.001285 - Doing this...
723 first_name: Mike
724 #(CDT) 2016/08/10 17:34:42.847510 - 0.000136 - Doing that...
725
726 This function recognizes several complex types of data such as dict, list
727 or tuple.
728
729 For example, the following python code:
730
731 my_dict = dict(one=1, two=2, three=3)
732 print_var(my_dict)
733
734 Will generate the following output:
735
736 my_dict:
737 my_dict[three]: 3
738 my_dict[two]: 2
739 my_dict[one]: 1
740
741 Description of arguments.
742 var_name The name of the variable to be printed.
743 var_value The value of the variable to be printed.
744 hex This indicates that the value should be
745 printed in hex format. It is the user's
746 responsibility to ensure that a var_value
Michael Walshbec416d2016-11-10 08:54:52 -0600747 contains a valid hex number. For string
748 var_values, this will be interpreted as
749 show_blanks which means that blank values
Michael Walshd995cb02017-02-07 14:46:01 -0600750 will be printed as "<blank>". For dict
751 var_values, this will be interpreted as
752 terse format where keys are not repeated
753 in the output.
Michael Walshde791732016-09-06 14:25:24 -0500754 loc_col1_indent The number of spaces to indent the output.
755 loc_col1_width The width of the output column containing
756 the variable name. The default value of
757 this is adjusted so that the var_value
758 lines up with text printed via the
759 print_time function.
Michael Walsh7423c012016-10-04 10:27:21 -0500760 trailing_char The character to be used at the end of the
761 returned string. The default value is a
762 line feed.
Michael Walshd2869032018-03-22 16:12:11 -0500763 key_list A list of which dictionary keys should be
764 printed. All others keys will be skipped.
765 Each value in key_list will be regarded
766 as a regular expression and it will be
767 regarded as anchored to the beginning and
768 ends of the dictionary key being
769 referenced. For example if key_list is
770 ["one", "two"], the resulting regex used
771 will be "^one|two$", i.e. only keys "one"
772 and "two" from the var_value dictionary
773 will be printed. As another example, if
774 the caller were to specify a key_list of
775 ["one.*"], then only dictionary keys whose
776 names begin with "one" will be printed.
777 Note: This argument pertains only to
778 var_values which are dictionaries.
Michael Walsh7423c012016-10-04 10:27:21 -0500779 """
Michael Walshde791732016-09-06 14:25:24 -0500780
781 # Determine the type
Michael Walsh37762f92018-08-07 14:59:18 -0500782 try:
783 int_types = (int, long)
784 except NameError:
785 int_types = (int,)
786 try:
787 string_types = (str, unicode)
788 except NameError:
789 string_types = (str,)
790 simple_types = int_types + string_types + (float, bool)
791 if type(var_value) in simple_types \
Michael Walshde791732016-09-06 14:25:24 -0500792 or var_value is None:
793 # The data type is simple in the sense that it has no subordinate
794 # parts.
Michael Walsh7423c012016-10-04 10:27:21 -0500795 # Adjust loc_col1_width.
796 loc_col1_width = loc_col1_width - loc_col1_indent
Michael Walshde791732016-09-06 14:25:24 -0500797 # See if the user wants the output in hex format.
798 if hex:
Michael Walsh37762f92018-08-07 14:59:18 -0500799 if type(var_value) not in int_types:
Michael Walshbec416d2016-11-10 08:54:52 -0600800 value_format = "%s"
Michael Walsh2795edc2016-12-13 16:00:33 -0600801 if var_value == "":
Michael Walshbec416d2016-11-10 08:54:52 -0600802 var_value = "<blank>"
803 else:
Michael Walsh3f248272018-06-01 13:59:35 -0500804 num_hex_digits = max(dft_num_hex_digits(),
805 get_req_num_hex_digits(var_value))
806 # Convert a negative number to its positive twos complement
807 # for proper printing. For example, instead of printing -1 as
808 # "0x-000000000000001" it will be printed as
809 # "0xffffffffffffffff".
810 var_value = var_value & (2 ** (num_hex_digits * 4) - 1)
811 value_format = "0x%0" + str(num_hex_digits) + "x"
Michael Walshde791732016-09-06 14:25:24 -0500812 else:
813 value_format = "%s"
814 format_string = "%" + str(loc_col1_indent) + "s%-" \
Michael Walsh7423c012016-10-04 10:27:21 -0500815 + str(loc_col1_width) + "s" + value_format + trailing_char
Michael Walsh3383e652017-09-01 17:10:59 -0500816 if value_format == "0x%08x":
817 return format_string % ("", str(var_name) + ":",
818 var_value & 0xffffffff)
819 else:
820 return format_string % ("", str(var_name) + ":", var_value)
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500821 elif isinstance(var_value, type):
Michael Walsh20a87ab2017-06-30 17:00:30 -0500822 return sprint_varx(var_name, str(var_value).split("'")[1], hex,
Michael Walshd2869032018-03-22 16:12:11 -0500823 loc_col1_indent, loc_col1_width, trailing_char,
824 key_list)
Michael Walshde791732016-09-06 14:25:24 -0500825 else:
826 # The data type is complex in the sense that it has subordinate parts.
827 format_string = "%" + str(loc_col1_indent) + "s%s\n"
828 buffer = format_string % ("", var_name + ":")
829 loc_col1_indent += 2
Michael Walsh7423c012016-10-04 10:27:21 -0500830 try:
831 length = len(var_value)
832 except TypeError:
Michael Walsh23e7f492017-01-10 11:34:47 -0600833 length = 0
Michael Walsh7423c012016-10-04 10:27:21 -0500834 ix = 0
835 loc_trailing_char = "\n"
Michael Walshbec416d2016-11-10 08:54:52 -0600836 type_is_dict = 0
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500837 if isinstance(var_value, dict):
Michael Walsh23e7f492017-01-10 11:34:47 -0600838 type_is_dict = 1
Michael Walsh8e6deb42017-01-27 14:22:41 -0600839 try:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500840 if isinstance(var_value, collections.OrderedDict):
Michael Walsh8e6deb42017-01-27 14:22:41 -0600841 type_is_dict = 1
842 except AttributeError:
843 pass
844 try:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500845 if isinstance(var_value, DotDict):
Michael Walsh8e6deb42017-01-27 14:22:41 -0600846 type_is_dict = 1
847 except NameError:
848 pass
849 try:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500850 if isinstance(var_value, NormalizedDict):
Michael Walsh8e6deb42017-01-27 14:22:41 -0600851 type_is_dict = 1
852 except NameError:
853 pass
Michael Walshbec416d2016-11-10 08:54:52 -0600854 if type_is_dict:
Michael Walsh37762f92018-08-07 14:59:18 -0500855 for key, value in var_value.items():
Michael Walshd2869032018-03-22 16:12:11 -0500856 if key_list is not None:
857 key_list_regex = "^" + "|".join(key_list) + "$"
858 if not re.match(key_list_regex, key):
859 continue
Michael Walsh7423c012016-10-04 10:27:21 -0500860 ix += 1
861 if ix == length:
862 loc_trailing_char = trailing_char
Michael Walshd995cb02017-02-07 14:46:01 -0600863 if hex:
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600864 # Since hex is being used as a format type, we want it
865 # turned off when processing integer dictionary values so
866 # it is not interpreted as a hex indicator.
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500867 loc_hex = not (isinstance(value, int))
Michael Walshf7b8a002017-08-29 10:38:39 -0500868 buffer += sprint_varx("[" + key + "]", value,
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600869 loc_hex, loc_col1_indent,
870 loc_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500871 loc_trailing_char,
872 key_list)
Michael Walshd995cb02017-02-07 14:46:01 -0600873 else:
Michael Walsh1173a522018-05-21 17:24:51 -0500874 buffer += sprint_varx(var_name + "[" + str(key) + "]",
875 value, hex, loc_col1_indent,
876 loc_col1_width, loc_trailing_char,
877 key_list)
Michael Walsh7423c012016-10-04 10:27:21 -0500878 elif type(var_value) in (list, tuple, set):
Michael Walshde791732016-09-06 14:25:24 -0500879 for key, value in enumerate(var_value):
Michael Walsh7423c012016-10-04 10:27:21 -0500880 ix += 1
881 if ix == length:
882 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500883 buffer += sprint_varx(var_name + "[" + str(key) + "]", value,
Michael Walsh7423c012016-10-04 10:27:21 -0500884 hex, loc_col1_indent, loc_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500885 loc_trailing_char, key_list)
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500886 elif isinstance(var_value, argparse.Namespace):
Michael Walshde791732016-09-06 14:25:24 -0500887 for key in var_value.__dict__:
Michael Walsh7423c012016-10-04 10:27:21 -0500888 ix += 1
889 if ix == length:
890 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500891 cmd_buf = "buffer += sprint_varx(var_name + \".\" + str(key)" \
Michael Walsh7423c012016-10-04 10:27:21 -0500892 + ", var_value." + key + ", hex, loc_col1_indent," \
Michael Walshd2869032018-03-22 16:12:11 -0500893 + " loc_col1_width, loc_trailing_char, key_list)"
Michael Walshde791732016-09-06 14:25:24 -0500894 exec(cmd_buf)
895 else:
896 var_type = type(var_value).__name__
897 func_name = sys._getframe().f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -0500898 var_value = "<" + var_type + " type not supported by " + \
899 func_name + "()>"
Michael Walshde791732016-09-06 14:25:24 -0500900 value_format = "%s"
901 loc_col1_indent -= 2
Michael Walsh7423c012016-10-04 10:27:21 -0500902 # Adjust loc_col1_width.
903 loc_col1_width = loc_col1_width - loc_col1_indent
Michael Walshde791732016-09-06 14:25:24 -0500904 format_string = "%" + str(loc_col1_indent) + "s%-" \
Michael Walsh7423c012016-10-04 10:27:21 -0500905 + str(loc_col1_width) + "s" + value_format + trailing_char
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600906 return format_string % ("", str(var_name) + ":", var_value)
Michael Walsh23e7f492017-01-10 11:34:47 -0600907
Michael Walshde791732016-09-06 14:25:24 -0500908 return buffer
909
910 return ""
911
Michael Walshde791732016-09-06 14:25:24 -0500912
Michael Walshfd2733c2017-11-13 11:36:20 -0600913def sprint_var(var_value,
914 hex=0,
915 loc_col1_indent=col1_indent,
916 loc_col1_width=col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500917 trailing_char="\n",
918 key_list=None):
Michael Walshde791732016-09-06 14:25:24 -0500919 r"""
920 Figure out the name of the first argument for you and then call
921 sprint_varx with it. Therefore, the following 2 calls are equivalent:
922 sprint_varx("var1", var1)
923 sprint_var(var1)
924 """
925
926 # Get the name of the first variable passed to this function.
927 stack_frame = 2
Michael Walsh7423c012016-10-04 10:27:21 -0500928 caller_func_name = sprint_func_name(2)
929 if caller_func_name.endswith("print_var"):
Michael Walshde791732016-09-06 14:25:24 -0500930 stack_frame += 1
931 var_name = get_arg_name(None, 1, stack_frame)
Michael Walshfd2733c2017-11-13 11:36:20 -0600932 return sprint_varx(var_name, var_value=var_value, hex=hex,
933 loc_col1_indent=loc_col1_indent,
934 loc_col1_width=loc_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500935 trailing_char=trailing_char,
936 key_list=key_list)
Michael Walshde791732016-09-06 14:25:24 -0500937
938
Michael Walsh18176322016-11-15 15:11:21 -0600939def sprint_vars(*args):
Michael Walsh18176322016-11-15 15:11:21 -0600940 r"""
941 Sprint the values of one or more variables.
942
943 Description of args:
944 args:
945 If the first argument is an integer, it will be interpreted to be the
946 "indent" value.
947 If the second argument is an integer, it will be interpreted to be the
948 "col1_width" value.
949 If the third argument is an integer, it will be interpreted to be the
950 "hex" value.
951 All remaining parms are considered variable names which are to be
952 sprinted.
953 """
954
955 if len(args) == 0:
956 return
957
958 # Get the name of the first variable passed to this function.
959 stack_frame = 2
960 caller_func_name = sprint_func_name(2)
961 if caller_func_name.endswith("print_vars"):
962 stack_frame += 1
963
964 parm_num = 1
965
966 # Create list from args (which is a tuple) so that it can be modified.
967 args_list = list(args)
968
969 var_name = get_arg_name(None, parm_num, stack_frame)
970 # See if parm 1 is to be interpreted as "indent".
971 try:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500972 if isinstance(int(var_name), int):
Michael Walsh18176322016-11-15 15:11:21 -0600973 indent = int(var_name)
974 args_list.pop(0)
975 parm_num += 1
976 except ValueError:
977 indent = 0
978
979 var_name = get_arg_name(None, parm_num, stack_frame)
980 # See if parm 1 is to be interpreted as "col1_width".
981 try:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500982 if isinstance(int(var_name), int):
Michael Walsh18176322016-11-15 15:11:21 -0600983 loc_col1_width = int(var_name)
984 args_list.pop(0)
985 parm_num += 1
986 except ValueError:
987 loc_col1_width = col1_width
988
989 var_name = get_arg_name(None, parm_num, stack_frame)
990 # See if parm 1 is to be interpreted as "hex".
991 try:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500992 if isinstance(int(var_name), int):
Michael Walsh18176322016-11-15 15:11:21 -0600993 hex = int(var_name)
994 args_list.pop(0)
995 parm_num += 1
996 except ValueError:
997 hex = 0
998
999 buffer = ""
1000 for var_value in args_list:
1001 var_name = get_arg_name(None, parm_num, stack_frame)
1002 buffer += sprint_varx(var_name, var_value, hex, indent, loc_col1_width)
1003 parm_num += 1
1004
1005 return buffer
1006
Michael Walsh18176322016-11-15 15:11:21 -06001007
Michael Walsh7423c012016-10-04 10:27:21 -05001008def sprint_dashes(indent=col1_indent,
1009 width=80,
1010 line_feed=1,
1011 char="-"):
Michael Walshde791732016-09-06 14:25:24 -05001012 r"""
1013 Return a string of dashes to the caller.
1014
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001015 Description of arguments:
Michael Walshde791732016-09-06 14:25:24 -05001016 indent The number of characters to indent the
1017 output.
1018 width The width of the string of dashes.
1019 line_feed Indicates whether the output should end
1020 with a line feed.
Michael Walsh7423c012016-10-04 10:27:21 -05001021 char The character to be repeated in the output
1022 string.
Michael Walshde791732016-09-06 14:25:24 -05001023 """
1024
Michael Walsh7423c012016-10-04 10:27:21 -05001025 width = int(width)
Michael Walsh23e7f492017-01-10 11:34:47 -06001026 buffer = " " * int(indent) + char * width
Michael Walshde791732016-09-06 14:25:24 -05001027 if line_feed:
1028 buffer += "\n"
1029
1030 return buffer
1031
Michael Walshde791732016-09-06 14:25:24 -05001032
Michael Walsh7423c012016-10-04 10:27:21 -05001033def sindent(text="",
1034 indent=0):
Michael Walsh7423c012016-10-04 10:27:21 -05001035 r"""
1036 Pre-pend the specified number of characters to the text string (i.e.
1037 indent it) and return it.
1038
1039 Description of arguments:
1040 text The string to be indented.
1041 indent The number of characters to indent the
1042 string.
1043 """
1044
1045 format_string = "%" + str(indent) + "s%s"
1046 buffer = format_string % ("", text)
1047
1048 return buffer
1049
Michael Walsh7423c012016-10-04 10:27:21 -05001050
Michael Walsh7423c012016-10-04 10:27:21 -05001051def sprint_call_stack(indent=0,
1052 stack_frame_ix=0):
Michael Walshde791732016-09-06 14:25:24 -05001053 r"""
1054 Return a call stack report for the given point in the program with line
1055 numbers, function names and function parameters and arguments.
1056
1057 Sample output:
1058
1059 -------------------------------------------------------------------------
1060 Python function call stack
1061
1062 Line # Function name and arguments
1063 ------ ------------------------------------------------------------------
1064 424 sprint_call_stack ()
1065 4 print_call_stack ()
1066 31 func1 (last_name = 'walsh', first_name = 'mikey')
1067 59 /tmp/scr5.py
1068 -------------------------------------------------------------------------
1069
1070 Description of arguments:
1071 indent The number of characters to indent each
1072 line of output.
1073 stack_frame_ix The index of the first stack frame which
1074 is to be returned.
1075 """
1076
1077 buffer = ""
Michael Walsh7423c012016-10-04 10:27:21 -05001078 buffer += sprint_dashes(indent)
1079 buffer += sindent("Python function call stack\n\n", indent)
1080 buffer += sindent("Line # Function name and arguments\n", indent)
1081 buffer += sprint_dashes(indent, 6, 0) + " " + sprint_dashes(0, 73)
Michael Walshde791732016-09-06 14:25:24 -05001082
1083 # Grab the current program stack.
1084 current_stack = inspect.stack()
1085
1086 # Process each frame in turn.
1087 format_string = "%6s %s\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001088 ix = 0
Michael Walshde791732016-09-06 14:25:24 -05001089 for stack_frame in current_stack:
Michael Walsh7423c012016-10-04 10:27:21 -05001090 if ix < stack_frame_ix:
1091 ix += 1
1092 continue
Michael Walsh23e7f492017-01-10 11:34:47 -06001093 # I want the line number shown to be the line where you find the line
1094 # shown.
1095 try:
1096 line_num = str(current_stack[ix + 1][2])
1097 except IndexError:
1098 line_num = ""
Michael Walshde791732016-09-06 14:25:24 -05001099 func_name = str(stack_frame[3])
1100 if func_name == "?":
1101 # "?" is the name used when code is not in a function.
1102 func_name = "(none)"
1103
1104 if func_name == "<module>":
Michael Walsh7423c012016-10-04 10:27:21 -05001105 # If the func_name is the "main" program, we simply get the
1106 # command line call string.
Michael Walshde791732016-09-06 14:25:24 -05001107 func_and_args = ' '.join(sys.argv)
1108 else:
1109 # Get the program arguments.
1110 arg_vals = inspect.getargvalues(stack_frame[0])
1111 function_parms = arg_vals[0]
1112 frame_locals = arg_vals[3]
1113
Michael Walsh7423c012016-10-04 10:27:21 -05001114 args_list = []
Michael Walshde791732016-09-06 14:25:24 -05001115 for arg_name in function_parms:
1116 # Get the arg value from frame locals.
1117 arg_value = frame_locals[arg_name]
Michael Walsh7423c012016-10-04 10:27:21 -05001118 args_list.append(arg_name + " = " + repr(arg_value))
1119 args_str = "(" + ', '.join(map(str, args_list)) + ")"
Michael Walshde791732016-09-06 14:25:24 -05001120
1121 # Now we need to print this in a nicely-wrapped way.
1122 func_and_args = func_name + " " + args_str
1123
Michael Walsh23e7f492017-01-10 11:34:47 -06001124 buffer += sindent(format_string % (line_num, func_and_args), indent)
Michael Walsh7423c012016-10-04 10:27:21 -05001125 ix += 1
Michael Walshde791732016-09-06 14:25:24 -05001126
Michael Walsh7423c012016-10-04 10:27:21 -05001127 buffer += sprint_dashes(indent)
Michael Walshde791732016-09-06 14:25:24 -05001128
1129 return buffer
1130
Michael Walshde791732016-09-06 14:25:24 -05001131
Michael Walshde791732016-09-06 14:25:24 -05001132def sprint_executing(stack_frame_ix=None):
Michael Walshde791732016-09-06 14:25:24 -05001133 r"""
1134 Print a line indicating what function is executing and with what parameter
1135 values. This is useful for debugging.
1136
1137 Sample output:
1138
1139 #(CDT) 2016/08/25 17:54:27 - Executing: func1 (x = 1)
1140
1141 Description of arguments:
1142 stack_frame_ix The index of the stack frame whose
1143 function info should be returned. If the
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001144 caller does not specify a value, this
Michael Walshde791732016-09-06 14:25:24 -05001145 function will set the value to 1 which is
1146 the index of the caller's stack frame. If
1147 the caller is the wrapper function
1148 "print_executing", this function will bump
1149 it up by 1.
1150 """
1151
1152 # If user wants default stack_frame_ix.
1153 if stack_frame_ix is None:
1154 func_name = sys._getframe().f_code.co_name
1155 caller_func_name = sys._getframe(1).f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -05001156 if caller_func_name.endswith(func_name[1:]):
Michael Walshde791732016-09-06 14:25:24 -05001157 stack_frame_ix = 2
1158 else:
1159 stack_frame_ix = 1
1160
1161 stack_frame = inspect.stack()[stack_frame_ix]
1162
1163 func_name = str(stack_frame[3])
1164 if func_name == "?":
1165 # "?" is the name used when code is not in a function.
1166 func_name = "(none)"
1167
1168 if func_name == "<module>":
1169 # If the func_name is the "main" program, we simply get the command
1170 # line call string.
1171 func_and_args = ' '.join(sys.argv)
1172 else:
1173 # Get the program arguments.
1174 arg_vals = inspect.getargvalues(stack_frame[0])
1175 function_parms = arg_vals[0]
1176 frame_locals = arg_vals[3]
1177
Michael Walsh7423c012016-10-04 10:27:21 -05001178 args_list = []
Michael Walshde791732016-09-06 14:25:24 -05001179 for arg_name in function_parms:
1180 # Get the arg value from frame locals.
1181 arg_value = frame_locals[arg_name]
Michael Walsh7423c012016-10-04 10:27:21 -05001182 args_list.append(arg_name + " = " + repr(arg_value))
1183 args_str = "(" + ', '.join(map(str, args_list)) + ")"
Michael Walshde791732016-09-06 14:25:24 -05001184
1185 # Now we need to print this in a nicely-wrapped way.
1186 func_and_args = func_name + " " + args_str
1187
1188 return sprint_time() + "Executing: " + func_and_args + "\n"
1189
Michael Walshde791732016-09-06 14:25:24 -05001190
Michael Walshbec416d2016-11-10 08:54:52 -06001191def sprint_pgm_header(indent=0,
1192 linefeed=1):
Michael Walshde791732016-09-06 14:25:24 -05001193 r"""
1194 Return a standardized header that programs should print at the beginning
1195 of the run. It includes useful information like command line, pid,
1196 userid, program parameters, etc.
1197
Michael Walsh7423c012016-10-04 10:27:21 -05001198 Description of arguments:
1199 indent The number of characters to indent each
1200 line of output.
Michael Walshbec416d2016-11-10 08:54:52 -06001201 linefeed Indicates whether a line feed be included
1202 at the beginning and end of the report.
Michael Walshde791732016-09-06 14:25:24 -05001203 """
1204
Michael Walshbec416d2016-11-10 08:54:52 -06001205 loc_col1_width = col1_width + indent
1206
1207 buffer = ""
1208 if linefeed:
1209 buffer = "\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001210
Michael Walshdb6e68a2017-05-23 17:55:31 -05001211 if robot_env:
1212 suite_name = BuiltIn().get_variable_value("${suite_name}")
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001213 buffer += sindent(sprint_time("Running test suite \"" + suite_name
1214 + "\".\n"), indent)
Michael Walshdb6e68a2017-05-23 17:55:31 -05001215
Michael Walsh7423c012016-10-04 10:27:21 -05001216 buffer += sindent(sprint_time() + "Running " + pgm_name + ".\n", indent)
1217 buffer += sindent(sprint_time() + "Program parameter values, etc.:\n\n",
1218 indent)
Michael Walshbec416d2016-11-10 08:54:52 -06001219 buffer += sprint_varx("command_line", ' '.join(sys.argv), 0, indent,
1220 loc_col1_width)
Michael Walsh7423c012016-10-04 10:27:21 -05001221 # We want the output to show a customized name for the pid and pgid but
1222 # we want it to look like a valid variable name. Therefore, we'll use
Michael Walshde791732016-09-06 14:25:24 -05001223 # pgm_name_var_name which was set when this module was imported.
Michael Walshbec416d2016-11-10 08:54:52 -06001224 buffer += sprint_varx(pgm_name_var_name + "_pid", os.getpid(), 0, indent,
1225 loc_col1_width)
1226 buffer += sprint_varx(pgm_name_var_name + "_pgid", os.getpgrp(), 0, indent,
1227 loc_col1_width)
Michael Walsh86de0d22016-12-05 10:13:15 -06001228 userid_num = str(os.geteuid())
1229 try:
1230 username = os.getlogin()
1231 except OSError:
1232 if userid_num == "0":
1233 username = "root"
1234 else:
1235 username = "?"
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001236 buffer += sprint_varx("uid", userid_num + " (" + username
1237 + ")", 0, indent, loc_col1_width)
1238 buffer += sprint_varx("gid", str(os.getgid()) + " ("
1239 + str(grp.getgrgid(os.getgid()).gr_name) + ")", 0,
Michael Walshbec416d2016-11-10 08:54:52 -06001240 indent, loc_col1_width)
1241 buffer += sprint_varx("host_name", socket.gethostname(), 0, indent,
1242 loc_col1_width)
Michael Walsh86de0d22016-12-05 10:13:15 -06001243 try:
1244 DISPLAY = os.environ['DISPLAY']
1245 except KeyError:
1246 DISPLAY = ""
1247 buffer += sprint_varx("DISPLAY", DISPLAY, 0, indent,
Michael Walshbec416d2016-11-10 08:54:52 -06001248 loc_col1_width)
Michael Walshde791732016-09-06 14:25:24 -05001249 # I want to add code to print caller's parms.
1250
Michael Walsh7423c012016-10-04 10:27:21 -05001251 # __builtin__.arg_obj is created by the get_arg module function,
1252 # gen_get_options.
1253 try:
1254 buffer += ga.sprint_args(__builtin__.arg_obj, indent)
1255 except AttributeError:
1256 pass
1257
Michael Walshdb6e68a2017-05-23 17:55:31 -05001258 if robot_env:
1259 # Get value of global parm_list.
1260 parm_list = BuiltIn().get_variable_value("${parm_list}")
1261
1262 for parm in parm_list:
1263 parm_value = BuiltIn().get_variable_value("${" + parm + "}")
1264 buffer += sprint_varx(parm, parm_value, 0, indent, loc_col1_width)
1265
1266 # Setting global program_pid.
1267 BuiltIn().set_global_variable("${program_pid}", os.getpid())
1268
Michael Walshbec416d2016-11-10 08:54:52 -06001269 if linefeed:
1270 buffer += "\n"
Michael Walshde791732016-09-06 14:25:24 -05001271
1272 return buffer
1273
Michael Walshde791732016-09-06 14:25:24 -05001274
Michael Walsh7423c012016-10-04 10:27:21 -05001275def sprint_error_report(error_text="\n",
Michael Walshdb6e68a2017-05-23 17:55:31 -05001276 indent=2,
1277 format=None):
Michael Walsh7423c012016-10-04 10:27:21 -05001278 r"""
1279 Return a string with a standardized report which includes the caller's
1280 error text, the call stack and the program header.
1281
1282 Description of args:
1283 error_text The error text to be included in the
1284 report. The caller should include any
1285 needed linefeeds.
1286 indent The number of characters to indent each
1287 line of output.
Michael Walshdb6e68a2017-05-23 17:55:31 -05001288 format Long or short format. Long includes
1289 extras like lines of dashes, call stack,
1290 etc.
Michael Walsh7423c012016-10-04 10:27:21 -05001291 """
1292
Michael Walshdb6e68a2017-05-23 17:55:31 -05001293 # Process input.
1294 indent = int(indent)
1295 if format is None:
1296 if robot_env:
1297 format = 'short'
1298 else:
1299 format = 'long'
1300 error_text = error_text.rstrip('\n') + '\n'
1301
1302 if format == 'short':
1303 return sprint_error(error_text)
1304
Michael Walsh7423c012016-10-04 10:27:21 -05001305 buffer = ""
1306 buffer += sprint_dashes(width=120, char="=")
1307 buffer += sprint_error(error_text)
1308 buffer += "\n"
1309 # Calling sprint_call_stack with stack_frame_ix of 0 causes it to show
1310 # itself and this function in the call stack. This is not helpful to a
1311 # debugger and is therefore clutter. We will adjust the stack_frame_ix to
1312 # hide that information.
Michael Walsh9c75f672017-09-12 17:11:35 -05001313 stack_frame_ix = 1
Michael Walsh7423c012016-10-04 10:27:21 -05001314 caller_func_name = sprint_func_name(2)
1315 if caller_func_name.endswith("print_error_report"):
1316 stack_frame_ix += 1
Michael Walshdb6e68a2017-05-23 17:55:31 -05001317 if not robot_env:
1318 buffer += sprint_call_stack(indent, stack_frame_ix)
Michael Walsh7423c012016-10-04 10:27:21 -05001319 buffer += sprint_pgm_header(indent)
1320 buffer += sprint_dashes(width=120, char="=")
1321
1322 return buffer
1323
Michael Walsh7423c012016-10-04 10:27:21 -05001324
Michael Walsh18176322016-11-15 15:11:21 -06001325def sprint_issuing(cmd_buf,
1326 test_mode=0):
Michael Walshde791732016-09-06 14:25:24 -05001327 r"""
1328 Return a line indicating a command that the program is about to execute.
1329
1330 Sample output for a cmd_buf of "ls"
1331
1332 #(CDT) 2016/08/25 17:57:36 - Issuing: ls
Michael Walshbec416d2016-11-10 08:54:52 -06001333
Michael Walshde791732016-09-06 14:25:24 -05001334 Description of args:
1335 cmd_buf The command to be executed by caller.
Michael Walshbec416d2016-11-10 08:54:52 -06001336 test_mode With test_mode set, your output will look
1337 like this:
1338
1339 #(CDT) 2016/08/25 17:57:36 - (test_mode) Issuing: ls
1340
Michael Walshde791732016-09-06 14:25:24 -05001341 """
1342
Michael Walshbec416d2016-11-10 08:54:52 -06001343 buffer = sprint_time()
1344 if test_mode:
1345 buffer += "(test_mode) "
1346 buffer += "Issuing: " + cmd_buf + "\n"
Michael Walshde791732016-09-06 14:25:24 -05001347
1348 return buffer
1349
Michael Walshde791732016-09-06 14:25:24 -05001350
Michael Walshde791732016-09-06 14:25:24 -05001351def sprint_pgm_footer():
Michael Walshde791732016-09-06 14:25:24 -05001352 r"""
1353 Return a standardized footer that programs should print at the end of the
1354 program run. It includes useful information like total run time, etc.
1355 """
1356
1357 buffer = "\n" + sprint_time() + "Finished running " + pgm_name + ".\n\n"
1358
1359 total_time = time.time() - start_time
1360 total_time_string = "%0.6f" % total_time
1361
Michael Walsh7423c012016-10-04 10:27:21 -05001362 buffer += sprint_varx(pgm_name_var_name + "_runtime", total_time_string)
Michael Walshbec416d2016-11-10 08:54:52 -06001363 buffer += "\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001364
1365 return buffer
1366
Michael Walsh7423c012016-10-04 10:27:21 -05001367
Michael Walsh7423c012016-10-04 10:27:21 -05001368def sprint(buffer=""):
Michael Walsh7423c012016-10-04 10:27:21 -05001369 r"""
1370 Simply return the user's buffer. This function is used by the qprint and
1371 dprint functions defined dynamically below, i.e. it would not normally be
1372 called for general use.
1373
1374 Description of arguments.
1375 buffer This will be returned to the caller.
1376 """
Michael Walshde791732016-09-06 14:25:24 -05001377
Michael Walsh95e45102018-02-09 12:44:43 -06001378 try:
1379 return str(buffer)
1380 except UnicodeEncodeError:
1381 return buffer
Michael Walshbec416d2016-11-10 08:54:52 -06001382
Michael Walshbec416d2016-11-10 08:54:52 -06001383
Michael Walshbec416d2016-11-10 08:54:52 -06001384def sprintn(buffer=""):
Michael Walshbec416d2016-11-10 08:54:52 -06001385 r"""
1386 Simply return the user's buffer with a line feed. This function is used
1387 by the qprint and dprint functions defined dynamically below, i.e. it
1388 would not normally be called for general use.
1389
1390 Description of arguments.
1391 buffer This will be returned to the caller.
1392 """
1393
Michael Walsh95e45102018-02-09 12:44:43 -06001394 try:
1395 buffer = str(buffer) + "\n"
1396 except UnicodeEncodeError:
1397 buffer = buffer + "\n"
Michael Walshbec416d2016-11-10 08:54:52 -06001398
Michael Walshde791732016-09-06 14:25:24 -05001399 return buffer
1400
Michael Walsh168eb0f2017-12-01 15:35:32 -06001401
Michael Walshfd2733c2017-11-13 11:36:20 -06001402def gp_print(buffer,
1403 stream='stdout'):
Michael Walshfd2733c2017-11-13 11:36:20 -06001404 r"""
1405 Print the buffer using either sys.stdout.write or BuiltIn().log_to_console
1406 depending on whether we are running in a robot environment.
1407
1408 This function is intended for use only by other functions in this module.
1409
1410 Description of arguments:
1411 buffer The string to be printed.
1412 stream Either "stdout" or "stderr".
1413 """
1414
1415 if robot_env:
1416 BuiltIn().log_to_console(buffer, stream=stream, no_newline=True)
1417 else:
1418 if stream == "stdout":
1419 sys.stdout.write(buffer)
1420 sys.stdout.flush()
1421 else:
1422 sys.stderr.write(buffer)
1423 sys.stderr.flush()
Michael Walshde791732016-09-06 14:25:24 -05001424
1425
Michael Walsh168eb0f2017-12-01 15:35:32 -06001426def gp_log(buffer):
Michael Walsh168eb0f2017-12-01 15:35:32 -06001427 r"""
1428 Log the buffer using either python logging or BuiltIn().log depending on
1429 whether we are running in a robot environment.
1430
1431 This function is intended for use only by other functions in this module.
1432
1433 Description of arguments:
1434 buffer The string to be logged.
1435 """
1436
1437 if robot_env:
1438 BuiltIn().log(buffer)
1439 else:
1440 logging.warning(buffer)
1441
1442
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001443def gp_debug_print(buffer):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001444 r"""
Michael Walshfd2733c2017-11-13 11:36:20 -06001445 Print with gp_print only if gen_print_debug is set.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001446
1447 This function is intended for use only by other functions in this module.
1448
1449 Description of arguments:
1450 buffer The string to be printed.
1451 """
1452
1453 if not gen_print_debug:
1454 return
1455
Michael Walshfd2733c2017-11-13 11:36:20 -06001456 gp_print(buffer)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001457
1458
Michael Walshb1500152017-04-12 15:42:43 -05001459def get_var_value(var_value=None,
1460 default=1,
1461 var_name=None):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001462 r"""
Michael Walshb1500152017-04-12 15:42:43 -05001463 Return either var_value, the corresponding global value or default.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001464
Michael Walshb1500152017-04-12 15:42:43 -05001465 If var_value is not None, it will simply be returned.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001466
Michael Walshb1500152017-04-12 15:42:43 -05001467 If var_value is None, this function will return the corresponding global
1468 value of the variable in question.
1469
1470 Note: For global values, if we are in a robot environment,
1471 get_variable_value will be used. Otherwise, the __builtin__ version of
1472 the variable is returned (which are set by gen_arg.py functions).
1473
1474 If there is no global value associated with the variable, default is
1475 returned.
1476
1477 This function is useful for other functions in setting default values for
1478 parameters.
1479
1480 Example use:
1481
1482 def my_func(quiet=None):
1483
1484 quiet = int(get_var_value(quiet, 0))
1485
1486 Example calls to my_func():
1487
1488 In the following example, the caller is explicitly asking to have quiet be
1489 set to 1.
1490
1491 my_func(quiet=1)
1492
1493 In the following example, quiet will be set to the global value of quiet,
1494 if defined, or to 0 (the default).
1495
1496 my_func()
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001497
1498 Description of arguments:
Michael Walshb1500152017-04-12 15:42:43 -05001499 var_value The value to be returned (if not equal to
1500 None).
1501 default The value that is returned if var_value is
1502 None and there is no corresponding global
1503 value defined.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001504 var_name The name of the variable whose value is to
Michael Walshb1500152017-04-12 15:42:43 -05001505 be returned. Under most circumstances,
1506 this value need not be provided. This
1507 function can figure out the name of the
1508 variable passed as var_value. One
1509 exception to this would be if this
1510 function is called directly from a .robot
1511 file.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001512 """
1513
Michael Walshb1500152017-04-12 15:42:43 -05001514 if var_value is not None:
1515 return var_value
1516
1517 if var_name is None:
1518 var_name = get_arg_name(None, 1, 2)
1519
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001520 if robot_env:
Michael Walshc6537442017-06-06 15:33:52 -05001521 var_value = BuiltIn().get_variable_value("${" + var_name + "}",
1522 default)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001523 else:
1524 var_value = getattr(__builtin__, var_name, default)
1525
1526 return var_value
1527
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001528
Michael Walsh052ff812018-05-18 16:09:09 -05001529def get_stack_var(var_name,
1530 default="",
1531 init_stack_ix=2):
Michael Walsh052ff812018-05-18 16:09:09 -05001532 r"""
1533 Starting with the caller's stack level, search upward in the call stack,
1534 for a variable named var_name and return its value. If the variable
1535 cannot be found, return default.
1536
1537 Example code:
1538
1539 def func12():
1540 my_loc_var1 = get_stack_var('my_var1', "default value")
1541
1542 def func11():
1543 my_var1 = 11
1544 func12()
1545
1546 In this example, get_stack_var will find the value of my_var1 in func11's
1547 stack and will therefore return the value 11. Therefore, my_loc_var1
1548 would get set to 11.
1549
1550 Description of argument(s):
1551 var_name The name of the variable to be searched
1552 for.
1553 default The value to return if the the variable
1554 cannot be found.
1555 init_stack_ix The initial stack index from which to
1556 begin the search. 0 would be the index of
1557 this func1tion ("get_stack_var"), 1 would
1558 be the index of the function calling this
1559 function, etc.
1560 """
1561
1562 return next((frame[0].f_locals[var_name]
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001563 for frame in inspect.stack()[init_stack_ix:]
1564 if var_name in frame[0].f_locals), default)
Michael Walsh052ff812018-05-18 16:09:09 -05001565
1566
Michael Walsh82acf002017-05-04 14:33:05 -05001567# hidden_text is a list of passwords which are to be replaced with asterisks
1568# by print functions defined in this module.
1569hidden_text = []
1570# password_regex is created based on the contents of hidden_text.
1571password_regex = ""
1572
1573
Michael Walsh82acf002017-05-04 14:33:05 -05001574def register_passwords(*args):
Michael Walsh82acf002017-05-04 14:33:05 -05001575 r"""
1576 Register one or more passwords which are to be hidden in output produced
1577 by the print functions in this module.
1578
1579 Note: Blank password values are NOT registered. They are simply ignored.
1580
1581 Description of argument(s):
1582 args One or more password values. If a given
1583 password value is already registered, this
1584 function will simply do nothing.
1585 """
1586
1587 global hidden_text
1588 global password_regex
1589
1590 for password in args:
1591 if password == "":
1592 break
1593 if password in hidden_text:
1594 break
1595
1596 # Place the password into the hidden_text list.
1597 hidden_text.append(password)
1598 # Create a corresponding password regular expression. Escape regex
1599 # special characters too.
1600 password_regex = '(' +\
1601 '|'.join([re.escape(x) for x in hidden_text]) + ')'
1602
Michael Walsh82acf002017-05-04 14:33:05 -05001603
Michael Walsh82acf002017-05-04 14:33:05 -05001604def replace_passwords(buffer):
Michael Walsh82acf002017-05-04 14:33:05 -05001605 r"""
1606 Return the buffer but with all registered passwords replaced by a string
1607 of asterisks.
1608
1609
1610 Description of argument(s):
1611 buffer The string to be returned but with
1612 passwords replaced.
1613 """
1614
1615 global password_regex
1616
1617 if int(os.environ.get("DEBUG_SHOW_PASSWORDS", "0")):
1618 return buffer
1619
1620 if password_regex == "":
1621 # No passwords to replace.
1622 return buffer
1623
1624 return re.sub(password_regex, "********", buffer)
1625
Michael Walshfd2733c2017-11-13 11:36:20 -06001626
1627def create_print_wrapper_funcs(func_names,
1628 stderr_func_names,
1629 replace_dict):
Michael Walshfd2733c2017-11-13 11:36:20 -06001630 r"""
1631 Generate code for print wrapper functions and return the generated code as
1632 a string.
1633
1634 To illustrate, suppose there is a "print_foo_bar" function in the
1635 func_names list.
1636 This function will...
1637 - Expect that there is an sprint_foo_bar function already in existence.
1638 - Create a print_foo_bar function which calls sprint_foo_bar and prints
Michael Walshfaafa9c2018-06-27 16:39:31 -05001639 the result.
Michael Walshfd2733c2017-11-13 11:36:20 -06001640 - Create a qprint_foo_bar function which calls upon sprint_foo_bar only if
Michael Walshfaafa9c2018-06-27 16:39:31 -05001641 global value quiet is 0.
Michael Walshfd2733c2017-11-13 11:36:20 -06001642 - Create a dprint_foo_bar function which calls upon sprint_foo_bar only if
Michael Walshfaafa9c2018-06-27 16:39:31 -05001643 global value debug is 1.
Michael Walshfd2733c2017-11-13 11:36:20 -06001644
1645 Also, code will be generated to define aliases for each function as well.
1646 Each alias will be created by replacing "print_" in the function name with
1647 "p" For example, the alias for print_foo_bar will be pfoo_bar.
1648
1649 Description of argument(s):
1650 func_names A list of functions for which print
1651 wrapper function code is to be generated.
1652 stderr_func_names A list of functions whose generated code
1653 should print to stderr rather than to
1654 stdout.
1655 replace_dict Please see the create_func_def_string
1656 function in wrap_utils.py for details on
1657 this parameter. This parameter will be
1658 passed directly to create_func_def_string.
1659 """
1660
1661 buffer = ""
1662
1663 for func_name in func_names:
1664 if func_name in stderr_func_names:
1665 replace_dict['output_stream'] = "stderr"
1666 else:
1667 replace_dict['output_stream'] = "stdout"
1668
1669 s_func_name = "s" + func_name
1670 q_func_name = "q" + func_name
1671 d_func_name = "d" + func_name
1672
1673 # We don't want to try to redefine the "print" function, thus the
1674 # following if statement.
1675 if func_name != "print":
1676 func_def = create_func_def_string(s_func_name, func_name,
1677 print_func_template,
1678 replace_dict)
1679 buffer += func_def
1680
1681 func_def = create_func_def_string(s_func_name, "q" + func_name,
1682 qprint_func_template, replace_dict)
1683 buffer += func_def
1684
1685 func_def = create_func_def_string(s_func_name, "d" + func_name,
1686 dprint_func_template, replace_dict)
1687 buffer += func_def
1688
Michael Walsh168eb0f2017-12-01 15:35:32 -06001689 func_def = create_func_def_string(s_func_name, "l" + func_name,
1690 lprint_func_template, replace_dict)
1691 buffer += func_def
1692
Michael Walshfd2733c2017-11-13 11:36:20 -06001693 # Create abbreviated aliases (e.g. spvar is an alias for sprint_var).
1694 alias = re.sub("print_", "p", func_name)
1695 alias = re.sub("print", "p", alias)
Michael Walsh168eb0f2017-12-01 15:35:32 -06001696 prefixes = ["", "s", "q", "d", "l"]
Michael Walshfd2733c2017-11-13 11:36:20 -06001697 for prefix in prefixes:
1698 if alias == "p":
1699 continue
1700 func_def = prefix + alias + " = " + prefix + func_name
1701 buffer += func_def + "\n"
1702
1703 return buffer
Michael Walsh82acf002017-05-04 14:33:05 -05001704
1705
Michael Walshde791732016-09-06 14:25:24 -05001706# In the following section of code, we will dynamically create print versions
1707# for each of the sprint functions defined above. So, for example, where we
1708# have an sprint_time() function defined above that returns the time to the
Michael Walsh7423c012016-10-04 10:27:21 -05001709# caller in a string, we will create a corresponding print_time() function
1710# that will print that string directly to stdout.
Michael Walshde791732016-09-06 14:25:24 -05001711
Michael Walshfd2733c2017-11-13 11:36:20 -06001712# It can be complicated to follow what's being created by below. Here is an
1713# example of the print_time() function that will be created:
Michael Walshde791732016-09-06 14:25:24 -05001714
Michael Walshfd2733c2017-11-13 11:36:20 -06001715# def print_time(buffer=''):
1716# sys.stdout.write(replace_passwords(sprint_time(buffer=buffer)))
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001717# sys.stdout.flush()
Michael Walshde791732016-09-06 14:25:24 -05001718
Michael Walshfd2733c2017-11-13 11:36:20 -06001719# Templates for the various print wrapper functions.
1720print_func_template = \
1721 [
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001722 " <mod_qualifier>gp_print(<mod_qualifier>replace_passwords("
1723 + "<call_line>), stream='<output_stream>')"
Michael Walshfd2733c2017-11-13 11:36:20 -06001724 ]
1725
1726qprint_func_template = \
1727 [
Michael Walsh81c02342018-01-05 15:43:28 -06001728 " if int(<mod_qualifier>get_var_value(None, 0, \"quiet\")): return"
Michael Walshfd2733c2017-11-13 11:36:20 -06001729 ] + print_func_template
1730
1731dprint_func_template = \
1732 [
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001733 " if not int(<mod_qualifier>get_var_value(None, 0, \"debug\")):"
1734 + " return"
Michael Walshfd2733c2017-11-13 11:36:20 -06001735 ] + print_func_template
1736
Michael Walsh168eb0f2017-12-01 15:35:32 -06001737lprint_func_template = \
1738 [
Michael Walsh81c02342018-01-05 15:43:28 -06001739 " gp_log(<mod_qualifier>replace_passwords(<call_line>))"
Michael Walsh168eb0f2017-12-01 15:35:32 -06001740 ]
1741
Michael Walsh81c02342018-01-05 15:43:28 -06001742replace_dict = {'output_stream': 'stdout', 'mod_qualifier': ''}
Michael Walshfd2733c2017-11-13 11:36:20 -06001743
1744
1745gp_debug_print("robot_env: " + str(robot_env))
Michael Walshde791732016-09-06 14:25:24 -05001746
1747# func_names contains a list of all print functions which should be created
1748# from their sprint counterparts.
1749func_names = ['print_time', 'print_timen', 'print_error', 'print_varx',
Michael Walsh18176322016-11-15 15:11:21 -06001750 'print_var', 'print_vars', 'print_dashes', 'indent',
1751 'print_call_stack', 'print_func_name', 'print_executing',
1752 'print_pgm_header', 'print_issuing', 'print_pgm_footer',
1753 'print_error_report', 'print', 'printn']
Michael Walshde791732016-09-06 14:25:24 -05001754
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001755# stderr_func_names is a list of functions whose output should go to stderr
1756# rather than stdout.
1757stderr_func_names = ['print_error', 'print_error_report']
Michael Walshde791732016-09-06 14:25:24 -05001758
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001759
Michael Walshfd2733c2017-11-13 11:36:20 -06001760func_defs = create_print_wrapper_funcs(func_names, stderr_func_names,
1761 replace_dict)
1762gp_debug_print(func_defs)
1763exec(func_defs)