blob: 0de4489eb849dcb281cf0da3324955fee973d434 [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}", "")
30 except:
31 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:
206 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")
210 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:
228 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")
234 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())).
311 recast_regex = "([^ ]+\([ ]*)?"
312 import_name_regex = "([a-zA-Z0-9_]+\.)?"
313 func_name_regex = recast_regex + import_name_regex + "(" +\
314 '|'.join(aliases) + ")"
Michael Walsh1173a522018-05-21 17:24:51 -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:
328 print_error("Programmer error - Could not find the source line with" +
Michael Walsh1173a522018-05-21 17:24:51 -0500329 " 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 = "(.*=[ ]+)?"
378 called_func_name_regex = lvalue_prefix_regex + func_name_regex + "[ ]*\(.*"
379 called_func_name = re.sub(called_func_name_regex, "\\4", composite_line)
Michael Walsh1173a522018-05-21 17:24:51 -0500380 arg_list_etc = "(" + re.sub(pre_args_regex, "", composite_line)
Michael Walshde791732016-09-06 14:25:24 -0500381 if local_debug:
Michael Walsh23e7f492017-01-10 11:34:47 -0600382 print_varx("aliases", aliases, 0, debug_indent)
Michael Walsh3f248272018-06-01 13:59:35 -0500383 print_varx("import_name_regex", import_name_regex, 0, debug_indent)
384 print_varx("func_name_regex", func_name_regex, 0, debug_indent)
Michael Walsh1173a522018-05-21 17:24:51 -0500385 print_varx("pre_args_regex", pre_args_regex, 0, debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600386 print_varx("start_line_ix", start_line_ix, 0, debug_indent)
387 print_varx("end_line_ix", end_line_ix, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500388 print_varx("composite_line", composite_line, 0, debug_indent)
Michael Walsh1173a522018-05-21 17:24:51 -0500389 print_varx("lvalue_regex", lvalue_regex, 0, debug_indent)
390 print_varx("lvalue_string", lvalue_string, 0, debug_indent)
391 print_varx("lvalues", lvalues, 0, debug_indent)
Michael Walsh3f248272018-06-01 13:59:35 -0500392 print_varx("called_func_name_regex", called_func_name_regex, 0,
393 debug_indent)
Michael Walsh1173a522018-05-21 17:24:51 -0500394 print_varx("called_func_name", called_func_name, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500395 print_varx("arg_list_etc", arg_list_etc, 0, debug_indent)
396
397 # Parse arg list...
398 # Initialize...
399 nest_level = -1
400 arg_ix = 0
Michael Walsh7423c012016-10-04 10:27:21 -0500401 args_list = [""]
Michael Walshde791732016-09-06 14:25:24 -0500402 for ix in range(0, len(arg_list_etc)):
403 char = arg_list_etc[ix]
404 # Set the nest_level based on whether we've encounted a parenthesis.
405 if char == "(":
406 nest_level += 1
407 if nest_level == 0:
408 continue
409 elif char == ")":
410 nest_level -= 1
411 if nest_level < 0:
412 break
413
414 # If we reach a comma at base nest level, we are done processing an
Michael Walsh7423c012016-10-04 10:27:21 -0500415 # argument so we increment arg_ix and initialize a new args_list entry.
Michael Walshde791732016-09-06 14:25:24 -0500416 if char == "," and nest_level == 0:
417 arg_ix += 1
Michael Walsh7423c012016-10-04 10:27:21 -0500418 args_list.append("")
Michael Walshde791732016-09-06 14:25:24 -0500419 continue
420
Michael Walsh7423c012016-10-04 10:27:21 -0500421 # For any other character, we append it it to the current arg list
Michael Walshde791732016-09-06 14:25:24 -0500422 # entry.
Michael Walsh7423c012016-10-04 10:27:21 -0500423 args_list[arg_ix] += char
Michael Walshde791732016-09-06 14:25:24 -0500424
425 # Trim whitespace from each list entry.
Michael Walsh7423c012016-10-04 10:27:21 -0500426 args_list = [arg.strip() for arg in args_list]
Michael Walshde791732016-09-06 14:25:24 -0500427
Michael Walsh1173a522018-05-21 17:24:51 -0500428 if arg_num < 0:
429 if abs(arg_num) > len(lvalues):
430 argument = lvalues
431 else:
432 argument = lvalues[arg_num]
433 elif arg_num == 0:
434 argument = called_func_name
Michael Walsh2750b442018-05-18 14:49:11 -0500435 else:
Michael Walsh1173a522018-05-21 17:24:51 -0500436 if arg_num > len(args_list):
437 argument = args_list
438 else:
439 argument = args_list[arg_num - 1]
Michael Walshde791732016-09-06 14:25:24 -0500440
441 if local_debug:
Michael Walsh7423c012016-10-04 10:27:21 -0500442 print_varx("args_list", args_list, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500443 print_varx("argument", argument, 0, debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600444 print_dashes(0, 120)
Michael Walshde791732016-09-06 14:25:24 -0500445
446 return argument
447
Michael Walshde791732016-09-06 14:25:24 -0500448
Michael Walshde791732016-09-06 14:25:24 -0500449def sprint_time(buffer=""):
Michael Walshde791732016-09-06 14:25:24 -0500450 r"""
451 Return the time in the following format.
452
453 Example:
454
455 The following python code...
456
457 sys.stdout.write(sprint_time())
458 sys.stdout.write("Hi.\n")
459
460 Will result in the following type of output:
461
462 #(CDT) 2016/07/08 15:25:35 - Hi.
463
464 Example:
465
466 The following python code...
467
468 sys.stdout.write(sprint_time("Hi.\n"))
469
470 Will result in the following type of output:
471
472 #(CDT) 2016/08/03 17:12:05 - Hi.
473
474 The following environment variables will affect the formatting as
475 described:
476 NANOSECONDS This will cause the time stamps to be
477 precise to the microsecond (Yes, it
478 probably should have been named
479 MICROSECONDS but the convention was set
480 long ago so we're sticking with it).
481 Example of the output when environment
482 variable NANOSECONDS=1.
483
484 #(CDT) 2016/08/03 17:16:25.510469 - Hi.
485
486 SHOW_ELAPSED_TIME This will cause the elapsed time to be
487 included in the output. This is the
488 amount of time that has elapsed since the
489 last time this function was called. The
490 precision of the elapsed time field is
491 also affected by the value of the
492 NANOSECONDS environment variable. Example
493 of the output when environment variable
494 NANOSECONDS=0 and SHOW_ELAPSED_TIME=1.
495
496 #(CDT) 2016/08/03 17:17:40 - 0 - Hi.
497
498 Example of the output when environment variable NANOSECONDS=1 and
499 SHOW_ELAPSED_TIME=1.
500
501 #(CDT) 2016/08/03 17:18:47.317339 - 0.000046 - Hi.
502
503 Description of arguments.
504 buffer This will be appended to the formatted
505 time string.
506 """
507
508 global NANOSECONDS
509 global SHOW_ELAPSED_TIME
510 global sprint_time_last_seconds
511
512 seconds = time.time()
513 loc_time = time.localtime(seconds)
514 nanoseconds = "%0.6f" % seconds
515 pos = nanoseconds.find(".")
516 nanoseconds = nanoseconds[pos:]
517
518 time_string = time.strftime("#(%Z) %Y/%m/%d %H:%M:%S", loc_time)
519 if NANOSECONDS == "1":
520 time_string = time_string + nanoseconds
521
522 if SHOW_ELAPSED_TIME == "1":
523 cur_time_seconds = seconds
524 math_string = "%9.9f" % cur_time_seconds + " - " + "%9.9f" % \
525 sprint_time_last_seconds
526 elapsed_seconds = eval(math_string)
527 if NANOSECONDS == "1":
528 elapsed_seconds = "%11.6f" % elapsed_seconds
529 else:
530 elapsed_seconds = "%4i" % elapsed_seconds
531 sprint_time_last_seconds = cur_time_seconds
532 time_string = time_string + " - " + elapsed_seconds
533
534 return time_string + " - " + buffer
535
Michael Walshde791732016-09-06 14:25:24 -0500536
Michael Walshde791732016-09-06 14:25:24 -0500537def sprint_timen(buffer=""):
Michael Walshde791732016-09-06 14:25:24 -0500538 r"""
539 Append a line feed to the buffer, pass it to sprint_time and return the
540 result.
541 """
542
543 return sprint_time(buffer + "\n")
544
Michael Walshde791732016-09-06 14:25:24 -0500545
Michael Walshde791732016-09-06 14:25:24 -0500546def sprint_error(buffer=""):
Michael Walshde791732016-09-06 14:25:24 -0500547 r"""
548 Return a standardized error string. This includes:
549 - A time stamp
550 - The "**ERROR**" string
551 - The caller's buffer string.
552
553 Example:
554
555 The following python code...
556
557 print(sprint_error("Oops.\n"))
558
559 Will result in the following type of output:
560
561 #(CDT) 2016/08/03 17:12:05 - **ERROR** Oops.
562
563 Description of arguments.
564 buffer This will be appended to the formatted
565 error string.
566 """
567
568 return sprint_time() + "**ERROR** " + buffer
569
Michael Walshde791732016-09-06 14:25:24 -0500570
Michael Walsh3f248272018-06-01 13:59:35 -0500571# Implement "constants" with functions.
572def digit_length_in_bits():
573 r"""
574 Return the digit length in bits.
575 """
576
577 return 4
578
579
580def word_length_in_digits():
581 r"""
582 Return the word length in digits.
583 """
584
585 return 8
586
587
588def bit_length(number):
589 r"""
590 Return the bit length of the number.
591
592 Description of argument(s):
593 number The number to be analyzed.
594 """
595
596 if number < 0:
597 # Convert negative numbers to positive and subtract one. The
598 # following example illustrates the reason for this:
599 # Consider a single nibble whose signed values can range from -8 to 7
600 # (0x8 to 0x7). A value of 0x7 equals 0b0111. Therefore, its length
601 # in bits is 3. Since the negative bit (i.e. 0b1000) is not set, the
602 # value 7 clearly will fit in one nibble. With -8 = 0x8 = 0b1000, you
603 # have the smallest negative value that will fit. Note that it
604 # requires 3 bits of 0. So by converting a number value of -8 to a
605 # working_number of 7, this function can accurately calculate the
606 # number of bits and therefore nibbles required to represent the
607 # number in print.
608 working_number = abs(number) - 1
609 else:
610 working_number = number
611
612 # Handle the special case of the number 0.
613 if working_number == 0:
614 return 0
615
616 return len(bin(working_number)) - 2
617
618
619def get_req_num_hex_digits(number):
620 r"""
621 Return the required number of hex digits required to display the given
622 number.
623
624 The returned value will always be rounded up to the nearest multiple of 8.
625
626 Description of argument(s):
627 number The number to be analyzed.
628 """
629
630 if number < 0:
631 # Convert negative numbers to positive and subtract one. The
632 # following example illustrates the reason for this:
633 # Consider a single nibble whose signed values can range from -8 to 7
634 # (0x8 to 0x7). A value of 0x7 equals 0b0111. Therefore, its length
635 # in bits is 3. Since the negative bit (i.e. 0b1000) is not set, the
636 # value 7 clearly will fit in one nibble. With -8 = 0x8 = 0b1000, you
637 # have the smallest negative value that will fit. Note that it
638 # requires 3 bits of 0. So by converting a number value of -8 to a
639 # working_number of 7, this function can accurately calculate the
640 # number of bits and therefore nibbles required to represent the
641 # number in print.
642 working_number = abs(number) - 1
643 else:
644 working_number = number
645
646 # Handle the special case of the number 0.
647 if working_number == 0:
648 return word_length_in_digits()
649
650 num_length_in_bits = bit_length(working_number)
651 num_hex_digits, remainder = divmod(num_length_in_bits,
652 digit_length_in_bits())
653 if remainder > 0:
654 # Example: the number 7 requires 3 bits. The divmod above produces,
655 # 0 with remainder of 3. So because we have a remainder, we increment
656 # num_hex_digits from 0 to 1.
657 num_hex_digits += 1
658
659 # Check to see whether the negative bit is set. This is the left-most
660 # bit in the highest order digit.
661 negative_mask = 2 ** (num_hex_digits * 4 - 1)
662 if working_number & negative_mask:
663 # If a number that is intended to be positive has its negative bit
664 # on, an additional digit will be required to represent it correctly
665 # in print.
666 num_hex_digits += 1
667
668 num_words, remainder = divmod(num_hex_digits, word_length_in_digits())
669 if remainder > 0 or num_words == 0:
670 num_words += 1
671
672 # Round up to the next word length in digits.
673 return num_words * word_length_in_digits()
674
675
676def dft_num_hex_digits():
677 r"""
678 Return the default number of hex digits to be used to represent a hex
679 number in print.
680
681 The value returned is a function of sys.maxsize.
682 """
683
684 global _gen_print_dft_num_hex_digits_
685 try:
686 return _gen_print_dft_num_hex_digits_
687 except NameError:
688 _gen_print_dft_num_hex_digits_ = get_req_num_hex_digits(sys.maxsize)
689 return _gen_print_dft_num_hex_digits_
690
691
Michael Walshde791732016-09-06 14:25:24 -0500692def sprint_varx(var_name,
693 var_value,
694 hex=0,
695 loc_col1_indent=col1_indent,
Michael Walsh7423c012016-10-04 10:27:21 -0500696 loc_col1_width=col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500697 trailing_char="\n",
698 key_list=None):
Michael Walshde791732016-09-06 14:25:24 -0500699 r"""
700 Print the var name/value passed to it. If the caller lets loc_col1_width
701 default, the printing lines up nicely with output generated by the
702 print_time functions.
703
704 Note that the sprint_var function (defined below) can be used to call this
705 function so that the programmer does not need to pass the var_name.
706 sprint_var will figure out the var_name. The sprint_var function is the
707 one that would normally be used by the general user.
708
709 For example, the following python code:
710
711 first_name = "Mike"
712 print_time("Doing this...\n")
713 print_varx("first_name", first_name)
714 print_time("Doing that...\n")
715
716 Will generate output like this:
717
718 #(CDT) 2016/08/10 17:34:42.847374 - 0.001285 - Doing this...
719 first_name: Mike
720 #(CDT) 2016/08/10 17:34:42.847510 - 0.000136 - Doing that...
721
722 This function recognizes several complex types of data such as dict, list
723 or tuple.
724
725 For example, the following python code:
726
727 my_dict = dict(one=1, two=2, three=3)
728 print_var(my_dict)
729
730 Will generate the following output:
731
732 my_dict:
733 my_dict[three]: 3
734 my_dict[two]: 2
735 my_dict[one]: 1
736
737 Description of arguments.
738 var_name The name of the variable to be printed.
739 var_value The value of the variable to be printed.
740 hex This indicates that the value should be
741 printed in hex format. It is the user's
742 responsibility to ensure that a var_value
Michael Walshbec416d2016-11-10 08:54:52 -0600743 contains a valid hex number. For string
744 var_values, this will be interpreted as
745 show_blanks which means that blank values
Michael Walshd995cb02017-02-07 14:46:01 -0600746 will be printed as "<blank>". For dict
747 var_values, this will be interpreted as
748 terse format where keys are not repeated
749 in the output.
Michael Walshde791732016-09-06 14:25:24 -0500750 loc_col1_indent The number of spaces to indent the output.
751 loc_col1_width The width of the output column containing
752 the variable name. The default value of
753 this is adjusted so that the var_value
754 lines up with text printed via the
755 print_time function.
Michael Walsh7423c012016-10-04 10:27:21 -0500756 trailing_char The character to be used at the end of the
757 returned string. The default value is a
758 line feed.
Michael Walshd2869032018-03-22 16:12:11 -0500759 key_list A list of which dictionary keys should be
760 printed. All others keys will be skipped.
761 Each value in key_list will be regarded
762 as a regular expression and it will be
763 regarded as anchored to the beginning and
764 ends of the dictionary key being
765 referenced. For example if key_list is
766 ["one", "two"], the resulting regex used
767 will be "^one|two$", i.e. only keys "one"
768 and "two" from the var_value dictionary
769 will be printed. As another example, if
770 the caller were to specify a key_list of
771 ["one.*"], then only dictionary keys whose
772 names begin with "one" will be printed.
773 Note: This argument pertains only to
774 var_values which are dictionaries.
Michael Walsh7423c012016-10-04 10:27:21 -0500775 """
Michael Walshde791732016-09-06 14:25:24 -0500776
777 # Determine the type
Michael Walsh92ac3d02018-03-30 14:38:15 -0500778 if type(var_value) in (int, long, float, bool, str, unicode) \
Michael Walshde791732016-09-06 14:25:24 -0500779 or var_value is None:
780 # The data type is simple in the sense that it has no subordinate
781 # parts.
Michael Walsh7423c012016-10-04 10:27:21 -0500782 # Adjust loc_col1_width.
783 loc_col1_width = loc_col1_width - loc_col1_indent
Michael Walshde791732016-09-06 14:25:24 -0500784 # See if the user wants the output in hex format.
785 if hex:
Michael Walsh18176322016-11-15 15:11:21 -0600786 if type(var_value) not in (int, long):
Michael Walshbec416d2016-11-10 08:54:52 -0600787 value_format = "%s"
Michael Walsh2795edc2016-12-13 16:00:33 -0600788 if var_value == "":
Michael Walshbec416d2016-11-10 08:54:52 -0600789 var_value = "<blank>"
790 else:
Michael Walsh3f248272018-06-01 13:59:35 -0500791 num_hex_digits = max(dft_num_hex_digits(),
792 get_req_num_hex_digits(var_value))
793 # Convert a negative number to its positive twos complement
794 # for proper printing. For example, instead of printing -1 as
795 # "0x-000000000000001" it will be printed as
796 # "0xffffffffffffffff".
797 var_value = var_value & (2 ** (num_hex_digits * 4) - 1)
798 value_format = "0x%0" + str(num_hex_digits) + "x"
Michael Walshde791732016-09-06 14:25:24 -0500799 else:
800 value_format = "%s"
801 format_string = "%" + str(loc_col1_indent) + "s%-" \
Michael Walsh7423c012016-10-04 10:27:21 -0500802 + str(loc_col1_width) + "s" + value_format + trailing_char
Michael Walsh3383e652017-09-01 17:10:59 -0500803 if value_format == "0x%08x":
804 return format_string % ("", str(var_name) + ":",
805 var_value & 0xffffffff)
806 else:
807 return format_string % ("", str(var_name) + ":", var_value)
Michael Walsh20a87ab2017-06-30 17:00:30 -0500808 elif type(var_value) is type:
809 return sprint_varx(var_name, str(var_value).split("'")[1], hex,
Michael Walshd2869032018-03-22 16:12:11 -0500810 loc_col1_indent, loc_col1_width, trailing_char,
811 key_list)
Michael Walshde791732016-09-06 14:25:24 -0500812 else:
813 # The data type is complex in the sense that it has subordinate parts.
814 format_string = "%" + str(loc_col1_indent) + "s%s\n"
815 buffer = format_string % ("", var_name + ":")
816 loc_col1_indent += 2
Michael Walsh7423c012016-10-04 10:27:21 -0500817 try:
818 length = len(var_value)
819 except TypeError:
Michael Walsh23e7f492017-01-10 11:34:47 -0600820 length = 0
Michael Walsh7423c012016-10-04 10:27:21 -0500821 ix = 0
822 loc_trailing_char = "\n"
Michael Walshbec416d2016-11-10 08:54:52 -0600823 type_is_dict = 0
Michael Walsh23e7f492017-01-10 11:34:47 -0600824 if type(var_value) is dict:
825 type_is_dict = 1
Michael Walsh8e6deb42017-01-27 14:22:41 -0600826 try:
827 if type(var_value) is collections.OrderedDict:
828 type_is_dict = 1
829 except AttributeError:
830 pass
831 try:
832 if type(var_value) is DotDict:
833 type_is_dict = 1
834 except NameError:
835 pass
836 try:
837 if type(var_value) is NormalizedDict:
838 type_is_dict = 1
839 except NameError:
840 pass
Michael Walshbec416d2016-11-10 08:54:52 -0600841 if type_is_dict:
Michael Walshde791732016-09-06 14:25:24 -0500842 for key, value in var_value.iteritems():
Michael Walshd2869032018-03-22 16:12:11 -0500843 if key_list is not None:
844 key_list_regex = "^" + "|".join(key_list) + "$"
845 if not re.match(key_list_regex, key):
846 continue
Michael Walsh7423c012016-10-04 10:27:21 -0500847 ix += 1
848 if ix == length:
849 loc_trailing_char = trailing_char
Michael Walshd995cb02017-02-07 14:46:01 -0600850 if hex:
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600851 # Since hex is being used as a format type, we want it
852 # turned off when processing integer dictionary values so
853 # it is not interpreted as a hex indicator.
854 loc_hex = not (type(value) is int)
Michael Walshf7b8a002017-08-29 10:38:39 -0500855 buffer += sprint_varx("[" + key + "]", value,
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600856 loc_hex, loc_col1_indent,
857 loc_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500858 loc_trailing_char,
859 key_list)
Michael Walshd995cb02017-02-07 14:46:01 -0600860 else:
Michael Walsh1173a522018-05-21 17:24:51 -0500861 buffer += sprint_varx(var_name + "[" + str(key) + "]",
862 value, hex, loc_col1_indent,
863 loc_col1_width, loc_trailing_char,
864 key_list)
Michael Walsh7423c012016-10-04 10:27:21 -0500865 elif type(var_value) in (list, tuple, set):
Michael Walshde791732016-09-06 14:25:24 -0500866 for key, value in enumerate(var_value):
Michael Walsh7423c012016-10-04 10:27:21 -0500867 ix += 1
868 if ix == length:
869 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500870 buffer += sprint_varx(var_name + "[" + str(key) + "]", value,
Michael Walsh7423c012016-10-04 10:27:21 -0500871 hex, loc_col1_indent, loc_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500872 loc_trailing_char, key_list)
Michael Walshde791732016-09-06 14:25:24 -0500873 elif type(var_value) is argparse.Namespace:
874 for key in var_value.__dict__:
Michael Walsh7423c012016-10-04 10:27:21 -0500875 ix += 1
876 if ix == length:
877 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500878 cmd_buf = "buffer += sprint_varx(var_name + \".\" + str(key)" \
Michael Walsh7423c012016-10-04 10:27:21 -0500879 + ", var_value." + key + ", hex, loc_col1_indent," \
Michael Walshd2869032018-03-22 16:12:11 -0500880 + " loc_col1_width, loc_trailing_char, key_list)"
Michael Walshde791732016-09-06 14:25:24 -0500881 exec(cmd_buf)
882 else:
883 var_type = type(var_value).__name__
884 func_name = sys._getframe().f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -0500885 var_value = "<" + var_type + " type not supported by " + \
886 func_name + "()>"
Michael Walshde791732016-09-06 14:25:24 -0500887 value_format = "%s"
888 loc_col1_indent -= 2
Michael Walsh7423c012016-10-04 10:27:21 -0500889 # Adjust loc_col1_width.
890 loc_col1_width = loc_col1_width - loc_col1_indent
Michael Walshde791732016-09-06 14:25:24 -0500891 format_string = "%" + str(loc_col1_indent) + "s%-" \
Michael Walsh7423c012016-10-04 10:27:21 -0500892 + str(loc_col1_width) + "s" + value_format + trailing_char
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600893 return format_string % ("", str(var_name) + ":", var_value)
Michael Walsh23e7f492017-01-10 11:34:47 -0600894
Michael Walshde791732016-09-06 14:25:24 -0500895 return buffer
896
897 return ""
898
Michael Walshde791732016-09-06 14:25:24 -0500899
Michael Walshfd2733c2017-11-13 11:36:20 -0600900def sprint_var(var_value,
901 hex=0,
902 loc_col1_indent=col1_indent,
903 loc_col1_width=col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500904 trailing_char="\n",
905 key_list=None):
Michael Walshde791732016-09-06 14:25:24 -0500906 r"""
907 Figure out the name of the first argument for you and then call
908 sprint_varx with it. Therefore, the following 2 calls are equivalent:
909 sprint_varx("var1", var1)
910 sprint_var(var1)
911 """
912
913 # Get the name of the first variable passed to this function.
914 stack_frame = 2
Michael Walsh7423c012016-10-04 10:27:21 -0500915 caller_func_name = sprint_func_name(2)
916 if caller_func_name.endswith("print_var"):
Michael Walshde791732016-09-06 14:25:24 -0500917 stack_frame += 1
918 var_name = get_arg_name(None, 1, stack_frame)
Michael Walshfd2733c2017-11-13 11:36:20 -0600919 return sprint_varx(var_name, var_value=var_value, hex=hex,
920 loc_col1_indent=loc_col1_indent,
921 loc_col1_width=loc_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500922 trailing_char=trailing_char,
923 key_list=key_list)
Michael Walshde791732016-09-06 14:25:24 -0500924
925
Michael Walsh18176322016-11-15 15:11:21 -0600926def sprint_vars(*args):
Michael Walsh18176322016-11-15 15:11:21 -0600927 r"""
928 Sprint the values of one or more variables.
929
930 Description of args:
931 args:
932 If the first argument is an integer, it will be interpreted to be the
933 "indent" value.
934 If the second argument is an integer, it will be interpreted to be the
935 "col1_width" value.
936 If the third argument is an integer, it will be interpreted to be the
937 "hex" value.
938 All remaining parms are considered variable names which are to be
939 sprinted.
940 """
941
942 if len(args) == 0:
943 return
944
945 # Get the name of the first variable passed to this function.
946 stack_frame = 2
947 caller_func_name = sprint_func_name(2)
948 if caller_func_name.endswith("print_vars"):
949 stack_frame += 1
950
951 parm_num = 1
952
953 # Create list from args (which is a tuple) so that it can be modified.
954 args_list = list(args)
955
956 var_name = get_arg_name(None, parm_num, stack_frame)
957 # See if parm 1 is to be interpreted as "indent".
958 try:
959 if type(int(var_name)) is int:
960 indent = int(var_name)
961 args_list.pop(0)
962 parm_num += 1
963 except ValueError:
964 indent = 0
965
966 var_name = get_arg_name(None, parm_num, stack_frame)
967 # See if parm 1 is to be interpreted as "col1_width".
968 try:
969 if type(int(var_name)) is int:
970 loc_col1_width = int(var_name)
971 args_list.pop(0)
972 parm_num += 1
973 except ValueError:
974 loc_col1_width = col1_width
975
976 var_name = get_arg_name(None, parm_num, stack_frame)
977 # See if parm 1 is to be interpreted as "hex".
978 try:
979 if type(int(var_name)) is int:
980 hex = int(var_name)
981 args_list.pop(0)
982 parm_num += 1
983 except ValueError:
984 hex = 0
985
986 buffer = ""
987 for var_value in args_list:
988 var_name = get_arg_name(None, parm_num, stack_frame)
989 buffer += sprint_varx(var_name, var_value, hex, indent, loc_col1_width)
990 parm_num += 1
991
992 return buffer
993
Michael Walsh18176322016-11-15 15:11:21 -0600994
Michael Walsh7423c012016-10-04 10:27:21 -0500995def sprint_dashes(indent=col1_indent,
996 width=80,
997 line_feed=1,
998 char="-"):
Michael Walshde791732016-09-06 14:25:24 -0500999 r"""
1000 Return a string of dashes to the caller.
1001
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001002 Description of arguments:
Michael Walshde791732016-09-06 14:25:24 -05001003 indent The number of characters to indent the
1004 output.
1005 width The width of the string of dashes.
1006 line_feed Indicates whether the output should end
1007 with a line feed.
Michael Walsh7423c012016-10-04 10:27:21 -05001008 char The character to be repeated in the output
1009 string.
Michael Walshde791732016-09-06 14:25:24 -05001010 """
1011
Michael Walsh7423c012016-10-04 10:27:21 -05001012 width = int(width)
Michael Walsh23e7f492017-01-10 11:34:47 -06001013 buffer = " " * int(indent) + char * width
Michael Walshde791732016-09-06 14:25:24 -05001014 if line_feed:
1015 buffer += "\n"
1016
1017 return buffer
1018
Michael Walshde791732016-09-06 14:25:24 -05001019
Michael Walsh7423c012016-10-04 10:27:21 -05001020def sindent(text="",
1021 indent=0):
Michael Walsh7423c012016-10-04 10:27:21 -05001022 r"""
1023 Pre-pend the specified number of characters to the text string (i.e.
1024 indent it) and return it.
1025
1026 Description of arguments:
1027 text The string to be indented.
1028 indent The number of characters to indent the
1029 string.
1030 """
1031
1032 format_string = "%" + str(indent) + "s%s"
1033 buffer = format_string % ("", text)
1034
1035 return buffer
1036
Michael Walsh7423c012016-10-04 10:27:21 -05001037
Michael Walsh7423c012016-10-04 10:27:21 -05001038def sprint_call_stack(indent=0,
1039 stack_frame_ix=0):
Michael Walshde791732016-09-06 14:25:24 -05001040 r"""
1041 Return a call stack report for the given point in the program with line
1042 numbers, function names and function parameters and arguments.
1043
1044 Sample output:
1045
1046 -------------------------------------------------------------------------
1047 Python function call stack
1048
1049 Line # Function name and arguments
1050 ------ ------------------------------------------------------------------
1051 424 sprint_call_stack ()
1052 4 print_call_stack ()
1053 31 func1 (last_name = 'walsh', first_name = 'mikey')
1054 59 /tmp/scr5.py
1055 -------------------------------------------------------------------------
1056
1057 Description of arguments:
1058 indent The number of characters to indent each
1059 line of output.
1060 stack_frame_ix The index of the first stack frame which
1061 is to be returned.
1062 """
1063
1064 buffer = ""
Michael Walsh7423c012016-10-04 10:27:21 -05001065 buffer += sprint_dashes(indent)
1066 buffer += sindent("Python function call stack\n\n", indent)
1067 buffer += sindent("Line # Function name and arguments\n", indent)
1068 buffer += sprint_dashes(indent, 6, 0) + " " + sprint_dashes(0, 73)
Michael Walshde791732016-09-06 14:25:24 -05001069
1070 # Grab the current program stack.
1071 current_stack = inspect.stack()
1072
1073 # Process each frame in turn.
1074 format_string = "%6s %s\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001075 ix = 0
Michael Walshde791732016-09-06 14:25:24 -05001076 for stack_frame in current_stack:
Michael Walsh7423c012016-10-04 10:27:21 -05001077 if ix < stack_frame_ix:
1078 ix += 1
1079 continue
Michael Walsh23e7f492017-01-10 11:34:47 -06001080 # I want the line number shown to be the line where you find the line
1081 # shown.
1082 try:
1083 line_num = str(current_stack[ix + 1][2])
1084 except IndexError:
1085 line_num = ""
Michael Walshde791732016-09-06 14:25:24 -05001086 func_name = str(stack_frame[3])
1087 if func_name == "?":
1088 # "?" is the name used when code is not in a function.
1089 func_name = "(none)"
1090
1091 if func_name == "<module>":
Michael Walsh7423c012016-10-04 10:27:21 -05001092 # If the func_name is the "main" program, we simply get the
1093 # command line call string.
Michael Walshde791732016-09-06 14:25:24 -05001094 func_and_args = ' '.join(sys.argv)
1095 else:
1096 # Get the program arguments.
1097 arg_vals = inspect.getargvalues(stack_frame[0])
1098 function_parms = arg_vals[0]
1099 frame_locals = arg_vals[3]
1100
Michael Walsh7423c012016-10-04 10:27:21 -05001101 args_list = []
Michael Walshde791732016-09-06 14:25:24 -05001102 for arg_name in function_parms:
1103 # Get the arg value from frame locals.
1104 arg_value = frame_locals[arg_name]
Michael Walsh7423c012016-10-04 10:27:21 -05001105 args_list.append(arg_name + " = " + repr(arg_value))
1106 args_str = "(" + ', '.join(map(str, args_list)) + ")"
Michael Walshde791732016-09-06 14:25:24 -05001107
1108 # Now we need to print this in a nicely-wrapped way.
1109 func_and_args = func_name + " " + args_str
1110
Michael Walsh23e7f492017-01-10 11:34:47 -06001111 buffer += sindent(format_string % (line_num, func_and_args), indent)
Michael Walsh7423c012016-10-04 10:27:21 -05001112 ix += 1
Michael Walshde791732016-09-06 14:25:24 -05001113
Michael Walsh7423c012016-10-04 10:27:21 -05001114 buffer += sprint_dashes(indent)
Michael Walshde791732016-09-06 14:25:24 -05001115
1116 return buffer
1117
Michael Walshde791732016-09-06 14:25:24 -05001118
Michael Walshde791732016-09-06 14:25:24 -05001119def sprint_executing(stack_frame_ix=None):
Michael Walshde791732016-09-06 14:25:24 -05001120 r"""
1121 Print a line indicating what function is executing and with what parameter
1122 values. This is useful for debugging.
1123
1124 Sample output:
1125
1126 #(CDT) 2016/08/25 17:54:27 - Executing: func1 (x = 1)
1127
1128 Description of arguments:
1129 stack_frame_ix The index of the stack frame whose
1130 function info should be returned. If the
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001131 caller does not specify a value, this
Michael Walshde791732016-09-06 14:25:24 -05001132 function will set the value to 1 which is
1133 the index of the caller's stack frame. If
1134 the caller is the wrapper function
1135 "print_executing", this function will bump
1136 it up by 1.
1137 """
1138
1139 # If user wants default stack_frame_ix.
1140 if stack_frame_ix is None:
1141 func_name = sys._getframe().f_code.co_name
1142 caller_func_name = sys._getframe(1).f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -05001143 if caller_func_name.endswith(func_name[1:]):
Michael Walshde791732016-09-06 14:25:24 -05001144 stack_frame_ix = 2
1145 else:
1146 stack_frame_ix = 1
1147
1148 stack_frame = inspect.stack()[stack_frame_ix]
1149
1150 func_name = str(stack_frame[3])
1151 if func_name == "?":
1152 # "?" is the name used when code is not in a function.
1153 func_name = "(none)"
1154
1155 if func_name == "<module>":
1156 # If the func_name is the "main" program, we simply get the command
1157 # line call string.
1158 func_and_args = ' '.join(sys.argv)
1159 else:
1160 # Get the program arguments.
1161 arg_vals = inspect.getargvalues(stack_frame[0])
1162 function_parms = arg_vals[0]
1163 frame_locals = arg_vals[3]
1164
Michael Walsh7423c012016-10-04 10:27:21 -05001165 args_list = []
Michael Walshde791732016-09-06 14:25:24 -05001166 for arg_name in function_parms:
1167 # Get the arg value from frame locals.
1168 arg_value = frame_locals[arg_name]
Michael Walsh7423c012016-10-04 10:27:21 -05001169 args_list.append(arg_name + " = " + repr(arg_value))
1170 args_str = "(" + ', '.join(map(str, args_list)) + ")"
Michael Walshde791732016-09-06 14:25:24 -05001171
1172 # Now we need to print this in a nicely-wrapped way.
1173 func_and_args = func_name + " " + args_str
1174
1175 return sprint_time() + "Executing: " + func_and_args + "\n"
1176
Michael Walshde791732016-09-06 14:25:24 -05001177
Michael Walshbec416d2016-11-10 08:54:52 -06001178def sprint_pgm_header(indent=0,
1179 linefeed=1):
Michael Walshde791732016-09-06 14:25:24 -05001180 r"""
1181 Return a standardized header that programs should print at the beginning
1182 of the run. It includes useful information like command line, pid,
1183 userid, program parameters, etc.
1184
Michael Walsh7423c012016-10-04 10:27:21 -05001185 Description of arguments:
1186 indent The number of characters to indent each
1187 line of output.
Michael Walshbec416d2016-11-10 08:54:52 -06001188 linefeed Indicates whether a line feed be included
1189 at the beginning and end of the report.
Michael Walshde791732016-09-06 14:25:24 -05001190 """
1191
Michael Walshbec416d2016-11-10 08:54:52 -06001192 loc_col1_width = col1_width + indent
1193
1194 buffer = ""
1195 if linefeed:
1196 buffer = "\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001197
Michael Walshdb6e68a2017-05-23 17:55:31 -05001198 if robot_env:
1199 suite_name = BuiltIn().get_variable_value("${suite_name}")
1200 buffer += sindent(sprint_time("Running test suite \"" + suite_name +
Gunnar Mills096cd562018-03-26 10:19:12 -05001201 "\".\n"), indent)
Michael Walshdb6e68a2017-05-23 17:55:31 -05001202
Michael Walsh7423c012016-10-04 10:27:21 -05001203 buffer += sindent(sprint_time() + "Running " + pgm_name + ".\n", indent)
1204 buffer += sindent(sprint_time() + "Program parameter values, etc.:\n\n",
1205 indent)
Michael Walshbec416d2016-11-10 08:54:52 -06001206 buffer += sprint_varx("command_line", ' '.join(sys.argv), 0, indent,
1207 loc_col1_width)
Michael Walsh7423c012016-10-04 10:27:21 -05001208 # We want the output to show a customized name for the pid and pgid but
1209 # we want it to look like a valid variable name. Therefore, we'll use
Michael Walshde791732016-09-06 14:25:24 -05001210 # pgm_name_var_name which was set when this module was imported.
Michael Walshbec416d2016-11-10 08:54:52 -06001211 buffer += sprint_varx(pgm_name_var_name + "_pid", os.getpid(), 0, indent,
1212 loc_col1_width)
1213 buffer += sprint_varx(pgm_name_var_name + "_pgid", os.getpgrp(), 0, indent,
1214 loc_col1_width)
Michael Walsh86de0d22016-12-05 10:13:15 -06001215 userid_num = str(os.geteuid())
1216 try:
1217 username = os.getlogin()
1218 except OSError:
1219 if userid_num == "0":
1220 username = "root"
1221 else:
1222 username = "?"
1223 buffer += sprint_varx("uid", userid_num + " (" + username +
Michael Walshbec416d2016-11-10 08:54:52 -06001224 ")", 0, indent, loc_col1_width)
Michael Walshde791732016-09-06 14:25:24 -05001225 buffer += sprint_varx("gid", str(os.getgid()) + " (" +
Michael Walsh7423c012016-10-04 10:27:21 -05001226 str(grp.getgrgid(os.getgid()).gr_name) + ")", 0,
Michael Walshbec416d2016-11-10 08:54:52 -06001227 indent, loc_col1_width)
1228 buffer += sprint_varx("host_name", socket.gethostname(), 0, indent,
1229 loc_col1_width)
Michael Walsh86de0d22016-12-05 10:13:15 -06001230 try:
1231 DISPLAY = os.environ['DISPLAY']
1232 except KeyError:
1233 DISPLAY = ""
1234 buffer += sprint_varx("DISPLAY", DISPLAY, 0, indent,
Michael Walshbec416d2016-11-10 08:54:52 -06001235 loc_col1_width)
Michael Walshde791732016-09-06 14:25:24 -05001236 # I want to add code to print caller's parms.
1237
Michael Walsh7423c012016-10-04 10:27:21 -05001238 # __builtin__.arg_obj is created by the get_arg module function,
1239 # gen_get_options.
1240 try:
1241 buffer += ga.sprint_args(__builtin__.arg_obj, indent)
1242 except AttributeError:
1243 pass
1244
Michael Walshdb6e68a2017-05-23 17:55:31 -05001245 if robot_env:
1246 # Get value of global parm_list.
1247 parm_list = BuiltIn().get_variable_value("${parm_list}")
1248
1249 for parm in parm_list:
1250 parm_value = BuiltIn().get_variable_value("${" + parm + "}")
1251 buffer += sprint_varx(parm, parm_value, 0, indent, loc_col1_width)
1252
1253 # Setting global program_pid.
1254 BuiltIn().set_global_variable("${program_pid}", os.getpid())
1255
Michael Walshbec416d2016-11-10 08:54:52 -06001256 if linefeed:
1257 buffer += "\n"
Michael Walshde791732016-09-06 14:25:24 -05001258
1259 return buffer
1260
Michael Walshde791732016-09-06 14:25:24 -05001261
Michael Walsh7423c012016-10-04 10:27:21 -05001262def sprint_error_report(error_text="\n",
Michael Walshdb6e68a2017-05-23 17:55:31 -05001263 indent=2,
1264 format=None):
Michael Walsh7423c012016-10-04 10:27:21 -05001265 r"""
1266 Return a string with a standardized report which includes the caller's
1267 error text, the call stack and the program header.
1268
1269 Description of args:
1270 error_text The error text to be included in the
1271 report. The caller should include any
1272 needed linefeeds.
1273 indent The number of characters to indent each
1274 line of output.
Michael Walshdb6e68a2017-05-23 17:55:31 -05001275 format Long or short format. Long includes
1276 extras like lines of dashes, call stack,
1277 etc.
Michael Walsh7423c012016-10-04 10:27:21 -05001278 """
1279
Michael Walshdb6e68a2017-05-23 17:55:31 -05001280 # Process input.
1281 indent = int(indent)
1282 if format is None:
1283 if robot_env:
1284 format = 'short'
1285 else:
1286 format = 'long'
1287 error_text = error_text.rstrip('\n') + '\n'
1288
1289 if format == 'short':
1290 return sprint_error(error_text)
1291
Michael Walsh7423c012016-10-04 10:27:21 -05001292 buffer = ""
1293 buffer += sprint_dashes(width=120, char="=")
1294 buffer += sprint_error(error_text)
1295 buffer += "\n"
1296 # Calling sprint_call_stack with stack_frame_ix of 0 causes it to show
1297 # itself and this function in the call stack. This is not helpful to a
1298 # debugger and is therefore clutter. We will adjust the stack_frame_ix to
1299 # hide that information.
Michael Walsh9c75f672017-09-12 17:11:35 -05001300 stack_frame_ix = 1
Michael Walsh7423c012016-10-04 10:27:21 -05001301 caller_func_name = sprint_func_name(2)
1302 if caller_func_name.endswith("print_error_report"):
1303 stack_frame_ix += 1
Michael Walshdb6e68a2017-05-23 17:55:31 -05001304 if not robot_env:
1305 buffer += sprint_call_stack(indent, stack_frame_ix)
Michael Walsh7423c012016-10-04 10:27:21 -05001306 buffer += sprint_pgm_header(indent)
1307 buffer += sprint_dashes(width=120, char="=")
1308
1309 return buffer
1310
Michael Walsh7423c012016-10-04 10:27:21 -05001311
Michael Walsh18176322016-11-15 15:11:21 -06001312def sprint_issuing(cmd_buf,
1313 test_mode=0):
Michael Walshde791732016-09-06 14:25:24 -05001314 r"""
1315 Return a line indicating a command that the program is about to execute.
1316
1317 Sample output for a cmd_buf of "ls"
1318
1319 #(CDT) 2016/08/25 17:57:36 - Issuing: ls
Michael Walshbec416d2016-11-10 08:54:52 -06001320
Michael Walshde791732016-09-06 14:25:24 -05001321 Description of args:
1322 cmd_buf The command to be executed by caller.
Michael Walshbec416d2016-11-10 08:54:52 -06001323 test_mode With test_mode set, your output will look
1324 like this:
1325
1326 #(CDT) 2016/08/25 17:57:36 - (test_mode) Issuing: ls
1327
Michael Walshde791732016-09-06 14:25:24 -05001328 """
1329
Michael Walshbec416d2016-11-10 08:54:52 -06001330 buffer = sprint_time()
1331 if test_mode:
1332 buffer += "(test_mode) "
1333 buffer += "Issuing: " + cmd_buf + "\n"
Michael Walshde791732016-09-06 14:25:24 -05001334
1335 return buffer
1336
Michael Walshde791732016-09-06 14:25:24 -05001337
Michael Walshde791732016-09-06 14:25:24 -05001338def sprint_pgm_footer():
Michael Walshde791732016-09-06 14:25:24 -05001339 r"""
1340 Return a standardized footer that programs should print at the end of the
1341 program run. It includes useful information like total run time, etc.
1342 """
1343
1344 buffer = "\n" + sprint_time() + "Finished running " + pgm_name + ".\n\n"
1345
1346 total_time = time.time() - start_time
1347 total_time_string = "%0.6f" % total_time
1348
Michael Walsh7423c012016-10-04 10:27:21 -05001349 buffer += sprint_varx(pgm_name_var_name + "_runtime", total_time_string)
Michael Walshbec416d2016-11-10 08:54:52 -06001350 buffer += "\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001351
1352 return buffer
1353
Michael Walsh7423c012016-10-04 10:27:21 -05001354
Michael Walsh7423c012016-10-04 10:27:21 -05001355def sprint(buffer=""):
Michael Walsh7423c012016-10-04 10:27:21 -05001356 r"""
1357 Simply return the user's buffer. This function is used by the qprint and
1358 dprint functions defined dynamically below, i.e. it would not normally be
1359 called for general use.
1360
1361 Description of arguments.
1362 buffer This will be returned to the caller.
1363 """
Michael Walshde791732016-09-06 14:25:24 -05001364
Michael Walsh95e45102018-02-09 12:44:43 -06001365 try:
1366 return str(buffer)
1367 except UnicodeEncodeError:
1368 return buffer
Michael Walshbec416d2016-11-10 08:54:52 -06001369
Michael Walshbec416d2016-11-10 08:54:52 -06001370
Michael Walshbec416d2016-11-10 08:54:52 -06001371def sprintn(buffer=""):
Michael Walshbec416d2016-11-10 08:54:52 -06001372 r"""
1373 Simply return the user's buffer with a line feed. This function is used
1374 by the qprint and dprint functions defined dynamically below, i.e. it
1375 would not normally be called for general use.
1376
1377 Description of arguments.
1378 buffer This will be returned to the caller.
1379 """
1380
Michael Walsh95e45102018-02-09 12:44:43 -06001381 try:
1382 buffer = str(buffer) + "\n"
1383 except UnicodeEncodeError:
1384 buffer = buffer + "\n"
Michael Walshbec416d2016-11-10 08:54:52 -06001385
Michael Walshde791732016-09-06 14:25:24 -05001386 return buffer
1387
Michael Walsh168eb0f2017-12-01 15:35:32 -06001388
Michael Walshfd2733c2017-11-13 11:36:20 -06001389def gp_print(buffer,
1390 stream='stdout'):
Michael Walshfd2733c2017-11-13 11:36:20 -06001391 r"""
1392 Print the buffer using either sys.stdout.write or BuiltIn().log_to_console
1393 depending on whether we are running in a robot environment.
1394
1395 This function is intended for use only by other functions in this module.
1396
1397 Description of arguments:
1398 buffer The string to be printed.
1399 stream Either "stdout" or "stderr".
1400 """
1401
1402 if robot_env:
1403 BuiltIn().log_to_console(buffer, stream=stream, no_newline=True)
1404 else:
1405 if stream == "stdout":
1406 sys.stdout.write(buffer)
1407 sys.stdout.flush()
1408 else:
1409 sys.stderr.write(buffer)
1410 sys.stderr.flush()
Michael Walshde791732016-09-06 14:25:24 -05001411
1412
Michael Walsh168eb0f2017-12-01 15:35:32 -06001413def gp_log(buffer):
Michael Walsh168eb0f2017-12-01 15:35:32 -06001414 r"""
1415 Log the buffer using either python logging or BuiltIn().log depending on
1416 whether we are running in a robot environment.
1417
1418 This function is intended for use only by other functions in this module.
1419
1420 Description of arguments:
1421 buffer The string to be logged.
1422 """
1423
1424 if robot_env:
1425 BuiltIn().log(buffer)
1426 else:
1427 logging.warning(buffer)
1428
1429
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001430def gp_debug_print(buffer):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001431 r"""
Michael Walshfd2733c2017-11-13 11:36:20 -06001432 Print with gp_print only if gen_print_debug is set.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001433
1434 This function is intended for use only by other functions in this module.
1435
1436 Description of arguments:
1437 buffer The string to be printed.
1438 """
1439
1440 if not gen_print_debug:
1441 return
1442
Michael Walshfd2733c2017-11-13 11:36:20 -06001443 gp_print(buffer)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001444
1445
Michael Walshb1500152017-04-12 15:42:43 -05001446def get_var_value(var_value=None,
1447 default=1,
1448 var_name=None):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001449 r"""
Michael Walshb1500152017-04-12 15:42:43 -05001450 Return either var_value, the corresponding global value or default.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001451
Michael Walshb1500152017-04-12 15:42:43 -05001452 If var_value is not None, it will simply be returned.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001453
Michael Walshb1500152017-04-12 15:42:43 -05001454 If var_value is None, this function will return the corresponding global
1455 value of the variable in question.
1456
1457 Note: For global values, if we are in a robot environment,
1458 get_variable_value will be used. Otherwise, the __builtin__ version of
1459 the variable is returned (which are set by gen_arg.py functions).
1460
1461 If there is no global value associated with the variable, default is
1462 returned.
1463
1464 This function is useful for other functions in setting default values for
1465 parameters.
1466
1467 Example use:
1468
1469 def my_func(quiet=None):
1470
1471 quiet = int(get_var_value(quiet, 0))
1472
1473 Example calls to my_func():
1474
1475 In the following example, the caller is explicitly asking to have quiet be
1476 set to 1.
1477
1478 my_func(quiet=1)
1479
1480 In the following example, quiet will be set to the global value of quiet,
1481 if defined, or to 0 (the default).
1482
1483 my_func()
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001484
1485 Description of arguments:
Michael Walshb1500152017-04-12 15:42:43 -05001486 var_value The value to be returned (if not equal to
1487 None).
1488 default The value that is returned if var_value is
1489 None and there is no corresponding global
1490 value defined.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001491 var_name The name of the variable whose value is to
Michael Walshb1500152017-04-12 15:42:43 -05001492 be returned. Under most circumstances,
1493 this value need not be provided. This
1494 function can figure out the name of the
1495 variable passed as var_value. One
1496 exception to this would be if this
1497 function is called directly from a .robot
1498 file.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001499 """
1500
Michael Walshb1500152017-04-12 15:42:43 -05001501 if var_value is not None:
1502 return var_value
1503
1504 if var_name is None:
1505 var_name = get_arg_name(None, 1, 2)
1506
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001507 if robot_env:
Michael Walshc6537442017-06-06 15:33:52 -05001508 var_value = BuiltIn().get_variable_value("${" + var_name + "}",
1509 default)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001510 else:
1511 var_value = getattr(__builtin__, var_name, default)
1512
1513 return var_value
1514
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001515
Michael Walsh052ff812018-05-18 16:09:09 -05001516def get_stack_var(var_name,
1517 default="",
1518 init_stack_ix=2):
1519
1520 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]
1551 for frame in inspect.stack()[init_stack_ix:]
1552 if var_name in frame[0].f_locals), default)
1553
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
1627 the result.
1628 - Create a qprint_foo_bar function which calls upon sprint_foo_bar only if
1629 global value quiet is 0.
1630 - Create a dprint_foo_bar function which calls upon sprint_foo_bar only if
1631 global value debug is 1.
1632
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 [
Michael Walsh81c02342018-01-05 15:43:28 -06001710 " <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 [
Michael Walsh81c02342018-01-05 15:43:28 -06001721 " if not int(<mod_qualifier>get_var_value(None, 0, \"debug\")):" +
Michael Walshfd2733c2017-11-13 11:36:20 -06001722 " return"
1723 ] + 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)