blob: 1ccde52f678a166118ae6603e8be537f066d8b80 [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 Walsh1173a522018-05-21 17:24:51 -0500132def get_line_indent(line):
133 r"""
134 Return the number of spaces at the beginning of the line.
135 """
136
137 return len(line) - len(line.lstrip(' '))
138
139
Michael Walshde791732016-09-06 14:25:24 -0500140# get_arg_name is not a print function per se. I have included it in this
141# module because it is used by sprint_var which is found in this module.
Michael Walshde791732016-09-06 14:25:24 -0500142def get_arg_name(var,
143 arg_num=1,
144 stack_frame_ix=1):
Michael Walshde791732016-09-06 14:25:24 -0500145 r"""
146 Return the "name" of an argument passed to a function. This could be a
147 literal or a variable name.
148
Michael Walsh2ee77cd2017-03-08 11:50:17 -0600149 Description of arguments:
Michael Walshde791732016-09-06 14:25:24 -0500150 var The variable whose name you want returned.
Michael Walsh1173a522018-05-21 17:24:51 -0500151 arg_num The arg number whose name is to be
152 returned. To illustrate how arg_num is
153 processed, suppose that a programmer codes
154 this line: "rc, outbuf = my_func(var1,
155 var2)" and suppose that my_func has this
156 line of code: "result = gp.get_arg_name(0,
157 arg_num, 2)". If arg_num is positive, the
158 indicated argument is returned. For
159 example, if arg_num is 1, "var1" would be
160 returned, If arg_num is 2, "var2" would be
161 returned. If arg_num exceeds the number
162 of arguments, get_arg_name will simply
163 return a complete list of the arguments.
164 If arg_num is 0, get_arg_name will return
165 the name of the target function as
166 specified in the calling line ("my_func"
167 in this case). To clarify, if the caller
168 of the target function uses an alias
169 function name, the alias name would be
170 returned. If arg_num is negative, an
171 lvalue variable name is returned.
172 Continuing with the given example, if
173 arg_num is -2 the 2nd parm to the left of
174 the "=" ("rc" in this case) should be
175 returned. If arg_num is -1, the 1st parm
176 to the left of the "=" ("out_buf" in this
177 case) should be returned. If arg_num is
178 less than -2, an entire dictionary is
179 returned. The keys to the dictionary for
180 this example would be -2 and -1.
Michael Walshde791732016-09-06 14:25:24 -0500181 stack_frame_ix The stack frame index of the target
182 function. This value must be 1 or
183 greater. 1 would indicate get_arg_name's
184 stack frame. 2 would be the caller of
185 get_arg_name's stack frame, etc.
186
187 Example 1:
188
189 my_var = "mike"
190 var_name = get_arg_name(my_var)
191
192 In this example, var_name will receive the value "my_var".
193
194 Example 2:
195
196 def test1(var):
197 # Getting the var name of the first arg to this function, test1.
198 # Note, in this case, it doesn't matter what you pass as the first arg
199 # to get_arg_name since it is the caller's variable name that matters.
200 dummy = 1
201 arg_num = 1
202 stack_frame = 2
203 var_name = get_arg_name(dummy, arg_num, stack_frame)
204
205 # Mainline...
206
207 another_var = "whatever"
208 test1(another_var)
209
210 In this example, var_name will be set to "another_var".
211
212 """
213
214 # Note: I wish to avoid recursion so I refrain from calling any function
215 # that calls this function (i.e. sprint_var, valid_value, etc.).
216
Michael Walsh23e7f492017-01-10 11:34:47 -0600217 # The user can set environment variable "GET_ARG_NAME_DEBUG" to get debug
218 # output from this function.
219 local_debug = int(os.environ.get('GET_ARG_NAME_DEBUG', 0))
220 # In addition to GET_ARG_NAME_DEBUG, the user can set environment
221 # variable "GET_ARG_NAME_SHOW_SOURCE" to have this function include source
222 # code in the debug output.
223 local_debug_show_source = int(
224 os.environ.get('GET_ARG_NAME_SHOW_SOURCE', 0))
Michael Walshde791732016-09-06 14:25:24 -0500225
Michael Walshde791732016-09-06 14:25:24 -0500226 if stack_frame_ix < 1:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500227 print_error("Programmer error - Variable \"stack_frame_ix\" has an"
228 + " invalid value of \"" + str(stack_frame_ix) + "\". The"
229 + " value must be an integer that is greater than or equal"
230 + " to 1.\n")
Michael Walshde791732016-09-06 14:25:24 -0500231 return
232
233 if local_debug:
234 debug_indent = 2
Michael Walsh23e7f492017-01-10 11:34:47 -0600235 print("")
236 print_dashes(0, 120)
Michael Walshde791732016-09-06 14:25:24 -0500237 print(sprint_func_name() + "() parms:")
238 print_varx("var", var, 0, debug_indent)
239 print_varx("arg_num", arg_num, 0, debug_indent)
240 print_varx("stack_frame_ix", stack_frame_ix, 0, debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600241 print("")
242 print_call_stack(debug_indent, 2)
Michael Walshde791732016-09-06 14:25:24 -0500243
Michael Walsh23e7f492017-01-10 11:34:47 -0600244 for count in range(0, 2):
245 try:
246 frame, filename, cur_line_no, function_name, lines, index = \
247 inspect.stack()[stack_frame_ix]
248 except IndexError:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500249 print_error("Programmer error - The caller has asked for"
250 + " information about the stack frame at index \""
251 + str(stack_frame_ix) + "\". However, the stack"
252 + " only contains " + str(len(inspect.stack()))
253 + " entries. Therefore the stack frame index is out"
254 + " of range.\n")
Michael Walsh23e7f492017-01-10 11:34:47 -0600255 return
256 if filename != "<string>":
257 break
258 # filename of "<string>" may mean that the function in question was
259 # defined dynamically and therefore its code stack is inaccessible.
260 # This may happen with functions like "rqprint_var". In this case,
261 # we'll increment the stack_frame_ix and try again.
262 stack_frame_ix += 1
263 if local_debug:
264 print("Adjusted stack_frame_ix...")
265 print_varx("stack_frame_ix", stack_frame_ix, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500266
Michael Walsh1173a522018-05-21 17:24:51 -0500267 real_called_func_name = sprint_func_name(stack_frame_ix)
Michael Walsh23e7f492017-01-10 11:34:47 -0600268
269 module = inspect.getmodule(frame)
270
271 # Though I would expect inspect.getsourcelines(frame) to get all module
272 # source lines if the frame is "<module>", it doesn't do that. Therefore,
273 # for this special case, I will do inspect.getsourcelines(module).
274 if function_name == "<module>":
275 source_lines, source_line_num =\
276 inspect.getsourcelines(module)
277 line_ix = cur_line_no - source_line_num - 1
278 else:
279 source_lines, source_line_num =\
280 inspect.getsourcelines(frame)
281 line_ix = cur_line_no - source_line_num
282
283 if local_debug:
284 print("\n Variables retrieved from inspect.stack() function:")
285 print_varx("frame", frame, 0, debug_indent + 2)
286 print_varx("filename", filename, 0, debug_indent + 2)
287 print_varx("cur_line_no", cur_line_no, 0, debug_indent + 2)
288 print_varx("function_name", function_name, 0, debug_indent + 2)
289 print_varx("lines", lines, 0, debug_indent + 2)
290 print_varx("index", index, 0, debug_indent + 2)
291 print_varx("source_line_num", source_line_num, 0, debug_indent)
292 print_varx("line_ix", line_ix, 0, debug_indent)
293 if local_debug_show_source:
294 print_varx("source_lines", source_lines, 0, debug_indent)
Michael Walsh1173a522018-05-21 17:24:51 -0500295 print_varx("real_called_func_name", real_called_func_name, 0,
296 debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600297
298 # Get a list of all functions defined for the module. Note that this
299 # doesn't work consistently when _run_exitfuncs is at the top of the stack
300 # (i.e. if we're running an exit function). I've coded a work-around
301 # below for this deficiency.
302 all_functions = inspect.getmembers(module, inspect.isfunction)
303
304 # Get called_func_id by searching for our function in the list of all
305 # functions.
306 called_func_id = None
307 for func_name, function in all_functions:
Michael Walsh1173a522018-05-21 17:24:51 -0500308 if func_name == real_called_func_name:
Michael Walsh23e7f492017-01-10 11:34:47 -0600309 called_func_id = id(function)
310 break
311 # NOTE: The only time I've found that called_func_id can't be found is
312 # when we're running from an exit function.
313
314 # Look for other functions in module with matching id.
Michael Walsh1173a522018-05-21 17:24:51 -0500315 aliases = set([real_called_func_name])
Michael Walsh23e7f492017-01-10 11:34:47 -0600316 for func_name, function in all_functions:
Michael Walsh1173a522018-05-21 17:24:51 -0500317 if func_name == real_called_func_name:
Michael Walsh23e7f492017-01-10 11:34:47 -0600318 continue
319 func_id = id(function)
320 if func_id == called_func_id:
321 aliases.add(func_name)
322
323 # In most cases, my general purpose code above will find all aliases.
324 # However, for the odd case (i.e. running from exit function), I've added
325 # code to handle pvar, qpvar, dpvar, etc. aliases explicitly since they
326 # are defined in this module and used frequently.
327 # pvar is an alias for print_var.
Michael Walsh1173a522018-05-21 17:24:51 -0500328 aliases.add(re.sub("print_var", "pvar", real_called_func_name))
Michael Walsh23e7f492017-01-10 11:34:47 -0600329
Michael Walsh3f248272018-06-01 13:59:35 -0500330 # The call to the function could be encased in a recast (e.g.
331 # int(func_name())).
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500332 recast_regex = "([^ ]+\\([ ]*)?"
333 import_name_regex = "([a-zA-Z0-9_]+\\.)?"
Michael Walsh3f248272018-06-01 13:59:35 -0500334 func_name_regex = recast_regex + import_name_regex + "(" +\
335 '|'.join(aliases) + ")"
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500336 pre_args_regex = ".*" + func_name_regex + "[ ]*\\("
Michael Walsh23e7f492017-01-10 11:34:47 -0600337
338 # Search backward through source lines looking for the calling function
339 # name.
340 found = False
341 for start_line_ix in range(line_ix, 0, -1):
342 # Skip comment lines.
343 if re.match(r"[ ]*#", source_lines[start_line_ix]):
344 continue
Michael Walsh1173a522018-05-21 17:24:51 -0500345 if re.match(pre_args_regex, source_lines[start_line_ix]):
Michael Walsh23e7f492017-01-10 11:34:47 -0600346 found = True
347 break
348 if not found:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500349 print_error("Programmer error - Could not find the source line with"
350 + " a reference to function \"" + real_called_func_name
351 + "\".\n")
Michael Walsh23e7f492017-01-10 11:34:47 -0600352 return
353
Michael Walsh82acf002017-05-04 14:33:05 -0500354 # Search forward through the source lines looking for a line whose
355 # indentation is the same or less than the start line. The end of our
356 # composite line should be the line preceding that line.
Michael Walsh1173a522018-05-21 17:24:51 -0500357 start_indent = get_line_indent(source_lines[start_line_ix])
Michael Walsh37cd29d2018-05-24 13:19:18 -0500358 end_line_ix = line_ix
Michael Walsh23e7f492017-01-10 11:34:47 -0600359 for end_line_ix in range(line_ix + 1, len(source_lines)):
360 if source_lines[end_line_ix].strip() == "":
361 continue
Michael Walsh1173a522018-05-21 17:24:51 -0500362 line_indent = get_line_indent(source_lines[end_line_ix])
Michael Walsh82acf002017-05-04 14:33:05 -0500363 if line_indent <= start_indent:
Michael Walsh23e7f492017-01-10 11:34:47 -0600364 end_line_ix -= 1
365 break
Michael Walsh1173a522018-05-21 17:24:51 -0500366 if start_line_ix != 0:
367 # Check to see whether the start line is a continuation of the prior
Michael Walsha52e9eb2018-09-10 13:56:01 -0500368 # line.
369 prior_line = source_lines[start_line_ix - 1]
370 prior_line_stripped = re.sub(r"[ ]*\\([\r\n]$)", " \\1", prior_line)
371 prior_line_indent = get_line_indent(prior_line)
372 if prior_line != prior_line_stripped and\
373 prior_line_indent < start_indent:
Michael Walsh1173a522018-05-21 17:24:51 -0500374 start_line_ix -= 1
Michael Walsha52e9eb2018-09-10 13:56:01 -0500375 # Remove the backslash (continuation char) from prior line.
376 source_lines[start_line_ix] = prior_line_stripped
Michael Walsh23e7f492017-01-10 11:34:47 -0600377
378 # Join the start line through the end line into a composite line.
379 composite_line = ''.join(map(str.strip,
Gunnar Mills096cd562018-03-26 10:19:12 -0500380 source_lines[start_line_ix:end_line_ix + 1]))
Michael Walsh1173a522018-05-21 17:24:51 -0500381 # Insert one space after first "=" if there isn't one already.
382 composite_line = re.sub("=[ ]*([^ ])", "= \\1", composite_line, 1)
Michael Walsh7423c012016-10-04 10:27:21 -0500383
Michael Walsh3f248272018-06-01 13:59:35 -0500384 lvalue_regex = "[ ]*=[ ]+" + func_name_regex + ".*"
Michael Walsh1173a522018-05-21 17:24:51 -0500385 lvalue_string = re.sub(lvalue_regex, "", composite_line)
Michael Walsh3f248272018-06-01 13:59:35 -0500386 if lvalue_string == composite_line:
387 # i.e. the regex did not match so there are no lvalues.
388 lvalue_string = ""
Michael Walsh37762f92018-08-07 14:59:18 -0500389 lvalues_list = list(filter(None, map(str.strip, lvalue_string.split(","))))
Michael Walsh3f248272018-06-01 13:59:35 -0500390 try:
391 lvalues = collections.OrderedDict()
392 except AttributeError:
393 # A non-ordered dict doesn't look as nice when printed but it will do.
394 lvalues = {}
Michael Walsh1173a522018-05-21 17:24:51 -0500395 ix = len(lvalues_list) * -1
396 for lvalue in lvalues_list:
397 lvalues[ix] = lvalue
398 ix += 1
Michael Walsh3f248272018-06-01 13:59:35 -0500399 lvalue_prefix_regex = "(.*=[ ]+)?"
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500400 called_func_name_regex = lvalue_prefix_regex + func_name_regex +\
401 "[ ]*\\(.*"
Michael Walsh3f248272018-06-01 13:59:35 -0500402 called_func_name = re.sub(called_func_name_regex, "\\4", composite_line)
Michael Walsh1173a522018-05-21 17:24:51 -0500403 arg_list_etc = "(" + re.sub(pre_args_regex, "", composite_line)
Michael Walshde791732016-09-06 14:25:24 -0500404 if local_debug:
Michael Walsh23e7f492017-01-10 11:34:47 -0600405 print_varx("aliases", aliases, 0, debug_indent)
Michael Walsh3f248272018-06-01 13:59:35 -0500406 print_varx("import_name_regex", import_name_regex, 0, debug_indent)
407 print_varx("func_name_regex", func_name_regex, 0, debug_indent)
Michael Walsh1173a522018-05-21 17:24:51 -0500408 print_varx("pre_args_regex", pre_args_regex, 0, debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600409 print_varx("start_line_ix", start_line_ix, 0, debug_indent)
410 print_varx("end_line_ix", end_line_ix, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500411 print_varx("composite_line", composite_line, 0, debug_indent)
Michael Walsh1173a522018-05-21 17:24:51 -0500412 print_varx("lvalue_regex", lvalue_regex, 0, debug_indent)
413 print_varx("lvalue_string", lvalue_string, 0, debug_indent)
414 print_varx("lvalues", lvalues, 0, debug_indent)
Michael Walsh3f248272018-06-01 13:59:35 -0500415 print_varx("called_func_name_regex", called_func_name_regex, 0,
416 debug_indent)
Michael Walsh1173a522018-05-21 17:24:51 -0500417 print_varx("called_func_name", called_func_name, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500418 print_varx("arg_list_etc", arg_list_etc, 0, debug_indent)
419
420 # Parse arg list...
421 # Initialize...
422 nest_level = -1
423 arg_ix = 0
Michael Walsh7423c012016-10-04 10:27:21 -0500424 args_list = [""]
Michael Walshde791732016-09-06 14:25:24 -0500425 for ix in range(0, len(arg_list_etc)):
426 char = arg_list_etc[ix]
427 # Set the nest_level based on whether we've encounted a parenthesis.
428 if char == "(":
429 nest_level += 1
430 if nest_level == 0:
431 continue
432 elif char == ")":
433 nest_level -= 1
434 if nest_level < 0:
435 break
436
437 # If we reach a comma at base nest level, we are done processing an
Michael Walsh7423c012016-10-04 10:27:21 -0500438 # argument so we increment arg_ix and initialize a new args_list entry.
Michael Walshde791732016-09-06 14:25:24 -0500439 if char == "," and nest_level == 0:
440 arg_ix += 1
Michael Walsh7423c012016-10-04 10:27:21 -0500441 args_list.append("")
Michael Walshde791732016-09-06 14:25:24 -0500442 continue
443
Michael Walsh7423c012016-10-04 10:27:21 -0500444 # For any other character, we append it it to the current arg list
Michael Walshde791732016-09-06 14:25:24 -0500445 # entry.
Michael Walsh7423c012016-10-04 10:27:21 -0500446 args_list[arg_ix] += char
Michael Walshde791732016-09-06 14:25:24 -0500447
448 # Trim whitespace from each list entry.
Michael Walsh7423c012016-10-04 10:27:21 -0500449 args_list = [arg.strip() for arg in args_list]
Michael Walshde791732016-09-06 14:25:24 -0500450
Michael Walsh1173a522018-05-21 17:24:51 -0500451 if arg_num < 0:
452 if abs(arg_num) > len(lvalues):
453 argument = lvalues
454 else:
455 argument = lvalues[arg_num]
456 elif arg_num == 0:
457 argument = called_func_name
Michael Walsh2750b442018-05-18 14:49:11 -0500458 else:
Michael Walsh1173a522018-05-21 17:24:51 -0500459 if arg_num > len(args_list):
460 argument = args_list
461 else:
462 argument = args_list[arg_num - 1]
Michael Walshde791732016-09-06 14:25:24 -0500463
464 if local_debug:
Michael Walsh7423c012016-10-04 10:27:21 -0500465 print_varx("args_list", args_list, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500466 print_varx("argument", argument, 0, debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600467 print_dashes(0, 120)
Michael Walshde791732016-09-06 14:25:24 -0500468
469 return argument
470
Michael Walshde791732016-09-06 14:25:24 -0500471
Michael Walshde791732016-09-06 14:25:24 -0500472def sprint_time(buffer=""):
Michael Walshde791732016-09-06 14:25:24 -0500473 r"""
474 Return the time in the following format.
475
476 Example:
477
478 The following python code...
479
480 sys.stdout.write(sprint_time())
481 sys.stdout.write("Hi.\n")
482
483 Will result in the following type of output:
484
485 #(CDT) 2016/07/08 15:25:35 - Hi.
486
487 Example:
488
489 The following python code...
490
491 sys.stdout.write(sprint_time("Hi.\n"))
492
493 Will result in the following type of output:
494
495 #(CDT) 2016/08/03 17:12:05 - Hi.
496
497 The following environment variables will affect the formatting as
498 described:
499 NANOSECONDS This will cause the time stamps to be
500 precise to the microsecond (Yes, it
501 probably should have been named
502 MICROSECONDS but the convention was set
503 long ago so we're sticking with it).
504 Example of the output when environment
505 variable NANOSECONDS=1.
506
507 #(CDT) 2016/08/03 17:16:25.510469 - Hi.
508
509 SHOW_ELAPSED_TIME This will cause the elapsed time to be
510 included in the output. This is the
511 amount of time that has elapsed since the
512 last time this function was called. The
513 precision of the elapsed time field is
514 also affected by the value of the
515 NANOSECONDS environment variable. Example
516 of the output when environment variable
517 NANOSECONDS=0 and SHOW_ELAPSED_TIME=1.
518
519 #(CDT) 2016/08/03 17:17:40 - 0 - Hi.
520
521 Example of the output when environment variable NANOSECONDS=1 and
522 SHOW_ELAPSED_TIME=1.
523
524 #(CDT) 2016/08/03 17:18:47.317339 - 0.000046 - Hi.
525
526 Description of arguments.
527 buffer This will be appended to the formatted
528 time string.
529 """
530
531 global NANOSECONDS
532 global SHOW_ELAPSED_TIME
533 global sprint_time_last_seconds
Michael Walsh4fea2cf2018-08-22 17:48:18 -0500534 global last_seconds_ix
Michael Walshde791732016-09-06 14:25:24 -0500535
536 seconds = time.time()
537 loc_time = time.localtime(seconds)
538 nanoseconds = "%0.6f" % seconds
539 pos = nanoseconds.find(".")
540 nanoseconds = nanoseconds[pos:]
541
542 time_string = time.strftime("#(%Z) %Y/%m/%d %H:%M:%S", loc_time)
543 if NANOSECONDS == "1":
544 time_string = time_string + nanoseconds
545
546 if SHOW_ELAPSED_TIME == "1":
547 cur_time_seconds = seconds
548 math_string = "%9.9f" % cur_time_seconds + " - " + "%9.9f" % \
Michael Walsh4fea2cf2018-08-22 17:48:18 -0500549 sprint_time_last_seconds[last_seconds_ix]
Michael Walshde791732016-09-06 14:25:24 -0500550 elapsed_seconds = eval(math_string)
551 if NANOSECONDS == "1":
552 elapsed_seconds = "%11.6f" % elapsed_seconds
553 else:
554 elapsed_seconds = "%4i" % elapsed_seconds
Michael Walsh4fea2cf2018-08-22 17:48:18 -0500555 sprint_time_last_seconds[last_seconds_ix] = cur_time_seconds
Michael Walshde791732016-09-06 14:25:24 -0500556 time_string = time_string + " - " + elapsed_seconds
557
558 return time_string + " - " + buffer
559
Michael Walshde791732016-09-06 14:25:24 -0500560
Michael Walshde791732016-09-06 14:25:24 -0500561def sprint_timen(buffer=""):
Michael Walshde791732016-09-06 14:25:24 -0500562 r"""
563 Append a line feed to the buffer, pass it to sprint_time and return the
564 result.
565 """
566
567 return sprint_time(buffer + "\n")
568
Michael Walshde791732016-09-06 14:25:24 -0500569
Michael Walshde791732016-09-06 14:25:24 -0500570def sprint_error(buffer=""):
Michael Walshde791732016-09-06 14:25:24 -0500571 r"""
572 Return a standardized error string. This includes:
573 - A time stamp
574 - The "**ERROR**" string
575 - The caller's buffer string.
576
577 Example:
578
579 The following python code...
580
581 print(sprint_error("Oops.\n"))
582
583 Will result in the following type of output:
584
585 #(CDT) 2016/08/03 17:12:05 - **ERROR** Oops.
586
587 Description of arguments.
588 buffer This will be appended to the formatted
589 error string.
590 """
591
592 return sprint_time() + "**ERROR** " + buffer
593
Michael Walshde791732016-09-06 14:25:24 -0500594
Michael Walsh3f248272018-06-01 13:59:35 -0500595# Implement "constants" with functions.
596def digit_length_in_bits():
597 r"""
598 Return the digit length in bits.
599 """
600
601 return 4
602
603
604def word_length_in_digits():
605 r"""
606 Return the word length in digits.
607 """
608
609 return 8
610
611
612def bit_length(number):
613 r"""
614 Return the bit length of the number.
615
616 Description of argument(s):
617 number The number to be analyzed.
618 """
619
620 if number < 0:
621 # Convert negative numbers to positive and subtract one. The
622 # following example illustrates the reason for this:
623 # Consider a single nibble whose signed values can range from -8 to 7
624 # (0x8 to 0x7). A value of 0x7 equals 0b0111. Therefore, its length
625 # in bits is 3. Since the negative bit (i.e. 0b1000) is not set, the
626 # value 7 clearly will fit in one nibble. With -8 = 0x8 = 0b1000, you
627 # have the smallest negative value that will fit. Note that it
628 # requires 3 bits of 0. So by converting a number value of -8 to a
629 # working_number of 7, this function can accurately calculate the
630 # number of bits and therefore nibbles required to represent the
631 # number in print.
632 working_number = abs(number) - 1
633 else:
634 working_number = number
635
636 # Handle the special case of the number 0.
637 if working_number == 0:
638 return 0
639
640 return len(bin(working_number)) - 2
641
642
643def get_req_num_hex_digits(number):
644 r"""
645 Return the required number of hex digits required to display the given
646 number.
647
648 The returned value will always be rounded up to the nearest multiple of 8.
649
650 Description of argument(s):
651 number The number to be analyzed.
652 """
653
654 if number < 0:
655 # Convert negative numbers to positive and subtract one. The
656 # following example illustrates the reason for this:
657 # Consider a single nibble whose signed values can range from -8 to 7
658 # (0x8 to 0x7). A value of 0x7 equals 0b0111. Therefore, its length
659 # in bits is 3. Since the negative bit (i.e. 0b1000) is not set, the
660 # value 7 clearly will fit in one nibble. With -8 = 0x8 = 0b1000, you
661 # have the smallest negative value that will fit. Note that it
662 # requires 3 bits of 0. So by converting a number value of -8 to a
663 # working_number of 7, this function can accurately calculate the
664 # number of bits and therefore nibbles required to represent the
665 # number in print.
666 working_number = abs(number) - 1
667 else:
668 working_number = number
669
670 # Handle the special case of the number 0.
671 if working_number == 0:
672 return word_length_in_digits()
673
674 num_length_in_bits = bit_length(working_number)
675 num_hex_digits, remainder = divmod(num_length_in_bits,
676 digit_length_in_bits())
677 if remainder > 0:
678 # Example: the number 7 requires 3 bits. The divmod above produces,
679 # 0 with remainder of 3. So because we have a remainder, we increment
680 # num_hex_digits from 0 to 1.
681 num_hex_digits += 1
682
683 # Check to see whether the negative bit is set. This is the left-most
684 # bit in the highest order digit.
685 negative_mask = 2 ** (num_hex_digits * 4 - 1)
686 if working_number & negative_mask:
687 # If a number that is intended to be positive has its negative bit
688 # on, an additional digit will be required to represent it correctly
689 # in print.
690 num_hex_digits += 1
691
692 num_words, remainder = divmod(num_hex_digits, word_length_in_digits())
693 if remainder > 0 or num_words == 0:
694 num_words += 1
695
696 # Round up to the next word length in digits.
697 return num_words * word_length_in_digits()
698
699
700def dft_num_hex_digits():
701 r"""
702 Return the default number of hex digits to be used to represent a hex
703 number in print.
704
705 The value returned is a function of sys.maxsize.
706 """
707
708 global _gen_print_dft_num_hex_digits_
709 try:
710 return _gen_print_dft_num_hex_digits_
711 except NameError:
712 _gen_print_dft_num_hex_digits_ = get_req_num_hex_digits(sys.maxsize)
713 return _gen_print_dft_num_hex_digits_
714
715
Michael Walsh8646d962019-01-21 14:36:13 -0600716def is_dict(var_value):
717 r"""
718 Return 1 if var_value is a type of dictionary and 0 if it is not.
719 """
720
721 type_is_dict = 0
722 if isinstance(var_value, dict):
723 type_is_dict = 1
724 try:
725 if isinstance(var_value, collections.OrderedDict):
726 type_is_dict = 1
727 except AttributeError:
728 pass
729 try:
730 if isinstance(var_value, DotDict):
731 type_is_dict = 1
732 except NameError:
733 pass
734 try:
735 if isinstance(var_value, NormalizedDict):
736 type_is_dict = 1
737 except NameError:
738 pass
739 return type_is_dict
740
741
Michael Walshde791732016-09-06 14:25:24 -0500742def sprint_varx(var_name,
743 var_value,
744 hex=0,
745 loc_col1_indent=col1_indent,
Michael Walsh7423c012016-10-04 10:27:21 -0500746 loc_col1_width=col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500747 trailing_char="\n",
748 key_list=None):
Michael Walshde791732016-09-06 14:25:24 -0500749 r"""
750 Print the var name/value passed to it. If the caller lets loc_col1_width
751 default, the printing lines up nicely with output generated by the
752 print_time functions.
753
754 Note that the sprint_var function (defined below) can be used to call this
755 function so that the programmer does not need to pass the var_name.
756 sprint_var will figure out the var_name. The sprint_var function is the
757 one that would normally be used by the general user.
758
759 For example, the following python code:
760
761 first_name = "Mike"
762 print_time("Doing this...\n")
763 print_varx("first_name", first_name)
764 print_time("Doing that...\n")
765
766 Will generate output like this:
767
768 #(CDT) 2016/08/10 17:34:42.847374 - 0.001285 - Doing this...
769 first_name: Mike
770 #(CDT) 2016/08/10 17:34:42.847510 - 0.000136 - Doing that...
771
772 This function recognizes several complex types of data such as dict, list
773 or tuple.
774
775 For example, the following python code:
776
777 my_dict = dict(one=1, two=2, three=3)
778 print_var(my_dict)
779
780 Will generate the following output:
781
782 my_dict:
783 my_dict[three]: 3
784 my_dict[two]: 2
785 my_dict[one]: 1
786
787 Description of arguments.
788 var_name The name of the variable to be printed.
789 var_value The value of the variable to be printed.
790 hex This indicates that the value should be
791 printed in hex format. It is the user's
792 responsibility to ensure that a var_value
Michael Walshbec416d2016-11-10 08:54:52 -0600793 contains a valid hex number. For string
794 var_values, this will be interpreted as
795 show_blanks which means that blank values
Michael Walshd995cb02017-02-07 14:46:01 -0600796 will be printed as "<blank>". For dict
797 var_values, this will be interpreted as
798 terse format where keys are not repeated
799 in the output.
Michael Walshde791732016-09-06 14:25:24 -0500800 loc_col1_indent The number of spaces to indent the output.
801 loc_col1_width The width of the output column containing
802 the variable name. The default value of
803 this is adjusted so that the var_value
804 lines up with text printed via the
805 print_time function.
Michael Walsh7423c012016-10-04 10:27:21 -0500806 trailing_char The character to be used at the end of the
807 returned string. The default value is a
808 line feed.
Michael Walshd2869032018-03-22 16:12:11 -0500809 key_list A list of which dictionary keys should be
810 printed. All others keys will be skipped.
811 Each value in key_list will be regarded
812 as a regular expression and it will be
813 regarded as anchored to the beginning and
814 ends of the dictionary key being
815 referenced. For example if key_list is
816 ["one", "two"], the resulting regex used
817 will be "^one|two$", i.e. only keys "one"
818 and "two" from the var_value dictionary
819 will be printed. As another example, if
820 the caller were to specify a key_list of
821 ["one.*"], then only dictionary keys whose
822 names begin with "one" will be printed.
823 Note: This argument pertains only to
824 var_values which are dictionaries.
Michael Walsh7423c012016-10-04 10:27:21 -0500825 """
Michael Walshde791732016-09-06 14:25:24 -0500826
827 # Determine the type
Michael Walsh37762f92018-08-07 14:59:18 -0500828 try:
829 int_types = (int, long)
830 except NameError:
831 int_types = (int,)
832 try:
833 string_types = (str, unicode)
834 except NameError:
George Keishing36efbc02018-12-12 10:18:23 -0600835 string_types = (bytes, str)
Michael Walsh37762f92018-08-07 14:59:18 -0500836 simple_types = int_types + string_types + (float, bool)
837 if type(var_value) in simple_types \
Michael Walshde791732016-09-06 14:25:24 -0500838 or var_value is None:
839 # The data type is simple in the sense that it has no subordinate
840 # parts.
Michael Walsh7423c012016-10-04 10:27:21 -0500841 # Adjust loc_col1_width.
842 loc_col1_width = loc_col1_width - loc_col1_indent
Michael Walshde791732016-09-06 14:25:24 -0500843 # See if the user wants the output in hex format.
844 if hex:
Michael Walsh37762f92018-08-07 14:59:18 -0500845 if type(var_value) not in int_types:
Michael Walshbec416d2016-11-10 08:54:52 -0600846 value_format = "%s"
Michael Walsh2795edc2016-12-13 16:00:33 -0600847 if var_value == "":
Michael Walshbec416d2016-11-10 08:54:52 -0600848 var_value = "<blank>"
849 else:
Michael Walsh3f248272018-06-01 13:59:35 -0500850 num_hex_digits = max(dft_num_hex_digits(),
851 get_req_num_hex_digits(var_value))
852 # Convert a negative number to its positive twos complement
853 # for proper printing. For example, instead of printing -1 as
854 # "0x-000000000000001" it will be printed as
855 # "0xffffffffffffffff".
856 var_value = var_value & (2 ** (num_hex_digits * 4) - 1)
857 value_format = "0x%0" + str(num_hex_digits) + "x"
Michael Walshde791732016-09-06 14:25:24 -0500858 else:
859 value_format = "%s"
860 format_string = "%" + str(loc_col1_indent) + "s%-" \
Michael Walsh7423c012016-10-04 10:27:21 -0500861 + str(loc_col1_width) + "s" + value_format + trailing_char
Michael Walsh3383e652017-09-01 17:10:59 -0500862 if value_format == "0x%08x":
863 return format_string % ("", str(var_name) + ":",
864 var_value & 0xffffffff)
865 else:
866 return format_string % ("", str(var_name) + ":", var_value)
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500867 elif isinstance(var_value, type):
Michael Walsh20a87ab2017-06-30 17:00:30 -0500868 return sprint_varx(var_name, str(var_value).split("'")[1], hex,
Michael Walshd2869032018-03-22 16:12:11 -0500869 loc_col1_indent, loc_col1_width, trailing_char,
870 key_list)
Michael Walshde791732016-09-06 14:25:24 -0500871 else:
872 # The data type is complex in the sense that it has subordinate parts.
873 format_string = "%" + str(loc_col1_indent) + "s%s\n"
874 buffer = format_string % ("", var_name + ":")
875 loc_col1_indent += 2
Michael Walsh7423c012016-10-04 10:27:21 -0500876 try:
877 length = len(var_value)
878 except TypeError:
Michael Walsh23e7f492017-01-10 11:34:47 -0600879 length = 0
Michael Walsh7423c012016-10-04 10:27:21 -0500880 ix = 0
881 loc_trailing_char = "\n"
Michael Walsh8646d962019-01-21 14:36:13 -0600882 if is_dict(var_value):
Michael Walsh37762f92018-08-07 14:59:18 -0500883 for key, value in var_value.items():
Michael Walshd2869032018-03-22 16:12:11 -0500884 if key_list is not None:
885 key_list_regex = "^" + "|".join(key_list) + "$"
886 if not re.match(key_list_regex, key):
887 continue
Michael Walsh7423c012016-10-04 10:27:21 -0500888 ix += 1
889 if ix == length:
890 loc_trailing_char = trailing_char
Michael Walshd995cb02017-02-07 14:46:01 -0600891 if hex:
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600892 # Since hex is being used as a format type, we want it
893 # turned off when processing integer dictionary values so
894 # it is not interpreted as a hex indicator.
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500895 loc_hex = not (isinstance(value, int))
Michael Walshf7b8a002017-08-29 10:38:39 -0500896 buffer += sprint_varx("[" + key + "]", value,
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600897 loc_hex, loc_col1_indent,
898 loc_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500899 loc_trailing_char,
900 key_list)
Michael Walshd995cb02017-02-07 14:46:01 -0600901 else:
Michael Walsh1173a522018-05-21 17:24:51 -0500902 buffer += sprint_varx(var_name + "[" + str(key) + "]",
903 value, hex, loc_col1_indent,
904 loc_col1_width, loc_trailing_char,
905 key_list)
Michael Walsh7423c012016-10-04 10:27:21 -0500906 elif type(var_value) in (list, tuple, set):
Michael Walshde791732016-09-06 14:25:24 -0500907 for key, value in enumerate(var_value):
Michael Walsh7423c012016-10-04 10:27:21 -0500908 ix += 1
909 if ix == length:
910 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500911 buffer += sprint_varx(var_name + "[" + str(key) + "]", value,
Michael Walsh7423c012016-10-04 10:27:21 -0500912 hex, loc_col1_indent, loc_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500913 loc_trailing_char, key_list)
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500914 elif isinstance(var_value, argparse.Namespace):
Michael Walshde791732016-09-06 14:25:24 -0500915 for key in var_value.__dict__:
Michael Walsh7423c012016-10-04 10:27:21 -0500916 ix += 1
917 if ix == length:
918 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500919 cmd_buf = "buffer += sprint_varx(var_name + \".\" + str(key)" \
Michael Walsh7423c012016-10-04 10:27:21 -0500920 + ", var_value." + key + ", hex, loc_col1_indent," \
Michael Walshd2869032018-03-22 16:12:11 -0500921 + " loc_col1_width, loc_trailing_char, key_list)"
Michael Walshde791732016-09-06 14:25:24 -0500922 exec(cmd_buf)
923 else:
924 var_type = type(var_value).__name__
925 func_name = sys._getframe().f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -0500926 var_value = "<" + var_type + " type not supported by " + \
927 func_name + "()>"
Michael Walshde791732016-09-06 14:25:24 -0500928 value_format = "%s"
929 loc_col1_indent -= 2
Michael Walsh7423c012016-10-04 10:27:21 -0500930 # Adjust loc_col1_width.
931 loc_col1_width = loc_col1_width - loc_col1_indent
Michael Walshde791732016-09-06 14:25:24 -0500932 format_string = "%" + str(loc_col1_indent) + "s%-" \
Michael Walsh7423c012016-10-04 10:27:21 -0500933 + str(loc_col1_width) + "s" + value_format + trailing_char
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600934 return format_string % ("", str(var_name) + ":", var_value)
Michael Walsh23e7f492017-01-10 11:34:47 -0600935
Michael Walshde791732016-09-06 14:25:24 -0500936 return buffer
937
938 return ""
939
Michael Walshde791732016-09-06 14:25:24 -0500940
Michael Walshfd2733c2017-11-13 11:36:20 -0600941def sprint_var(var_value,
942 hex=0,
943 loc_col1_indent=col1_indent,
944 loc_col1_width=col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500945 trailing_char="\n",
946 key_list=None):
Michael Walshde791732016-09-06 14:25:24 -0500947 r"""
948 Figure out the name of the first argument for you and then call
949 sprint_varx with it. Therefore, the following 2 calls are equivalent:
950 sprint_varx("var1", var1)
951 sprint_var(var1)
952 """
953
954 # Get the name of the first variable passed to this function.
955 stack_frame = 2
Michael Walsh7423c012016-10-04 10:27:21 -0500956 caller_func_name = sprint_func_name(2)
957 if caller_func_name.endswith("print_var"):
Michael Walshde791732016-09-06 14:25:24 -0500958 stack_frame += 1
959 var_name = get_arg_name(None, 1, stack_frame)
Michael Walshfd2733c2017-11-13 11:36:20 -0600960 return sprint_varx(var_name, var_value=var_value, hex=hex,
961 loc_col1_indent=loc_col1_indent,
962 loc_col1_width=loc_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500963 trailing_char=trailing_char,
964 key_list=key_list)
Michael Walshde791732016-09-06 14:25:24 -0500965
966
Michael Walsh18176322016-11-15 15:11:21 -0600967def sprint_vars(*args):
Michael Walsh18176322016-11-15 15:11:21 -0600968 r"""
969 Sprint the values of one or more variables.
970
971 Description of args:
972 args:
973 If the first argument is an integer, it will be interpreted to be the
974 "indent" value.
975 If the second argument is an integer, it will be interpreted to be the
976 "col1_width" value.
977 If the third argument is an integer, it will be interpreted to be the
978 "hex" value.
979 All remaining parms are considered variable names which are to be
980 sprinted.
981 """
982
983 if len(args) == 0:
984 return
985
986 # Get the name of the first variable passed to this function.
987 stack_frame = 2
988 caller_func_name = sprint_func_name(2)
989 if caller_func_name.endswith("print_vars"):
990 stack_frame += 1
991
992 parm_num = 1
993
994 # Create list from args (which is a tuple) so that it can be modified.
995 args_list = list(args)
996
997 var_name = get_arg_name(None, parm_num, stack_frame)
998 # See if parm 1 is to be interpreted as "indent".
999 try:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001000 if isinstance(int(var_name), int):
Michael Walsh18176322016-11-15 15:11:21 -06001001 indent = int(var_name)
1002 args_list.pop(0)
1003 parm_num += 1
1004 except ValueError:
1005 indent = 0
1006
1007 var_name = get_arg_name(None, parm_num, stack_frame)
1008 # See if parm 1 is to be interpreted as "col1_width".
1009 try:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001010 if isinstance(int(var_name), int):
Michael Walsh18176322016-11-15 15:11:21 -06001011 loc_col1_width = int(var_name)
1012 args_list.pop(0)
1013 parm_num += 1
1014 except ValueError:
1015 loc_col1_width = col1_width
1016
1017 var_name = get_arg_name(None, parm_num, stack_frame)
1018 # See if parm 1 is to be interpreted as "hex".
1019 try:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001020 if isinstance(int(var_name), int):
Michael Walsh18176322016-11-15 15:11:21 -06001021 hex = int(var_name)
1022 args_list.pop(0)
1023 parm_num += 1
1024 except ValueError:
1025 hex = 0
1026
1027 buffer = ""
1028 for var_value in args_list:
1029 var_name = get_arg_name(None, parm_num, stack_frame)
1030 buffer += sprint_varx(var_name, var_value, hex, indent, loc_col1_width)
1031 parm_num += 1
1032
1033 return buffer
1034
Michael Walsh18176322016-11-15 15:11:21 -06001035
Michael Walsh7423c012016-10-04 10:27:21 -05001036def sprint_dashes(indent=col1_indent,
1037 width=80,
1038 line_feed=1,
1039 char="-"):
Michael Walshde791732016-09-06 14:25:24 -05001040 r"""
1041 Return a string of dashes to the caller.
1042
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001043 Description of arguments:
Michael Walshde791732016-09-06 14:25:24 -05001044 indent The number of characters to indent the
1045 output.
1046 width The width of the string of dashes.
1047 line_feed Indicates whether the output should end
1048 with a line feed.
Michael Walsh7423c012016-10-04 10:27:21 -05001049 char The character to be repeated in the output
1050 string.
Michael Walshde791732016-09-06 14:25:24 -05001051 """
1052
Michael Walsh7423c012016-10-04 10:27:21 -05001053 width = int(width)
Michael Walsh23e7f492017-01-10 11:34:47 -06001054 buffer = " " * int(indent) + char * width
Michael Walshde791732016-09-06 14:25:24 -05001055 if line_feed:
1056 buffer += "\n"
1057
1058 return buffer
1059
Michael Walshde791732016-09-06 14:25:24 -05001060
Michael Walsh7423c012016-10-04 10:27:21 -05001061def sindent(text="",
1062 indent=0):
Michael Walsh7423c012016-10-04 10:27:21 -05001063 r"""
1064 Pre-pend the specified number of characters to the text string (i.e.
1065 indent it) and return it.
1066
1067 Description of arguments:
1068 text The string to be indented.
1069 indent The number of characters to indent the
1070 string.
1071 """
1072
1073 format_string = "%" + str(indent) + "s%s"
1074 buffer = format_string % ("", text)
1075
1076 return buffer
1077
Michael Walsh7423c012016-10-04 10:27:21 -05001078
Michael Walsh662e13b2019-03-01 15:54:08 -06001079func_line_style_std = None
1080func_line_style_short = 1
1081
1082
1083def sprint_func_line(stack_frame, style=None):
Michael Walsh47aa2a42018-12-10 15:06:02 -06001084 r"""
1085 For the given stack_frame, return a formatted string containing the
1086 function name and all its arguments.
1087
1088 Example:
1089
1090 func1(last_name = 'walsh', first_name = 'mikey')
1091
1092 Description of argument(s):
1093 stack_frame A stack frame (such as is returned by
1094 inspect.stack()).
Michael Walsh662e13b2019-03-01 15:54:08 -06001095 style Indicates the style or formatting of the
1096 result string. Acceptable values are
1097 shown above.
1098
1099 Description of styles:
1100 func_line_style_std The standard formatting.
1101 func_line_style_short 1) The self parm (associated with methods)
1102 will be dropped. 2) The args and kwargs
1103 values will be treated as special. In
1104 both cases the arg name ('args' or
1105 'kwargs') will be dropped and only the
1106 values will be shown.
Michael Walsh47aa2a42018-12-10 15:06:02 -06001107 """
1108
1109 func_name = str(stack_frame[3])
1110 if func_name == "?":
1111 # "?" is the name used when code is not in a function.
1112 func_name = "(none)"
1113
1114 if func_name == "<module>":
1115 # If the func_name is the "main" program, we simply get the command
1116 # line call string.
1117 func_and_args = ' '.join(sys.argv)
1118 else:
1119 # Get the program arguments.
1120 (args, varargs, keywords, locals) =\
1121 inspect.getargvalues(stack_frame[0])
1122
1123 args_list = []
1124 for arg_name in filter(None, args + [varargs, keywords]):
1125 # Get the arg value from frame locals.
1126 arg_value = locals[arg_name]
1127 if arg_name == 'self':
Michael Walsh662e13b2019-03-01 15:54:08 -06001128 if style == func_line_style_short:
1129 continue
Michael Walsh47aa2a42018-12-10 15:06:02 -06001130 # Manipulations to improve output for class methods.
1131 func_name = arg_value.__class__.__name__ + "." + func_name
1132 args_list.append(arg_name + " = <self>")
Michael Walsh662e13b2019-03-01 15:54:08 -06001133 elif (style == func_line_style_short
1134 and arg_name == 'args'
1135 and type(arg_value) in (list, tuple)):
1136 if len(arg_value) == 0:
1137 continue
1138 args_list.append(repr(', '.join(arg_value)))
1139 elif (style == func_line_style_short
1140 and arg_name == 'kwargs'
1141 and type(arg_value) is dict):
1142 for key, value in arg_value.items():
1143 args_list.append(key + "=" + repr(value))
Michael Walsh47aa2a42018-12-10 15:06:02 -06001144 else:
1145 args_list.append(arg_name + " = " + repr(arg_value))
1146 args_str = "(" + ', '.join(map(str, args_list)) + ")"
1147
1148 # Now we need to print this in a nicely-wrapped way.
1149 func_and_args = func_name + args_str
1150
1151 return func_and_args
1152
1153
Michael Walsh7423c012016-10-04 10:27:21 -05001154def sprint_call_stack(indent=0,
Michael Walsh662e13b2019-03-01 15:54:08 -06001155 stack_frame_ix=0,
1156 style=None):
Michael Walshde791732016-09-06 14:25:24 -05001157 r"""
1158 Return a call stack report for the given point in the program with line
1159 numbers, function names and function parameters and arguments.
1160
1161 Sample output:
1162
1163 -------------------------------------------------------------------------
1164 Python function call stack
1165
1166 Line # Function name and arguments
1167 ------ ------------------------------------------------------------------
Michael Walsh47aa2a42018-12-10 15:06:02 -06001168 424 sprint_call_stack()
1169 4 print_call_stack()
1170 31 func1(last_name = 'walsh', first_name = 'mikey')
Michael Walshde791732016-09-06 14:25:24 -05001171 59 /tmp/scr5.py
1172 -------------------------------------------------------------------------
1173
1174 Description of arguments:
1175 indent The number of characters to indent each
1176 line of output.
1177 stack_frame_ix The index of the first stack frame which
1178 is to be returned.
Michael Walsh662e13b2019-03-01 15:54:08 -06001179 style See the sprint_line_func prolog above for
1180 details.
Michael Walshde791732016-09-06 14:25:24 -05001181 """
1182
1183 buffer = ""
Michael Walsh7423c012016-10-04 10:27:21 -05001184 buffer += sprint_dashes(indent)
1185 buffer += sindent("Python function call stack\n\n", indent)
1186 buffer += sindent("Line # Function name and arguments\n", indent)
1187 buffer += sprint_dashes(indent, 6, 0) + " " + sprint_dashes(0, 73)
Michael Walshde791732016-09-06 14:25:24 -05001188
1189 # Grab the current program stack.
1190 current_stack = inspect.stack()
1191
1192 # Process each frame in turn.
1193 format_string = "%6s %s\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001194 ix = 0
Michael Walshde791732016-09-06 14:25:24 -05001195 for stack_frame in current_stack:
Michael Walsh7423c012016-10-04 10:27:21 -05001196 if ix < stack_frame_ix:
1197 ix += 1
1198 continue
Michael Walsh23e7f492017-01-10 11:34:47 -06001199 # I want the line number shown to be the line where you find the line
1200 # shown.
1201 try:
1202 line_num = str(current_stack[ix + 1][2])
1203 except IndexError:
1204 line_num = ""
Michael Walsh662e13b2019-03-01 15:54:08 -06001205 func_and_args = sprint_func_line(stack_frame, style=style)
Michael Walshde791732016-09-06 14:25:24 -05001206
Michael Walsh23e7f492017-01-10 11:34:47 -06001207 buffer += sindent(format_string % (line_num, func_and_args), indent)
Michael Walsh7423c012016-10-04 10:27:21 -05001208 ix += 1
Michael Walshde791732016-09-06 14:25:24 -05001209
Michael Walsh7423c012016-10-04 10:27:21 -05001210 buffer += sprint_dashes(indent)
Michael Walshde791732016-09-06 14:25:24 -05001211
1212 return buffer
1213
Michael Walshde791732016-09-06 14:25:24 -05001214
Michael Walsh662e13b2019-03-01 15:54:08 -06001215def sprint_executing(stack_frame_ix=None, style=None):
Michael Walshde791732016-09-06 14:25:24 -05001216 r"""
1217 Print a line indicating what function is executing and with what parameter
1218 values. This is useful for debugging.
1219
1220 Sample output:
1221
Michael Walsh47aa2a42018-12-10 15:06:02 -06001222 #(CDT) 2016/08/25 17:54:27 - Executing: func1(x = 1)
Michael Walshde791732016-09-06 14:25:24 -05001223
1224 Description of arguments:
1225 stack_frame_ix The index of the stack frame whose
1226 function info should be returned. If the
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001227 caller does not specify a value, this
Michael Walshde791732016-09-06 14:25:24 -05001228 function will set the value to 1 which is
1229 the index of the caller's stack frame. If
1230 the caller is the wrapper function
1231 "print_executing", this function will bump
1232 it up by 1.
Michael Walsh662e13b2019-03-01 15:54:08 -06001233 style See the sprint_line_func prolog above for
1234 details.
Michael Walshde791732016-09-06 14:25:24 -05001235 """
1236
1237 # If user wants default stack_frame_ix.
1238 if stack_frame_ix is None:
1239 func_name = sys._getframe().f_code.co_name
1240 caller_func_name = sys._getframe(1).f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -05001241 if caller_func_name.endswith(func_name[1:]):
Michael Walshde791732016-09-06 14:25:24 -05001242 stack_frame_ix = 2
1243 else:
1244 stack_frame_ix = 1
1245
1246 stack_frame = inspect.stack()[stack_frame_ix]
1247
Michael Walsh662e13b2019-03-01 15:54:08 -06001248 func_and_args = sprint_func_line(stack_frame, style)
Michael Walshde791732016-09-06 14:25:24 -05001249
1250 return sprint_time() + "Executing: " + func_and_args + "\n"
1251
Michael Walshde791732016-09-06 14:25:24 -05001252
Michael Walshbec416d2016-11-10 08:54:52 -06001253def sprint_pgm_header(indent=0,
1254 linefeed=1):
Michael Walshde791732016-09-06 14:25:24 -05001255 r"""
1256 Return a standardized header that programs should print at the beginning
1257 of the run. It includes useful information like command line, pid,
1258 userid, program parameters, etc.
1259
Michael Walsh7423c012016-10-04 10:27:21 -05001260 Description of arguments:
1261 indent The number of characters to indent each
1262 line of output.
Michael Walshbec416d2016-11-10 08:54:52 -06001263 linefeed Indicates whether a line feed be included
1264 at the beginning and end of the report.
Michael Walshde791732016-09-06 14:25:24 -05001265 """
1266
Michael Walshbec416d2016-11-10 08:54:52 -06001267 loc_col1_width = col1_width + indent
1268
1269 buffer = ""
1270 if linefeed:
1271 buffer = "\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001272
Michael Walshdb6e68a2017-05-23 17:55:31 -05001273 if robot_env:
1274 suite_name = BuiltIn().get_variable_value("${suite_name}")
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001275 buffer += sindent(sprint_time("Running test suite \"" + suite_name
1276 + "\".\n"), indent)
Michael Walshdb6e68a2017-05-23 17:55:31 -05001277
Michael Walsh7423c012016-10-04 10:27:21 -05001278 buffer += sindent(sprint_time() + "Running " + pgm_name + ".\n", indent)
1279 buffer += sindent(sprint_time() + "Program parameter values, etc.:\n\n",
1280 indent)
Michael Walshbec416d2016-11-10 08:54:52 -06001281 buffer += sprint_varx("command_line", ' '.join(sys.argv), 0, indent,
1282 loc_col1_width)
Michael Walsh7423c012016-10-04 10:27:21 -05001283 # We want the output to show a customized name for the pid and pgid but
1284 # we want it to look like a valid variable name. Therefore, we'll use
Michael Walshde791732016-09-06 14:25:24 -05001285 # pgm_name_var_name which was set when this module was imported.
Michael Walshbec416d2016-11-10 08:54:52 -06001286 buffer += sprint_varx(pgm_name_var_name + "_pid", os.getpid(), 0, indent,
1287 loc_col1_width)
1288 buffer += sprint_varx(pgm_name_var_name + "_pgid", os.getpgrp(), 0, indent,
1289 loc_col1_width)
Michael Walsh86de0d22016-12-05 10:13:15 -06001290 userid_num = str(os.geteuid())
1291 try:
1292 username = os.getlogin()
1293 except OSError:
1294 if userid_num == "0":
1295 username = "root"
1296 else:
1297 username = "?"
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001298 buffer += sprint_varx("uid", userid_num + " (" + username
1299 + ")", 0, indent, loc_col1_width)
1300 buffer += sprint_varx("gid", str(os.getgid()) + " ("
1301 + str(grp.getgrgid(os.getgid()).gr_name) + ")", 0,
Michael Walshbec416d2016-11-10 08:54:52 -06001302 indent, loc_col1_width)
1303 buffer += sprint_varx("host_name", socket.gethostname(), 0, indent,
1304 loc_col1_width)
Michael Walsh86de0d22016-12-05 10:13:15 -06001305 try:
1306 DISPLAY = os.environ['DISPLAY']
1307 except KeyError:
1308 DISPLAY = ""
1309 buffer += sprint_varx("DISPLAY", DISPLAY, 0, indent,
Michael Walshbec416d2016-11-10 08:54:52 -06001310 loc_col1_width)
Michael Walshde791732016-09-06 14:25:24 -05001311 # I want to add code to print caller's parms.
1312
Michael Walsh7423c012016-10-04 10:27:21 -05001313 # __builtin__.arg_obj is created by the get_arg module function,
1314 # gen_get_options.
1315 try:
1316 buffer += ga.sprint_args(__builtin__.arg_obj, indent)
1317 except AttributeError:
1318 pass
1319
Michael Walshdb6e68a2017-05-23 17:55:31 -05001320 if robot_env:
1321 # Get value of global parm_list.
1322 parm_list = BuiltIn().get_variable_value("${parm_list}")
1323
1324 for parm in parm_list:
1325 parm_value = BuiltIn().get_variable_value("${" + parm + "}")
1326 buffer += sprint_varx(parm, parm_value, 0, indent, loc_col1_width)
1327
1328 # Setting global program_pid.
1329 BuiltIn().set_global_variable("${program_pid}", os.getpid())
1330
Michael Walshbec416d2016-11-10 08:54:52 -06001331 if linefeed:
1332 buffer += "\n"
Michael Walshde791732016-09-06 14:25:24 -05001333
1334 return buffer
1335
Michael Walshde791732016-09-06 14:25:24 -05001336
Michael Walsh7423c012016-10-04 10:27:21 -05001337def sprint_error_report(error_text="\n",
Michael Walshdb6e68a2017-05-23 17:55:31 -05001338 indent=2,
1339 format=None):
Michael Walsh7423c012016-10-04 10:27:21 -05001340 r"""
1341 Return a string with a standardized report which includes the caller's
1342 error text, the call stack and the program header.
1343
1344 Description of args:
1345 error_text The error text to be included in the
1346 report. The caller should include any
1347 needed linefeeds.
1348 indent The number of characters to indent each
1349 line of output.
Michael Walshdb6e68a2017-05-23 17:55:31 -05001350 format Long or short format. Long includes
1351 extras like lines of dashes, call stack,
1352 etc.
Michael Walsh7423c012016-10-04 10:27:21 -05001353 """
1354
Michael Walshdb6e68a2017-05-23 17:55:31 -05001355 # Process input.
1356 indent = int(indent)
1357 if format is None:
1358 if robot_env:
1359 format = 'short'
1360 else:
1361 format = 'long'
1362 error_text = error_text.rstrip('\n') + '\n'
1363
1364 if format == 'short':
1365 return sprint_error(error_text)
1366
Michael Walsh7423c012016-10-04 10:27:21 -05001367 buffer = ""
1368 buffer += sprint_dashes(width=120, char="=")
1369 buffer += sprint_error(error_text)
1370 buffer += "\n"
1371 # Calling sprint_call_stack with stack_frame_ix of 0 causes it to show
1372 # itself and this function in the call stack. This is not helpful to a
1373 # debugger and is therefore clutter. We will adjust the stack_frame_ix to
1374 # hide that information.
Michael Walsh9c75f672017-09-12 17:11:35 -05001375 stack_frame_ix = 1
Michael Walsh7423c012016-10-04 10:27:21 -05001376 caller_func_name = sprint_func_name(2)
1377 if caller_func_name.endswith("print_error_report"):
1378 stack_frame_ix += 1
Michael Walsh7bfa9ab2018-11-16 15:24:26 -06001379 buffer += sprint_call_stack(indent, stack_frame_ix)
Michael Walsh7423c012016-10-04 10:27:21 -05001380 buffer += sprint_pgm_header(indent)
1381 buffer += sprint_dashes(width=120, char="=")
1382
1383 return buffer
1384
Michael Walsh7423c012016-10-04 10:27:21 -05001385
Michael Walsh18176322016-11-15 15:11:21 -06001386def sprint_issuing(cmd_buf,
1387 test_mode=0):
Michael Walshde791732016-09-06 14:25:24 -05001388 r"""
1389 Return a line indicating a command that the program is about to execute.
1390
1391 Sample output for a cmd_buf of "ls"
1392
1393 #(CDT) 2016/08/25 17:57:36 - Issuing: ls
Michael Walshbec416d2016-11-10 08:54:52 -06001394
Michael Walshde791732016-09-06 14:25:24 -05001395 Description of args:
1396 cmd_buf The command to be executed by caller.
Michael Walshbec416d2016-11-10 08:54:52 -06001397 test_mode With test_mode set, your output will look
1398 like this:
1399
1400 #(CDT) 2016/08/25 17:57:36 - (test_mode) Issuing: ls
1401
Michael Walshde791732016-09-06 14:25:24 -05001402 """
1403
Michael Walshbec416d2016-11-10 08:54:52 -06001404 buffer = sprint_time()
1405 if test_mode:
1406 buffer += "(test_mode) "
1407 buffer += "Issuing: " + cmd_buf + "\n"
Michael Walshde791732016-09-06 14:25:24 -05001408
1409 return buffer
1410
Michael Walshde791732016-09-06 14:25:24 -05001411
Michael Walshde791732016-09-06 14:25:24 -05001412def sprint_pgm_footer():
Michael Walshde791732016-09-06 14:25:24 -05001413 r"""
1414 Return a standardized footer that programs should print at the end of the
1415 program run. It includes useful information like total run time, etc.
1416 """
1417
1418 buffer = "\n" + sprint_time() + "Finished running " + pgm_name + ".\n\n"
1419
1420 total_time = time.time() - start_time
1421 total_time_string = "%0.6f" % total_time
1422
Michael Walsh7423c012016-10-04 10:27:21 -05001423 buffer += sprint_varx(pgm_name_var_name + "_runtime", total_time_string)
Michael Walshbec416d2016-11-10 08:54:52 -06001424 buffer += "\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001425
1426 return buffer
1427
Michael Walsh7423c012016-10-04 10:27:21 -05001428
Michael Walsh7423c012016-10-04 10:27:21 -05001429def sprint(buffer=""):
Michael Walsh7423c012016-10-04 10:27:21 -05001430 r"""
1431 Simply return the user's buffer. This function is used by the qprint and
1432 dprint functions defined dynamically below, i.e. it would not normally be
1433 called for general use.
1434
1435 Description of arguments.
1436 buffer This will be returned to the caller.
1437 """
Michael Walshde791732016-09-06 14:25:24 -05001438
Michael Walsh95e45102018-02-09 12:44:43 -06001439 try:
1440 return str(buffer)
1441 except UnicodeEncodeError:
1442 return buffer
Michael Walshbec416d2016-11-10 08:54:52 -06001443
Michael Walshbec416d2016-11-10 08:54:52 -06001444
Michael Walshbec416d2016-11-10 08:54:52 -06001445def sprintn(buffer=""):
Michael Walshbec416d2016-11-10 08:54:52 -06001446 r"""
1447 Simply return the user's buffer with a line feed. This function is used
1448 by the qprint and dprint functions defined dynamically below, i.e. it
1449 would not normally be called for general use.
1450
1451 Description of arguments.
1452 buffer This will be returned to the caller.
1453 """
1454
Michael Walsh95e45102018-02-09 12:44:43 -06001455 try:
1456 buffer = str(buffer) + "\n"
1457 except UnicodeEncodeError:
1458 buffer = buffer + "\n"
Michael Walshbec416d2016-11-10 08:54:52 -06001459
Michael Walshde791732016-09-06 14:25:24 -05001460 return buffer
1461
Michael Walsh168eb0f2017-12-01 15:35:32 -06001462
Michael Walshfd2733c2017-11-13 11:36:20 -06001463def gp_print(buffer,
1464 stream='stdout'):
Michael Walshfd2733c2017-11-13 11:36:20 -06001465 r"""
1466 Print the buffer using either sys.stdout.write or BuiltIn().log_to_console
1467 depending on whether we are running in a robot environment.
1468
1469 This function is intended for use only by other functions in this module.
1470
1471 Description of arguments:
1472 buffer The string to be printed.
1473 stream Either "stdout" or "stderr".
1474 """
1475
1476 if robot_env:
1477 BuiltIn().log_to_console(buffer, stream=stream, no_newline=True)
1478 else:
1479 if stream == "stdout":
1480 sys.stdout.write(buffer)
1481 sys.stdout.flush()
1482 else:
1483 sys.stderr.write(buffer)
1484 sys.stderr.flush()
Michael Walshde791732016-09-06 14:25:24 -05001485
1486
Michael Walsh168eb0f2017-12-01 15:35:32 -06001487def gp_log(buffer):
Michael Walsh168eb0f2017-12-01 15:35:32 -06001488 r"""
1489 Log the buffer using either python logging or BuiltIn().log depending on
1490 whether we are running in a robot environment.
1491
1492 This function is intended for use only by other functions in this module.
1493
1494 Description of arguments:
1495 buffer The string to be logged.
1496 """
1497
1498 if robot_env:
1499 BuiltIn().log(buffer)
1500 else:
1501 logging.warning(buffer)
1502
1503
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001504def gp_debug_print(buffer):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001505 r"""
Michael Walshfd2733c2017-11-13 11:36:20 -06001506 Print with gp_print only if gen_print_debug is set.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001507
1508 This function is intended for use only by other functions in this module.
1509
1510 Description of arguments:
1511 buffer The string to be printed.
1512 """
1513
1514 if not gen_print_debug:
1515 return
1516
Michael Walshfd2733c2017-11-13 11:36:20 -06001517 gp_print(buffer)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001518
1519
Michael Walshb1500152017-04-12 15:42:43 -05001520def get_var_value(var_value=None,
1521 default=1,
1522 var_name=None):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001523 r"""
Michael Walshb1500152017-04-12 15:42:43 -05001524 Return either var_value, the corresponding global value or default.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001525
Michael Walshb1500152017-04-12 15:42:43 -05001526 If var_value is not None, it will simply be returned.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001527
Michael Walshb1500152017-04-12 15:42:43 -05001528 If var_value is None, this function will return the corresponding global
1529 value of the variable in question.
1530
1531 Note: For global values, if we are in a robot environment,
1532 get_variable_value will be used. Otherwise, the __builtin__ version of
1533 the variable is returned (which are set by gen_arg.py functions).
1534
1535 If there is no global value associated with the variable, default is
1536 returned.
1537
1538 This function is useful for other functions in setting default values for
1539 parameters.
1540
1541 Example use:
1542
1543 def my_func(quiet=None):
1544
1545 quiet = int(get_var_value(quiet, 0))
1546
1547 Example calls to my_func():
1548
1549 In the following example, the caller is explicitly asking to have quiet be
1550 set to 1.
1551
1552 my_func(quiet=1)
1553
1554 In the following example, quiet will be set to the global value of quiet,
1555 if defined, or to 0 (the default).
1556
1557 my_func()
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001558
1559 Description of arguments:
Michael Walshb1500152017-04-12 15:42:43 -05001560 var_value The value to be returned (if not equal to
1561 None).
1562 default The value that is returned if var_value is
1563 None and there is no corresponding global
1564 value defined.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001565 var_name The name of the variable whose value is to
Michael Walshb1500152017-04-12 15:42:43 -05001566 be returned. Under most circumstances,
1567 this value need not be provided. This
1568 function can figure out the name of the
1569 variable passed as var_value. One
1570 exception to this would be if this
1571 function is called directly from a .robot
1572 file.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001573 """
1574
Michael Walshb1500152017-04-12 15:42:43 -05001575 if var_value is not None:
1576 return var_value
1577
1578 if var_name is None:
1579 var_name = get_arg_name(None, 1, 2)
1580
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001581 if robot_env:
Michael Walshc6537442017-06-06 15:33:52 -05001582 var_value = BuiltIn().get_variable_value("${" + var_name + "}",
1583 default)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001584 else:
1585 var_value = getattr(__builtin__, var_name, default)
1586
1587 return var_value
1588
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001589
Michael Walsh052ff812018-05-18 16:09:09 -05001590def get_stack_var(var_name,
1591 default="",
1592 init_stack_ix=2):
Michael Walsh052ff812018-05-18 16:09:09 -05001593 r"""
1594 Starting with the caller's stack level, search upward in the call stack,
1595 for a variable named var_name and return its value. If the variable
1596 cannot be found, return default.
1597
1598 Example code:
1599
1600 def func12():
1601 my_loc_var1 = get_stack_var('my_var1', "default value")
1602
1603 def func11():
1604 my_var1 = 11
1605 func12()
1606
1607 In this example, get_stack_var will find the value of my_var1 in func11's
1608 stack and will therefore return the value 11. Therefore, my_loc_var1
1609 would get set to 11.
1610
1611 Description of argument(s):
1612 var_name The name of the variable to be searched
1613 for.
1614 default The value to return if the the variable
1615 cannot be found.
1616 init_stack_ix The initial stack index from which to
1617 begin the search. 0 would be the index of
1618 this func1tion ("get_stack_var"), 1 would
1619 be the index of the function calling this
1620 function, etc.
1621 """
1622
1623 return next((frame[0].f_locals[var_name]
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001624 for frame in inspect.stack()[init_stack_ix:]
1625 if var_name in frame[0].f_locals), default)
Michael Walsh052ff812018-05-18 16:09:09 -05001626
1627
Michael Walsh82acf002017-05-04 14:33:05 -05001628# hidden_text is a list of passwords which are to be replaced with asterisks
1629# by print functions defined in this module.
1630hidden_text = []
1631# password_regex is created based on the contents of hidden_text.
1632password_regex = ""
1633
1634
Michael Walsh82acf002017-05-04 14:33:05 -05001635def register_passwords(*args):
Michael Walsh82acf002017-05-04 14:33:05 -05001636 r"""
1637 Register one or more passwords which are to be hidden in output produced
1638 by the print functions in this module.
1639
1640 Note: Blank password values are NOT registered. They are simply ignored.
1641
1642 Description of argument(s):
1643 args One or more password values. If a given
1644 password value is already registered, this
1645 function will simply do nothing.
1646 """
1647
1648 global hidden_text
1649 global password_regex
1650
1651 for password in args:
1652 if password == "":
1653 break
1654 if password in hidden_text:
1655 break
1656
1657 # Place the password into the hidden_text list.
1658 hidden_text.append(password)
1659 # Create a corresponding password regular expression. Escape regex
1660 # special characters too.
1661 password_regex = '(' +\
1662 '|'.join([re.escape(x) for x in hidden_text]) + ')'
1663
Michael Walsh82acf002017-05-04 14:33:05 -05001664
Michael Walsh82acf002017-05-04 14:33:05 -05001665def replace_passwords(buffer):
Michael Walsh82acf002017-05-04 14:33:05 -05001666 r"""
1667 Return the buffer but with all registered passwords replaced by a string
1668 of asterisks.
1669
1670
1671 Description of argument(s):
1672 buffer The string to be returned but with
1673 passwords replaced.
1674 """
1675
1676 global password_regex
1677
1678 if int(os.environ.get("DEBUG_SHOW_PASSWORDS", "0")):
1679 return buffer
1680
1681 if password_regex == "":
1682 # No passwords to replace.
1683 return buffer
1684
1685 return re.sub(password_regex, "********", buffer)
1686
Michael Walshfd2733c2017-11-13 11:36:20 -06001687
1688def create_print_wrapper_funcs(func_names,
1689 stderr_func_names,
1690 replace_dict):
Michael Walshfd2733c2017-11-13 11:36:20 -06001691 r"""
1692 Generate code for print wrapper functions and return the generated code as
1693 a string.
1694
1695 To illustrate, suppose there is a "print_foo_bar" function in the
1696 func_names list.
1697 This function will...
1698 - Expect that there is an sprint_foo_bar function already in existence.
1699 - Create a print_foo_bar function which calls sprint_foo_bar and prints
Michael Walshfaafa9c2018-06-27 16:39:31 -05001700 the result.
Michael Walshfd2733c2017-11-13 11:36:20 -06001701 - Create a qprint_foo_bar function which calls upon sprint_foo_bar only if
Michael Walshfaafa9c2018-06-27 16:39:31 -05001702 global value quiet is 0.
Michael Walshfd2733c2017-11-13 11:36:20 -06001703 - Create a dprint_foo_bar function which calls upon sprint_foo_bar only if
Michael Walshfaafa9c2018-06-27 16:39:31 -05001704 global value debug is 1.
Michael Walshfd2733c2017-11-13 11:36:20 -06001705
1706 Also, code will be generated to define aliases for each function as well.
1707 Each alias will be created by replacing "print_" in the function name with
1708 "p" For example, the alias for print_foo_bar will be pfoo_bar.
1709
1710 Description of argument(s):
1711 func_names A list of functions for which print
1712 wrapper function code is to be generated.
1713 stderr_func_names A list of functions whose generated code
1714 should print to stderr rather than to
1715 stdout.
1716 replace_dict Please see the create_func_def_string
1717 function in wrap_utils.py for details on
1718 this parameter. This parameter will be
1719 passed directly to create_func_def_string.
1720 """
1721
1722 buffer = ""
1723
1724 for func_name in func_names:
1725 if func_name in stderr_func_names:
1726 replace_dict['output_stream'] = "stderr"
1727 else:
1728 replace_dict['output_stream'] = "stdout"
1729
1730 s_func_name = "s" + func_name
1731 q_func_name = "q" + func_name
1732 d_func_name = "d" + func_name
1733
1734 # We don't want to try to redefine the "print" function, thus the
1735 # following if statement.
1736 if func_name != "print":
1737 func_def = create_func_def_string(s_func_name, func_name,
1738 print_func_template,
1739 replace_dict)
1740 buffer += func_def
1741
1742 func_def = create_func_def_string(s_func_name, "q" + func_name,
1743 qprint_func_template, replace_dict)
1744 buffer += func_def
1745
1746 func_def = create_func_def_string(s_func_name, "d" + func_name,
1747 dprint_func_template, replace_dict)
1748 buffer += func_def
1749
Michael Walsh168eb0f2017-12-01 15:35:32 -06001750 func_def = create_func_def_string(s_func_name, "l" + func_name,
1751 lprint_func_template, replace_dict)
1752 buffer += func_def
1753
Michael Walshfd2733c2017-11-13 11:36:20 -06001754 # Create abbreviated aliases (e.g. spvar is an alias for sprint_var).
1755 alias = re.sub("print_", "p", func_name)
1756 alias = re.sub("print", "p", alias)
Michael Walsh168eb0f2017-12-01 15:35:32 -06001757 prefixes = ["", "s", "q", "d", "l"]
Michael Walshfd2733c2017-11-13 11:36:20 -06001758 for prefix in prefixes:
1759 if alias == "p":
1760 continue
1761 func_def = prefix + alias + " = " + prefix + func_name
1762 buffer += func_def + "\n"
1763
1764 return buffer
Michael Walsh82acf002017-05-04 14:33:05 -05001765
1766
Michael Walshde791732016-09-06 14:25:24 -05001767# In the following section of code, we will dynamically create print versions
1768# for each of the sprint functions defined above. So, for example, where we
1769# have an sprint_time() function defined above that returns the time to the
Michael Walsh7423c012016-10-04 10:27:21 -05001770# caller in a string, we will create a corresponding print_time() function
1771# that will print that string directly to stdout.
Michael Walshde791732016-09-06 14:25:24 -05001772
Michael Walshfd2733c2017-11-13 11:36:20 -06001773# It can be complicated to follow what's being created by below. Here is an
1774# example of the print_time() function that will be created:
Michael Walshde791732016-09-06 14:25:24 -05001775
Michael Walshfd2733c2017-11-13 11:36:20 -06001776# def print_time(buffer=''):
1777# sys.stdout.write(replace_passwords(sprint_time(buffer=buffer)))
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001778# sys.stdout.flush()
Michael Walshde791732016-09-06 14:25:24 -05001779
Michael Walshfd2733c2017-11-13 11:36:20 -06001780# Templates for the various print wrapper functions.
1781print_func_template = \
1782 [
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001783 " <mod_qualifier>gp_print(<mod_qualifier>replace_passwords("
1784 + "<call_line>), stream='<output_stream>')"
Michael Walshfd2733c2017-11-13 11:36:20 -06001785 ]
1786
1787qprint_func_template = \
1788 [
Michael Walsh589ae762019-03-19 13:22:39 -05001789 " quiet_default = <mod_qualifier>get_var_value(None, 0, \"quiet\")",
1790 " quiet = <mod_qualifier>get_stack_var(\"quiet\", quiet_default)",
1791 " if int(quiet): return"
Michael Walshfd2733c2017-11-13 11:36:20 -06001792 ] + print_func_template
1793
1794dprint_func_template = \
1795 [
Michael Walsh589ae762019-03-19 13:22:39 -05001796 " debug_default = <mod_qualifier>get_var_value(None, 0, \"debug\")",
1797 " debug = <mod_qualifier>get_stack_var(\"debug\", debug_default)",
1798 " if not int(debug): return"
Michael Walshfd2733c2017-11-13 11:36:20 -06001799 ] + print_func_template
1800
Michael Walsh168eb0f2017-12-01 15:35:32 -06001801lprint_func_template = \
1802 [
Michael Walsh4fea2cf2018-08-22 17:48:18 -05001803 " global sprint_time_last_seconds",
1804 " global last_seconds_ix",
1805 " if len(sprint_time_last_seconds) <= lprint_last_seconds_ix():",
1806 " sprint_time_last_seconds.append(start_time)",
1807 " save_last_seconds_ix = last_seconds_ix",
1808 " last_seconds_ix = lprint_last_seconds_ix()",
1809 " gp_log(<mod_qualifier>replace_passwords(<call_line>))",
1810 " last_seconds_ix = save_last_seconds_ix",
Michael Walsh168eb0f2017-12-01 15:35:32 -06001811 ]
1812
Michael Walsh81c02342018-01-05 15:43:28 -06001813replace_dict = {'output_stream': 'stdout', 'mod_qualifier': ''}
Michael Walshfd2733c2017-11-13 11:36:20 -06001814
1815
1816gp_debug_print("robot_env: " + str(robot_env))
Michael Walshde791732016-09-06 14:25:24 -05001817
1818# func_names contains a list of all print functions which should be created
1819# from their sprint counterparts.
1820func_names = ['print_time', 'print_timen', 'print_error', 'print_varx',
Michael Walsh18176322016-11-15 15:11:21 -06001821 'print_var', 'print_vars', 'print_dashes', 'indent',
1822 'print_call_stack', 'print_func_name', 'print_executing',
1823 'print_pgm_header', 'print_issuing', 'print_pgm_footer',
1824 'print_error_report', 'print', 'printn']
Michael Walshde791732016-09-06 14:25:24 -05001825
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001826# stderr_func_names is a list of functions whose output should go to stderr
1827# rather than stdout.
1828stderr_func_names = ['print_error', 'print_error_report']
Michael Walshde791732016-09-06 14:25:24 -05001829
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001830
Michael Walshfd2733c2017-11-13 11:36:20 -06001831func_defs = create_print_wrapper_funcs(func_names, stderr_func_names,
1832 replace_dict)
1833gp_debug_print(func_defs)
1834exec(func_defs)