blob: 081e8f9a67f72b787d4e9c2868ce0a1be460a925 [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
368 # line?
369 line_indent = get_line_indent(source_lines[start_line_ix - 1])
370 if line_indent < start_indent:
371 start_line_ix -= 1
372 # Remove the backslash (continuation char).
373 source_lines[start_line_ix] = re.sub(r"[ ]*\\([\r\n]$)",
374 " \\1",
375 source_lines[start_line_ix])
Michael Walsh23e7f492017-01-10 11:34:47 -0600376
377 # Join the start line through the end line into a composite line.
378 composite_line = ''.join(map(str.strip,
Gunnar Mills096cd562018-03-26 10:19:12 -0500379 source_lines[start_line_ix:end_line_ix + 1]))
Michael Walsh1173a522018-05-21 17:24:51 -0500380 # Insert one space after first "=" if there isn't one already.
381 composite_line = re.sub("=[ ]*([^ ])", "= \\1", composite_line, 1)
Michael Walsh7423c012016-10-04 10:27:21 -0500382
Michael Walsh3f248272018-06-01 13:59:35 -0500383 lvalue_regex = "[ ]*=[ ]+" + func_name_regex + ".*"
Michael Walsh1173a522018-05-21 17:24:51 -0500384 lvalue_string = re.sub(lvalue_regex, "", composite_line)
Michael Walsh3f248272018-06-01 13:59:35 -0500385 if lvalue_string == composite_line:
386 # i.e. the regex did not match so there are no lvalues.
387 lvalue_string = ""
Michael Walsh37762f92018-08-07 14:59:18 -0500388 lvalues_list = list(filter(None, map(str.strip, lvalue_string.split(","))))
Michael Walsh3f248272018-06-01 13:59:35 -0500389 try:
390 lvalues = collections.OrderedDict()
391 except AttributeError:
392 # A non-ordered dict doesn't look as nice when printed but it will do.
393 lvalues = {}
Michael Walsh1173a522018-05-21 17:24:51 -0500394 ix = len(lvalues_list) * -1
395 for lvalue in lvalues_list:
396 lvalues[ix] = lvalue
397 ix += 1
Michael Walsh3f248272018-06-01 13:59:35 -0500398 lvalue_prefix_regex = "(.*=[ ]+)?"
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500399 called_func_name_regex = lvalue_prefix_regex + func_name_regex +\
400 "[ ]*\\(.*"
Michael Walsh3f248272018-06-01 13:59:35 -0500401 called_func_name = re.sub(called_func_name_regex, "\\4", composite_line)
Michael Walsh1173a522018-05-21 17:24:51 -0500402 arg_list_etc = "(" + re.sub(pre_args_regex, "", composite_line)
Michael Walshde791732016-09-06 14:25:24 -0500403 if local_debug:
Michael Walsh23e7f492017-01-10 11:34:47 -0600404 print_varx("aliases", aliases, 0, debug_indent)
Michael Walsh3f248272018-06-01 13:59:35 -0500405 print_varx("import_name_regex", import_name_regex, 0, debug_indent)
406 print_varx("func_name_regex", func_name_regex, 0, debug_indent)
Michael Walsh1173a522018-05-21 17:24:51 -0500407 print_varx("pre_args_regex", pre_args_regex, 0, debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600408 print_varx("start_line_ix", start_line_ix, 0, debug_indent)
409 print_varx("end_line_ix", end_line_ix, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500410 print_varx("composite_line", composite_line, 0, debug_indent)
Michael Walsh1173a522018-05-21 17:24:51 -0500411 print_varx("lvalue_regex", lvalue_regex, 0, debug_indent)
412 print_varx("lvalue_string", lvalue_string, 0, debug_indent)
413 print_varx("lvalues", lvalues, 0, debug_indent)
Michael Walsh3f248272018-06-01 13:59:35 -0500414 print_varx("called_func_name_regex", called_func_name_regex, 0,
415 debug_indent)
Michael Walsh1173a522018-05-21 17:24:51 -0500416 print_varx("called_func_name", called_func_name, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500417 print_varx("arg_list_etc", arg_list_etc, 0, debug_indent)
418
419 # Parse arg list...
420 # Initialize...
421 nest_level = -1
422 arg_ix = 0
Michael Walsh7423c012016-10-04 10:27:21 -0500423 args_list = [""]
Michael Walshde791732016-09-06 14:25:24 -0500424 for ix in range(0, len(arg_list_etc)):
425 char = arg_list_etc[ix]
426 # Set the nest_level based on whether we've encounted a parenthesis.
427 if char == "(":
428 nest_level += 1
429 if nest_level == 0:
430 continue
431 elif char == ")":
432 nest_level -= 1
433 if nest_level < 0:
434 break
435
436 # If we reach a comma at base nest level, we are done processing an
Michael Walsh7423c012016-10-04 10:27:21 -0500437 # argument so we increment arg_ix and initialize a new args_list entry.
Michael Walshde791732016-09-06 14:25:24 -0500438 if char == "," and nest_level == 0:
439 arg_ix += 1
Michael Walsh7423c012016-10-04 10:27:21 -0500440 args_list.append("")
Michael Walshde791732016-09-06 14:25:24 -0500441 continue
442
Michael Walsh7423c012016-10-04 10:27:21 -0500443 # For any other character, we append it it to the current arg list
Michael Walshde791732016-09-06 14:25:24 -0500444 # entry.
Michael Walsh7423c012016-10-04 10:27:21 -0500445 args_list[arg_ix] += char
Michael Walshde791732016-09-06 14:25:24 -0500446
447 # Trim whitespace from each list entry.
Michael Walsh7423c012016-10-04 10:27:21 -0500448 args_list = [arg.strip() for arg in args_list]
Michael Walshde791732016-09-06 14:25:24 -0500449
Michael Walsh1173a522018-05-21 17:24:51 -0500450 if arg_num < 0:
451 if abs(arg_num) > len(lvalues):
452 argument = lvalues
453 else:
454 argument = lvalues[arg_num]
455 elif arg_num == 0:
456 argument = called_func_name
Michael Walsh2750b442018-05-18 14:49:11 -0500457 else:
Michael Walsh1173a522018-05-21 17:24:51 -0500458 if arg_num > len(args_list):
459 argument = args_list
460 else:
461 argument = args_list[arg_num - 1]
Michael Walshde791732016-09-06 14:25:24 -0500462
463 if local_debug:
Michael Walsh7423c012016-10-04 10:27:21 -0500464 print_varx("args_list", args_list, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500465 print_varx("argument", argument, 0, debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600466 print_dashes(0, 120)
Michael Walshde791732016-09-06 14:25:24 -0500467
468 return argument
469
Michael Walshde791732016-09-06 14:25:24 -0500470
Michael Walshde791732016-09-06 14:25:24 -0500471def sprint_time(buffer=""):
Michael Walshde791732016-09-06 14:25:24 -0500472 r"""
473 Return the time in the following format.
474
475 Example:
476
477 The following python code...
478
479 sys.stdout.write(sprint_time())
480 sys.stdout.write("Hi.\n")
481
482 Will result in the following type of output:
483
484 #(CDT) 2016/07/08 15:25:35 - Hi.
485
486 Example:
487
488 The following python code...
489
490 sys.stdout.write(sprint_time("Hi.\n"))
491
492 Will result in the following type of output:
493
494 #(CDT) 2016/08/03 17:12:05 - Hi.
495
496 The following environment variables will affect the formatting as
497 described:
498 NANOSECONDS This will cause the time stamps to be
499 precise to the microsecond (Yes, it
500 probably should have been named
501 MICROSECONDS but the convention was set
502 long ago so we're sticking with it).
503 Example of the output when environment
504 variable NANOSECONDS=1.
505
506 #(CDT) 2016/08/03 17:16:25.510469 - Hi.
507
508 SHOW_ELAPSED_TIME This will cause the elapsed time to be
509 included in the output. This is the
510 amount of time that has elapsed since the
511 last time this function was called. The
512 precision of the elapsed time field is
513 also affected by the value of the
514 NANOSECONDS environment variable. Example
515 of the output when environment variable
516 NANOSECONDS=0 and SHOW_ELAPSED_TIME=1.
517
518 #(CDT) 2016/08/03 17:17:40 - 0 - Hi.
519
520 Example of the output when environment variable NANOSECONDS=1 and
521 SHOW_ELAPSED_TIME=1.
522
523 #(CDT) 2016/08/03 17:18:47.317339 - 0.000046 - Hi.
524
525 Description of arguments.
526 buffer This will be appended to the formatted
527 time string.
528 """
529
530 global NANOSECONDS
531 global SHOW_ELAPSED_TIME
532 global sprint_time_last_seconds
Michael Walsh4fea2cf2018-08-22 17:48:18 -0500533 global last_seconds_ix
Michael Walshde791732016-09-06 14:25:24 -0500534
535 seconds = time.time()
536 loc_time = time.localtime(seconds)
537 nanoseconds = "%0.6f" % seconds
538 pos = nanoseconds.find(".")
539 nanoseconds = nanoseconds[pos:]
540
541 time_string = time.strftime("#(%Z) %Y/%m/%d %H:%M:%S", loc_time)
542 if NANOSECONDS == "1":
543 time_string = time_string + nanoseconds
544
545 if SHOW_ELAPSED_TIME == "1":
546 cur_time_seconds = seconds
547 math_string = "%9.9f" % cur_time_seconds + " - " + "%9.9f" % \
Michael Walsh4fea2cf2018-08-22 17:48:18 -0500548 sprint_time_last_seconds[last_seconds_ix]
Michael Walshde791732016-09-06 14:25:24 -0500549 elapsed_seconds = eval(math_string)
550 if NANOSECONDS == "1":
551 elapsed_seconds = "%11.6f" % elapsed_seconds
552 else:
553 elapsed_seconds = "%4i" % elapsed_seconds
Michael Walsh4fea2cf2018-08-22 17:48:18 -0500554 sprint_time_last_seconds[last_seconds_ix] = cur_time_seconds
Michael Walshde791732016-09-06 14:25:24 -0500555 time_string = time_string + " - " + elapsed_seconds
556
557 return time_string + " - " + buffer
558
Michael Walshde791732016-09-06 14:25:24 -0500559
Michael Walshde791732016-09-06 14:25:24 -0500560def sprint_timen(buffer=""):
Michael Walshde791732016-09-06 14:25:24 -0500561 r"""
562 Append a line feed to the buffer, pass it to sprint_time and return the
563 result.
564 """
565
566 return sprint_time(buffer + "\n")
567
Michael Walshde791732016-09-06 14:25:24 -0500568
Michael Walshde791732016-09-06 14:25:24 -0500569def sprint_error(buffer=""):
Michael Walshde791732016-09-06 14:25:24 -0500570 r"""
571 Return a standardized error string. This includes:
572 - A time stamp
573 - The "**ERROR**" string
574 - The caller's buffer string.
575
576 Example:
577
578 The following python code...
579
580 print(sprint_error("Oops.\n"))
581
582 Will result in the following type of output:
583
584 #(CDT) 2016/08/03 17:12:05 - **ERROR** Oops.
585
586 Description of arguments.
587 buffer This will be appended to the formatted
588 error string.
589 """
590
591 return sprint_time() + "**ERROR** " + buffer
592
Michael Walshde791732016-09-06 14:25:24 -0500593
Michael Walsh3f248272018-06-01 13:59:35 -0500594# Implement "constants" with functions.
595def digit_length_in_bits():
596 r"""
597 Return the digit length in bits.
598 """
599
600 return 4
601
602
603def word_length_in_digits():
604 r"""
605 Return the word length in digits.
606 """
607
608 return 8
609
610
611def bit_length(number):
612 r"""
613 Return the bit length of the number.
614
615 Description of argument(s):
616 number The number to be analyzed.
617 """
618
619 if number < 0:
620 # Convert negative numbers to positive and subtract one. The
621 # following example illustrates the reason for this:
622 # Consider a single nibble whose signed values can range from -8 to 7
623 # (0x8 to 0x7). A value of 0x7 equals 0b0111. Therefore, its length
624 # in bits is 3. Since the negative bit (i.e. 0b1000) is not set, the
625 # value 7 clearly will fit in one nibble. With -8 = 0x8 = 0b1000, you
626 # have the smallest negative value that will fit. Note that it
627 # requires 3 bits of 0. So by converting a number value of -8 to a
628 # working_number of 7, this function can accurately calculate the
629 # number of bits and therefore nibbles required to represent the
630 # number in print.
631 working_number = abs(number) - 1
632 else:
633 working_number = number
634
635 # Handle the special case of the number 0.
636 if working_number == 0:
637 return 0
638
639 return len(bin(working_number)) - 2
640
641
642def get_req_num_hex_digits(number):
643 r"""
644 Return the required number of hex digits required to display the given
645 number.
646
647 The returned value will always be rounded up to the nearest multiple of 8.
648
649 Description of argument(s):
650 number The number to be analyzed.
651 """
652
653 if number < 0:
654 # Convert negative numbers to positive and subtract one. The
655 # following example illustrates the reason for this:
656 # Consider a single nibble whose signed values can range from -8 to 7
657 # (0x8 to 0x7). A value of 0x7 equals 0b0111. Therefore, its length
658 # in bits is 3. Since the negative bit (i.e. 0b1000) is not set, the
659 # value 7 clearly will fit in one nibble. With -8 = 0x8 = 0b1000, you
660 # have the smallest negative value that will fit. Note that it
661 # requires 3 bits of 0. So by converting a number value of -8 to a
662 # working_number of 7, this function can accurately calculate the
663 # number of bits and therefore nibbles required to represent the
664 # number in print.
665 working_number = abs(number) - 1
666 else:
667 working_number = number
668
669 # Handle the special case of the number 0.
670 if working_number == 0:
671 return word_length_in_digits()
672
673 num_length_in_bits = bit_length(working_number)
674 num_hex_digits, remainder = divmod(num_length_in_bits,
675 digit_length_in_bits())
676 if remainder > 0:
677 # Example: the number 7 requires 3 bits. The divmod above produces,
678 # 0 with remainder of 3. So because we have a remainder, we increment
679 # num_hex_digits from 0 to 1.
680 num_hex_digits += 1
681
682 # Check to see whether the negative bit is set. This is the left-most
683 # bit in the highest order digit.
684 negative_mask = 2 ** (num_hex_digits * 4 - 1)
685 if working_number & negative_mask:
686 # If a number that is intended to be positive has its negative bit
687 # on, an additional digit will be required to represent it correctly
688 # in print.
689 num_hex_digits += 1
690
691 num_words, remainder = divmod(num_hex_digits, word_length_in_digits())
692 if remainder > 0 or num_words == 0:
693 num_words += 1
694
695 # Round up to the next word length in digits.
696 return num_words * word_length_in_digits()
697
698
699def dft_num_hex_digits():
700 r"""
701 Return the default number of hex digits to be used to represent a hex
702 number in print.
703
704 The value returned is a function of sys.maxsize.
705 """
706
707 global _gen_print_dft_num_hex_digits_
708 try:
709 return _gen_print_dft_num_hex_digits_
710 except NameError:
711 _gen_print_dft_num_hex_digits_ = get_req_num_hex_digits(sys.maxsize)
712 return _gen_print_dft_num_hex_digits_
713
714
Michael Walshde791732016-09-06 14:25:24 -0500715def sprint_varx(var_name,
716 var_value,
717 hex=0,
718 loc_col1_indent=col1_indent,
Michael Walsh7423c012016-10-04 10:27:21 -0500719 loc_col1_width=col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500720 trailing_char="\n",
721 key_list=None):
Michael Walshde791732016-09-06 14:25:24 -0500722 r"""
723 Print the var name/value passed to it. If the caller lets loc_col1_width
724 default, the printing lines up nicely with output generated by the
725 print_time functions.
726
727 Note that the sprint_var function (defined below) can be used to call this
728 function so that the programmer does not need to pass the var_name.
729 sprint_var will figure out the var_name. The sprint_var function is the
730 one that would normally be used by the general user.
731
732 For example, the following python code:
733
734 first_name = "Mike"
735 print_time("Doing this...\n")
736 print_varx("first_name", first_name)
737 print_time("Doing that...\n")
738
739 Will generate output like this:
740
741 #(CDT) 2016/08/10 17:34:42.847374 - 0.001285 - Doing this...
742 first_name: Mike
743 #(CDT) 2016/08/10 17:34:42.847510 - 0.000136 - Doing that...
744
745 This function recognizes several complex types of data such as dict, list
746 or tuple.
747
748 For example, the following python code:
749
750 my_dict = dict(one=1, two=2, three=3)
751 print_var(my_dict)
752
753 Will generate the following output:
754
755 my_dict:
756 my_dict[three]: 3
757 my_dict[two]: 2
758 my_dict[one]: 1
759
760 Description of arguments.
761 var_name The name of the variable to be printed.
762 var_value The value of the variable to be printed.
763 hex This indicates that the value should be
764 printed in hex format. It is the user's
765 responsibility to ensure that a var_value
Michael Walshbec416d2016-11-10 08:54:52 -0600766 contains a valid hex number. For string
767 var_values, this will be interpreted as
768 show_blanks which means that blank values
Michael Walshd995cb02017-02-07 14:46:01 -0600769 will be printed as "<blank>". For dict
770 var_values, this will be interpreted as
771 terse format where keys are not repeated
772 in the output.
Michael Walshde791732016-09-06 14:25:24 -0500773 loc_col1_indent The number of spaces to indent the output.
774 loc_col1_width The width of the output column containing
775 the variable name. The default value of
776 this is adjusted so that the var_value
777 lines up with text printed via the
778 print_time function.
Michael Walsh7423c012016-10-04 10:27:21 -0500779 trailing_char The character to be used at the end of the
780 returned string. The default value is a
781 line feed.
Michael Walshd2869032018-03-22 16:12:11 -0500782 key_list A list of which dictionary keys should be
783 printed. All others keys will be skipped.
784 Each value in key_list will be regarded
785 as a regular expression and it will be
786 regarded as anchored to the beginning and
787 ends of the dictionary key being
788 referenced. For example if key_list is
789 ["one", "two"], the resulting regex used
790 will be "^one|two$", i.e. only keys "one"
791 and "two" from the var_value dictionary
792 will be printed. As another example, if
793 the caller were to specify a key_list of
794 ["one.*"], then only dictionary keys whose
795 names begin with "one" will be printed.
796 Note: This argument pertains only to
797 var_values which are dictionaries.
Michael Walsh7423c012016-10-04 10:27:21 -0500798 """
Michael Walshde791732016-09-06 14:25:24 -0500799
800 # Determine the type
Michael Walsh37762f92018-08-07 14:59:18 -0500801 try:
802 int_types = (int, long)
803 except NameError:
804 int_types = (int,)
805 try:
806 string_types = (str, unicode)
807 except NameError:
808 string_types = (str,)
809 simple_types = int_types + string_types + (float, bool)
810 if type(var_value) in simple_types \
Michael Walshde791732016-09-06 14:25:24 -0500811 or var_value is None:
812 # The data type is simple in the sense that it has no subordinate
813 # parts.
Michael Walsh7423c012016-10-04 10:27:21 -0500814 # Adjust loc_col1_width.
815 loc_col1_width = loc_col1_width - loc_col1_indent
Michael Walshde791732016-09-06 14:25:24 -0500816 # See if the user wants the output in hex format.
817 if hex:
Michael Walsh37762f92018-08-07 14:59:18 -0500818 if type(var_value) not in int_types:
Michael Walshbec416d2016-11-10 08:54:52 -0600819 value_format = "%s"
Michael Walsh2795edc2016-12-13 16:00:33 -0600820 if var_value == "":
Michael Walshbec416d2016-11-10 08:54:52 -0600821 var_value = "<blank>"
822 else:
Michael Walsh3f248272018-06-01 13:59:35 -0500823 num_hex_digits = max(dft_num_hex_digits(),
824 get_req_num_hex_digits(var_value))
825 # Convert a negative number to its positive twos complement
826 # for proper printing. For example, instead of printing -1 as
827 # "0x-000000000000001" it will be printed as
828 # "0xffffffffffffffff".
829 var_value = var_value & (2 ** (num_hex_digits * 4) - 1)
830 value_format = "0x%0" + str(num_hex_digits) + "x"
Michael Walshde791732016-09-06 14:25:24 -0500831 else:
832 value_format = "%s"
833 format_string = "%" + str(loc_col1_indent) + "s%-" \
Michael Walsh7423c012016-10-04 10:27:21 -0500834 + str(loc_col1_width) + "s" + value_format + trailing_char
Michael Walsh3383e652017-09-01 17:10:59 -0500835 if value_format == "0x%08x":
836 return format_string % ("", str(var_name) + ":",
837 var_value & 0xffffffff)
838 else:
839 return format_string % ("", str(var_name) + ":", var_value)
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500840 elif isinstance(var_value, type):
Michael Walsh20a87ab2017-06-30 17:00:30 -0500841 return sprint_varx(var_name, str(var_value).split("'")[1], hex,
Michael Walshd2869032018-03-22 16:12:11 -0500842 loc_col1_indent, loc_col1_width, trailing_char,
843 key_list)
Michael Walshde791732016-09-06 14:25:24 -0500844 else:
845 # The data type is complex in the sense that it has subordinate parts.
846 format_string = "%" + str(loc_col1_indent) + "s%s\n"
847 buffer = format_string % ("", var_name + ":")
848 loc_col1_indent += 2
Michael Walsh7423c012016-10-04 10:27:21 -0500849 try:
850 length = len(var_value)
851 except TypeError:
Michael Walsh23e7f492017-01-10 11:34:47 -0600852 length = 0
Michael Walsh7423c012016-10-04 10:27:21 -0500853 ix = 0
854 loc_trailing_char = "\n"
Michael Walshbec416d2016-11-10 08:54:52 -0600855 type_is_dict = 0
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500856 if isinstance(var_value, dict):
Michael Walsh23e7f492017-01-10 11:34:47 -0600857 type_is_dict = 1
Michael Walsh8e6deb42017-01-27 14:22:41 -0600858 try:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500859 if isinstance(var_value, collections.OrderedDict):
Michael Walsh8e6deb42017-01-27 14:22:41 -0600860 type_is_dict = 1
861 except AttributeError:
862 pass
863 try:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500864 if isinstance(var_value, DotDict):
Michael Walsh8e6deb42017-01-27 14:22:41 -0600865 type_is_dict = 1
866 except NameError:
867 pass
868 try:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500869 if isinstance(var_value, NormalizedDict):
Michael Walsh8e6deb42017-01-27 14:22:41 -0600870 type_is_dict = 1
871 except NameError:
872 pass
Michael Walshbec416d2016-11-10 08:54:52 -0600873 if type_is_dict:
Michael Walsh37762f92018-08-07 14:59:18 -0500874 for key, value in var_value.items():
Michael Walshd2869032018-03-22 16:12:11 -0500875 if key_list is not None:
876 key_list_regex = "^" + "|".join(key_list) + "$"
877 if not re.match(key_list_regex, key):
878 continue
Michael Walsh7423c012016-10-04 10:27:21 -0500879 ix += 1
880 if ix == length:
881 loc_trailing_char = trailing_char
Michael Walshd995cb02017-02-07 14:46:01 -0600882 if hex:
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600883 # Since hex is being used as a format type, we want it
884 # turned off when processing integer dictionary values so
885 # it is not interpreted as a hex indicator.
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500886 loc_hex = not (isinstance(value, int))
Michael Walshf7b8a002017-08-29 10:38:39 -0500887 buffer += sprint_varx("[" + key + "]", value,
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600888 loc_hex, loc_col1_indent,
889 loc_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500890 loc_trailing_char,
891 key_list)
Michael Walshd995cb02017-02-07 14:46:01 -0600892 else:
Michael Walsh1173a522018-05-21 17:24:51 -0500893 buffer += sprint_varx(var_name + "[" + str(key) + "]",
894 value, hex, loc_col1_indent,
895 loc_col1_width, loc_trailing_char,
896 key_list)
Michael Walsh7423c012016-10-04 10:27:21 -0500897 elif type(var_value) in (list, tuple, set):
Michael Walshde791732016-09-06 14:25:24 -0500898 for key, value in enumerate(var_value):
Michael Walsh7423c012016-10-04 10:27:21 -0500899 ix += 1
900 if ix == length:
901 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500902 buffer += sprint_varx(var_name + "[" + str(key) + "]", value,
Michael Walsh7423c012016-10-04 10:27:21 -0500903 hex, loc_col1_indent, loc_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500904 loc_trailing_char, key_list)
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500905 elif isinstance(var_value, argparse.Namespace):
Michael Walshde791732016-09-06 14:25:24 -0500906 for key in var_value.__dict__:
Michael Walsh7423c012016-10-04 10:27:21 -0500907 ix += 1
908 if ix == length:
909 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500910 cmd_buf = "buffer += sprint_varx(var_name + \".\" + str(key)" \
Michael Walsh7423c012016-10-04 10:27:21 -0500911 + ", var_value." + key + ", hex, loc_col1_indent," \
Michael Walshd2869032018-03-22 16:12:11 -0500912 + " loc_col1_width, loc_trailing_char, key_list)"
Michael Walshde791732016-09-06 14:25:24 -0500913 exec(cmd_buf)
914 else:
915 var_type = type(var_value).__name__
916 func_name = sys._getframe().f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -0500917 var_value = "<" + var_type + " type not supported by " + \
918 func_name + "()>"
Michael Walshde791732016-09-06 14:25:24 -0500919 value_format = "%s"
920 loc_col1_indent -= 2
Michael Walsh7423c012016-10-04 10:27:21 -0500921 # Adjust loc_col1_width.
922 loc_col1_width = loc_col1_width - loc_col1_indent
Michael Walshde791732016-09-06 14:25:24 -0500923 format_string = "%" + str(loc_col1_indent) + "s%-" \
Michael Walsh7423c012016-10-04 10:27:21 -0500924 + str(loc_col1_width) + "s" + value_format + trailing_char
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600925 return format_string % ("", str(var_name) + ":", var_value)
Michael Walsh23e7f492017-01-10 11:34:47 -0600926
Michael Walshde791732016-09-06 14:25:24 -0500927 return buffer
928
929 return ""
930
Michael Walshde791732016-09-06 14:25:24 -0500931
Michael Walshfd2733c2017-11-13 11:36:20 -0600932def sprint_var(var_value,
933 hex=0,
934 loc_col1_indent=col1_indent,
935 loc_col1_width=col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500936 trailing_char="\n",
937 key_list=None):
Michael Walshde791732016-09-06 14:25:24 -0500938 r"""
939 Figure out the name of the first argument for you and then call
940 sprint_varx with it. Therefore, the following 2 calls are equivalent:
941 sprint_varx("var1", var1)
942 sprint_var(var1)
943 """
944
945 # Get the name of the first variable passed to this function.
946 stack_frame = 2
Michael Walsh7423c012016-10-04 10:27:21 -0500947 caller_func_name = sprint_func_name(2)
948 if caller_func_name.endswith("print_var"):
Michael Walshde791732016-09-06 14:25:24 -0500949 stack_frame += 1
950 var_name = get_arg_name(None, 1, stack_frame)
Michael Walshfd2733c2017-11-13 11:36:20 -0600951 return sprint_varx(var_name, var_value=var_value, hex=hex,
952 loc_col1_indent=loc_col1_indent,
953 loc_col1_width=loc_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500954 trailing_char=trailing_char,
955 key_list=key_list)
Michael Walshde791732016-09-06 14:25:24 -0500956
957
Michael Walsh18176322016-11-15 15:11:21 -0600958def sprint_vars(*args):
Michael Walsh18176322016-11-15 15:11:21 -0600959 r"""
960 Sprint the values of one or more variables.
961
962 Description of args:
963 args:
964 If the first argument is an integer, it will be interpreted to be the
965 "indent" value.
966 If the second argument is an integer, it will be interpreted to be the
967 "col1_width" value.
968 If the third argument is an integer, it will be interpreted to be the
969 "hex" value.
970 All remaining parms are considered variable names which are to be
971 sprinted.
972 """
973
974 if len(args) == 0:
975 return
976
977 # Get the name of the first variable passed to this function.
978 stack_frame = 2
979 caller_func_name = sprint_func_name(2)
980 if caller_func_name.endswith("print_vars"):
981 stack_frame += 1
982
983 parm_num = 1
984
985 # Create list from args (which is a tuple) so that it can be modified.
986 args_list = list(args)
987
988 var_name = get_arg_name(None, parm_num, stack_frame)
989 # See if parm 1 is to be interpreted as "indent".
990 try:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500991 if isinstance(int(var_name), int):
Michael Walsh18176322016-11-15 15:11:21 -0600992 indent = int(var_name)
993 args_list.pop(0)
994 parm_num += 1
995 except ValueError:
996 indent = 0
997
998 var_name = get_arg_name(None, parm_num, stack_frame)
999 # See if parm 1 is to be interpreted as "col1_width".
1000 try:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001001 if isinstance(int(var_name), int):
Michael Walsh18176322016-11-15 15:11:21 -06001002 loc_col1_width = int(var_name)
1003 args_list.pop(0)
1004 parm_num += 1
1005 except ValueError:
1006 loc_col1_width = col1_width
1007
1008 var_name = get_arg_name(None, parm_num, stack_frame)
1009 # See if parm 1 is to be interpreted as "hex".
1010 try:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001011 if isinstance(int(var_name), int):
Michael Walsh18176322016-11-15 15:11:21 -06001012 hex = int(var_name)
1013 args_list.pop(0)
1014 parm_num += 1
1015 except ValueError:
1016 hex = 0
1017
1018 buffer = ""
1019 for var_value in args_list:
1020 var_name = get_arg_name(None, parm_num, stack_frame)
1021 buffer += sprint_varx(var_name, var_value, hex, indent, loc_col1_width)
1022 parm_num += 1
1023
1024 return buffer
1025
Michael Walsh18176322016-11-15 15:11:21 -06001026
Michael Walsh7423c012016-10-04 10:27:21 -05001027def sprint_dashes(indent=col1_indent,
1028 width=80,
1029 line_feed=1,
1030 char="-"):
Michael Walshde791732016-09-06 14:25:24 -05001031 r"""
1032 Return a string of dashes to the caller.
1033
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001034 Description of arguments:
Michael Walshde791732016-09-06 14:25:24 -05001035 indent The number of characters to indent the
1036 output.
1037 width The width of the string of dashes.
1038 line_feed Indicates whether the output should end
1039 with a line feed.
Michael Walsh7423c012016-10-04 10:27:21 -05001040 char The character to be repeated in the output
1041 string.
Michael Walshde791732016-09-06 14:25:24 -05001042 """
1043
Michael Walsh7423c012016-10-04 10:27:21 -05001044 width = int(width)
Michael Walsh23e7f492017-01-10 11:34:47 -06001045 buffer = " " * int(indent) + char * width
Michael Walshde791732016-09-06 14:25:24 -05001046 if line_feed:
1047 buffer += "\n"
1048
1049 return buffer
1050
Michael Walshde791732016-09-06 14:25:24 -05001051
Michael Walsh7423c012016-10-04 10:27:21 -05001052def sindent(text="",
1053 indent=0):
Michael Walsh7423c012016-10-04 10:27:21 -05001054 r"""
1055 Pre-pend the specified number of characters to the text string (i.e.
1056 indent it) and return it.
1057
1058 Description of arguments:
1059 text The string to be indented.
1060 indent The number of characters to indent the
1061 string.
1062 """
1063
1064 format_string = "%" + str(indent) + "s%s"
1065 buffer = format_string % ("", text)
1066
1067 return buffer
1068
Michael Walsh7423c012016-10-04 10:27:21 -05001069
Michael Walsh7423c012016-10-04 10:27:21 -05001070def sprint_call_stack(indent=0,
1071 stack_frame_ix=0):
Michael Walshde791732016-09-06 14:25:24 -05001072 r"""
1073 Return a call stack report for the given point in the program with line
1074 numbers, function names and function parameters and arguments.
1075
1076 Sample output:
1077
1078 -------------------------------------------------------------------------
1079 Python function call stack
1080
1081 Line # Function name and arguments
1082 ------ ------------------------------------------------------------------
1083 424 sprint_call_stack ()
1084 4 print_call_stack ()
1085 31 func1 (last_name = 'walsh', first_name = 'mikey')
1086 59 /tmp/scr5.py
1087 -------------------------------------------------------------------------
1088
1089 Description of arguments:
1090 indent The number of characters to indent each
1091 line of output.
1092 stack_frame_ix The index of the first stack frame which
1093 is to be returned.
1094 """
1095
1096 buffer = ""
Michael Walsh7423c012016-10-04 10:27:21 -05001097 buffer += sprint_dashes(indent)
1098 buffer += sindent("Python function call stack\n\n", indent)
1099 buffer += sindent("Line # Function name and arguments\n", indent)
1100 buffer += sprint_dashes(indent, 6, 0) + " " + sprint_dashes(0, 73)
Michael Walshde791732016-09-06 14:25:24 -05001101
1102 # Grab the current program stack.
1103 current_stack = inspect.stack()
1104
1105 # Process each frame in turn.
1106 format_string = "%6s %s\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001107 ix = 0
Michael Walshde791732016-09-06 14:25:24 -05001108 for stack_frame in current_stack:
Michael Walsh7423c012016-10-04 10:27:21 -05001109 if ix < stack_frame_ix:
1110 ix += 1
1111 continue
Michael Walsh23e7f492017-01-10 11:34:47 -06001112 # I want the line number shown to be the line where you find the line
1113 # shown.
1114 try:
1115 line_num = str(current_stack[ix + 1][2])
1116 except IndexError:
1117 line_num = ""
Michael Walshde791732016-09-06 14:25:24 -05001118 func_name = str(stack_frame[3])
1119 if func_name == "?":
1120 # "?" is the name used when code is not in a function.
1121 func_name = "(none)"
1122
1123 if func_name == "<module>":
Michael Walsh7423c012016-10-04 10:27:21 -05001124 # If the func_name is the "main" program, we simply get the
1125 # command line call string.
Michael Walshde791732016-09-06 14:25:24 -05001126 func_and_args = ' '.join(sys.argv)
1127 else:
1128 # Get the program arguments.
1129 arg_vals = inspect.getargvalues(stack_frame[0])
1130 function_parms = arg_vals[0]
1131 frame_locals = arg_vals[3]
1132
Michael Walsh7423c012016-10-04 10:27:21 -05001133 args_list = []
Michael Walshde791732016-09-06 14:25:24 -05001134 for arg_name in function_parms:
1135 # Get the arg value from frame locals.
1136 arg_value = frame_locals[arg_name]
Michael Walsh7423c012016-10-04 10:27:21 -05001137 args_list.append(arg_name + " = " + repr(arg_value))
1138 args_str = "(" + ', '.join(map(str, args_list)) + ")"
Michael Walshde791732016-09-06 14:25:24 -05001139
1140 # Now we need to print this in a nicely-wrapped way.
1141 func_and_args = func_name + " " + args_str
1142
Michael Walsh23e7f492017-01-10 11:34:47 -06001143 buffer += sindent(format_string % (line_num, func_and_args), indent)
Michael Walsh7423c012016-10-04 10:27:21 -05001144 ix += 1
Michael Walshde791732016-09-06 14:25:24 -05001145
Michael Walsh7423c012016-10-04 10:27:21 -05001146 buffer += sprint_dashes(indent)
Michael Walshde791732016-09-06 14:25:24 -05001147
1148 return buffer
1149
Michael Walshde791732016-09-06 14:25:24 -05001150
Michael Walshde791732016-09-06 14:25:24 -05001151def sprint_executing(stack_frame_ix=None):
Michael Walshde791732016-09-06 14:25:24 -05001152 r"""
1153 Print a line indicating what function is executing and with what parameter
1154 values. This is useful for debugging.
1155
1156 Sample output:
1157
1158 #(CDT) 2016/08/25 17:54:27 - Executing: func1 (x = 1)
1159
1160 Description of arguments:
1161 stack_frame_ix The index of the stack frame whose
1162 function info should be returned. If the
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001163 caller does not specify a value, this
Michael Walshde791732016-09-06 14:25:24 -05001164 function will set the value to 1 which is
1165 the index of the caller's stack frame. If
1166 the caller is the wrapper function
1167 "print_executing", this function will bump
1168 it up by 1.
1169 """
1170
1171 # If user wants default stack_frame_ix.
1172 if stack_frame_ix is None:
1173 func_name = sys._getframe().f_code.co_name
1174 caller_func_name = sys._getframe(1).f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -05001175 if caller_func_name.endswith(func_name[1:]):
Michael Walshde791732016-09-06 14:25:24 -05001176 stack_frame_ix = 2
1177 else:
1178 stack_frame_ix = 1
1179
1180 stack_frame = inspect.stack()[stack_frame_ix]
1181
1182 func_name = str(stack_frame[3])
1183 if func_name == "?":
1184 # "?" is the name used when code is not in a function.
1185 func_name = "(none)"
1186
1187 if func_name == "<module>":
1188 # If the func_name is the "main" program, we simply get the command
1189 # line call string.
1190 func_and_args = ' '.join(sys.argv)
1191 else:
1192 # Get the program arguments.
1193 arg_vals = inspect.getargvalues(stack_frame[0])
1194 function_parms = arg_vals[0]
1195 frame_locals = arg_vals[3]
1196
Michael Walsh7423c012016-10-04 10:27:21 -05001197 args_list = []
Michael Walshde791732016-09-06 14:25:24 -05001198 for arg_name in function_parms:
1199 # Get the arg value from frame locals.
1200 arg_value = frame_locals[arg_name]
Michael Walsh7423c012016-10-04 10:27:21 -05001201 args_list.append(arg_name + " = " + repr(arg_value))
1202 args_str = "(" + ', '.join(map(str, args_list)) + ")"
Michael Walshde791732016-09-06 14:25:24 -05001203
1204 # Now we need to print this in a nicely-wrapped way.
1205 func_and_args = func_name + " " + args_str
1206
1207 return sprint_time() + "Executing: " + func_and_args + "\n"
1208
Michael Walshde791732016-09-06 14:25:24 -05001209
Michael Walshbec416d2016-11-10 08:54:52 -06001210def sprint_pgm_header(indent=0,
1211 linefeed=1):
Michael Walshde791732016-09-06 14:25:24 -05001212 r"""
1213 Return a standardized header that programs should print at the beginning
1214 of the run. It includes useful information like command line, pid,
1215 userid, program parameters, etc.
1216
Michael Walsh7423c012016-10-04 10:27:21 -05001217 Description of arguments:
1218 indent The number of characters to indent each
1219 line of output.
Michael Walshbec416d2016-11-10 08:54:52 -06001220 linefeed Indicates whether a line feed be included
1221 at the beginning and end of the report.
Michael Walshde791732016-09-06 14:25:24 -05001222 """
1223
Michael Walshbec416d2016-11-10 08:54:52 -06001224 loc_col1_width = col1_width + indent
1225
1226 buffer = ""
1227 if linefeed:
1228 buffer = "\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001229
Michael Walshdb6e68a2017-05-23 17:55:31 -05001230 if robot_env:
1231 suite_name = BuiltIn().get_variable_value("${suite_name}")
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001232 buffer += sindent(sprint_time("Running test suite \"" + suite_name
1233 + "\".\n"), indent)
Michael Walshdb6e68a2017-05-23 17:55:31 -05001234
Michael Walsh7423c012016-10-04 10:27:21 -05001235 buffer += sindent(sprint_time() + "Running " + pgm_name + ".\n", indent)
1236 buffer += sindent(sprint_time() + "Program parameter values, etc.:\n\n",
1237 indent)
Michael Walshbec416d2016-11-10 08:54:52 -06001238 buffer += sprint_varx("command_line", ' '.join(sys.argv), 0, indent,
1239 loc_col1_width)
Michael Walsh7423c012016-10-04 10:27:21 -05001240 # We want the output to show a customized name for the pid and pgid but
1241 # we want it to look like a valid variable name. Therefore, we'll use
Michael Walshde791732016-09-06 14:25:24 -05001242 # pgm_name_var_name which was set when this module was imported.
Michael Walshbec416d2016-11-10 08:54:52 -06001243 buffer += sprint_varx(pgm_name_var_name + "_pid", os.getpid(), 0, indent,
1244 loc_col1_width)
1245 buffer += sprint_varx(pgm_name_var_name + "_pgid", os.getpgrp(), 0, indent,
1246 loc_col1_width)
Michael Walsh86de0d22016-12-05 10:13:15 -06001247 userid_num = str(os.geteuid())
1248 try:
1249 username = os.getlogin()
1250 except OSError:
1251 if userid_num == "0":
1252 username = "root"
1253 else:
1254 username = "?"
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001255 buffer += sprint_varx("uid", userid_num + " (" + username
1256 + ")", 0, indent, loc_col1_width)
1257 buffer += sprint_varx("gid", str(os.getgid()) + " ("
1258 + str(grp.getgrgid(os.getgid()).gr_name) + ")", 0,
Michael Walshbec416d2016-11-10 08:54:52 -06001259 indent, loc_col1_width)
1260 buffer += sprint_varx("host_name", socket.gethostname(), 0, indent,
1261 loc_col1_width)
Michael Walsh86de0d22016-12-05 10:13:15 -06001262 try:
1263 DISPLAY = os.environ['DISPLAY']
1264 except KeyError:
1265 DISPLAY = ""
1266 buffer += sprint_varx("DISPLAY", DISPLAY, 0, indent,
Michael Walshbec416d2016-11-10 08:54:52 -06001267 loc_col1_width)
Michael Walshde791732016-09-06 14:25:24 -05001268 # I want to add code to print caller's parms.
1269
Michael Walsh7423c012016-10-04 10:27:21 -05001270 # __builtin__.arg_obj is created by the get_arg module function,
1271 # gen_get_options.
1272 try:
1273 buffer += ga.sprint_args(__builtin__.arg_obj, indent)
1274 except AttributeError:
1275 pass
1276
Michael Walshdb6e68a2017-05-23 17:55:31 -05001277 if robot_env:
1278 # Get value of global parm_list.
1279 parm_list = BuiltIn().get_variable_value("${parm_list}")
1280
1281 for parm in parm_list:
1282 parm_value = BuiltIn().get_variable_value("${" + parm + "}")
1283 buffer += sprint_varx(parm, parm_value, 0, indent, loc_col1_width)
1284
1285 # Setting global program_pid.
1286 BuiltIn().set_global_variable("${program_pid}", os.getpid())
1287
Michael Walshbec416d2016-11-10 08:54:52 -06001288 if linefeed:
1289 buffer += "\n"
Michael Walshde791732016-09-06 14:25:24 -05001290
1291 return buffer
1292
Michael Walshde791732016-09-06 14:25:24 -05001293
Michael Walsh7423c012016-10-04 10:27:21 -05001294def sprint_error_report(error_text="\n",
Michael Walshdb6e68a2017-05-23 17:55:31 -05001295 indent=2,
1296 format=None):
Michael Walsh7423c012016-10-04 10:27:21 -05001297 r"""
1298 Return a string with a standardized report which includes the caller's
1299 error text, the call stack and the program header.
1300
1301 Description of args:
1302 error_text The error text to be included in the
1303 report. The caller should include any
1304 needed linefeeds.
1305 indent The number of characters to indent each
1306 line of output.
Michael Walshdb6e68a2017-05-23 17:55:31 -05001307 format Long or short format. Long includes
1308 extras like lines of dashes, call stack,
1309 etc.
Michael Walsh7423c012016-10-04 10:27:21 -05001310 """
1311
Michael Walshdb6e68a2017-05-23 17:55:31 -05001312 # Process input.
1313 indent = int(indent)
1314 if format is None:
1315 if robot_env:
1316 format = 'short'
1317 else:
1318 format = 'long'
1319 error_text = error_text.rstrip('\n') + '\n'
1320
1321 if format == 'short':
1322 return sprint_error(error_text)
1323
Michael Walsh7423c012016-10-04 10:27:21 -05001324 buffer = ""
1325 buffer += sprint_dashes(width=120, char="=")
1326 buffer += sprint_error(error_text)
1327 buffer += "\n"
1328 # Calling sprint_call_stack with stack_frame_ix of 0 causes it to show
1329 # itself and this function in the call stack. This is not helpful to a
1330 # debugger and is therefore clutter. We will adjust the stack_frame_ix to
1331 # hide that information.
Michael Walsh9c75f672017-09-12 17:11:35 -05001332 stack_frame_ix = 1
Michael Walsh7423c012016-10-04 10:27:21 -05001333 caller_func_name = sprint_func_name(2)
1334 if caller_func_name.endswith("print_error_report"):
1335 stack_frame_ix += 1
Michael Walshdb6e68a2017-05-23 17:55:31 -05001336 if not robot_env:
1337 buffer += sprint_call_stack(indent, stack_frame_ix)
Michael Walsh7423c012016-10-04 10:27:21 -05001338 buffer += sprint_pgm_header(indent)
1339 buffer += sprint_dashes(width=120, char="=")
1340
1341 return buffer
1342
Michael Walsh7423c012016-10-04 10:27:21 -05001343
Michael Walsh18176322016-11-15 15:11:21 -06001344def sprint_issuing(cmd_buf,
1345 test_mode=0):
Michael Walshde791732016-09-06 14:25:24 -05001346 r"""
1347 Return a line indicating a command that the program is about to execute.
1348
1349 Sample output for a cmd_buf of "ls"
1350
1351 #(CDT) 2016/08/25 17:57:36 - Issuing: ls
Michael Walshbec416d2016-11-10 08:54:52 -06001352
Michael Walshde791732016-09-06 14:25:24 -05001353 Description of args:
1354 cmd_buf The command to be executed by caller.
Michael Walshbec416d2016-11-10 08:54:52 -06001355 test_mode With test_mode set, your output will look
1356 like this:
1357
1358 #(CDT) 2016/08/25 17:57:36 - (test_mode) Issuing: ls
1359
Michael Walshde791732016-09-06 14:25:24 -05001360 """
1361
Michael Walshbec416d2016-11-10 08:54:52 -06001362 buffer = sprint_time()
1363 if test_mode:
1364 buffer += "(test_mode) "
1365 buffer += "Issuing: " + cmd_buf + "\n"
Michael Walshde791732016-09-06 14:25:24 -05001366
1367 return buffer
1368
Michael Walshde791732016-09-06 14:25:24 -05001369
Michael Walshde791732016-09-06 14:25:24 -05001370def sprint_pgm_footer():
Michael Walshde791732016-09-06 14:25:24 -05001371 r"""
1372 Return a standardized footer that programs should print at the end of the
1373 program run. It includes useful information like total run time, etc.
1374 """
1375
1376 buffer = "\n" + sprint_time() + "Finished running " + pgm_name + ".\n\n"
1377
1378 total_time = time.time() - start_time
1379 total_time_string = "%0.6f" % total_time
1380
Michael Walsh7423c012016-10-04 10:27:21 -05001381 buffer += sprint_varx(pgm_name_var_name + "_runtime", total_time_string)
Michael Walshbec416d2016-11-10 08:54:52 -06001382 buffer += "\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001383
1384 return buffer
1385
Michael Walsh7423c012016-10-04 10:27:21 -05001386
Michael Walsh7423c012016-10-04 10:27:21 -05001387def sprint(buffer=""):
Michael Walsh7423c012016-10-04 10:27:21 -05001388 r"""
1389 Simply return the user's buffer. This function is used by the qprint and
1390 dprint functions defined dynamically below, i.e. it would not normally be
1391 called for general use.
1392
1393 Description of arguments.
1394 buffer This will be returned to the caller.
1395 """
Michael Walshde791732016-09-06 14:25:24 -05001396
Michael Walsh95e45102018-02-09 12:44:43 -06001397 try:
1398 return str(buffer)
1399 except UnicodeEncodeError:
1400 return buffer
Michael Walshbec416d2016-11-10 08:54:52 -06001401
Michael Walshbec416d2016-11-10 08:54:52 -06001402
Michael Walshbec416d2016-11-10 08:54:52 -06001403def sprintn(buffer=""):
Michael Walshbec416d2016-11-10 08:54:52 -06001404 r"""
1405 Simply return the user's buffer with a line feed. This function is used
1406 by the qprint and dprint functions defined dynamically below, i.e. it
1407 would not normally be called for general use.
1408
1409 Description of arguments.
1410 buffer This will be returned to the caller.
1411 """
1412
Michael Walsh95e45102018-02-09 12:44:43 -06001413 try:
1414 buffer = str(buffer) + "\n"
1415 except UnicodeEncodeError:
1416 buffer = buffer + "\n"
Michael Walshbec416d2016-11-10 08:54:52 -06001417
Michael Walshde791732016-09-06 14:25:24 -05001418 return buffer
1419
Michael Walsh168eb0f2017-12-01 15:35:32 -06001420
Michael Walshfd2733c2017-11-13 11:36:20 -06001421def gp_print(buffer,
1422 stream='stdout'):
Michael Walshfd2733c2017-11-13 11:36:20 -06001423 r"""
1424 Print the buffer using either sys.stdout.write or BuiltIn().log_to_console
1425 depending on whether we are running in a robot environment.
1426
1427 This function is intended for use only by other functions in this module.
1428
1429 Description of arguments:
1430 buffer The string to be printed.
1431 stream Either "stdout" or "stderr".
1432 """
1433
1434 if robot_env:
1435 BuiltIn().log_to_console(buffer, stream=stream, no_newline=True)
1436 else:
1437 if stream == "stdout":
1438 sys.stdout.write(buffer)
1439 sys.stdout.flush()
1440 else:
1441 sys.stderr.write(buffer)
1442 sys.stderr.flush()
Michael Walshde791732016-09-06 14:25:24 -05001443
1444
Michael Walsh168eb0f2017-12-01 15:35:32 -06001445def gp_log(buffer):
Michael Walsh168eb0f2017-12-01 15:35:32 -06001446 r"""
1447 Log the buffer using either python logging or BuiltIn().log depending on
1448 whether we are running in a robot environment.
1449
1450 This function is intended for use only by other functions in this module.
1451
1452 Description of arguments:
1453 buffer The string to be logged.
1454 """
1455
1456 if robot_env:
1457 BuiltIn().log(buffer)
1458 else:
1459 logging.warning(buffer)
1460
1461
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001462def gp_debug_print(buffer):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001463 r"""
Michael Walshfd2733c2017-11-13 11:36:20 -06001464 Print with gp_print only if gen_print_debug is set.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001465
1466 This function is intended for use only by other functions in this module.
1467
1468 Description of arguments:
1469 buffer The string to be printed.
1470 """
1471
1472 if not gen_print_debug:
1473 return
1474
Michael Walshfd2733c2017-11-13 11:36:20 -06001475 gp_print(buffer)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001476
1477
Michael Walshb1500152017-04-12 15:42:43 -05001478def get_var_value(var_value=None,
1479 default=1,
1480 var_name=None):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001481 r"""
Michael Walshb1500152017-04-12 15:42:43 -05001482 Return either var_value, the corresponding global value or default.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001483
Michael Walshb1500152017-04-12 15:42:43 -05001484 If var_value is not None, it will simply be returned.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001485
Michael Walshb1500152017-04-12 15:42:43 -05001486 If var_value is None, this function will return the corresponding global
1487 value of the variable in question.
1488
1489 Note: For global values, if we are in a robot environment,
1490 get_variable_value will be used. Otherwise, the __builtin__ version of
1491 the variable is returned (which are set by gen_arg.py functions).
1492
1493 If there is no global value associated with the variable, default is
1494 returned.
1495
1496 This function is useful for other functions in setting default values for
1497 parameters.
1498
1499 Example use:
1500
1501 def my_func(quiet=None):
1502
1503 quiet = int(get_var_value(quiet, 0))
1504
1505 Example calls to my_func():
1506
1507 In the following example, the caller is explicitly asking to have quiet be
1508 set to 1.
1509
1510 my_func(quiet=1)
1511
1512 In the following example, quiet will be set to the global value of quiet,
1513 if defined, or to 0 (the default).
1514
1515 my_func()
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001516
1517 Description of arguments:
Michael Walshb1500152017-04-12 15:42:43 -05001518 var_value The value to be returned (if not equal to
1519 None).
1520 default The value that is returned if var_value is
1521 None and there is no corresponding global
1522 value defined.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001523 var_name The name of the variable whose value is to
Michael Walshb1500152017-04-12 15:42:43 -05001524 be returned. Under most circumstances,
1525 this value need not be provided. This
1526 function can figure out the name of the
1527 variable passed as var_value. One
1528 exception to this would be if this
1529 function is called directly from a .robot
1530 file.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001531 """
1532
Michael Walshb1500152017-04-12 15:42:43 -05001533 if var_value is not None:
1534 return var_value
1535
1536 if var_name is None:
1537 var_name = get_arg_name(None, 1, 2)
1538
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001539 if robot_env:
Michael Walshc6537442017-06-06 15:33:52 -05001540 var_value = BuiltIn().get_variable_value("${" + var_name + "}",
1541 default)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001542 else:
1543 var_value = getattr(__builtin__, var_name, default)
1544
1545 return var_value
1546
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001547
Michael Walsh052ff812018-05-18 16:09:09 -05001548def get_stack_var(var_name,
1549 default="",
1550 init_stack_ix=2):
Michael Walsh052ff812018-05-18 16:09:09 -05001551 r"""
1552 Starting with the caller's stack level, search upward in the call stack,
1553 for a variable named var_name and return its value. If the variable
1554 cannot be found, return default.
1555
1556 Example code:
1557
1558 def func12():
1559 my_loc_var1 = get_stack_var('my_var1', "default value")
1560
1561 def func11():
1562 my_var1 = 11
1563 func12()
1564
1565 In this example, get_stack_var will find the value of my_var1 in func11's
1566 stack and will therefore return the value 11. Therefore, my_loc_var1
1567 would get set to 11.
1568
1569 Description of argument(s):
1570 var_name The name of the variable to be searched
1571 for.
1572 default The value to return if the the variable
1573 cannot be found.
1574 init_stack_ix The initial stack index from which to
1575 begin the search. 0 would be the index of
1576 this func1tion ("get_stack_var"), 1 would
1577 be the index of the function calling this
1578 function, etc.
1579 """
1580
1581 return next((frame[0].f_locals[var_name]
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001582 for frame in inspect.stack()[init_stack_ix:]
1583 if var_name in frame[0].f_locals), default)
Michael Walsh052ff812018-05-18 16:09:09 -05001584
1585
Michael Walsh82acf002017-05-04 14:33:05 -05001586# hidden_text is a list of passwords which are to be replaced with asterisks
1587# by print functions defined in this module.
1588hidden_text = []
1589# password_regex is created based on the contents of hidden_text.
1590password_regex = ""
1591
1592
Michael Walsh82acf002017-05-04 14:33:05 -05001593def register_passwords(*args):
Michael Walsh82acf002017-05-04 14:33:05 -05001594 r"""
1595 Register one or more passwords which are to be hidden in output produced
1596 by the print functions in this module.
1597
1598 Note: Blank password values are NOT registered. They are simply ignored.
1599
1600 Description of argument(s):
1601 args One or more password values. If a given
1602 password value is already registered, this
1603 function will simply do nothing.
1604 """
1605
1606 global hidden_text
1607 global password_regex
1608
1609 for password in args:
1610 if password == "":
1611 break
1612 if password in hidden_text:
1613 break
1614
1615 # Place the password into the hidden_text list.
1616 hidden_text.append(password)
1617 # Create a corresponding password regular expression. Escape regex
1618 # special characters too.
1619 password_regex = '(' +\
1620 '|'.join([re.escape(x) for x in hidden_text]) + ')'
1621
Michael Walsh82acf002017-05-04 14:33:05 -05001622
Michael Walsh82acf002017-05-04 14:33:05 -05001623def replace_passwords(buffer):
Michael Walsh82acf002017-05-04 14:33:05 -05001624 r"""
1625 Return the buffer but with all registered passwords replaced by a string
1626 of asterisks.
1627
1628
1629 Description of argument(s):
1630 buffer The string to be returned but with
1631 passwords replaced.
1632 """
1633
1634 global password_regex
1635
1636 if int(os.environ.get("DEBUG_SHOW_PASSWORDS", "0")):
1637 return buffer
1638
1639 if password_regex == "":
1640 # No passwords to replace.
1641 return buffer
1642
1643 return re.sub(password_regex, "********", buffer)
1644
Michael Walshfd2733c2017-11-13 11:36:20 -06001645
1646def create_print_wrapper_funcs(func_names,
1647 stderr_func_names,
1648 replace_dict):
Michael Walshfd2733c2017-11-13 11:36:20 -06001649 r"""
1650 Generate code for print wrapper functions and return the generated code as
1651 a string.
1652
1653 To illustrate, suppose there is a "print_foo_bar" function in the
1654 func_names list.
1655 This function will...
1656 - Expect that there is an sprint_foo_bar function already in existence.
1657 - Create a print_foo_bar function which calls sprint_foo_bar and prints
Michael Walshfaafa9c2018-06-27 16:39:31 -05001658 the result.
Michael Walshfd2733c2017-11-13 11:36:20 -06001659 - Create a qprint_foo_bar function which calls upon sprint_foo_bar only if
Michael Walshfaafa9c2018-06-27 16:39:31 -05001660 global value quiet is 0.
Michael Walshfd2733c2017-11-13 11:36:20 -06001661 - Create a dprint_foo_bar function which calls upon sprint_foo_bar only if
Michael Walshfaafa9c2018-06-27 16:39:31 -05001662 global value debug is 1.
Michael Walshfd2733c2017-11-13 11:36:20 -06001663
1664 Also, code will be generated to define aliases for each function as well.
1665 Each alias will be created by replacing "print_" in the function name with
1666 "p" For example, the alias for print_foo_bar will be pfoo_bar.
1667
1668 Description of argument(s):
1669 func_names A list of functions for which print
1670 wrapper function code is to be generated.
1671 stderr_func_names A list of functions whose generated code
1672 should print to stderr rather than to
1673 stdout.
1674 replace_dict Please see the create_func_def_string
1675 function in wrap_utils.py for details on
1676 this parameter. This parameter will be
1677 passed directly to create_func_def_string.
1678 """
1679
1680 buffer = ""
1681
1682 for func_name in func_names:
1683 if func_name in stderr_func_names:
1684 replace_dict['output_stream'] = "stderr"
1685 else:
1686 replace_dict['output_stream'] = "stdout"
1687
1688 s_func_name = "s" + func_name
1689 q_func_name = "q" + func_name
1690 d_func_name = "d" + func_name
1691
1692 # We don't want to try to redefine the "print" function, thus the
1693 # following if statement.
1694 if func_name != "print":
1695 func_def = create_func_def_string(s_func_name, func_name,
1696 print_func_template,
1697 replace_dict)
1698 buffer += func_def
1699
1700 func_def = create_func_def_string(s_func_name, "q" + func_name,
1701 qprint_func_template, replace_dict)
1702 buffer += func_def
1703
1704 func_def = create_func_def_string(s_func_name, "d" + func_name,
1705 dprint_func_template, replace_dict)
1706 buffer += func_def
1707
Michael Walsh168eb0f2017-12-01 15:35:32 -06001708 func_def = create_func_def_string(s_func_name, "l" + func_name,
1709 lprint_func_template, replace_dict)
1710 buffer += func_def
1711
Michael Walshfd2733c2017-11-13 11:36:20 -06001712 # Create abbreviated aliases (e.g. spvar is an alias for sprint_var).
1713 alias = re.sub("print_", "p", func_name)
1714 alias = re.sub("print", "p", alias)
Michael Walsh168eb0f2017-12-01 15:35:32 -06001715 prefixes = ["", "s", "q", "d", "l"]
Michael Walshfd2733c2017-11-13 11:36:20 -06001716 for prefix in prefixes:
1717 if alias == "p":
1718 continue
1719 func_def = prefix + alias + " = " + prefix + func_name
1720 buffer += func_def + "\n"
1721
1722 return buffer
Michael Walsh82acf002017-05-04 14:33:05 -05001723
1724
Michael Walshde791732016-09-06 14:25:24 -05001725# In the following section of code, we will dynamically create print versions
1726# for each of the sprint functions defined above. So, for example, where we
1727# have an sprint_time() function defined above that returns the time to the
Michael Walsh7423c012016-10-04 10:27:21 -05001728# caller in a string, we will create a corresponding print_time() function
1729# that will print that string directly to stdout.
Michael Walshde791732016-09-06 14:25:24 -05001730
Michael Walshfd2733c2017-11-13 11:36:20 -06001731# It can be complicated to follow what's being created by below. Here is an
1732# example of the print_time() function that will be created:
Michael Walshde791732016-09-06 14:25:24 -05001733
Michael Walshfd2733c2017-11-13 11:36:20 -06001734# def print_time(buffer=''):
1735# sys.stdout.write(replace_passwords(sprint_time(buffer=buffer)))
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001736# sys.stdout.flush()
Michael Walshde791732016-09-06 14:25:24 -05001737
Michael Walshfd2733c2017-11-13 11:36:20 -06001738# Templates for the various print wrapper functions.
1739print_func_template = \
1740 [
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001741 " <mod_qualifier>gp_print(<mod_qualifier>replace_passwords("
1742 + "<call_line>), stream='<output_stream>')"
Michael Walshfd2733c2017-11-13 11:36:20 -06001743 ]
1744
1745qprint_func_template = \
1746 [
Michael Walsh81c02342018-01-05 15:43:28 -06001747 " if int(<mod_qualifier>get_var_value(None, 0, \"quiet\")): return"
Michael Walshfd2733c2017-11-13 11:36:20 -06001748 ] + print_func_template
1749
1750dprint_func_template = \
1751 [
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001752 " if not int(<mod_qualifier>get_var_value(None, 0, \"debug\")):"
1753 + " return"
Michael Walshfd2733c2017-11-13 11:36:20 -06001754 ] + print_func_template
1755
Michael Walsh168eb0f2017-12-01 15:35:32 -06001756lprint_func_template = \
1757 [
Michael Walsh4fea2cf2018-08-22 17:48:18 -05001758 " global sprint_time_last_seconds",
1759 " global last_seconds_ix",
1760 " if len(sprint_time_last_seconds) <= lprint_last_seconds_ix():",
1761 " sprint_time_last_seconds.append(start_time)",
1762 " save_last_seconds_ix = last_seconds_ix",
1763 " last_seconds_ix = lprint_last_seconds_ix()",
1764 " gp_log(<mod_qualifier>replace_passwords(<call_line>))",
1765 " last_seconds_ix = save_last_seconds_ix",
Michael Walsh168eb0f2017-12-01 15:35:32 -06001766 ]
1767
Michael Walsh81c02342018-01-05 15:43:28 -06001768replace_dict = {'output_stream': 'stdout', 'mod_qualifier': ''}
Michael Walshfd2733c2017-11-13 11:36:20 -06001769
1770
1771gp_debug_print("robot_env: " + str(robot_env))
Michael Walshde791732016-09-06 14:25:24 -05001772
1773# func_names contains a list of all print functions which should be created
1774# from their sprint counterparts.
1775func_names = ['print_time', 'print_timen', 'print_error', 'print_varx',
Michael Walsh18176322016-11-15 15:11:21 -06001776 'print_var', 'print_vars', 'print_dashes', 'indent',
1777 'print_call_stack', 'print_func_name', 'print_executing',
1778 'print_pgm_header', 'print_issuing', 'print_pgm_footer',
1779 'print_error_report', 'print', 'printn']
Michael Walshde791732016-09-06 14:25:24 -05001780
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001781# stderr_func_names is a list of functions whose output should go to stderr
1782# rather than stdout.
1783stderr_func_names = ['print_error', 'print_error_report']
Michael Walshde791732016-09-06 14:25:24 -05001784
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001785
Michael Walshfd2733c2017-11-13 11:36:20 -06001786func_defs = create_print_wrapper_funcs(func_names, stderr_func_names,
1787 replace_dict)
1788gp_debug_print(func_defs)
1789exec(func_defs)