blob: 48d7f1c4cb6d22c67bcda7074fade4337a2ad08b [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()
Michael Walsh4fea2cf2018-08-22 17:48:18 -050077# sprint_time_last_seconds is used to calculate elapsed seconds.
78sprint_time_last_seconds = [start_time]
79# Define global index for the sprint_time_last_seconds list.
80last_seconds_ix = 0
81
82
83# Since output from the lprint_ functions goes to a different location than
84# the output from the print_ functions (e.g. a file vs. the console),
85# sprint_time_last_seconds has been created as a list rather than a simple
86# integer so that it can store multiple sprint_time_last_seconds values.
87# Standard print_ functions defined in this file will use
88# sprint_time_last_seconds[0] and the lprint_ functions will use
89# sprint_time_last_seconds[1].
90def lprint_last_seconds_ix():
91 r"""
92 Return lprint last_seconds index value to the caller.
93 """
94 return 1
95
Michael Walshde791732016-09-06 14:25:24 -050096
Michael Walsh2ee77cd2017-03-08 11:50:17 -060097# The user can set environment variable "GEN_PRINT_DEBUG" to get debug output
98# from this module.
99gen_print_debug = int(os.environ.get('GEN_PRINT_DEBUG', 0))
Michael Walsh7423c012016-10-04 10:27:21 -0500100
Michael Walshde791732016-09-06 14:25:24 -0500101
Michael Walshde791732016-09-06 14:25:24 -0500102def sprint_func_name(stack_frame_ix=None):
Michael Walshde791732016-09-06 14:25:24 -0500103 r"""
104 Return the function name associated with the indicated stack frame.
105
106 Description of arguments:
107 stack_frame_ix The index of the stack frame whose
108 function name should be returned. If the
Michael Walsh2ee77cd2017-03-08 11:50:17 -0600109 caller does not specify a value, this
Michael Walshde791732016-09-06 14:25:24 -0500110 function will set the value to 1 which is
111 the index of the caller's stack frame. If
112 the caller is the wrapper function
113 "print_func_name", this function will bump
114 it up by 1.
115 """
116
117 # If user specified no stack_frame_ix, we'll set it to a proper default
118 # value.
119 if stack_frame_ix is None:
120 func_name = sys._getframe().f_code.co_name
121 caller_func_name = sys._getframe(1).f_code.co_name
122 if func_name[1:] == caller_func_name:
123 stack_frame_ix = 2
124 else:
125 stack_frame_ix = 1
126
127 func_name = sys._getframe(stack_frame_ix).f_code.co_name
128
129 return func_name
130
Michael Walshde791732016-09-06 14:25:24 -0500131
Michael Walsh6f0362c2019-03-25 14:05:14 -0500132def work_around_inspect_stack_cwd_failure():
133 r"""
134 Work around the inspect.stack() getcwd() failure by making "/tmp" the
135 current working directory.
136
137 If the current working directory has been deleted, inspect.stack() will
138 fail with "OSError: [Errno 2] No such file or directory" because it tries
139 to do a getcwd().
140
141 This function will try to prevent this failure by detecting the scenario
142 in advance and making "/tmp" the current working directory.
143 """
144 try:
145 os.getcwd()
146 except OSError:
147 os.chdir("/tmp")
148
149
Michael Walsh1173a522018-05-21 17:24:51 -0500150def get_line_indent(line):
151 r"""
152 Return the number of spaces at the beginning of the line.
153 """
154
155 return len(line) - len(line.lstrip(' '))
156
157
Michael Walshde791732016-09-06 14:25:24 -0500158# get_arg_name is not a print function per se. I have included it in this
159# module because it is used by sprint_var which is found in this module.
Michael Walshde791732016-09-06 14:25:24 -0500160def get_arg_name(var,
161 arg_num=1,
162 stack_frame_ix=1):
Michael Walshde791732016-09-06 14:25:24 -0500163 r"""
164 Return the "name" of an argument passed to a function. This could be a
165 literal or a variable name.
166
Michael Walsh2ee77cd2017-03-08 11:50:17 -0600167 Description of arguments:
Michael Walshde791732016-09-06 14:25:24 -0500168 var The variable whose name you want returned.
Michael Walsh1173a522018-05-21 17:24:51 -0500169 arg_num The arg number whose name is to be
170 returned. To illustrate how arg_num is
171 processed, suppose that a programmer codes
172 this line: "rc, outbuf = my_func(var1,
173 var2)" and suppose that my_func has this
174 line of code: "result = gp.get_arg_name(0,
175 arg_num, 2)". If arg_num is positive, the
176 indicated argument is returned. For
177 example, if arg_num is 1, "var1" would be
178 returned, If arg_num is 2, "var2" would be
179 returned. If arg_num exceeds the number
180 of arguments, get_arg_name will simply
181 return a complete list of the arguments.
182 If arg_num is 0, get_arg_name will return
183 the name of the target function as
184 specified in the calling line ("my_func"
185 in this case). To clarify, if the caller
186 of the target function uses an alias
187 function name, the alias name would be
188 returned. If arg_num is negative, an
189 lvalue variable name is returned.
190 Continuing with the given example, if
191 arg_num is -2 the 2nd parm to the left of
192 the "=" ("rc" in this case) should be
193 returned. If arg_num is -1, the 1st parm
194 to the left of the "=" ("out_buf" in this
195 case) should be returned. If arg_num is
196 less than -2, an entire dictionary is
197 returned. The keys to the dictionary for
198 this example would be -2 and -1.
Michael Walshde791732016-09-06 14:25:24 -0500199 stack_frame_ix The stack frame index of the target
200 function. This value must be 1 or
201 greater. 1 would indicate get_arg_name's
202 stack frame. 2 would be the caller of
203 get_arg_name's stack frame, etc.
204
205 Example 1:
206
207 my_var = "mike"
208 var_name = get_arg_name(my_var)
209
210 In this example, var_name will receive the value "my_var".
211
212 Example 2:
213
214 def test1(var):
215 # Getting the var name of the first arg to this function, test1.
216 # Note, in this case, it doesn't matter what you pass as the first arg
217 # to get_arg_name since it is the caller's variable name that matters.
218 dummy = 1
219 arg_num = 1
220 stack_frame = 2
221 var_name = get_arg_name(dummy, arg_num, stack_frame)
222
223 # Mainline...
224
225 another_var = "whatever"
226 test1(another_var)
227
228 In this example, var_name will be set to "another_var".
229
230 """
231
232 # Note: I wish to avoid recursion so I refrain from calling any function
233 # that calls this function (i.e. sprint_var, valid_value, etc.).
234
Michael Walsh23e7f492017-01-10 11:34:47 -0600235 # The user can set environment variable "GET_ARG_NAME_DEBUG" to get debug
236 # output from this function.
237 local_debug = int(os.environ.get('GET_ARG_NAME_DEBUG', 0))
238 # In addition to GET_ARG_NAME_DEBUG, the user can set environment
239 # variable "GET_ARG_NAME_SHOW_SOURCE" to have this function include source
240 # code in the debug output.
241 local_debug_show_source = int(
242 os.environ.get('GET_ARG_NAME_SHOW_SOURCE', 0))
Michael Walshde791732016-09-06 14:25:24 -0500243
Michael Walshde791732016-09-06 14:25:24 -0500244 if stack_frame_ix < 1:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500245 print_error("Programmer error - Variable \"stack_frame_ix\" has an"
246 + " invalid value of \"" + str(stack_frame_ix) + "\". The"
247 + " value must be an integer that is greater than or equal"
248 + " to 1.\n")
Michael Walshde791732016-09-06 14:25:24 -0500249 return
250
251 if local_debug:
252 debug_indent = 2
Michael Walsh23e7f492017-01-10 11:34:47 -0600253 print("")
254 print_dashes(0, 120)
Michael Walshde791732016-09-06 14:25:24 -0500255 print(sprint_func_name() + "() parms:")
256 print_varx("var", var, 0, debug_indent)
257 print_varx("arg_num", arg_num, 0, debug_indent)
258 print_varx("stack_frame_ix", stack_frame_ix, 0, debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600259 print("")
260 print_call_stack(debug_indent, 2)
Michael Walshde791732016-09-06 14:25:24 -0500261
Michael Walsh6f0362c2019-03-25 14:05:14 -0500262 work_around_inspect_stack_cwd_failure()
Michael Walsh23e7f492017-01-10 11:34:47 -0600263 for count in range(0, 2):
264 try:
265 frame, filename, cur_line_no, function_name, lines, index = \
266 inspect.stack()[stack_frame_ix]
267 except IndexError:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500268 print_error("Programmer error - The caller has asked for"
269 + " information about the stack frame at index \""
270 + str(stack_frame_ix) + "\". However, the stack"
271 + " only contains " + str(len(inspect.stack()))
272 + " entries. Therefore the stack frame index is out"
273 + " of range.\n")
Michael Walsh23e7f492017-01-10 11:34:47 -0600274 return
275 if filename != "<string>":
276 break
277 # filename of "<string>" may mean that the function in question was
278 # defined dynamically and therefore its code stack is inaccessible.
279 # This may happen with functions like "rqprint_var". In this case,
280 # we'll increment the stack_frame_ix and try again.
281 stack_frame_ix += 1
282 if local_debug:
283 print("Adjusted stack_frame_ix...")
284 print_varx("stack_frame_ix", stack_frame_ix, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500285
Michael Walsh1173a522018-05-21 17:24:51 -0500286 real_called_func_name = sprint_func_name(stack_frame_ix)
Michael Walsh23e7f492017-01-10 11:34:47 -0600287
288 module = inspect.getmodule(frame)
289
290 # Though I would expect inspect.getsourcelines(frame) to get all module
291 # source lines if the frame is "<module>", it doesn't do that. Therefore,
292 # for this special case, I will do inspect.getsourcelines(module).
293 if function_name == "<module>":
294 source_lines, source_line_num =\
295 inspect.getsourcelines(module)
296 line_ix = cur_line_no - source_line_num - 1
297 else:
298 source_lines, source_line_num =\
299 inspect.getsourcelines(frame)
300 line_ix = cur_line_no - source_line_num
301
302 if local_debug:
303 print("\n Variables retrieved from inspect.stack() function:")
304 print_varx("frame", frame, 0, debug_indent + 2)
305 print_varx("filename", filename, 0, debug_indent + 2)
306 print_varx("cur_line_no", cur_line_no, 0, debug_indent + 2)
307 print_varx("function_name", function_name, 0, debug_indent + 2)
308 print_varx("lines", lines, 0, debug_indent + 2)
309 print_varx("index", index, 0, debug_indent + 2)
310 print_varx("source_line_num", source_line_num, 0, debug_indent)
311 print_varx("line_ix", line_ix, 0, debug_indent)
312 if local_debug_show_source:
313 print_varx("source_lines", source_lines, 0, debug_indent)
Michael Walsh1173a522018-05-21 17:24:51 -0500314 print_varx("real_called_func_name", real_called_func_name, 0,
315 debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600316
317 # Get a list of all functions defined for the module. Note that this
318 # doesn't work consistently when _run_exitfuncs is at the top of the stack
319 # (i.e. if we're running an exit function). I've coded a work-around
320 # below for this deficiency.
321 all_functions = inspect.getmembers(module, inspect.isfunction)
322
323 # Get called_func_id by searching for our function in the list of all
324 # functions.
325 called_func_id = None
326 for func_name, function in all_functions:
Michael Walsh1173a522018-05-21 17:24:51 -0500327 if func_name == real_called_func_name:
Michael Walsh23e7f492017-01-10 11:34:47 -0600328 called_func_id = id(function)
329 break
330 # NOTE: The only time I've found that called_func_id can't be found is
331 # when we're running from an exit function.
332
333 # Look for other functions in module with matching id.
Michael Walsh1173a522018-05-21 17:24:51 -0500334 aliases = set([real_called_func_name])
Michael Walsh23e7f492017-01-10 11:34:47 -0600335 for func_name, function in all_functions:
Michael Walsh1173a522018-05-21 17:24:51 -0500336 if func_name == real_called_func_name:
Michael Walsh23e7f492017-01-10 11:34:47 -0600337 continue
338 func_id = id(function)
339 if func_id == called_func_id:
340 aliases.add(func_name)
341
342 # In most cases, my general purpose code above will find all aliases.
343 # However, for the odd case (i.e. running from exit function), I've added
344 # code to handle pvar, qpvar, dpvar, etc. aliases explicitly since they
345 # are defined in this module and used frequently.
346 # pvar is an alias for print_var.
Michael Walsh1173a522018-05-21 17:24:51 -0500347 aliases.add(re.sub("print_var", "pvar", real_called_func_name))
Michael Walsh23e7f492017-01-10 11:34:47 -0600348
Michael Walsh3f248272018-06-01 13:59:35 -0500349 # The call to the function could be encased in a recast (e.g.
350 # int(func_name())).
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500351 recast_regex = "([^ ]+\\([ ]*)?"
352 import_name_regex = "([a-zA-Z0-9_]+\\.)?"
Michael Walsh3f248272018-06-01 13:59:35 -0500353 func_name_regex = recast_regex + import_name_regex + "(" +\
354 '|'.join(aliases) + ")"
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500355 pre_args_regex = ".*" + func_name_regex + "[ ]*\\("
Michael Walsh23e7f492017-01-10 11:34:47 -0600356
357 # Search backward through source lines looking for the calling function
358 # name.
359 found = False
360 for start_line_ix in range(line_ix, 0, -1):
361 # Skip comment lines.
362 if re.match(r"[ ]*#", source_lines[start_line_ix]):
363 continue
Michael Walsh1173a522018-05-21 17:24:51 -0500364 if re.match(pre_args_regex, source_lines[start_line_ix]):
Michael Walsh23e7f492017-01-10 11:34:47 -0600365 found = True
366 break
367 if not found:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500368 print_error("Programmer error - Could not find the source line with"
369 + " a reference to function \"" + real_called_func_name
370 + "\".\n")
Michael Walsh23e7f492017-01-10 11:34:47 -0600371 return
372
Michael Walsh82acf002017-05-04 14:33:05 -0500373 # Search forward through the source lines looking for a line whose
374 # indentation is the same or less than the start line. The end of our
375 # composite line should be the line preceding that line.
Michael Walsh1173a522018-05-21 17:24:51 -0500376 start_indent = get_line_indent(source_lines[start_line_ix])
Michael Walsh37cd29d2018-05-24 13:19:18 -0500377 end_line_ix = line_ix
Michael Walsh23e7f492017-01-10 11:34:47 -0600378 for end_line_ix in range(line_ix + 1, len(source_lines)):
379 if source_lines[end_line_ix].strip() == "":
380 continue
Michael Walsh1173a522018-05-21 17:24:51 -0500381 line_indent = get_line_indent(source_lines[end_line_ix])
Michael Walsh82acf002017-05-04 14:33:05 -0500382 if line_indent <= start_indent:
Michael Walsh23e7f492017-01-10 11:34:47 -0600383 end_line_ix -= 1
384 break
Michael Walsh1173a522018-05-21 17:24:51 -0500385 if start_line_ix != 0:
386 # Check to see whether the start line is a continuation of the prior
Michael Walsha52e9eb2018-09-10 13:56:01 -0500387 # line.
388 prior_line = source_lines[start_line_ix - 1]
389 prior_line_stripped = re.sub(r"[ ]*\\([\r\n]$)", " \\1", prior_line)
390 prior_line_indent = get_line_indent(prior_line)
391 if prior_line != prior_line_stripped and\
392 prior_line_indent < start_indent:
Michael Walsh1173a522018-05-21 17:24:51 -0500393 start_line_ix -= 1
Michael Walsha52e9eb2018-09-10 13:56:01 -0500394 # Remove the backslash (continuation char) from prior line.
395 source_lines[start_line_ix] = prior_line_stripped
Michael Walsh23e7f492017-01-10 11:34:47 -0600396
397 # Join the start line through the end line into a composite line.
398 composite_line = ''.join(map(str.strip,
Gunnar Mills096cd562018-03-26 10:19:12 -0500399 source_lines[start_line_ix:end_line_ix + 1]))
Michael Walsh1173a522018-05-21 17:24:51 -0500400 # Insert one space after first "=" if there isn't one already.
401 composite_line = re.sub("=[ ]*([^ ])", "= \\1", composite_line, 1)
Michael Walsh7423c012016-10-04 10:27:21 -0500402
Michael Walsh3f248272018-06-01 13:59:35 -0500403 lvalue_regex = "[ ]*=[ ]+" + func_name_regex + ".*"
Michael Walsh1173a522018-05-21 17:24:51 -0500404 lvalue_string = re.sub(lvalue_regex, "", composite_line)
Michael Walsh3f248272018-06-01 13:59:35 -0500405 if lvalue_string == composite_line:
406 # i.e. the regex did not match so there are no lvalues.
407 lvalue_string = ""
Michael Walsh37762f92018-08-07 14:59:18 -0500408 lvalues_list = list(filter(None, map(str.strip, lvalue_string.split(","))))
Michael Walsh3f248272018-06-01 13:59:35 -0500409 try:
410 lvalues = collections.OrderedDict()
411 except AttributeError:
412 # A non-ordered dict doesn't look as nice when printed but it will do.
413 lvalues = {}
Michael Walsh1173a522018-05-21 17:24:51 -0500414 ix = len(lvalues_list) * -1
415 for lvalue in lvalues_list:
416 lvalues[ix] = lvalue
417 ix += 1
Michael Walsh3f248272018-06-01 13:59:35 -0500418 lvalue_prefix_regex = "(.*=[ ]+)?"
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500419 called_func_name_regex = lvalue_prefix_regex + func_name_regex +\
420 "[ ]*\\(.*"
Michael Walsh3f248272018-06-01 13:59:35 -0500421 called_func_name = re.sub(called_func_name_regex, "\\4", composite_line)
Michael Walsh1173a522018-05-21 17:24:51 -0500422 arg_list_etc = "(" + re.sub(pre_args_regex, "", composite_line)
Michael Walshde791732016-09-06 14:25:24 -0500423 if local_debug:
Michael Walsh23e7f492017-01-10 11:34:47 -0600424 print_varx("aliases", aliases, 0, debug_indent)
Michael Walsh3f248272018-06-01 13:59:35 -0500425 print_varx("import_name_regex", import_name_regex, 0, debug_indent)
426 print_varx("func_name_regex", func_name_regex, 0, debug_indent)
Michael Walsh1173a522018-05-21 17:24:51 -0500427 print_varx("pre_args_regex", pre_args_regex, 0, debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600428 print_varx("start_line_ix", start_line_ix, 0, debug_indent)
429 print_varx("end_line_ix", end_line_ix, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500430 print_varx("composite_line", composite_line, 0, debug_indent)
Michael Walsh1173a522018-05-21 17:24:51 -0500431 print_varx("lvalue_regex", lvalue_regex, 0, debug_indent)
432 print_varx("lvalue_string", lvalue_string, 0, debug_indent)
433 print_varx("lvalues", lvalues, 0, debug_indent)
Michael Walsh3f248272018-06-01 13:59:35 -0500434 print_varx("called_func_name_regex", called_func_name_regex, 0,
435 debug_indent)
Michael Walsh1173a522018-05-21 17:24:51 -0500436 print_varx("called_func_name", called_func_name, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500437 print_varx("arg_list_etc", arg_list_etc, 0, debug_indent)
438
439 # Parse arg list...
440 # Initialize...
441 nest_level = -1
442 arg_ix = 0
Michael Walsh7423c012016-10-04 10:27:21 -0500443 args_list = [""]
Michael Walshde791732016-09-06 14:25:24 -0500444 for ix in range(0, len(arg_list_etc)):
445 char = arg_list_etc[ix]
446 # Set the nest_level based on whether we've encounted a parenthesis.
447 if char == "(":
448 nest_level += 1
449 if nest_level == 0:
450 continue
451 elif char == ")":
452 nest_level -= 1
453 if nest_level < 0:
454 break
455
456 # If we reach a comma at base nest level, we are done processing an
Michael Walsh7423c012016-10-04 10:27:21 -0500457 # argument so we increment arg_ix and initialize a new args_list entry.
Michael Walshde791732016-09-06 14:25:24 -0500458 if char == "," and nest_level == 0:
459 arg_ix += 1
Michael Walsh7423c012016-10-04 10:27:21 -0500460 args_list.append("")
Michael Walshde791732016-09-06 14:25:24 -0500461 continue
462
Michael Walsh7423c012016-10-04 10:27:21 -0500463 # For any other character, we append it it to the current arg list
Michael Walshde791732016-09-06 14:25:24 -0500464 # entry.
Michael Walsh7423c012016-10-04 10:27:21 -0500465 args_list[arg_ix] += char
Michael Walshde791732016-09-06 14:25:24 -0500466
467 # Trim whitespace from each list entry.
Michael Walsh7423c012016-10-04 10:27:21 -0500468 args_list = [arg.strip() for arg in args_list]
Michael Walshde791732016-09-06 14:25:24 -0500469
Michael Walsh1173a522018-05-21 17:24:51 -0500470 if arg_num < 0:
471 if abs(arg_num) > len(lvalues):
472 argument = lvalues
473 else:
474 argument = lvalues[arg_num]
475 elif arg_num == 0:
476 argument = called_func_name
Michael Walsh2750b442018-05-18 14:49:11 -0500477 else:
Michael Walsh1173a522018-05-21 17:24:51 -0500478 if arg_num > len(args_list):
479 argument = args_list
480 else:
481 argument = args_list[arg_num - 1]
Michael Walshde791732016-09-06 14:25:24 -0500482
483 if local_debug:
Michael Walsh7423c012016-10-04 10:27:21 -0500484 print_varx("args_list", args_list, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500485 print_varx("argument", argument, 0, debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600486 print_dashes(0, 120)
Michael Walshde791732016-09-06 14:25:24 -0500487
488 return argument
489
Michael Walshde791732016-09-06 14:25:24 -0500490
Michael Walshde791732016-09-06 14:25:24 -0500491def sprint_time(buffer=""):
Michael Walshde791732016-09-06 14:25:24 -0500492 r"""
493 Return the time in the following format.
494
495 Example:
496
497 The following python code...
498
499 sys.stdout.write(sprint_time())
500 sys.stdout.write("Hi.\n")
501
502 Will result in the following type of output:
503
504 #(CDT) 2016/07/08 15:25:35 - Hi.
505
506 Example:
507
508 The following python code...
509
510 sys.stdout.write(sprint_time("Hi.\n"))
511
512 Will result in the following type of output:
513
514 #(CDT) 2016/08/03 17:12:05 - Hi.
515
516 The following environment variables will affect the formatting as
517 described:
518 NANOSECONDS This will cause the time stamps to be
519 precise to the microsecond (Yes, it
520 probably should have been named
521 MICROSECONDS but the convention was set
522 long ago so we're sticking with it).
523 Example of the output when environment
524 variable NANOSECONDS=1.
525
526 #(CDT) 2016/08/03 17:16:25.510469 - Hi.
527
528 SHOW_ELAPSED_TIME This will cause the elapsed time to be
529 included in the output. This is the
530 amount of time that has elapsed since the
531 last time this function was called. The
532 precision of the elapsed time field is
533 also affected by the value of the
534 NANOSECONDS environment variable. Example
535 of the output when environment variable
536 NANOSECONDS=0 and SHOW_ELAPSED_TIME=1.
537
538 #(CDT) 2016/08/03 17:17:40 - 0 - Hi.
539
540 Example of the output when environment variable NANOSECONDS=1 and
541 SHOW_ELAPSED_TIME=1.
542
543 #(CDT) 2016/08/03 17:18:47.317339 - 0.000046 - Hi.
544
545 Description of arguments.
546 buffer This will be appended to the formatted
547 time string.
548 """
549
550 global NANOSECONDS
551 global SHOW_ELAPSED_TIME
552 global sprint_time_last_seconds
Michael Walsh4fea2cf2018-08-22 17:48:18 -0500553 global last_seconds_ix
Michael Walshde791732016-09-06 14:25:24 -0500554
555 seconds = time.time()
556 loc_time = time.localtime(seconds)
557 nanoseconds = "%0.6f" % seconds
558 pos = nanoseconds.find(".")
559 nanoseconds = nanoseconds[pos:]
560
561 time_string = time.strftime("#(%Z) %Y/%m/%d %H:%M:%S", loc_time)
562 if NANOSECONDS == "1":
563 time_string = time_string + nanoseconds
564
565 if SHOW_ELAPSED_TIME == "1":
566 cur_time_seconds = seconds
567 math_string = "%9.9f" % cur_time_seconds + " - " + "%9.9f" % \
Michael Walsh4fea2cf2018-08-22 17:48:18 -0500568 sprint_time_last_seconds[last_seconds_ix]
Michael Walshde791732016-09-06 14:25:24 -0500569 elapsed_seconds = eval(math_string)
570 if NANOSECONDS == "1":
571 elapsed_seconds = "%11.6f" % elapsed_seconds
572 else:
573 elapsed_seconds = "%4i" % elapsed_seconds
Michael Walsh4fea2cf2018-08-22 17:48:18 -0500574 sprint_time_last_seconds[last_seconds_ix] = cur_time_seconds
Michael Walshde791732016-09-06 14:25:24 -0500575 time_string = time_string + " - " + elapsed_seconds
576
577 return time_string + " - " + buffer
578
Michael Walshde791732016-09-06 14:25:24 -0500579
Michael Walshde791732016-09-06 14:25:24 -0500580def sprint_timen(buffer=""):
Michael Walshde791732016-09-06 14:25:24 -0500581 r"""
582 Append a line feed to the buffer, pass it to sprint_time and return the
583 result.
584 """
585
586 return sprint_time(buffer + "\n")
587
Michael Walshde791732016-09-06 14:25:24 -0500588
Michael Walshde791732016-09-06 14:25:24 -0500589def sprint_error(buffer=""):
Michael Walshde791732016-09-06 14:25:24 -0500590 r"""
591 Return a standardized error string. This includes:
592 - A time stamp
593 - The "**ERROR**" string
594 - The caller's buffer string.
595
596 Example:
597
598 The following python code...
599
600 print(sprint_error("Oops.\n"))
601
602 Will result in the following type of output:
603
604 #(CDT) 2016/08/03 17:12:05 - **ERROR** Oops.
605
606 Description of arguments.
607 buffer This will be appended to the formatted
608 error string.
609 """
610
611 return sprint_time() + "**ERROR** " + buffer
612
Michael Walshde791732016-09-06 14:25:24 -0500613
Michael Walsh3f248272018-06-01 13:59:35 -0500614# Implement "constants" with functions.
615def digit_length_in_bits():
616 r"""
617 Return the digit length in bits.
618 """
619
620 return 4
621
622
623def word_length_in_digits():
624 r"""
625 Return the word length in digits.
626 """
627
628 return 8
629
630
631def bit_length(number):
632 r"""
633 Return the bit length of the number.
634
635 Description of argument(s):
636 number The number to be analyzed.
637 """
638
639 if number < 0:
640 # Convert negative numbers to positive and subtract one. The
641 # following example illustrates the reason for this:
642 # Consider a single nibble whose signed values can range from -8 to 7
643 # (0x8 to 0x7). A value of 0x7 equals 0b0111. Therefore, its length
644 # in bits is 3. Since the negative bit (i.e. 0b1000) is not set, the
645 # value 7 clearly will fit in one nibble. With -8 = 0x8 = 0b1000, you
646 # have the smallest negative value that will fit. Note that it
647 # requires 3 bits of 0. So by converting a number value of -8 to a
648 # working_number of 7, this function can accurately calculate the
649 # number of bits and therefore nibbles required to represent the
650 # number in print.
651 working_number = abs(number) - 1
652 else:
653 working_number = number
654
655 # Handle the special case of the number 0.
656 if working_number == 0:
657 return 0
658
659 return len(bin(working_number)) - 2
660
661
662def get_req_num_hex_digits(number):
663 r"""
664 Return the required number of hex digits required to display the given
665 number.
666
667 The returned value will always be rounded up to the nearest multiple of 8.
668
669 Description of argument(s):
670 number The number to be analyzed.
671 """
672
673 if number < 0:
674 # Convert negative numbers to positive and subtract one. The
675 # following example illustrates the reason for this:
676 # Consider a single nibble whose signed values can range from -8 to 7
677 # (0x8 to 0x7). A value of 0x7 equals 0b0111. Therefore, its length
678 # in bits is 3. Since the negative bit (i.e. 0b1000) is not set, the
679 # value 7 clearly will fit in one nibble. With -8 = 0x8 = 0b1000, you
680 # have the smallest negative value that will fit. Note that it
681 # requires 3 bits of 0. So by converting a number value of -8 to a
682 # working_number of 7, this function can accurately calculate the
683 # number of bits and therefore nibbles required to represent the
684 # number in print.
685 working_number = abs(number) - 1
686 else:
687 working_number = number
688
689 # Handle the special case of the number 0.
690 if working_number == 0:
691 return word_length_in_digits()
692
693 num_length_in_bits = bit_length(working_number)
694 num_hex_digits, remainder = divmod(num_length_in_bits,
695 digit_length_in_bits())
696 if remainder > 0:
697 # Example: the number 7 requires 3 bits. The divmod above produces,
698 # 0 with remainder of 3. So because we have a remainder, we increment
699 # num_hex_digits from 0 to 1.
700 num_hex_digits += 1
701
702 # Check to see whether the negative bit is set. This is the left-most
703 # bit in the highest order digit.
704 negative_mask = 2 ** (num_hex_digits * 4 - 1)
705 if working_number & negative_mask:
706 # If a number that is intended to be positive has its negative bit
707 # on, an additional digit will be required to represent it correctly
708 # in print.
709 num_hex_digits += 1
710
711 num_words, remainder = divmod(num_hex_digits, word_length_in_digits())
712 if remainder > 0 or num_words == 0:
713 num_words += 1
714
715 # Round up to the next word length in digits.
716 return num_words * word_length_in_digits()
717
718
719def dft_num_hex_digits():
720 r"""
721 Return the default number of hex digits to be used to represent a hex
722 number in print.
723
724 The value returned is a function of sys.maxsize.
725 """
726
727 global _gen_print_dft_num_hex_digits_
728 try:
729 return _gen_print_dft_num_hex_digits_
730 except NameError:
731 _gen_print_dft_num_hex_digits_ = get_req_num_hex_digits(sys.maxsize)
732 return _gen_print_dft_num_hex_digits_
733
734
Michael Walsh8646d962019-01-21 14:36:13 -0600735def is_dict(var_value):
736 r"""
737 Return 1 if var_value is a type of dictionary and 0 if it is not.
738 """
739
740 type_is_dict = 0
741 if isinstance(var_value, dict):
742 type_is_dict = 1
743 try:
744 if isinstance(var_value, collections.OrderedDict):
745 type_is_dict = 1
746 except AttributeError:
747 pass
748 try:
749 if isinstance(var_value, DotDict):
750 type_is_dict = 1
751 except NameError:
752 pass
753 try:
754 if isinstance(var_value, NormalizedDict):
755 type_is_dict = 1
756 except NameError:
757 pass
758 return type_is_dict
759
760
Michael Walshde791732016-09-06 14:25:24 -0500761def sprint_varx(var_name,
762 var_value,
763 hex=0,
764 loc_col1_indent=col1_indent,
Michael Walsh7423c012016-10-04 10:27:21 -0500765 loc_col1_width=col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500766 trailing_char="\n",
767 key_list=None):
Michael Walshde791732016-09-06 14:25:24 -0500768 r"""
769 Print the var name/value passed to it. If the caller lets loc_col1_width
770 default, the printing lines up nicely with output generated by the
771 print_time functions.
772
773 Note that the sprint_var function (defined below) can be used to call this
774 function so that the programmer does not need to pass the var_name.
775 sprint_var will figure out the var_name. The sprint_var function is the
776 one that would normally be used by the general user.
777
778 For example, the following python code:
779
780 first_name = "Mike"
781 print_time("Doing this...\n")
782 print_varx("first_name", first_name)
783 print_time("Doing that...\n")
784
785 Will generate output like this:
786
787 #(CDT) 2016/08/10 17:34:42.847374 - 0.001285 - Doing this...
788 first_name: Mike
789 #(CDT) 2016/08/10 17:34:42.847510 - 0.000136 - Doing that...
790
791 This function recognizes several complex types of data such as dict, list
792 or tuple.
793
794 For example, the following python code:
795
796 my_dict = dict(one=1, two=2, three=3)
797 print_var(my_dict)
798
799 Will generate the following output:
800
801 my_dict:
802 my_dict[three]: 3
803 my_dict[two]: 2
804 my_dict[one]: 1
805
806 Description of arguments.
807 var_name The name of the variable to be printed.
808 var_value The value of the variable to be printed.
809 hex This indicates that the value should be
810 printed in hex format. It is the user's
811 responsibility to ensure that a var_value
Michael Walshbec416d2016-11-10 08:54:52 -0600812 contains a valid hex number. For string
813 var_values, this will be interpreted as
814 show_blanks which means that blank values
Michael Walshd995cb02017-02-07 14:46:01 -0600815 will be printed as "<blank>". For dict
816 var_values, this will be interpreted as
817 terse format where keys are not repeated
818 in the output.
Michael Walshde791732016-09-06 14:25:24 -0500819 loc_col1_indent The number of spaces to indent the output.
820 loc_col1_width The width of the output column containing
821 the variable name. The default value of
822 this is adjusted so that the var_value
823 lines up with text printed via the
824 print_time function.
Michael Walsh7423c012016-10-04 10:27:21 -0500825 trailing_char The character to be used at the end of the
826 returned string. The default value is a
827 line feed.
Michael Walshd2869032018-03-22 16:12:11 -0500828 key_list A list of which dictionary keys should be
829 printed. All others keys will be skipped.
830 Each value in key_list will be regarded
831 as a regular expression and it will be
832 regarded as anchored to the beginning and
833 ends of the dictionary key being
834 referenced. For example if key_list is
835 ["one", "two"], the resulting regex used
836 will be "^one|two$", i.e. only keys "one"
837 and "two" from the var_value dictionary
838 will be printed. As another example, if
839 the caller were to specify a key_list of
840 ["one.*"], then only dictionary keys whose
841 names begin with "one" will be printed.
842 Note: This argument pertains only to
843 var_values which are dictionaries.
Michael Walsh7423c012016-10-04 10:27:21 -0500844 """
Michael Walshde791732016-09-06 14:25:24 -0500845
846 # Determine the type
Michael Walsh37762f92018-08-07 14:59:18 -0500847 try:
848 int_types = (int, long)
849 except NameError:
850 int_types = (int,)
851 try:
852 string_types = (str, unicode)
853 except NameError:
George Keishing36efbc02018-12-12 10:18:23 -0600854 string_types = (bytes, str)
Michael Walsh37762f92018-08-07 14:59:18 -0500855 simple_types = int_types + string_types + (float, bool)
856 if type(var_value) in simple_types \
Michael Walshde791732016-09-06 14:25:24 -0500857 or var_value is None:
858 # The data type is simple in the sense that it has no subordinate
859 # parts.
Michael Walsh7423c012016-10-04 10:27:21 -0500860 # Adjust loc_col1_width.
861 loc_col1_width = loc_col1_width - loc_col1_indent
Michael Walshde791732016-09-06 14:25:24 -0500862 # See if the user wants the output in hex format.
863 if hex:
Michael Walsh37762f92018-08-07 14:59:18 -0500864 if type(var_value) not in int_types:
Michael Walshbec416d2016-11-10 08:54:52 -0600865 value_format = "%s"
Michael Walsh2795edc2016-12-13 16:00:33 -0600866 if var_value == "":
Michael Walshbec416d2016-11-10 08:54:52 -0600867 var_value = "<blank>"
868 else:
Michael Walsh3f248272018-06-01 13:59:35 -0500869 num_hex_digits = max(dft_num_hex_digits(),
870 get_req_num_hex_digits(var_value))
871 # Convert a negative number to its positive twos complement
872 # for proper printing. For example, instead of printing -1 as
873 # "0x-000000000000001" it will be printed as
874 # "0xffffffffffffffff".
875 var_value = var_value & (2 ** (num_hex_digits * 4) - 1)
876 value_format = "0x%0" + str(num_hex_digits) + "x"
Michael Walshde791732016-09-06 14:25:24 -0500877 else:
878 value_format = "%s"
879 format_string = "%" + str(loc_col1_indent) + "s%-" \
Michael Walsh7423c012016-10-04 10:27:21 -0500880 + str(loc_col1_width) + "s" + value_format + trailing_char
Michael Walsh3383e652017-09-01 17:10:59 -0500881 if value_format == "0x%08x":
882 return format_string % ("", str(var_name) + ":",
883 var_value & 0xffffffff)
884 else:
885 return format_string % ("", str(var_name) + ":", var_value)
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500886 elif isinstance(var_value, type):
Michael Walsh20a87ab2017-06-30 17:00:30 -0500887 return sprint_varx(var_name, str(var_value).split("'")[1], hex,
Michael Walshd2869032018-03-22 16:12:11 -0500888 loc_col1_indent, loc_col1_width, trailing_char,
889 key_list)
Michael Walshde791732016-09-06 14:25:24 -0500890 else:
891 # The data type is complex in the sense that it has subordinate parts.
892 format_string = "%" + str(loc_col1_indent) + "s%s\n"
893 buffer = format_string % ("", var_name + ":")
894 loc_col1_indent += 2
Michael Walsh7423c012016-10-04 10:27:21 -0500895 try:
896 length = len(var_value)
897 except TypeError:
Michael Walsh23e7f492017-01-10 11:34:47 -0600898 length = 0
Michael Walsh7423c012016-10-04 10:27:21 -0500899 ix = 0
900 loc_trailing_char = "\n"
Michael Walsh8646d962019-01-21 14:36:13 -0600901 if is_dict(var_value):
Michael Walsh37762f92018-08-07 14:59:18 -0500902 for key, value in var_value.items():
Michael Walshd2869032018-03-22 16:12:11 -0500903 if key_list is not None:
904 key_list_regex = "^" + "|".join(key_list) + "$"
905 if not re.match(key_list_regex, key):
906 continue
Michael Walsh7423c012016-10-04 10:27:21 -0500907 ix += 1
908 if ix == length:
909 loc_trailing_char = trailing_char
Michael Walshd995cb02017-02-07 14:46:01 -0600910 if hex:
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600911 # Since hex is being used as a format type, we want it
912 # turned off when processing integer dictionary values so
913 # it is not interpreted as a hex indicator.
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500914 loc_hex = not (isinstance(value, int))
Michael Walshf7b8a002017-08-29 10:38:39 -0500915 buffer += sprint_varx("[" + key + "]", value,
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600916 loc_hex, loc_col1_indent,
917 loc_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500918 loc_trailing_char,
919 key_list)
Michael Walshd995cb02017-02-07 14:46:01 -0600920 else:
Michael Walsh1173a522018-05-21 17:24:51 -0500921 buffer += sprint_varx(var_name + "[" + str(key) + "]",
922 value, hex, loc_col1_indent,
923 loc_col1_width, loc_trailing_char,
924 key_list)
Michael Walsh7423c012016-10-04 10:27:21 -0500925 elif type(var_value) in (list, tuple, set):
Michael Walshde791732016-09-06 14:25:24 -0500926 for key, value in enumerate(var_value):
Michael Walsh7423c012016-10-04 10:27:21 -0500927 ix += 1
928 if ix == length:
929 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500930 buffer += sprint_varx(var_name + "[" + str(key) + "]", value,
Michael Walsh7423c012016-10-04 10:27:21 -0500931 hex, loc_col1_indent, loc_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500932 loc_trailing_char, key_list)
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500933 elif isinstance(var_value, argparse.Namespace):
Michael Walshde791732016-09-06 14:25:24 -0500934 for key in var_value.__dict__:
Michael Walsh7423c012016-10-04 10:27:21 -0500935 ix += 1
936 if ix == length:
937 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500938 cmd_buf = "buffer += sprint_varx(var_name + \".\" + str(key)" \
Michael Walsh7423c012016-10-04 10:27:21 -0500939 + ", var_value." + key + ", hex, loc_col1_indent," \
Michael Walshd2869032018-03-22 16:12:11 -0500940 + " loc_col1_width, loc_trailing_char, key_list)"
Michael Walshde791732016-09-06 14:25:24 -0500941 exec(cmd_buf)
942 else:
943 var_type = type(var_value).__name__
944 func_name = sys._getframe().f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -0500945 var_value = "<" + var_type + " type not supported by " + \
946 func_name + "()>"
Michael Walshde791732016-09-06 14:25:24 -0500947 value_format = "%s"
948 loc_col1_indent -= 2
Michael Walsh7423c012016-10-04 10:27:21 -0500949 # Adjust loc_col1_width.
950 loc_col1_width = loc_col1_width - loc_col1_indent
Michael Walshde791732016-09-06 14:25:24 -0500951 format_string = "%" + str(loc_col1_indent) + "s%-" \
Michael Walsh7423c012016-10-04 10:27:21 -0500952 + str(loc_col1_width) + "s" + value_format + trailing_char
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600953 return format_string % ("", str(var_name) + ":", var_value)
Michael Walsh23e7f492017-01-10 11:34:47 -0600954
Michael Walshde791732016-09-06 14:25:24 -0500955 return buffer
956
957 return ""
958
Michael Walshde791732016-09-06 14:25:24 -0500959
Michael Walshfd2733c2017-11-13 11:36:20 -0600960def sprint_var(var_value,
961 hex=0,
962 loc_col1_indent=col1_indent,
963 loc_col1_width=col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500964 trailing_char="\n",
965 key_list=None):
Michael Walshde791732016-09-06 14:25:24 -0500966 r"""
967 Figure out the name of the first argument for you and then call
968 sprint_varx with it. Therefore, the following 2 calls are equivalent:
969 sprint_varx("var1", var1)
970 sprint_var(var1)
971 """
972
973 # Get the name of the first variable passed to this function.
974 stack_frame = 2
Michael Walsh7423c012016-10-04 10:27:21 -0500975 caller_func_name = sprint_func_name(2)
976 if caller_func_name.endswith("print_var"):
Michael Walshde791732016-09-06 14:25:24 -0500977 stack_frame += 1
978 var_name = get_arg_name(None, 1, stack_frame)
Michael Walshfd2733c2017-11-13 11:36:20 -0600979 return sprint_varx(var_name, var_value=var_value, hex=hex,
980 loc_col1_indent=loc_col1_indent,
981 loc_col1_width=loc_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500982 trailing_char=trailing_char,
983 key_list=key_list)
Michael Walshde791732016-09-06 14:25:24 -0500984
985
Michael Walsh18176322016-11-15 15:11:21 -0600986def sprint_vars(*args):
Michael Walsh18176322016-11-15 15:11:21 -0600987 r"""
988 Sprint the values of one or more variables.
989
990 Description of args:
991 args:
992 If the first argument is an integer, it will be interpreted to be the
993 "indent" value.
994 If the second argument is an integer, it will be interpreted to be the
995 "col1_width" value.
996 If the third argument is an integer, it will be interpreted to be the
997 "hex" value.
998 All remaining parms are considered variable names which are to be
999 sprinted.
1000 """
1001
1002 if len(args) == 0:
1003 return
1004
1005 # Get the name of the first variable passed to this function.
1006 stack_frame = 2
1007 caller_func_name = sprint_func_name(2)
1008 if caller_func_name.endswith("print_vars"):
1009 stack_frame += 1
1010
1011 parm_num = 1
1012
1013 # Create list from args (which is a tuple) so that it can be modified.
1014 args_list = list(args)
1015
1016 var_name = get_arg_name(None, parm_num, stack_frame)
1017 # See if parm 1 is to be interpreted as "indent".
1018 try:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001019 if isinstance(int(var_name), int):
Michael Walsh18176322016-11-15 15:11:21 -06001020 indent = int(var_name)
1021 args_list.pop(0)
1022 parm_num += 1
1023 except ValueError:
1024 indent = 0
1025
1026 var_name = get_arg_name(None, parm_num, stack_frame)
1027 # See if parm 1 is to be interpreted as "col1_width".
1028 try:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001029 if isinstance(int(var_name), int):
Michael Walsh18176322016-11-15 15:11:21 -06001030 loc_col1_width = int(var_name)
1031 args_list.pop(0)
1032 parm_num += 1
1033 except ValueError:
1034 loc_col1_width = col1_width
1035
1036 var_name = get_arg_name(None, parm_num, stack_frame)
1037 # See if parm 1 is to be interpreted as "hex".
1038 try:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001039 if isinstance(int(var_name), int):
Michael Walsh18176322016-11-15 15:11:21 -06001040 hex = int(var_name)
1041 args_list.pop(0)
1042 parm_num += 1
1043 except ValueError:
1044 hex = 0
1045
1046 buffer = ""
1047 for var_value in args_list:
1048 var_name = get_arg_name(None, parm_num, stack_frame)
1049 buffer += sprint_varx(var_name, var_value, hex, indent, loc_col1_width)
1050 parm_num += 1
1051
1052 return buffer
1053
Michael Walsh18176322016-11-15 15:11:21 -06001054
Michael Walsh7423c012016-10-04 10:27:21 -05001055def sprint_dashes(indent=col1_indent,
1056 width=80,
1057 line_feed=1,
1058 char="-"):
Michael Walshde791732016-09-06 14:25:24 -05001059 r"""
1060 Return a string of dashes to the caller.
1061
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001062 Description of arguments:
Michael Walshde791732016-09-06 14:25:24 -05001063 indent The number of characters to indent the
1064 output.
1065 width The width of the string of dashes.
1066 line_feed Indicates whether the output should end
1067 with a line feed.
Michael Walsh7423c012016-10-04 10:27:21 -05001068 char The character to be repeated in the output
1069 string.
Michael Walshde791732016-09-06 14:25:24 -05001070 """
1071
Michael Walsh7423c012016-10-04 10:27:21 -05001072 width = int(width)
Michael Walsh23e7f492017-01-10 11:34:47 -06001073 buffer = " " * int(indent) + char * width
Michael Walshde791732016-09-06 14:25:24 -05001074 if line_feed:
1075 buffer += "\n"
1076
1077 return buffer
1078
Michael Walshde791732016-09-06 14:25:24 -05001079
Michael Walsh7423c012016-10-04 10:27:21 -05001080def sindent(text="",
1081 indent=0):
Michael Walsh7423c012016-10-04 10:27:21 -05001082 r"""
1083 Pre-pend the specified number of characters to the text string (i.e.
1084 indent it) and return it.
1085
1086 Description of arguments:
1087 text The string to be indented.
1088 indent The number of characters to indent the
1089 string.
1090 """
1091
1092 format_string = "%" + str(indent) + "s%s"
1093 buffer = format_string % ("", text)
1094
1095 return buffer
1096
Michael Walsh7423c012016-10-04 10:27:21 -05001097
Michael Walsh662e13b2019-03-01 15:54:08 -06001098func_line_style_std = None
1099func_line_style_short = 1
1100
1101
1102def sprint_func_line(stack_frame, style=None):
Michael Walsh47aa2a42018-12-10 15:06:02 -06001103 r"""
1104 For the given stack_frame, return a formatted string containing the
1105 function name and all its arguments.
1106
1107 Example:
1108
1109 func1(last_name = 'walsh', first_name = 'mikey')
1110
1111 Description of argument(s):
1112 stack_frame A stack frame (such as is returned by
1113 inspect.stack()).
Michael Walsh662e13b2019-03-01 15:54:08 -06001114 style Indicates the style or formatting of the
1115 result string. Acceptable values are
1116 shown above.
1117
1118 Description of styles:
1119 func_line_style_std The standard formatting.
1120 func_line_style_short 1) The self parm (associated with methods)
1121 will be dropped. 2) The args and kwargs
1122 values will be treated as special. In
1123 both cases the arg name ('args' or
1124 'kwargs') will be dropped and only the
1125 values will be shown.
Michael Walsh47aa2a42018-12-10 15:06:02 -06001126 """
1127
1128 func_name = str(stack_frame[3])
1129 if func_name == "?":
1130 # "?" is the name used when code is not in a function.
1131 func_name = "(none)"
1132
1133 if func_name == "<module>":
1134 # If the func_name is the "main" program, we simply get the command
1135 # line call string.
1136 func_and_args = ' '.join(sys.argv)
1137 else:
1138 # Get the program arguments.
1139 (args, varargs, keywords, locals) =\
1140 inspect.getargvalues(stack_frame[0])
1141
1142 args_list = []
1143 for arg_name in filter(None, args + [varargs, keywords]):
1144 # Get the arg value from frame locals.
1145 arg_value = locals[arg_name]
1146 if arg_name == 'self':
Michael Walsh662e13b2019-03-01 15:54:08 -06001147 if style == func_line_style_short:
1148 continue
Michael Walsh47aa2a42018-12-10 15:06:02 -06001149 # Manipulations to improve output for class methods.
1150 func_name = arg_value.__class__.__name__ + "." + func_name
1151 args_list.append(arg_name + " = <self>")
Michael Walsh662e13b2019-03-01 15:54:08 -06001152 elif (style == func_line_style_short
1153 and arg_name == 'args'
1154 and type(arg_value) in (list, tuple)):
1155 if len(arg_value) == 0:
1156 continue
1157 args_list.append(repr(', '.join(arg_value)))
1158 elif (style == func_line_style_short
1159 and arg_name == 'kwargs'
1160 and type(arg_value) is dict):
1161 for key, value in arg_value.items():
1162 args_list.append(key + "=" + repr(value))
Michael Walsh47aa2a42018-12-10 15:06:02 -06001163 else:
1164 args_list.append(arg_name + " = " + repr(arg_value))
1165 args_str = "(" + ', '.join(map(str, args_list)) + ")"
1166
1167 # Now we need to print this in a nicely-wrapped way.
1168 func_and_args = func_name + args_str
1169
1170 return func_and_args
1171
1172
Michael Walsh7423c012016-10-04 10:27:21 -05001173def sprint_call_stack(indent=0,
Michael Walsh662e13b2019-03-01 15:54:08 -06001174 stack_frame_ix=0,
1175 style=None):
Michael Walshde791732016-09-06 14:25:24 -05001176 r"""
1177 Return a call stack report for the given point in the program with line
1178 numbers, function names and function parameters and arguments.
1179
1180 Sample output:
1181
1182 -------------------------------------------------------------------------
1183 Python function call stack
1184
1185 Line # Function name and arguments
1186 ------ ------------------------------------------------------------------
Michael Walsh47aa2a42018-12-10 15:06:02 -06001187 424 sprint_call_stack()
1188 4 print_call_stack()
1189 31 func1(last_name = 'walsh', first_name = 'mikey')
Michael Walshde791732016-09-06 14:25:24 -05001190 59 /tmp/scr5.py
1191 -------------------------------------------------------------------------
1192
1193 Description of arguments:
1194 indent The number of characters to indent each
1195 line of output.
1196 stack_frame_ix The index of the first stack frame which
1197 is to be returned.
Michael Walsh662e13b2019-03-01 15:54:08 -06001198 style See the sprint_line_func prolog above for
1199 details.
Michael Walshde791732016-09-06 14:25:24 -05001200 """
1201
1202 buffer = ""
Michael Walsh7423c012016-10-04 10:27:21 -05001203 buffer += sprint_dashes(indent)
1204 buffer += sindent("Python function call stack\n\n", indent)
1205 buffer += sindent("Line # Function name and arguments\n", indent)
1206 buffer += sprint_dashes(indent, 6, 0) + " " + sprint_dashes(0, 73)
Michael Walshde791732016-09-06 14:25:24 -05001207
1208 # Grab the current program stack.
Michael Walsh6f0362c2019-03-25 14:05:14 -05001209 work_around_inspect_stack_cwd_failure()
Michael Walshde791732016-09-06 14:25:24 -05001210 current_stack = inspect.stack()
1211
1212 # Process each frame in turn.
1213 format_string = "%6s %s\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001214 ix = 0
Michael Walshde791732016-09-06 14:25:24 -05001215 for stack_frame in current_stack:
Michael Walsh7423c012016-10-04 10:27:21 -05001216 if ix < stack_frame_ix:
1217 ix += 1
1218 continue
Michael Walsh23e7f492017-01-10 11:34:47 -06001219 # I want the line number shown to be the line where you find the line
1220 # shown.
1221 try:
1222 line_num = str(current_stack[ix + 1][2])
1223 except IndexError:
1224 line_num = ""
Michael Walsh662e13b2019-03-01 15:54:08 -06001225 func_and_args = sprint_func_line(stack_frame, style=style)
Michael Walshde791732016-09-06 14:25:24 -05001226
Michael Walsh23e7f492017-01-10 11:34:47 -06001227 buffer += sindent(format_string % (line_num, func_and_args), indent)
Michael Walsh7423c012016-10-04 10:27:21 -05001228 ix += 1
Michael Walshde791732016-09-06 14:25:24 -05001229
Michael Walsh7423c012016-10-04 10:27:21 -05001230 buffer += sprint_dashes(indent)
Michael Walshde791732016-09-06 14:25:24 -05001231
1232 return buffer
1233
Michael Walshde791732016-09-06 14:25:24 -05001234
Michael Walsh662e13b2019-03-01 15:54:08 -06001235def sprint_executing(stack_frame_ix=None, style=None):
Michael Walshde791732016-09-06 14:25:24 -05001236 r"""
1237 Print a line indicating what function is executing and with what parameter
1238 values. This is useful for debugging.
1239
1240 Sample output:
1241
Michael Walsh47aa2a42018-12-10 15:06:02 -06001242 #(CDT) 2016/08/25 17:54:27 - Executing: func1(x = 1)
Michael Walshde791732016-09-06 14:25:24 -05001243
1244 Description of arguments:
1245 stack_frame_ix The index of the stack frame whose
1246 function info should be returned. If the
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001247 caller does not specify a value, this
Michael Walshde791732016-09-06 14:25:24 -05001248 function will set the value to 1 which is
1249 the index of the caller's stack frame. If
1250 the caller is the wrapper function
1251 "print_executing", this function will bump
1252 it up by 1.
Michael Walsh662e13b2019-03-01 15:54:08 -06001253 style See the sprint_line_func prolog above for
1254 details.
Michael Walshde791732016-09-06 14:25:24 -05001255 """
1256
1257 # If user wants default stack_frame_ix.
1258 if stack_frame_ix is None:
1259 func_name = sys._getframe().f_code.co_name
1260 caller_func_name = sys._getframe(1).f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -05001261 if caller_func_name.endswith(func_name[1:]):
Michael Walshde791732016-09-06 14:25:24 -05001262 stack_frame_ix = 2
1263 else:
1264 stack_frame_ix = 1
1265
Michael Walsh6f0362c2019-03-25 14:05:14 -05001266 work_around_inspect_stack_cwd_failure()
Michael Walshde791732016-09-06 14:25:24 -05001267 stack_frame = inspect.stack()[stack_frame_ix]
1268
Michael Walsh662e13b2019-03-01 15:54:08 -06001269 func_and_args = sprint_func_line(stack_frame, style)
Michael Walshde791732016-09-06 14:25:24 -05001270
1271 return sprint_time() + "Executing: " + func_and_args + "\n"
1272
Michael Walshde791732016-09-06 14:25:24 -05001273
Michael Walshbec416d2016-11-10 08:54:52 -06001274def sprint_pgm_header(indent=0,
1275 linefeed=1):
Michael Walshde791732016-09-06 14:25:24 -05001276 r"""
1277 Return a standardized header that programs should print at the beginning
1278 of the run. It includes useful information like command line, pid,
1279 userid, program parameters, etc.
1280
Michael Walsh7423c012016-10-04 10:27:21 -05001281 Description of arguments:
1282 indent The number of characters to indent each
1283 line of output.
Michael Walshbec416d2016-11-10 08:54:52 -06001284 linefeed Indicates whether a line feed be included
1285 at the beginning and end of the report.
Michael Walshde791732016-09-06 14:25:24 -05001286 """
1287
Michael Walshbec416d2016-11-10 08:54:52 -06001288 loc_col1_width = col1_width + indent
1289
1290 buffer = ""
1291 if linefeed:
1292 buffer = "\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001293
Michael Walshdb6e68a2017-05-23 17:55:31 -05001294 if robot_env:
1295 suite_name = BuiltIn().get_variable_value("${suite_name}")
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001296 buffer += sindent(sprint_time("Running test suite \"" + suite_name
1297 + "\".\n"), indent)
Michael Walshdb6e68a2017-05-23 17:55:31 -05001298
Michael Walsh7423c012016-10-04 10:27:21 -05001299 buffer += sindent(sprint_time() + "Running " + pgm_name + ".\n", indent)
1300 buffer += sindent(sprint_time() + "Program parameter values, etc.:\n\n",
1301 indent)
Michael Walshbec416d2016-11-10 08:54:52 -06001302 buffer += sprint_varx("command_line", ' '.join(sys.argv), 0, indent,
1303 loc_col1_width)
Michael Walsh7423c012016-10-04 10:27:21 -05001304 # We want the output to show a customized name for the pid and pgid but
1305 # we want it to look like a valid variable name. Therefore, we'll use
Michael Walshde791732016-09-06 14:25:24 -05001306 # pgm_name_var_name which was set when this module was imported.
Michael Walshbec416d2016-11-10 08:54:52 -06001307 buffer += sprint_varx(pgm_name_var_name + "_pid", os.getpid(), 0, indent,
1308 loc_col1_width)
1309 buffer += sprint_varx(pgm_name_var_name + "_pgid", os.getpgrp(), 0, indent,
1310 loc_col1_width)
Michael Walsh86de0d22016-12-05 10:13:15 -06001311 userid_num = str(os.geteuid())
1312 try:
1313 username = os.getlogin()
1314 except OSError:
1315 if userid_num == "0":
1316 username = "root"
1317 else:
1318 username = "?"
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001319 buffer += sprint_varx("uid", userid_num + " (" + username
1320 + ")", 0, indent, loc_col1_width)
1321 buffer += sprint_varx("gid", str(os.getgid()) + " ("
1322 + str(grp.getgrgid(os.getgid()).gr_name) + ")", 0,
Michael Walshbec416d2016-11-10 08:54:52 -06001323 indent, loc_col1_width)
1324 buffer += sprint_varx("host_name", socket.gethostname(), 0, indent,
1325 loc_col1_width)
Michael Walsh86de0d22016-12-05 10:13:15 -06001326 try:
1327 DISPLAY = os.environ['DISPLAY']
1328 except KeyError:
1329 DISPLAY = ""
1330 buffer += sprint_varx("DISPLAY", DISPLAY, 0, indent,
Michael Walshbec416d2016-11-10 08:54:52 -06001331 loc_col1_width)
Michael Walshde791732016-09-06 14:25:24 -05001332 # I want to add code to print caller's parms.
1333
Michael Walsh7423c012016-10-04 10:27:21 -05001334 # __builtin__.arg_obj is created by the get_arg module function,
1335 # gen_get_options.
1336 try:
1337 buffer += ga.sprint_args(__builtin__.arg_obj, indent)
1338 except AttributeError:
1339 pass
1340
Michael Walshdb6e68a2017-05-23 17:55:31 -05001341 if robot_env:
1342 # Get value of global parm_list.
1343 parm_list = BuiltIn().get_variable_value("${parm_list}")
1344
1345 for parm in parm_list:
1346 parm_value = BuiltIn().get_variable_value("${" + parm + "}")
1347 buffer += sprint_varx(parm, parm_value, 0, indent, loc_col1_width)
1348
1349 # Setting global program_pid.
1350 BuiltIn().set_global_variable("${program_pid}", os.getpid())
1351
Michael Walshbec416d2016-11-10 08:54:52 -06001352 if linefeed:
1353 buffer += "\n"
Michael Walshde791732016-09-06 14:25:24 -05001354
1355 return buffer
1356
Michael Walshde791732016-09-06 14:25:24 -05001357
Michael Walsh7423c012016-10-04 10:27:21 -05001358def sprint_error_report(error_text="\n",
Michael Walshdb6e68a2017-05-23 17:55:31 -05001359 indent=2,
1360 format=None):
Michael Walsh7423c012016-10-04 10:27:21 -05001361 r"""
1362 Return a string with a standardized report which includes the caller's
1363 error text, the call stack and the program header.
1364
1365 Description of args:
1366 error_text The error text to be included in the
1367 report. The caller should include any
1368 needed linefeeds.
1369 indent The number of characters to indent each
1370 line of output.
Michael Walshdb6e68a2017-05-23 17:55:31 -05001371 format Long or short format. Long includes
1372 extras like lines of dashes, call stack,
1373 etc.
Michael Walsh7423c012016-10-04 10:27:21 -05001374 """
1375
Michael Walshdb6e68a2017-05-23 17:55:31 -05001376 # Process input.
1377 indent = int(indent)
1378 if format is None:
1379 if robot_env:
1380 format = 'short'
1381 else:
1382 format = 'long'
1383 error_text = error_text.rstrip('\n') + '\n'
1384
1385 if format == 'short':
1386 return sprint_error(error_text)
1387
Michael Walsh7423c012016-10-04 10:27:21 -05001388 buffer = ""
1389 buffer += sprint_dashes(width=120, char="=")
1390 buffer += sprint_error(error_text)
1391 buffer += "\n"
1392 # Calling sprint_call_stack with stack_frame_ix of 0 causes it to show
1393 # itself and this function in the call stack. This is not helpful to a
1394 # debugger and is therefore clutter. We will adjust the stack_frame_ix to
1395 # hide that information.
Michael Walsh9c75f672017-09-12 17:11:35 -05001396 stack_frame_ix = 1
Michael Walsh7423c012016-10-04 10:27:21 -05001397 caller_func_name = sprint_func_name(2)
1398 if caller_func_name.endswith("print_error_report"):
1399 stack_frame_ix += 1
Michael Walsh7bfa9ab2018-11-16 15:24:26 -06001400 buffer += sprint_call_stack(indent, stack_frame_ix)
Michael Walsh7423c012016-10-04 10:27:21 -05001401 buffer += sprint_pgm_header(indent)
1402 buffer += sprint_dashes(width=120, char="=")
1403
1404 return buffer
1405
Michael Walsh7423c012016-10-04 10:27:21 -05001406
Michael Walsh18176322016-11-15 15:11:21 -06001407def sprint_issuing(cmd_buf,
1408 test_mode=0):
Michael Walshde791732016-09-06 14:25:24 -05001409 r"""
1410 Return a line indicating a command that the program is about to execute.
1411
1412 Sample output for a cmd_buf of "ls"
1413
1414 #(CDT) 2016/08/25 17:57:36 - Issuing: ls
Michael Walshbec416d2016-11-10 08:54:52 -06001415
Michael Walshde791732016-09-06 14:25:24 -05001416 Description of args:
1417 cmd_buf The command to be executed by caller.
Michael Walshbec416d2016-11-10 08:54:52 -06001418 test_mode With test_mode set, your output will look
1419 like this:
1420
1421 #(CDT) 2016/08/25 17:57:36 - (test_mode) Issuing: ls
1422
Michael Walshde791732016-09-06 14:25:24 -05001423 """
1424
Michael Walshbec416d2016-11-10 08:54:52 -06001425 buffer = sprint_time()
1426 if test_mode:
1427 buffer += "(test_mode) "
1428 buffer += "Issuing: " + cmd_buf + "\n"
Michael Walshde791732016-09-06 14:25:24 -05001429
1430 return buffer
1431
Michael Walshde791732016-09-06 14:25:24 -05001432
Michael Walshde791732016-09-06 14:25:24 -05001433def sprint_pgm_footer():
Michael Walshde791732016-09-06 14:25:24 -05001434 r"""
1435 Return a standardized footer that programs should print at the end of the
1436 program run. It includes useful information like total run time, etc.
1437 """
1438
1439 buffer = "\n" + sprint_time() + "Finished running " + pgm_name + ".\n\n"
1440
1441 total_time = time.time() - start_time
1442 total_time_string = "%0.6f" % total_time
1443
Michael Walsh7423c012016-10-04 10:27:21 -05001444 buffer += sprint_varx(pgm_name_var_name + "_runtime", total_time_string)
Michael Walshbec416d2016-11-10 08:54:52 -06001445 buffer += "\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001446
1447 return buffer
1448
Michael Walsh7423c012016-10-04 10:27:21 -05001449
Michael Walsh7423c012016-10-04 10:27:21 -05001450def sprint(buffer=""):
Michael Walsh7423c012016-10-04 10:27:21 -05001451 r"""
1452 Simply return the user's buffer. This function is used by the qprint and
1453 dprint functions defined dynamically below, i.e. it would not normally be
1454 called for general use.
1455
1456 Description of arguments.
1457 buffer This will be returned to the caller.
1458 """
Michael Walshde791732016-09-06 14:25:24 -05001459
Michael Walsh95e45102018-02-09 12:44:43 -06001460 try:
1461 return str(buffer)
1462 except UnicodeEncodeError:
1463 return buffer
Michael Walshbec416d2016-11-10 08:54:52 -06001464
Michael Walshbec416d2016-11-10 08:54:52 -06001465
Michael Walshbec416d2016-11-10 08:54:52 -06001466def sprintn(buffer=""):
Michael Walshbec416d2016-11-10 08:54:52 -06001467 r"""
1468 Simply return the user's buffer with a line feed. This function is used
1469 by the qprint and dprint functions defined dynamically below, i.e. it
1470 would not normally be called for general use.
1471
1472 Description of arguments.
1473 buffer This will be returned to the caller.
1474 """
1475
Michael Walsh95e45102018-02-09 12:44:43 -06001476 try:
1477 buffer = str(buffer) + "\n"
1478 except UnicodeEncodeError:
1479 buffer = buffer + "\n"
Michael Walshbec416d2016-11-10 08:54:52 -06001480
Michael Walshde791732016-09-06 14:25:24 -05001481 return buffer
1482
Michael Walsh168eb0f2017-12-01 15:35:32 -06001483
Michael Walshfd2733c2017-11-13 11:36:20 -06001484def gp_print(buffer,
1485 stream='stdout'):
Michael Walshfd2733c2017-11-13 11:36:20 -06001486 r"""
1487 Print the buffer using either sys.stdout.write or BuiltIn().log_to_console
1488 depending on whether we are running in a robot environment.
1489
1490 This function is intended for use only by other functions in this module.
1491
1492 Description of arguments:
1493 buffer The string to be printed.
1494 stream Either "stdout" or "stderr".
1495 """
1496
1497 if robot_env:
1498 BuiltIn().log_to_console(buffer, stream=stream, no_newline=True)
1499 else:
1500 if stream == "stdout":
1501 sys.stdout.write(buffer)
1502 sys.stdout.flush()
1503 else:
1504 sys.stderr.write(buffer)
1505 sys.stderr.flush()
Michael Walshde791732016-09-06 14:25:24 -05001506
1507
Michael Walsh168eb0f2017-12-01 15:35:32 -06001508def gp_log(buffer):
Michael Walsh168eb0f2017-12-01 15:35:32 -06001509 r"""
1510 Log the buffer using either python logging or BuiltIn().log depending on
1511 whether we are running in a robot environment.
1512
1513 This function is intended for use only by other functions in this module.
1514
1515 Description of arguments:
1516 buffer The string to be logged.
1517 """
1518
1519 if robot_env:
1520 BuiltIn().log(buffer)
1521 else:
1522 logging.warning(buffer)
1523
1524
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001525def gp_debug_print(buffer):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001526 r"""
Michael Walshfd2733c2017-11-13 11:36:20 -06001527 Print with gp_print only if gen_print_debug is set.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001528
1529 This function is intended for use only by other functions in this module.
1530
1531 Description of arguments:
1532 buffer The string to be printed.
1533 """
1534
1535 if not gen_print_debug:
1536 return
1537
Michael Walshfd2733c2017-11-13 11:36:20 -06001538 gp_print(buffer)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001539
1540
Michael Walshb1500152017-04-12 15:42:43 -05001541def get_var_value(var_value=None,
1542 default=1,
1543 var_name=None):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001544 r"""
Michael Walshb1500152017-04-12 15:42:43 -05001545 Return either var_value, the corresponding global value or default.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001546
Michael Walshb1500152017-04-12 15:42:43 -05001547 If var_value is not None, it will simply be returned.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001548
Michael Walshb1500152017-04-12 15:42:43 -05001549 If var_value is None, this function will return the corresponding global
1550 value of the variable in question.
1551
1552 Note: For global values, if we are in a robot environment,
1553 get_variable_value will be used. Otherwise, the __builtin__ version of
1554 the variable is returned (which are set by gen_arg.py functions).
1555
1556 If there is no global value associated with the variable, default is
1557 returned.
1558
1559 This function is useful for other functions in setting default values for
1560 parameters.
1561
1562 Example use:
1563
1564 def my_func(quiet=None):
1565
1566 quiet = int(get_var_value(quiet, 0))
1567
1568 Example calls to my_func():
1569
1570 In the following example, the caller is explicitly asking to have quiet be
1571 set to 1.
1572
1573 my_func(quiet=1)
1574
1575 In the following example, quiet will be set to the global value of quiet,
1576 if defined, or to 0 (the default).
1577
1578 my_func()
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001579
1580 Description of arguments:
Michael Walshb1500152017-04-12 15:42:43 -05001581 var_value The value to be returned (if not equal to
1582 None).
1583 default The value that is returned if var_value is
1584 None and there is no corresponding global
1585 value defined.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001586 var_name The name of the variable whose value is to
Michael Walshb1500152017-04-12 15:42:43 -05001587 be returned. Under most circumstances,
1588 this value need not be provided. This
1589 function can figure out the name of the
1590 variable passed as var_value. One
1591 exception to this would be if this
1592 function is called directly from a .robot
1593 file.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001594 """
1595
Michael Walshb1500152017-04-12 15:42:43 -05001596 if var_value is not None:
1597 return var_value
1598
1599 if var_name is None:
1600 var_name = get_arg_name(None, 1, 2)
1601
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001602 if robot_env:
Michael Walshc6537442017-06-06 15:33:52 -05001603 var_value = BuiltIn().get_variable_value("${" + var_name + "}",
1604 default)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001605 else:
1606 var_value = getattr(__builtin__, var_name, default)
1607
1608 return var_value
1609
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001610
Michael Walsh052ff812018-05-18 16:09:09 -05001611def get_stack_var(var_name,
1612 default="",
1613 init_stack_ix=2):
Michael Walsh052ff812018-05-18 16:09:09 -05001614 r"""
1615 Starting with the caller's stack level, search upward in the call stack,
1616 for a variable named var_name and return its value. If the variable
1617 cannot be found, return default.
1618
1619 Example code:
1620
1621 def func12():
1622 my_loc_var1 = get_stack_var('my_var1', "default value")
1623
1624 def func11():
1625 my_var1 = 11
1626 func12()
1627
1628 In this example, get_stack_var will find the value of my_var1 in func11's
1629 stack and will therefore return the value 11. Therefore, my_loc_var1
1630 would get set to 11.
1631
1632 Description of argument(s):
1633 var_name The name of the variable to be searched
1634 for.
1635 default The value to return if the the variable
1636 cannot be found.
1637 init_stack_ix The initial stack index from which to
1638 begin the search. 0 would be the index of
1639 this func1tion ("get_stack_var"), 1 would
1640 be the index of the function calling this
1641 function, etc.
1642 """
1643
Michael Walsh6f0362c2019-03-25 14:05:14 -05001644 work_around_inspect_stack_cwd_failure()
Michael Walsh052ff812018-05-18 16:09:09 -05001645 return next((frame[0].f_locals[var_name]
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001646 for frame in inspect.stack()[init_stack_ix:]
1647 if var_name in frame[0].f_locals), default)
Michael Walsh052ff812018-05-18 16:09:09 -05001648
1649
Michael Walsh82acf002017-05-04 14:33:05 -05001650# hidden_text is a list of passwords which are to be replaced with asterisks
1651# by print functions defined in this module.
1652hidden_text = []
1653# password_regex is created based on the contents of hidden_text.
1654password_regex = ""
1655
1656
Michael Walsh82acf002017-05-04 14:33:05 -05001657def register_passwords(*args):
Michael Walsh82acf002017-05-04 14:33:05 -05001658 r"""
1659 Register one or more passwords which are to be hidden in output produced
1660 by the print functions in this module.
1661
1662 Note: Blank password values are NOT registered. They are simply ignored.
1663
1664 Description of argument(s):
1665 args One or more password values. If a given
1666 password value is already registered, this
1667 function will simply do nothing.
1668 """
1669
1670 global hidden_text
1671 global password_regex
1672
1673 for password in args:
1674 if password == "":
1675 break
1676 if password in hidden_text:
1677 break
1678
1679 # Place the password into the hidden_text list.
1680 hidden_text.append(password)
1681 # Create a corresponding password regular expression. Escape regex
1682 # special characters too.
1683 password_regex = '(' +\
1684 '|'.join([re.escape(x) for x in hidden_text]) + ')'
1685
Michael Walsh82acf002017-05-04 14:33:05 -05001686
Michael Walsh82acf002017-05-04 14:33:05 -05001687def replace_passwords(buffer):
Michael Walsh82acf002017-05-04 14:33:05 -05001688 r"""
1689 Return the buffer but with all registered passwords replaced by a string
1690 of asterisks.
1691
1692
1693 Description of argument(s):
1694 buffer The string to be returned but with
1695 passwords replaced.
1696 """
1697
1698 global password_regex
1699
1700 if int(os.environ.get("DEBUG_SHOW_PASSWORDS", "0")):
1701 return buffer
1702
1703 if password_regex == "":
1704 # No passwords to replace.
1705 return buffer
1706
1707 return re.sub(password_regex, "********", buffer)
1708
Michael Walshfd2733c2017-11-13 11:36:20 -06001709
1710def create_print_wrapper_funcs(func_names,
1711 stderr_func_names,
1712 replace_dict):
Michael Walshfd2733c2017-11-13 11:36:20 -06001713 r"""
1714 Generate code for print wrapper functions and return the generated code as
1715 a string.
1716
1717 To illustrate, suppose there is a "print_foo_bar" function in the
1718 func_names list.
1719 This function will...
1720 - Expect that there is an sprint_foo_bar function already in existence.
1721 - Create a print_foo_bar function which calls sprint_foo_bar and prints
Michael Walshfaafa9c2018-06-27 16:39:31 -05001722 the result.
Michael Walshfd2733c2017-11-13 11:36:20 -06001723 - Create a qprint_foo_bar function which calls upon sprint_foo_bar only if
Michael Walshfaafa9c2018-06-27 16:39:31 -05001724 global value quiet is 0.
Michael Walshfd2733c2017-11-13 11:36:20 -06001725 - Create a dprint_foo_bar function which calls upon sprint_foo_bar only if
Michael Walshfaafa9c2018-06-27 16:39:31 -05001726 global value debug is 1.
Michael Walshfd2733c2017-11-13 11:36:20 -06001727
1728 Also, code will be generated to define aliases for each function as well.
1729 Each alias will be created by replacing "print_" in the function name with
1730 "p" For example, the alias for print_foo_bar will be pfoo_bar.
1731
1732 Description of argument(s):
1733 func_names A list of functions for which print
1734 wrapper function code is to be generated.
1735 stderr_func_names A list of functions whose generated code
1736 should print to stderr rather than to
1737 stdout.
1738 replace_dict Please see the create_func_def_string
1739 function in wrap_utils.py for details on
1740 this parameter. This parameter will be
1741 passed directly to create_func_def_string.
1742 """
1743
1744 buffer = ""
1745
1746 for func_name in func_names:
1747 if func_name in stderr_func_names:
1748 replace_dict['output_stream'] = "stderr"
1749 else:
1750 replace_dict['output_stream'] = "stdout"
1751
1752 s_func_name = "s" + func_name
1753 q_func_name = "q" + func_name
1754 d_func_name = "d" + func_name
1755
1756 # We don't want to try to redefine the "print" function, thus the
1757 # following if statement.
1758 if func_name != "print":
1759 func_def = create_func_def_string(s_func_name, func_name,
1760 print_func_template,
1761 replace_dict)
1762 buffer += func_def
1763
1764 func_def = create_func_def_string(s_func_name, "q" + func_name,
1765 qprint_func_template, replace_dict)
1766 buffer += func_def
1767
1768 func_def = create_func_def_string(s_func_name, "d" + func_name,
1769 dprint_func_template, replace_dict)
1770 buffer += func_def
1771
Michael Walsh168eb0f2017-12-01 15:35:32 -06001772 func_def = create_func_def_string(s_func_name, "l" + func_name,
1773 lprint_func_template, replace_dict)
1774 buffer += func_def
1775
Michael Walshfd2733c2017-11-13 11:36:20 -06001776 # Create abbreviated aliases (e.g. spvar is an alias for sprint_var).
1777 alias = re.sub("print_", "p", func_name)
1778 alias = re.sub("print", "p", alias)
Michael Walsh168eb0f2017-12-01 15:35:32 -06001779 prefixes = ["", "s", "q", "d", "l"]
Michael Walshfd2733c2017-11-13 11:36:20 -06001780 for prefix in prefixes:
1781 if alias == "p":
1782 continue
1783 func_def = prefix + alias + " = " + prefix + func_name
1784 buffer += func_def + "\n"
1785
1786 return buffer
Michael Walsh82acf002017-05-04 14:33:05 -05001787
1788
Michael Walshde791732016-09-06 14:25:24 -05001789# In the following section of code, we will dynamically create print versions
1790# for each of the sprint functions defined above. So, for example, where we
1791# have an sprint_time() function defined above that returns the time to the
Michael Walsh7423c012016-10-04 10:27:21 -05001792# caller in a string, we will create a corresponding print_time() function
1793# that will print that string directly to stdout.
Michael Walshde791732016-09-06 14:25:24 -05001794
Michael Walshfd2733c2017-11-13 11:36:20 -06001795# It can be complicated to follow what's being created by below. Here is an
1796# example of the print_time() function that will be created:
Michael Walshde791732016-09-06 14:25:24 -05001797
Michael Walshfd2733c2017-11-13 11:36:20 -06001798# def print_time(buffer=''):
1799# sys.stdout.write(replace_passwords(sprint_time(buffer=buffer)))
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001800# sys.stdout.flush()
Michael Walshde791732016-09-06 14:25:24 -05001801
Michael Walshfd2733c2017-11-13 11:36:20 -06001802# Templates for the various print wrapper functions.
1803print_func_template = \
1804 [
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001805 " <mod_qualifier>gp_print(<mod_qualifier>replace_passwords("
1806 + "<call_line>), stream='<output_stream>')"
Michael Walshfd2733c2017-11-13 11:36:20 -06001807 ]
1808
1809qprint_func_template = \
1810 [
Michael Walsh589ae762019-03-19 13:22:39 -05001811 " quiet_default = <mod_qualifier>get_var_value(None, 0, \"quiet\")",
1812 " quiet = <mod_qualifier>get_stack_var(\"quiet\", quiet_default)",
1813 " if int(quiet): return"
Michael Walshfd2733c2017-11-13 11:36:20 -06001814 ] + print_func_template
1815
1816dprint_func_template = \
1817 [
Michael Walsh589ae762019-03-19 13:22:39 -05001818 " debug_default = <mod_qualifier>get_var_value(None, 0, \"debug\")",
1819 " debug = <mod_qualifier>get_stack_var(\"debug\", debug_default)",
1820 " if not int(debug): return"
Michael Walshfd2733c2017-11-13 11:36:20 -06001821 ] + print_func_template
1822
Michael Walsh168eb0f2017-12-01 15:35:32 -06001823lprint_func_template = \
1824 [
Michael Walsh4fea2cf2018-08-22 17:48:18 -05001825 " global sprint_time_last_seconds",
1826 " global last_seconds_ix",
1827 " if len(sprint_time_last_seconds) <= lprint_last_seconds_ix():",
1828 " sprint_time_last_seconds.append(start_time)",
1829 " save_last_seconds_ix = last_seconds_ix",
1830 " last_seconds_ix = lprint_last_seconds_ix()",
1831 " gp_log(<mod_qualifier>replace_passwords(<call_line>))",
1832 " last_seconds_ix = save_last_seconds_ix",
Michael Walsh168eb0f2017-12-01 15:35:32 -06001833 ]
1834
Michael Walsh81c02342018-01-05 15:43:28 -06001835replace_dict = {'output_stream': 'stdout', 'mod_qualifier': ''}
Michael Walshfd2733c2017-11-13 11:36:20 -06001836
1837
1838gp_debug_print("robot_env: " + str(robot_env))
Michael Walshde791732016-09-06 14:25:24 -05001839
1840# func_names contains a list of all print functions which should be created
1841# from their sprint counterparts.
1842func_names = ['print_time', 'print_timen', 'print_error', 'print_varx',
Michael Walsh18176322016-11-15 15:11:21 -06001843 'print_var', 'print_vars', 'print_dashes', 'indent',
1844 'print_call_stack', 'print_func_name', 'print_executing',
1845 'print_pgm_header', 'print_issuing', 'print_pgm_footer',
1846 'print_error_report', 'print', 'printn']
Michael Walshde791732016-09-06 14:25:24 -05001847
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001848# stderr_func_names is a list of functions whose output should go to stderr
1849# rather than stdout.
1850stderr_func_names = ['print_error', 'print_error_report']
Michael Walshde791732016-09-06 14:25:24 -05001851
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001852
Michael Walshfd2733c2017-11-13 11:36:20 -06001853func_defs = create_print_wrapper_funcs(func_names, stderr_func_names,
1854 replace_dict)
1855gp_debug_print(func_defs)
1856exec(func_defs)