blob: 4cc16e0c35ff1887064e37fd535f73aa6c861899 [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.
Michael Walsh61c12982019-03-28 12:38:01 -050078sprint_time_last_seconds = [start_time, start_time]
Michael Walsh4fea2cf2018-08-22 17:48:18 -050079# Define global index for the sprint_time_last_seconds list.
80last_seconds_ix = 0
81
82
Michael Walsh61c12982019-03-28 12:38:01 -050083def set_last_seconds_ix(ix):
84 r"""
85 Set the "last_seconds_ix" module variable to the index value.
86
87 Description of argument(s):
88 ix The index value to be set into the module
89 global last_seconds_ix variable.
90 """
91 global last_seconds_ix
92 last_seconds_ix = ix
93
94
Michael Walsh4fea2cf2018-08-22 17:48:18 -050095# Since output from the lprint_ functions goes to a different location than
96# the output from the print_ functions (e.g. a file vs. the console),
97# sprint_time_last_seconds has been created as a list rather than a simple
98# integer so that it can store multiple sprint_time_last_seconds values.
99# Standard print_ functions defined in this file will use
100# sprint_time_last_seconds[0] and the lprint_ functions will use
101# sprint_time_last_seconds[1].
Michael Walsh61c12982019-03-28 12:38:01 -0500102def standard_print_last_seconds_ix():
103 r"""
104 Return the standard print last_seconds index value to the caller.
105 """
106 return 0
107
108
Michael Walsh4fea2cf2018-08-22 17:48:18 -0500109def lprint_last_seconds_ix():
110 r"""
111 Return lprint last_seconds index value to the caller.
112 """
113 return 1
114
Michael Walshde791732016-09-06 14:25:24 -0500115
Michael Walsh2ee77cd2017-03-08 11:50:17 -0600116# The user can set environment variable "GEN_PRINT_DEBUG" to get debug output
117# from this module.
118gen_print_debug = int(os.environ.get('GEN_PRINT_DEBUG', 0))
Michael Walsh7423c012016-10-04 10:27:21 -0500119
Michael Walshde791732016-09-06 14:25:24 -0500120
Michael Walshde791732016-09-06 14:25:24 -0500121def sprint_func_name(stack_frame_ix=None):
Michael Walshde791732016-09-06 14:25:24 -0500122 r"""
123 Return the function name associated with the indicated stack frame.
124
125 Description of arguments:
126 stack_frame_ix The index of the stack frame whose
127 function name should be returned. If the
Michael Walsh2ee77cd2017-03-08 11:50:17 -0600128 caller does not specify a value, this
Michael Walshde791732016-09-06 14:25:24 -0500129 function will set the value to 1 which is
130 the index of the caller's stack frame. If
131 the caller is the wrapper function
132 "print_func_name", this function will bump
133 it up by 1.
134 """
135
136 # If user specified no stack_frame_ix, we'll set it to a proper default
137 # value.
138 if stack_frame_ix is None:
139 func_name = sys._getframe().f_code.co_name
140 caller_func_name = sys._getframe(1).f_code.co_name
141 if func_name[1:] == caller_func_name:
142 stack_frame_ix = 2
143 else:
144 stack_frame_ix = 1
145
146 func_name = sys._getframe(stack_frame_ix).f_code.co_name
147
148 return func_name
149
Michael Walshde791732016-09-06 14:25:24 -0500150
Michael Walsh6f0362c2019-03-25 14:05:14 -0500151def work_around_inspect_stack_cwd_failure():
152 r"""
153 Work around the inspect.stack() getcwd() failure by making "/tmp" the
154 current working directory.
155
156 If the current working directory has been deleted, inspect.stack() will
157 fail with "OSError: [Errno 2] No such file or directory" because it tries
158 to do a getcwd().
159
160 This function will try to prevent this failure by detecting the scenario
161 in advance and making "/tmp" the current working directory.
162 """
163 try:
164 os.getcwd()
165 except OSError:
166 os.chdir("/tmp")
167
168
Michael Walsh1173a522018-05-21 17:24:51 -0500169def get_line_indent(line):
170 r"""
171 Return the number of spaces at the beginning of the line.
172 """
173
174 return len(line) - len(line.lstrip(' '))
175
176
Michael Walshde791732016-09-06 14:25:24 -0500177# get_arg_name is not a print function per se. I have included it in this
178# module because it is used by sprint_var which is found in this module.
Michael Walshde791732016-09-06 14:25:24 -0500179def get_arg_name(var,
180 arg_num=1,
181 stack_frame_ix=1):
Michael Walshde791732016-09-06 14:25:24 -0500182 r"""
183 Return the "name" of an argument passed to a function. This could be a
184 literal or a variable name.
185
Michael Walsh2ee77cd2017-03-08 11:50:17 -0600186 Description of arguments:
Michael Walshde791732016-09-06 14:25:24 -0500187 var The variable whose name you want returned.
Michael Walsh1173a522018-05-21 17:24:51 -0500188 arg_num The arg number whose name is to be
189 returned. To illustrate how arg_num is
190 processed, suppose that a programmer codes
191 this line: "rc, outbuf = my_func(var1,
192 var2)" and suppose that my_func has this
193 line of code: "result = gp.get_arg_name(0,
194 arg_num, 2)". If arg_num is positive, the
195 indicated argument is returned. For
196 example, if arg_num is 1, "var1" would be
197 returned, If arg_num is 2, "var2" would be
198 returned. If arg_num exceeds the number
199 of arguments, get_arg_name will simply
200 return a complete list of the arguments.
201 If arg_num is 0, get_arg_name will return
202 the name of the target function as
203 specified in the calling line ("my_func"
204 in this case). To clarify, if the caller
205 of the target function uses an alias
206 function name, the alias name would be
207 returned. If arg_num is negative, an
208 lvalue variable name is returned.
209 Continuing with the given example, if
210 arg_num is -2 the 2nd parm to the left of
211 the "=" ("rc" in this case) should be
212 returned. If arg_num is -1, the 1st parm
213 to the left of the "=" ("out_buf" in this
214 case) should be returned. If arg_num is
215 less than -2, an entire dictionary is
216 returned. The keys to the dictionary for
217 this example would be -2 and -1.
Michael Walshde791732016-09-06 14:25:24 -0500218 stack_frame_ix The stack frame index of the target
219 function. This value must be 1 or
220 greater. 1 would indicate get_arg_name's
221 stack frame. 2 would be the caller of
222 get_arg_name's stack frame, etc.
223
224 Example 1:
225
226 my_var = "mike"
227 var_name = get_arg_name(my_var)
228
229 In this example, var_name will receive the value "my_var".
230
231 Example 2:
232
233 def test1(var):
234 # Getting the var name of the first arg to this function, test1.
235 # Note, in this case, it doesn't matter what you pass as the first arg
236 # to get_arg_name since it is the caller's variable name that matters.
237 dummy = 1
238 arg_num = 1
239 stack_frame = 2
240 var_name = get_arg_name(dummy, arg_num, stack_frame)
241
242 # Mainline...
243
244 another_var = "whatever"
245 test1(another_var)
246
247 In this example, var_name will be set to "another_var".
248
249 """
250
251 # Note: I wish to avoid recursion so I refrain from calling any function
252 # that calls this function (i.e. sprint_var, valid_value, etc.).
253
Michael Walsh23e7f492017-01-10 11:34:47 -0600254 # The user can set environment variable "GET_ARG_NAME_DEBUG" to get debug
255 # output from this function.
256 local_debug = int(os.environ.get('GET_ARG_NAME_DEBUG', 0))
257 # In addition to GET_ARG_NAME_DEBUG, the user can set environment
258 # variable "GET_ARG_NAME_SHOW_SOURCE" to have this function include source
259 # code in the debug output.
260 local_debug_show_source = int(
261 os.environ.get('GET_ARG_NAME_SHOW_SOURCE', 0))
Michael Walshde791732016-09-06 14:25:24 -0500262
Michael Walshde791732016-09-06 14:25:24 -0500263 if stack_frame_ix < 1:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500264 print_error("Programmer error - Variable \"stack_frame_ix\" has an"
265 + " invalid value of \"" + str(stack_frame_ix) + "\". The"
266 + " value must be an integer that is greater than or equal"
267 + " to 1.\n")
Michael Walshde791732016-09-06 14:25:24 -0500268 return
269
270 if local_debug:
271 debug_indent = 2
Michael Walsh23e7f492017-01-10 11:34:47 -0600272 print("")
273 print_dashes(0, 120)
Michael Walshde791732016-09-06 14:25:24 -0500274 print(sprint_func_name() + "() parms:")
275 print_varx("var", var, 0, debug_indent)
276 print_varx("arg_num", arg_num, 0, debug_indent)
277 print_varx("stack_frame_ix", stack_frame_ix, 0, debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600278 print("")
279 print_call_stack(debug_indent, 2)
Michael Walshde791732016-09-06 14:25:24 -0500280
Michael Walsh6f0362c2019-03-25 14:05:14 -0500281 work_around_inspect_stack_cwd_failure()
Michael Walsh23e7f492017-01-10 11:34:47 -0600282 for count in range(0, 2):
283 try:
284 frame, filename, cur_line_no, function_name, lines, index = \
285 inspect.stack()[stack_frame_ix]
286 except IndexError:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500287 print_error("Programmer error - The caller has asked for"
288 + " information about the stack frame at index \""
289 + str(stack_frame_ix) + "\". However, the stack"
290 + " only contains " + str(len(inspect.stack()))
291 + " entries. Therefore the stack frame index is out"
292 + " of range.\n")
Michael Walsh23e7f492017-01-10 11:34:47 -0600293 return
294 if filename != "<string>":
295 break
296 # filename of "<string>" may mean that the function in question was
297 # defined dynamically and therefore its code stack is inaccessible.
298 # This may happen with functions like "rqprint_var". In this case,
299 # we'll increment the stack_frame_ix and try again.
300 stack_frame_ix += 1
301 if local_debug:
302 print("Adjusted stack_frame_ix...")
303 print_varx("stack_frame_ix", stack_frame_ix, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500304
Michael Walsh1173a522018-05-21 17:24:51 -0500305 real_called_func_name = sprint_func_name(stack_frame_ix)
Michael Walsh23e7f492017-01-10 11:34:47 -0600306
307 module = inspect.getmodule(frame)
308
309 # Though I would expect inspect.getsourcelines(frame) to get all module
310 # source lines if the frame is "<module>", it doesn't do that. Therefore,
311 # for this special case, I will do inspect.getsourcelines(module).
312 if function_name == "<module>":
313 source_lines, source_line_num =\
314 inspect.getsourcelines(module)
315 line_ix = cur_line_no - source_line_num - 1
316 else:
317 source_lines, source_line_num =\
318 inspect.getsourcelines(frame)
319 line_ix = cur_line_no - source_line_num
320
321 if local_debug:
322 print("\n Variables retrieved from inspect.stack() function:")
323 print_varx("frame", frame, 0, debug_indent + 2)
324 print_varx("filename", filename, 0, debug_indent + 2)
325 print_varx("cur_line_no", cur_line_no, 0, debug_indent + 2)
326 print_varx("function_name", function_name, 0, debug_indent + 2)
327 print_varx("lines", lines, 0, debug_indent + 2)
328 print_varx("index", index, 0, debug_indent + 2)
329 print_varx("source_line_num", source_line_num, 0, debug_indent)
330 print_varx("line_ix", line_ix, 0, debug_indent)
331 if local_debug_show_source:
332 print_varx("source_lines", source_lines, 0, debug_indent)
Michael Walsh1173a522018-05-21 17:24:51 -0500333 print_varx("real_called_func_name", real_called_func_name, 0,
334 debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600335
336 # Get a list of all functions defined for the module. Note that this
337 # doesn't work consistently when _run_exitfuncs is at the top of the stack
338 # (i.e. if we're running an exit function). I've coded a work-around
339 # below for this deficiency.
340 all_functions = inspect.getmembers(module, inspect.isfunction)
341
342 # Get called_func_id by searching for our function in the list of all
343 # functions.
344 called_func_id = None
345 for func_name, function in all_functions:
Michael Walsh1173a522018-05-21 17:24:51 -0500346 if func_name == real_called_func_name:
Michael Walsh23e7f492017-01-10 11:34:47 -0600347 called_func_id = id(function)
348 break
349 # NOTE: The only time I've found that called_func_id can't be found is
350 # when we're running from an exit function.
351
352 # Look for other functions in module with matching id.
Michael Walsh1173a522018-05-21 17:24:51 -0500353 aliases = set([real_called_func_name])
Michael Walsh23e7f492017-01-10 11:34:47 -0600354 for func_name, function in all_functions:
Michael Walsh1173a522018-05-21 17:24:51 -0500355 if func_name == real_called_func_name:
Michael Walsh23e7f492017-01-10 11:34:47 -0600356 continue
357 func_id = id(function)
358 if func_id == called_func_id:
359 aliases.add(func_name)
360
361 # In most cases, my general purpose code above will find all aliases.
362 # However, for the odd case (i.e. running from exit function), I've added
363 # code to handle pvar, qpvar, dpvar, etc. aliases explicitly since they
364 # are defined in this module and used frequently.
365 # pvar is an alias for print_var.
Michael Walsh1173a522018-05-21 17:24:51 -0500366 aliases.add(re.sub("print_var", "pvar", real_called_func_name))
Michael Walsh23e7f492017-01-10 11:34:47 -0600367
Michael Walsh3f248272018-06-01 13:59:35 -0500368 # The call to the function could be encased in a recast (e.g.
369 # int(func_name())).
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500370 recast_regex = "([^ ]+\\([ ]*)?"
371 import_name_regex = "([a-zA-Z0-9_]+\\.)?"
Michael Walsh3f248272018-06-01 13:59:35 -0500372 func_name_regex = recast_regex + import_name_regex + "(" +\
373 '|'.join(aliases) + ")"
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500374 pre_args_regex = ".*" + func_name_regex + "[ ]*\\("
Michael Walsh23e7f492017-01-10 11:34:47 -0600375
376 # Search backward through source lines looking for the calling function
377 # name.
378 found = False
379 for start_line_ix in range(line_ix, 0, -1):
380 # Skip comment lines.
381 if re.match(r"[ ]*#", source_lines[start_line_ix]):
382 continue
Michael Walsh1173a522018-05-21 17:24:51 -0500383 if re.match(pre_args_regex, source_lines[start_line_ix]):
Michael Walsh23e7f492017-01-10 11:34:47 -0600384 found = True
385 break
386 if not found:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500387 print_error("Programmer error - Could not find the source line with"
388 + " a reference to function \"" + real_called_func_name
389 + "\".\n")
Michael Walsh23e7f492017-01-10 11:34:47 -0600390 return
391
Michael Walsh82acf002017-05-04 14:33:05 -0500392 # Search forward through the source lines looking for a line whose
393 # indentation is the same or less than the start line. The end of our
394 # composite line should be the line preceding that line.
Michael Walsh1173a522018-05-21 17:24:51 -0500395 start_indent = get_line_indent(source_lines[start_line_ix])
Michael Walsh37cd29d2018-05-24 13:19:18 -0500396 end_line_ix = line_ix
Michael Walsh23e7f492017-01-10 11:34:47 -0600397 for end_line_ix in range(line_ix + 1, len(source_lines)):
398 if source_lines[end_line_ix].strip() == "":
399 continue
Michael Walsh1173a522018-05-21 17:24:51 -0500400 line_indent = get_line_indent(source_lines[end_line_ix])
Michael Walsh82acf002017-05-04 14:33:05 -0500401 if line_indent <= start_indent:
Michael Walsh23e7f492017-01-10 11:34:47 -0600402 end_line_ix -= 1
403 break
Michael Walsh1173a522018-05-21 17:24:51 -0500404 if start_line_ix != 0:
405 # Check to see whether the start line is a continuation of the prior
Michael Walsha52e9eb2018-09-10 13:56:01 -0500406 # line.
407 prior_line = source_lines[start_line_ix - 1]
408 prior_line_stripped = re.sub(r"[ ]*\\([\r\n]$)", " \\1", prior_line)
409 prior_line_indent = get_line_indent(prior_line)
410 if prior_line != prior_line_stripped and\
411 prior_line_indent < start_indent:
Michael Walsh1173a522018-05-21 17:24:51 -0500412 start_line_ix -= 1
Michael Walsha52e9eb2018-09-10 13:56:01 -0500413 # Remove the backslash (continuation char) from prior line.
414 source_lines[start_line_ix] = prior_line_stripped
Michael Walsh23e7f492017-01-10 11:34:47 -0600415
416 # Join the start line through the end line into a composite line.
417 composite_line = ''.join(map(str.strip,
Gunnar Mills096cd562018-03-26 10:19:12 -0500418 source_lines[start_line_ix:end_line_ix + 1]))
Michael Walsh1173a522018-05-21 17:24:51 -0500419 # Insert one space after first "=" if there isn't one already.
420 composite_line = re.sub("=[ ]*([^ ])", "= \\1", composite_line, 1)
Michael Walsh7423c012016-10-04 10:27:21 -0500421
Michael Walsh3f248272018-06-01 13:59:35 -0500422 lvalue_regex = "[ ]*=[ ]+" + func_name_regex + ".*"
Michael Walsh1173a522018-05-21 17:24:51 -0500423 lvalue_string = re.sub(lvalue_regex, "", composite_line)
Michael Walsh3f248272018-06-01 13:59:35 -0500424 if lvalue_string == composite_line:
425 # i.e. the regex did not match so there are no lvalues.
426 lvalue_string = ""
Michael Walsh37762f92018-08-07 14:59:18 -0500427 lvalues_list = list(filter(None, map(str.strip, lvalue_string.split(","))))
Michael Walsh3f248272018-06-01 13:59:35 -0500428 try:
429 lvalues = collections.OrderedDict()
430 except AttributeError:
431 # A non-ordered dict doesn't look as nice when printed but it will do.
432 lvalues = {}
Michael Walsh1173a522018-05-21 17:24:51 -0500433 ix = len(lvalues_list) * -1
434 for lvalue in lvalues_list:
435 lvalues[ix] = lvalue
436 ix += 1
Michael Walsh3f248272018-06-01 13:59:35 -0500437 lvalue_prefix_regex = "(.*=[ ]+)?"
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500438 called_func_name_regex = lvalue_prefix_regex + func_name_regex +\
439 "[ ]*\\(.*"
Michael Walsh3f248272018-06-01 13:59:35 -0500440 called_func_name = re.sub(called_func_name_regex, "\\4", composite_line)
Michael Walsh1173a522018-05-21 17:24:51 -0500441 arg_list_etc = "(" + re.sub(pre_args_regex, "", composite_line)
Michael Walshde791732016-09-06 14:25:24 -0500442 if local_debug:
Michael Walsh23e7f492017-01-10 11:34:47 -0600443 print_varx("aliases", aliases, 0, debug_indent)
Michael Walsh3f248272018-06-01 13:59:35 -0500444 print_varx("import_name_regex", import_name_regex, 0, debug_indent)
445 print_varx("func_name_regex", func_name_regex, 0, debug_indent)
Michael Walsh1173a522018-05-21 17:24:51 -0500446 print_varx("pre_args_regex", pre_args_regex, 0, debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600447 print_varx("start_line_ix", start_line_ix, 0, debug_indent)
448 print_varx("end_line_ix", end_line_ix, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500449 print_varx("composite_line", composite_line, 0, debug_indent)
Michael Walsh1173a522018-05-21 17:24:51 -0500450 print_varx("lvalue_regex", lvalue_regex, 0, debug_indent)
451 print_varx("lvalue_string", lvalue_string, 0, debug_indent)
452 print_varx("lvalues", lvalues, 0, debug_indent)
Michael Walsh3f248272018-06-01 13:59:35 -0500453 print_varx("called_func_name_regex", called_func_name_regex, 0,
454 debug_indent)
Michael Walsh1173a522018-05-21 17:24:51 -0500455 print_varx("called_func_name", called_func_name, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500456 print_varx("arg_list_etc", arg_list_etc, 0, debug_indent)
457
458 # Parse arg list...
459 # Initialize...
460 nest_level = -1
461 arg_ix = 0
Michael Walsh7423c012016-10-04 10:27:21 -0500462 args_list = [""]
Michael Walshde791732016-09-06 14:25:24 -0500463 for ix in range(0, len(arg_list_etc)):
464 char = arg_list_etc[ix]
465 # Set the nest_level based on whether we've encounted a parenthesis.
466 if char == "(":
467 nest_level += 1
468 if nest_level == 0:
469 continue
470 elif char == ")":
471 nest_level -= 1
472 if nest_level < 0:
473 break
474
475 # If we reach a comma at base nest level, we are done processing an
Michael Walsh7423c012016-10-04 10:27:21 -0500476 # argument so we increment arg_ix and initialize a new args_list entry.
Michael Walshde791732016-09-06 14:25:24 -0500477 if char == "," and nest_level == 0:
478 arg_ix += 1
Michael Walsh7423c012016-10-04 10:27:21 -0500479 args_list.append("")
Michael Walshde791732016-09-06 14:25:24 -0500480 continue
481
Michael Walsh7423c012016-10-04 10:27:21 -0500482 # For any other character, we append it it to the current arg list
Michael Walshde791732016-09-06 14:25:24 -0500483 # entry.
Michael Walsh7423c012016-10-04 10:27:21 -0500484 args_list[arg_ix] += char
Michael Walshde791732016-09-06 14:25:24 -0500485
486 # Trim whitespace from each list entry.
Michael Walsh7423c012016-10-04 10:27:21 -0500487 args_list = [arg.strip() for arg in args_list]
Michael Walshde791732016-09-06 14:25:24 -0500488
Michael Walsh1173a522018-05-21 17:24:51 -0500489 if arg_num < 0:
490 if abs(arg_num) > len(lvalues):
491 argument = lvalues
492 else:
493 argument = lvalues[arg_num]
494 elif arg_num == 0:
495 argument = called_func_name
Michael Walsh2750b442018-05-18 14:49:11 -0500496 else:
Michael Walsh1173a522018-05-21 17:24:51 -0500497 if arg_num > len(args_list):
498 argument = args_list
499 else:
500 argument = args_list[arg_num - 1]
Michael Walshde791732016-09-06 14:25:24 -0500501
502 if local_debug:
Michael Walsh7423c012016-10-04 10:27:21 -0500503 print_varx("args_list", args_list, 0, debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500504 print_varx("argument", argument, 0, debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600505 print_dashes(0, 120)
Michael Walshde791732016-09-06 14:25:24 -0500506
507 return argument
508
Michael Walshde791732016-09-06 14:25:24 -0500509
Michael Walshde791732016-09-06 14:25:24 -0500510def sprint_time(buffer=""):
Michael Walshde791732016-09-06 14:25:24 -0500511 r"""
512 Return the time in the following format.
513
514 Example:
515
516 The following python code...
517
518 sys.stdout.write(sprint_time())
519 sys.stdout.write("Hi.\n")
520
521 Will result in the following type of output:
522
523 #(CDT) 2016/07/08 15:25:35 - Hi.
524
525 Example:
526
527 The following python code...
528
529 sys.stdout.write(sprint_time("Hi.\n"))
530
531 Will result in the following type of output:
532
533 #(CDT) 2016/08/03 17:12:05 - Hi.
534
535 The following environment variables will affect the formatting as
536 described:
537 NANOSECONDS This will cause the time stamps to be
538 precise to the microsecond (Yes, it
539 probably should have been named
540 MICROSECONDS but the convention was set
541 long ago so we're sticking with it).
542 Example of the output when environment
543 variable NANOSECONDS=1.
544
545 #(CDT) 2016/08/03 17:16:25.510469 - Hi.
546
547 SHOW_ELAPSED_TIME This will cause the elapsed time to be
548 included in the output. This is the
549 amount of time that has elapsed since the
550 last time this function was called. The
551 precision of the elapsed time field is
552 also affected by the value of the
553 NANOSECONDS environment variable. Example
554 of the output when environment variable
555 NANOSECONDS=0 and SHOW_ELAPSED_TIME=1.
556
557 #(CDT) 2016/08/03 17:17:40 - 0 - Hi.
558
559 Example of the output when environment variable NANOSECONDS=1 and
560 SHOW_ELAPSED_TIME=1.
561
562 #(CDT) 2016/08/03 17:18:47.317339 - 0.000046 - Hi.
563
564 Description of arguments.
565 buffer This will be appended to the formatted
566 time string.
567 """
568
569 global NANOSECONDS
570 global SHOW_ELAPSED_TIME
571 global sprint_time_last_seconds
Michael Walsh4fea2cf2018-08-22 17:48:18 -0500572 global last_seconds_ix
Michael Walshde791732016-09-06 14:25:24 -0500573
574 seconds = time.time()
575 loc_time = time.localtime(seconds)
576 nanoseconds = "%0.6f" % seconds
577 pos = nanoseconds.find(".")
578 nanoseconds = nanoseconds[pos:]
579
580 time_string = time.strftime("#(%Z) %Y/%m/%d %H:%M:%S", loc_time)
581 if NANOSECONDS == "1":
582 time_string = time_string + nanoseconds
583
584 if SHOW_ELAPSED_TIME == "1":
585 cur_time_seconds = seconds
586 math_string = "%9.9f" % cur_time_seconds + " - " + "%9.9f" % \
Michael Walsh4fea2cf2018-08-22 17:48:18 -0500587 sprint_time_last_seconds[last_seconds_ix]
Michael Walshde791732016-09-06 14:25:24 -0500588 elapsed_seconds = eval(math_string)
589 if NANOSECONDS == "1":
590 elapsed_seconds = "%11.6f" % elapsed_seconds
591 else:
592 elapsed_seconds = "%4i" % elapsed_seconds
Michael Walsh4fea2cf2018-08-22 17:48:18 -0500593 sprint_time_last_seconds[last_seconds_ix] = cur_time_seconds
Michael Walshde791732016-09-06 14:25:24 -0500594 time_string = time_string + " - " + elapsed_seconds
595
596 return time_string + " - " + buffer
597
Michael Walshde791732016-09-06 14:25:24 -0500598
Michael Walshde791732016-09-06 14:25:24 -0500599def sprint_timen(buffer=""):
Michael Walshde791732016-09-06 14:25:24 -0500600 r"""
601 Append a line feed to the buffer, pass it to sprint_time and return the
602 result.
603 """
604
605 return sprint_time(buffer + "\n")
606
Michael Walshde791732016-09-06 14:25:24 -0500607
Michael Walshde791732016-09-06 14:25:24 -0500608def sprint_error(buffer=""):
Michael Walshde791732016-09-06 14:25:24 -0500609 r"""
610 Return a standardized error string. This includes:
611 - A time stamp
612 - The "**ERROR**" string
613 - The caller's buffer string.
614
615 Example:
616
617 The following python code...
618
619 print(sprint_error("Oops.\n"))
620
621 Will result in the following type of output:
622
623 #(CDT) 2016/08/03 17:12:05 - **ERROR** Oops.
624
625 Description of arguments.
626 buffer This will be appended to the formatted
627 error string.
628 """
629
630 return sprint_time() + "**ERROR** " + buffer
631
Michael Walshde791732016-09-06 14:25:24 -0500632
Michael Walsh3f248272018-06-01 13:59:35 -0500633# Implement "constants" with functions.
634def digit_length_in_bits():
635 r"""
636 Return the digit length in bits.
637 """
638
639 return 4
640
641
642def word_length_in_digits():
643 r"""
644 Return the word length in digits.
645 """
646
647 return 8
648
649
650def bit_length(number):
651 r"""
652 Return the bit length of the number.
653
654 Description of argument(s):
655 number The number to be analyzed.
656 """
657
658 if number < 0:
659 # Convert negative numbers to positive and subtract one. The
660 # following example illustrates the reason for this:
661 # Consider a single nibble whose signed values can range from -8 to 7
662 # (0x8 to 0x7). A value of 0x7 equals 0b0111. Therefore, its length
663 # in bits is 3. Since the negative bit (i.e. 0b1000) is not set, the
664 # value 7 clearly will fit in one nibble. With -8 = 0x8 = 0b1000, you
665 # have the smallest negative value that will fit. Note that it
666 # requires 3 bits of 0. So by converting a number value of -8 to a
667 # working_number of 7, this function can accurately calculate the
668 # number of bits and therefore nibbles required to represent the
669 # number in print.
670 working_number = abs(number) - 1
671 else:
672 working_number = number
673
674 # Handle the special case of the number 0.
675 if working_number == 0:
676 return 0
677
678 return len(bin(working_number)) - 2
679
680
681def get_req_num_hex_digits(number):
682 r"""
683 Return the required number of hex digits required to display the given
684 number.
685
686 The returned value will always be rounded up to the nearest multiple of 8.
687
688 Description of argument(s):
689 number The number to be analyzed.
690 """
691
692 if number < 0:
693 # Convert negative numbers to positive and subtract one. The
694 # following example illustrates the reason for this:
695 # Consider a single nibble whose signed values can range from -8 to 7
696 # (0x8 to 0x7). A value of 0x7 equals 0b0111. Therefore, its length
697 # in bits is 3. Since the negative bit (i.e. 0b1000) is not set, the
698 # value 7 clearly will fit in one nibble. With -8 = 0x8 = 0b1000, you
699 # have the smallest negative value that will fit. Note that it
700 # requires 3 bits of 0. So by converting a number value of -8 to a
701 # working_number of 7, this function can accurately calculate the
702 # number of bits and therefore nibbles required to represent the
703 # number in print.
704 working_number = abs(number) - 1
705 else:
706 working_number = number
707
708 # Handle the special case of the number 0.
709 if working_number == 0:
710 return word_length_in_digits()
711
712 num_length_in_bits = bit_length(working_number)
713 num_hex_digits, remainder = divmod(num_length_in_bits,
714 digit_length_in_bits())
715 if remainder > 0:
716 # Example: the number 7 requires 3 bits. The divmod above produces,
717 # 0 with remainder of 3. So because we have a remainder, we increment
718 # num_hex_digits from 0 to 1.
719 num_hex_digits += 1
720
721 # Check to see whether the negative bit is set. This is the left-most
722 # bit in the highest order digit.
723 negative_mask = 2 ** (num_hex_digits * 4 - 1)
724 if working_number & negative_mask:
725 # If a number that is intended to be positive has its negative bit
726 # on, an additional digit will be required to represent it correctly
727 # in print.
728 num_hex_digits += 1
729
730 num_words, remainder = divmod(num_hex_digits, word_length_in_digits())
731 if remainder > 0 or num_words == 0:
732 num_words += 1
733
734 # Round up to the next word length in digits.
735 return num_words * word_length_in_digits()
736
737
738def dft_num_hex_digits():
739 r"""
740 Return the default number of hex digits to be used to represent a hex
741 number in print.
742
743 The value returned is a function of sys.maxsize.
744 """
745
746 global _gen_print_dft_num_hex_digits_
747 try:
748 return _gen_print_dft_num_hex_digits_
749 except NameError:
750 _gen_print_dft_num_hex_digits_ = get_req_num_hex_digits(sys.maxsize)
751 return _gen_print_dft_num_hex_digits_
752
753
Michael Walsh8646d962019-01-21 14:36:13 -0600754def is_dict(var_value):
755 r"""
756 Return 1 if var_value is a type of dictionary and 0 if it is not.
757 """
758
759 type_is_dict = 0
760 if isinstance(var_value, dict):
761 type_is_dict = 1
762 try:
763 if isinstance(var_value, collections.OrderedDict):
764 type_is_dict = 1
765 except AttributeError:
766 pass
767 try:
768 if isinstance(var_value, DotDict):
769 type_is_dict = 1
770 except NameError:
771 pass
772 try:
773 if isinstance(var_value, NormalizedDict):
774 type_is_dict = 1
775 except NameError:
776 pass
777 return type_is_dict
778
779
Michael Walshde791732016-09-06 14:25:24 -0500780def sprint_varx(var_name,
781 var_value,
782 hex=0,
783 loc_col1_indent=col1_indent,
Michael Walsh7423c012016-10-04 10:27:21 -0500784 loc_col1_width=col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500785 trailing_char="\n",
786 key_list=None):
Michael Walshde791732016-09-06 14:25:24 -0500787 r"""
788 Print the var name/value passed to it. If the caller lets loc_col1_width
789 default, the printing lines up nicely with output generated by the
790 print_time functions.
791
792 Note that the sprint_var function (defined below) can be used to call this
793 function so that the programmer does not need to pass the var_name.
794 sprint_var will figure out the var_name. The sprint_var function is the
795 one that would normally be used by the general user.
796
797 For example, the following python code:
798
799 first_name = "Mike"
800 print_time("Doing this...\n")
801 print_varx("first_name", first_name)
802 print_time("Doing that...\n")
803
804 Will generate output like this:
805
806 #(CDT) 2016/08/10 17:34:42.847374 - 0.001285 - Doing this...
807 first_name: Mike
808 #(CDT) 2016/08/10 17:34:42.847510 - 0.000136 - Doing that...
809
810 This function recognizes several complex types of data such as dict, list
811 or tuple.
812
813 For example, the following python code:
814
815 my_dict = dict(one=1, two=2, three=3)
816 print_var(my_dict)
817
818 Will generate the following output:
819
820 my_dict:
821 my_dict[three]: 3
822 my_dict[two]: 2
823 my_dict[one]: 1
824
825 Description of arguments.
826 var_name The name of the variable to be printed.
827 var_value The value of the variable to be printed.
828 hex This indicates that the value should be
829 printed in hex format. It is the user's
830 responsibility to ensure that a var_value
Michael Walshbec416d2016-11-10 08:54:52 -0600831 contains a valid hex number. For string
832 var_values, this will be interpreted as
833 show_blanks which means that blank values
Michael Walshd995cb02017-02-07 14:46:01 -0600834 will be printed as "<blank>". For dict
835 var_values, this will be interpreted as
836 terse format where keys are not repeated
837 in the output.
Michael Walshde791732016-09-06 14:25:24 -0500838 loc_col1_indent The number of spaces to indent the output.
839 loc_col1_width The width of the output column containing
840 the variable name. The default value of
841 this is adjusted so that the var_value
842 lines up with text printed via the
843 print_time function.
Michael Walsh7423c012016-10-04 10:27:21 -0500844 trailing_char The character to be used at the end of the
845 returned string. The default value is a
846 line feed.
Michael Walshd2869032018-03-22 16:12:11 -0500847 key_list A list of which dictionary keys should be
848 printed. All others keys will be skipped.
849 Each value in key_list will be regarded
850 as a regular expression and it will be
851 regarded as anchored to the beginning and
852 ends of the dictionary key being
853 referenced. For example if key_list is
854 ["one", "two"], the resulting regex used
855 will be "^one|two$", i.e. only keys "one"
856 and "two" from the var_value dictionary
857 will be printed. As another example, if
858 the caller were to specify a key_list of
859 ["one.*"], then only dictionary keys whose
860 names begin with "one" will be printed.
861 Note: This argument pertains only to
862 var_values which are dictionaries.
Michael Walsh7423c012016-10-04 10:27:21 -0500863 """
Michael Walshde791732016-09-06 14:25:24 -0500864
865 # Determine the type
Michael Walsh37762f92018-08-07 14:59:18 -0500866 try:
867 int_types = (int, long)
868 except NameError:
869 int_types = (int,)
870 try:
871 string_types = (str, unicode)
872 except NameError:
George Keishing36efbc02018-12-12 10:18:23 -0600873 string_types = (bytes, str)
Michael Walsh37762f92018-08-07 14:59:18 -0500874 simple_types = int_types + string_types + (float, bool)
875 if type(var_value) in simple_types \
Michael Walshde791732016-09-06 14:25:24 -0500876 or var_value is None:
877 # The data type is simple in the sense that it has no subordinate
878 # parts.
Michael Walsh7423c012016-10-04 10:27:21 -0500879 # Adjust loc_col1_width.
880 loc_col1_width = loc_col1_width - loc_col1_indent
Michael Walshde791732016-09-06 14:25:24 -0500881 # See if the user wants the output in hex format.
882 if hex:
Michael Walsh37762f92018-08-07 14:59:18 -0500883 if type(var_value) not in int_types:
Michael Walshbec416d2016-11-10 08:54:52 -0600884 value_format = "%s"
Michael Walsh2795edc2016-12-13 16:00:33 -0600885 if var_value == "":
Michael Walshbec416d2016-11-10 08:54:52 -0600886 var_value = "<blank>"
887 else:
Michael Walsh3f248272018-06-01 13:59:35 -0500888 num_hex_digits = max(dft_num_hex_digits(),
889 get_req_num_hex_digits(var_value))
890 # Convert a negative number to its positive twos complement
891 # for proper printing. For example, instead of printing -1 as
892 # "0x-000000000000001" it will be printed as
893 # "0xffffffffffffffff".
894 var_value = var_value & (2 ** (num_hex_digits * 4) - 1)
895 value_format = "0x%0" + str(num_hex_digits) + "x"
Michael Walshde791732016-09-06 14:25:24 -0500896 else:
897 value_format = "%s"
898 format_string = "%" + str(loc_col1_indent) + "s%-" \
Michael Walsh7423c012016-10-04 10:27:21 -0500899 + str(loc_col1_width) + "s" + value_format + trailing_char
Michael Walsh3383e652017-09-01 17:10:59 -0500900 if value_format == "0x%08x":
901 return format_string % ("", str(var_name) + ":",
902 var_value & 0xffffffff)
903 else:
904 return format_string % ("", str(var_name) + ":", var_value)
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500905 elif isinstance(var_value, type):
Michael Walsh20a87ab2017-06-30 17:00:30 -0500906 return sprint_varx(var_name, str(var_value).split("'")[1], hex,
Michael Walshd2869032018-03-22 16:12:11 -0500907 loc_col1_indent, loc_col1_width, trailing_char,
908 key_list)
Michael Walshde791732016-09-06 14:25:24 -0500909 else:
910 # The data type is complex in the sense that it has subordinate parts.
911 format_string = "%" + str(loc_col1_indent) + "s%s\n"
912 buffer = format_string % ("", var_name + ":")
913 loc_col1_indent += 2
Michael Walsh7423c012016-10-04 10:27:21 -0500914 try:
915 length = len(var_value)
916 except TypeError:
Michael Walsh23e7f492017-01-10 11:34:47 -0600917 length = 0
Michael Walsh7423c012016-10-04 10:27:21 -0500918 ix = 0
919 loc_trailing_char = "\n"
Michael Walsh8646d962019-01-21 14:36:13 -0600920 if is_dict(var_value):
Michael Walsh37762f92018-08-07 14:59:18 -0500921 for key, value in var_value.items():
Michael Walshd2869032018-03-22 16:12:11 -0500922 if key_list is not None:
923 key_list_regex = "^" + "|".join(key_list) + "$"
924 if not re.match(key_list_regex, key):
925 continue
Michael Walsh7423c012016-10-04 10:27:21 -0500926 ix += 1
927 if ix == length:
928 loc_trailing_char = trailing_char
Michael Walshd995cb02017-02-07 14:46:01 -0600929 if hex:
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600930 # Since hex is being used as a format type, we want it
931 # turned off when processing integer dictionary values so
932 # it is not interpreted as a hex indicator.
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500933 loc_hex = not (isinstance(value, int))
Michael Walshf7b8a002017-08-29 10:38:39 -0500934 buffer += sprint_varx("[" + key + "]", value,
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600935 loc_hex, loc_col1_indent,
936 loc_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500937 loc_trailing_char,
938 key_list)
Michael Walshd995cb02017-02-07 14:46:01 -0600939 else:
Michael Walsh1173a522018-05-21 17:24:51 -0500940 buffer += sprint_varx(var_name + "[" + str(key) + "]",
941 value, hex, loc_col1_indent,
942 loc_col1_width, loc_trailing_char,
943 key_list)
Michael Walsh7423c012016-10-04 10:27:21 -0500944 elif type(var_value) in (list, tuple, set):
Michael Walshde791732016-09-06 14:25:24 -0500945 for key, value in enumerate(var_value):
Michael Walsh7423c012016-10-04 10:27:21 -0500946 ix += 1
947 if ix == length:
948 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500949 buffer += sprint_varx(var_name + "[" + str(key) + "]", value,
Michael Walsh7423c012016-10-04 10:27:21 -0500950 hex, loc_col1_indent, loc_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500951 loc_trailing_char, key_list)
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500952 elif isinstance(var_value, argparse.Namespace):
Michael Walshde791732016-09-06 14:25:24 -0500953 for key in var_value.__dict__:
Michael Walsh7423c012016-10-04 10:27:21 -0500954 ix += 1
955 if ix == length:
956 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -0500957 cmd_buf = "buffer += sprint_varx(var_name + \".\" + str(key)" \
Michael Walsh7423c012016-10-04 10:27:21 -0500958 + ", var_value." + key + ", hex, loc_col1_indent," \
Michael Walshd2869032018-03-22 16:12:11 -0500959 + " loc_col1_width, loc_trailing_char, key_list)"
Michael Walshde791732016-09-06 14:25:24 -0500960 exec(cmd_buf)
961 else:
962 var_type = type(var_value).__name__
963 func_name = sys._getframe().f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -0500964 var_value = "<" + var_type + " type not supported by " + \
965 func_name + "()>"
Michael Walshde791732016-09-06 14:25:24 -0500966 value_format = "%s"
967 loc_col1_indent -= 2
Michael Walsh7423c012016-10-04 10:27:21 -0500968 # Adjust loc_col1_width.
969 loc_col1_width = loc_col1_width - loc_col1_indent
Michael Walshde791732016-09-06 14:25:24 -0500970 format_string = "%" + str(loc_col1_indent) + "s%-" \
Michael Walsh7423c012016-10-04 10:27:21 -0500971 + str(loc_col1_width) + "s" + value_format + trailing_char
Michael Walsh0f2ea5f2017-02-20 15:55:00 -0600972 return format_string % ("", str(var_name) + ":", var_value)
Michael Walsh23e7f492017-01-10 11:34:47 -0600973
Michael Walshde791732016-09-06 14:25:24 -0500974 return buffer
975
976 return ""
977
Michael Walshde791732016-09-06 14:25:24 -0500978
Michael Walshfd2733c2017-11-13 11:36:20 -0600979def sprint_var(var_value,
980 hex=0,
981 loc_col1_indent=col1_indent,
982 loc_col1_width=col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500983 trailing_char="\n",
984 key_list=None):
Michael Walshde791732016-09-06 14:25:24 -0500985 r"""
986 Figure out the name of the first argument for you and then call
987 sprint_varx with it. Therefore, the following 2 calls are equivalent:
988 sprint_varx("var1", var1)
989 sprint_var(var1)
990 """
991
992 # Get the name of the first variable passed to this function.
993 stack_frame = 2
Michael Walsh7423c012016-10-04 10:27:21 -0500994 caller_func_name = sprint_func_name(2)
995 if caller_func_name.endswith("print_var"):
Michael Walshde791732016-09-06 14:25:24 -0500996 stack_frame += 1
997 var_name = get_arg_name(None, 1, stack_frame)
Michael Walshfd2733c2017-11-13 11:36:20 -0600998 return sprint_varx(var_name, var_value=var_value, hex=hex,
999 loc_col1_indent=loc_col1_indent,
1000 loc_col1_width=loc_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -05001001 trailing_char=trailing_char,
1002 key_list=key_list)
Michael Walshde791732016-09-06 14:25:24 -05001003
1004
Michael Walsh18176322016-11-15 15:11:21 -06001005def sprint_vars(*args):
Michael Walsh18176322016-11-15 15:11:21 -06001006 r"""
1007 Sprint the values of one or more variables.
1008
1009 Description of args:
1010 args:
1011 If the first argument is an integer, it will be interpreted to be the
1012 "indent" value.
1013 If the second argument is an integer, it will be interpreted to be the
1014 "col1_width" value.
1015 If the third argument is an integer, it will be interpreted to be the
1016 "hex" value.
1017 All remaining parms are considered variable names which are to be
1018 sprinted.
1019 """
1020
1021 if len(args) == 0:
1022 return
1023
1024 # Get the name of the first variable passed to this function.
1025 stack_frame = 2
1026 caller_func_name = sprint_func_name(2)
1027 if caller_func_name.endswith("print_vars"):
1028 stack_frame += 1
1029
1030 parm_num = 1
1031
1032 # Create list from args (which is a tuple) so that it can be modified.
1033 args_list = list(args)
1034
1035 var_name = get_arg_name(None, parm_num, stack_frame)
1036 # See if parm 1 is to be interpreted as "indent".
1037 try:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001038 if isinstance(int(var_name), int):
Michael Walsh18176322016-11-15 15:11:21 -06001039 indent = int(var_name)
1040 args_list.pop(0)
1041 parm_num += 1
1042 except ValueError:
1043 indent = 0
1044
1045 var_name = get_arg_name(None, parm_num, stack_frame)
1046 # See if parm 1 is to be interpreted as "col1_width".
1047 try:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001048 if isinstance(int(var_name), int):
Michael Walsh18176322016-11-15 15:11:21 -06001049 loc_col1_width = int(var_name)
1050 args_list.pop(0)
1051 parm_num += 1
1052 except ValueError:
1053 loc_col1_width = col1_width
1054
1055 var_name = get_arg_name(None, parm_num, stack_frame)
1056 # See if parm 1 is to be interpreted as "hex".
1057 try:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001058 if isinstance(int(var_name), int):
Michael Walsh18176322016-11-15 15:11:21 -06001059 hex = int(var_name)
1060 args_list.pop(0)
1061 parm_num += 1
1062 except ValueError:
1063 hex = 0
1064
1065 buffer = ""
1066 for var_value in args_list:
1067 var_name = get_arg_name(None, parm_num, stack_frame)
1068 buffer += sprint_varx(var_name, var_value, hex, indent, loc_col1_width)
1069 parm_num += 1
1070
1071 return buffer
1072
Michael Walsh18176322016-11-15 15:11:21 -06001073
Michael Walsh7423c012016-10-04 10:27:21 -05001074def sprint_dashes(indent=col1_indent,
1075 width=80,
1076 line_feed=1,
1077 char="-"):
Michael Walshde791732016-09-06 14:25:24 -05001078 r"""
1079 Return a string of dashes to the caller.
1080
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001081 Description of arguments:
Michael Walshde791732016-09-06 14:25:24 -05001082 indent The number of characters to indent the
1083 output.
1084 width The width of the string of dashes.
1085 line_feed Indicates whether the output should end
1086 with a line feed.
Michael Walsh7423c012016-10-04 10:27:21 -05001087 char The character to be repeated in the output
1088 string.
Michael Walshde791732016-09-06 14:25:24 -05001089 """
1090
Michael Walsh7423c012016-10-04 10:27:21 -05001091 width = int(width)
Michael Walsh23e7f492017-01-10 11:34:47 -06001092 buffer = " " * int(indent) + char * width
Michael Walshde791732016-09-06 14:25:24 -05001093 if line_feed:
1094 buffer += "\n"
1095
1096 return buffer
1097
Michael Walshde791732016-09-06 14:25:24 -05001098
Michael Walsh7423c012016-10-04 10:27:21 -05001099def sindent(text="",
1100 indent=0):
Michael Walsh7423c012016-10-04 10:27:21 -05001101 r"""
1102 Pre-pend the specified number of characters to the text string (i.e.
1103 indent it) and return it.
1104
1105 Description of arguments:
1106 text The string to be indented.
1107 indent The number of characters to indent the
1108 string.
1109 """
1110
1111 format_string = "%" + str(indent) + "s%s"
1112 buffer = format_string % ("", text)
1113
1114 return buffer
1115
Michael Walsh7423c012016-10-04 10:27:21 -05001116
Michael Walsh662e13b2019-03-01 15:54:08 -06001117func_line_style_std = None
1118func_line_style_short = 1
1119
1120
1121def sprint_func_line(stack_frame, style=None):
Michael Walsh47aa2a42018-12-10 15:06:02 -06001122 r"""
1123 For the given stack_frame, return a formatted string containing the
1124 function name and all its arguments.
1125
1126 Example:
1127
1128 func1(last_name = 'walsh', first_name = 'mikey')
1129
1130 Description of argument(s):
1131 stack_frame A stack frame (such as is returned by
1132 inspect.stack()).
Michael Walsh662e13b2019-03-01 15:54:08 -06001133 style Indicates the style or formatting of the
1134 result string. Acceptable values are
1135 shown above.
1136
1137 Description of styles:
1138 func_line_style_std The standard formatting.
1139 func_line_style_short 1) The self parm (associated with methods)
1140 will be dropped. 2) The args and kwargs
1141 values will be treated as special. In
1142 both cases the arg name ('args' or
1143 'kwargs') will be dropped and only the
1144 values will be shown.
Michael Walsh47aa2a42018-12-10 15:06:02 -06001145 """
1146
1147 func_name = str(stack_frame[3])
1148 if func_name == "?":
1149 # "?" is the name used when code is not in a function.
1150 func_name = "(none)"
1151
1152 if func_name == "<module>":
1153 # If the func_name is the "main" program, we simply get the command
1154 # line call string.
1155 func_and_args = ' '.join(sys.argv)
1156 else:
1157 # Get the program arguments.
1158 (args, varargs, keywords, locals) =\
1159 inspect.getargvalues(stack_frame[0])
1160
1161 args_list = []
1162 for arg_name in filter(None, args + [varargs, keywords]):
1163 # Get the arg value from frame locals.
1164 arg_value = locals[arg_name]
1165 if arg_name == 'self':
Michael Walsh662e13b2019-03-01 15:54:08 -06001166 if style == func_line_style_short:
1167 continue
Michael Walsh47aa2a42018-12-10 15:06:02 -06001168 # Manipulations to improve output for class methods.
1169 func_name = arg_value.__class__.__name__ + "." + func_name
1170 args_list.append(arg_name + " = <self>")
Michael Walsh662e13b2019-03-01 15:54:08 -06001171 elif (style == func_line_style_short
1172 and arg_name == 'args'
1173 and type(arg_value) in (list, tuple)):
1174 if len(arg_value) == 0:
1175 continue
1176 args_list.append(repr(', '.join(arg_value)))
1177 elif (style == func_line_style_short
1178 and arg_name == 'kwargs'
1179 and type(arg_value) is dict):
1180 for key, value in arg_value.items():
1181 args_list.append(key + "=" + repr(value))
Michael Walsh47aa2a42018-12-10 15:06:02 -06001182 else:
1183 args_list.append(arg_name + " = " + repr(arg_value))
1184 args_str = "(" + ', '.join(map(str, args_list)) + ")"
1185
1186 # Now we need to print this in a nicely-wrapped way.
1187 func_and_args = func_name + args_str
1188
1189 return func_and_args
1190
1191
Michael Walsh7423c012016-10-04 10:27:21 -05001192def sprint_call_stack(indent=0,
Michael Walsh662e13b2019-03-01 15:54:08 -06001193 stack_frame_ix=0,
1194 style=None):
Michael Walshde791732016-09-06 14:25:24 -05001195 r"""
1196 Return a call stack report for the given point in the program with line
1197 numbers, function names and function parameters and arguments.
1198
1199 Sample output:
1200
1201 -------------------------------------------------------------------------
1202 Python function call stack
1203
1204 Line # Function name and arguments
1205 ------ ------------------------------------------------------------------
Michael Walsh47aa2a42018-12-10 15:06:02 -06001206 424 sprint_call_stack()
1207 4 print_call_stack()
1208 31 func1(last_name = 'walsh', first_name = 'mikey')
Michael Walshde791732016-09-06 14:25:24 -05001209 59 /tmp/scr5.py
1210 -------------------------------------------------------------------------
1211
1212 Description of arguments:
1213 indent The number of characters to indent each
1214 line of output.
1215 stack_frame_ix The index of the first stack frame which
1216 is to be returned.
Michael Walsh662e13b2019-03-01 15:54:08 -06001217 style See the sprint_line_func prolog above for
1218 details.
Michael Walshde791732016-09-06 14:25:24 -05001219 """
1220
1221 buffer = ""
Michael Walsh7423c012016-10-04 10:27:21 -05001222 buffer += sprint_dashes(indent)
1223 buffer += sindent("Python function call stack\n\n", indent)
1224 buffer += sindent("Line # Function name and arguments\n", indent)
1225 buffer += sprint_dashes(indent, 6, 0) + " " + sprint_dashes(0, 73)
Michael Walshde791732016-09-06 14:25:24 -05001226
1227 # Grab the current program stack.
Michael Walsh6f0362c2019-03-25 14:05:14 -05001228 work_around_inspect_stack_cwd_failure()
Michael Walshde791732016-09-06 14:25:24 -05001229 current_stack = inspect.stack()
1230
1231 # Process each frame in turn.
1232 format_string = "%6s %s\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001233 ix = 0
Michael Walshde791732016-09-06 14:25:24 -05001234 for stack_frame in current_stack:
Michael Walsh7423c012016-10-04 10:27:21 -05001235 if ix < stack_frame_ix:
1236 ix += 1
1237 continue
Michael Walsh23e7f492017-01-10 11:34:47 -06001238 # I want the line number shown to be the line where you find the line
1239 # shown.
1240 try:
1241 line_num = str(current_stack[ix + 1][2])
1242 except IndexError:
1243 line_num = ""
Michael Walsh662e13b2019-03-01 15:54:08 -06001244 func_and_args = sprint_func_line(stack_frame, style=style)
Michael Walshde791732016-09-06 14:25:24 -05001245
Michael Walsh23e7f492017-01-10 11:34:47 -06001246 buffer += sindent(format_string % (line_num, func_and_args), indent)
Michael Walsh7423c012016-10-04 10:27:21 -05001247 ix += 1
Michael Walshde791732016-09-06 14:25:24 -05001248
Michael Walsh7423c012016-10-04 10:27:21 -05001249 buffer += sprint_dashes(indent)
Michael Walshde791732016-09-06 14:25:24 -05001250
1251 return buffer
1252
Michael Walshde791732016-09-06 14:25:24 -05001253
Michael Walsh662e13b2019-03-01 15:54:08 -06001254def sprint_executing(stack_frame_ix=None, style=None):
Michael Walshde791732016-09-06 14:25:24 -05001255 r"""
1256 Print a line indicating what function is executing and with what parameter
1257 values. This is useful for debugging.
1258
1259 Sample output:
1260
Michael Walsh47aa2a42018-12-10 15:06:02 -06001261 #(CDT) 2016/08/25 17:54:27 - Executing: func1(x = 1)
Michael Walshde791732016-09-06 14:25:24 -05001262
1263 Description of arguments:
1264 stack_frame_ix The index of the stack frame whose
1265 function info should be returned. If the
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001266 caller does not specify a value, this
Michael Walshde791732016-09-06 14:25:24 -05001267 function will set the value to 1 which is
1268 the index of the caller's stack frame. If
1269 the caller is the wrapper function
1270 "print_executing", this function will bump
1271 it up by 1.
Michael Walsh662e13b2019-03-01 15:54:08 -06001272 style See the sprint_line_func prolog above for
1273 details.
Michael Walshde791732016-09-06 14:25:24 -05001274 """
1275
1276 # If user wants default stack_frame_ix.
1277 if stack_frame_ix is None:
1278 func_name = sys._getframe().f_code.co_name
1279 caller_func_name = sys._getframe(1).f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -05001280 if caller_func_name.endswith(func_name[1:]):
Michael Walshde791732016-09-06 14:25:24 -05001281 stack_frame_ix = 2
1282 else:
1283 stack_frame_ix = 1
1284
Michael Walsh6f0362c2019-03-25 14:05:14 -05001285 work_around_inspect_stack_cwd_failure()
Michael Walshde791732016-09-06 14:25:24 -05001286 stack_frame = inspect.stack()[stack_frame_ix]
1287
Michael Walsh662e13b2019-03-01 15:54:08 -06001288 func_and_args = sprint_func_line(stack_frame, style)
Michael Walshde791732016-09-06 14:25:24 -05001289
1290 return sprint_time() + "Executing: " + func_and_args + "\n"
1291
Michael Walshde791732016-09-06 14:25:24 -05001292
Michael Walshbec416d2016-11-10 08:54:52 -06001293def sprint_pgm_header(indent=0,
1294 linefeed=1):
Michael Walshde791732016-09-06 14:25:24 -05001295 r"""
1296 Return a standardized header that programs should print at the beginning
1297 of the run. It includes useful information like command line, pid,
1298 userid, program parameters, etc.
1299
Michael Walsh7423c012016-10-04 10:27:21 -05001300 Description of arguments:
1301 indent The number of characters to indent each
1302 line of output.
Michael Walshbec416d2016-11-10 08:54:52 -06001303 linefeed Indicates whether a line feed be included
1304 at the beginning and end of the report.
Michael Walshde791732016-09-06 14:25:24 -05001305 """
1306
Michael Walshbec416d2016-11-10 08:54:52 -06001307 loc_col1_width = col1_width + indent
1308
1309 buffer = ""
1310 if linefeed:
1311 buffer = "\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001312
Michael Walshdb6e68a2017-05-23 17:55:31 -05001313 if robot_env:
1314 suite_name = BuiltIn().get_variable_value("${suite_name}")
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001315 buffer += sindent(sprint_time("Running test suite \"" + suite_name
1316 + "\".\n"), indent)
Michael Walshdb6e68a2017-05-23 17:55:31 -05001317
Michael Walsh7423c012016-10-04 10:27:21 -05001318 buffer += sindent(sprint_time() + "Running " + pgm_name + ".\n", indent)
1319 buffer += sindent(sprint_time() + "Program parameter values, etc.:\n\n",
1320 indent)
Michael Walshbec416d2016-11-10 08:54:52 -06001321 buffer += sprint_varx("command_line", ' '.join(sys.argv), 0, indent,
1322 loc_col1_width)
Michael Walsh7423c012016-10-04 10:27:21 -05001323 # We want the output to show a customized name for the pid and pgid but
1324 # we want it to look like a valid variable name. Therefore, we'll use
Michael Walshde791732016-09-06 14:25:24 -05001325 # pgm_name_var_name which was set when this module was imported.
Michael Walshbec416d2016-11-10 08:54:52 -06001326 buffer += sprint_varx(pgm_name_var_name + "_pid", os.getpid(), 0, indent,
1327 loc_col1_width)
1328 buffer += sprint_varx(pgm_name_var_name + "_pgid", os.getpgrp(), 0, indent,
1329 loc_col1_width)
Michael Walsh86de0d22016-12-05 10:13:15 -06001330 userid_num = str(os.geteuid())
1331 try:
1332 username = os.getlogin()
1333 except OSError:
1334 if userid_num == "0":
1335 username = "root"
1336 else:
1337 username = "?"
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001338 buffer += sprint_varx("uid", userid_num + " (" + username
1339 + ")", 0, indent, loc_col1_width)
1340 buffer += sprint_varx("gid", str(os.getgid()) + " ("
1341 + str(grp.getgrgid(os.getgid()).gr_name) + ")", 0,
Michael Walshbec416d2016-11-10 08:54:52 -06001342 indent, loc_col1_width)
1343 buffer += sprint_varx("host_name", socket.gethostname(), 0, indent,
1344 loc_col1_width)
Michael Walsh86de0d22016-12-05 10:13:15 -06001345 try:
1346 DISPLAY = os.environ['DISPLAY']
1347 except KeyError:
1348 DISPLAY = ""
1349 buffer += sprint_varx("DISPLAY", DISPLAY, 0, indent,
Michael Walshbec416d2016-11-10 08:54:52 -06001350 loc_col1_width)
Michael Walshde791732016-09-06 14:25:24 -05001351 # I want to add code to print caller's parms.
1352
Michael Walsh7423c012016-10-04 10:27:21 -05001353 # __builtin__.arg_obj is created by the get_arg module function,
1354 # gen_get_options.
1355 try:
1356 buffer += ga.sprint_args(__builtin__.arg_obj, indent)
1357 except AttributeError:
1358 pass
1359
Michael Walshdb6e68a2017-05-23 17:55:31 -05001360 if robot_env:
1361 # Get value of global parm_list.
1362 parm_list = BuiltIn().get_variable_value("${parm_list}")
1363
1364 for parm in parm_list:
1365 parm_value = BuiltIn().get_variable_value("${" + parm + "}")
1366 buffer += sprint_varx(parm, parm_value, 0, indent, loc_col1_width)
1367
1368 # Setting global program_pid.
1369 BuiltIn().set_global_variable("${program_pid}", os.getpid())
1370
Michael Walshbec416d2016-11-10 08:54:52 -06001371 if linefeed:
1372 buffer += "\n"
Michael Walshde791732016-09-06 14:25:24 -05001373
1374 return buffer
1375
Michael Walshde791732016-09-06 14:25:24 -05001376
Michael Walsh7423c012016-10-04 10:27:21 -05001377def sprint_error_report(error_text="\n",
Michael Walshdb6e68a2017-05-23 17:55:31 -05001378 indent=2,
1379 format=None):
Michael Walsh7423c012016-10-04 10:27:21 -05001380 r"""
1381 Return a string with a standardized report which includes the caller's
1382 error text, the call stack and the program header.
1383
1384 Description of args:
1385 error_text The error text to be included in the
1386 report. The caller should include any
1387 needed linefeeds.
1388 indent The number of characters to indent each
1389 line of output.
Michael Walshdb6e68a2017-05-23 17:55:31 -05001390 format Long or short format. Long includes
1391 extras like lines of dashes, call stack,
1392 etc.
Michael Walsh7423c012016-10-04 10:27:21 -05001393 """
1394
Michael Walshdb6e68a2017-05-23 17:55:31 -05001395 # Process input.
1396 indent = int(indent)
1397 if format is None:
1398 if robot_env:
1399 format = 'short'
1400 else:
1401 format = 'long'
1402 error_text = error_text.rstrip('\n') + '\n'
1403
1404 if format == 'short':
1405 return sprint_error(error_text)
1406
Michael Walsh7423c012016-10-04 10:27:21 -05001407 buffer = ""
1408 buffer += sprint_dashes(width=120, char="=")
1409 buffer += sprint_error(error_text)
1410 buffer += "\n"
1411 # Calling sprint_call_stack with stack_frame_ix of 0 causes it to show
1412 # itself and this function in the call stack. This is not helpful to a
1413 # debugger and is therefore clutter. We will adjust the stack_frame_ix to
1414 # hide that information.
Michael Walsh9c75f672017-09-12 17:11:35 -05001415 stack_frame_ix = 1
Michael Walsh7423c012016-10-04 10:27:21 -05001416 caller_func_name = sprint_func_name(2)
1417 if caller_func_name.endswith("print_error_report"):
1418 stack_frame_ix += 1
Michael Walsh7bfa9ab2018-11-16 15:24:26 -06001419 buffer += sprint_call_stack(indent, stack_frame_ix)
Michael Walsh7423c012016-10-04 10:27:21 -05001420 buffer += sprint_pgm_header(indent)
1421 buffer += sprint_dashes(width=120, char="=")
1422
1423 return buffer
1424
Michael Walsh7423c012016-10-04 10:27:21 -05001425
Michael Walsh18176322016-11-15 15:11:21 -06001426def sprint_issuing(cmd_buf,
1427 test_mode=0):
Michael Walshde791732016-09-06 14:25:24 -05001428 r"""
1429 Return a line indicating a command that the program is about to execute.
1430
1431 Sample output for a cmd_buf of "ls"
1432
1433 #(CDT) 2016/08/25 17:57:36 - Issuing: ls
Michael Walshbec416d2016-11-10 08:54:52 -06001434
Michael Walshde791732016-09-06 14:25:24 -05001435 Description of args:
1436 cmd_buf The command to be executed by caller.
Michael Walshbec416d2016-11-10 08:54:52 -06001437 test_mode With test_mode set, your output will look
1438 like this:
1439
1440 #(CDT) 2016/08/25 17:57:36 - (test_mode) Issuing: ls
1441
Michael Walshde791732016-09-06 14:25:24 -05001442 """
1443
Michael Walshbec416d2016-11-10 08:54:52 -06001444 buffer = sprint_time()
1445 if test_mode:
1446 buffer += "(test_mode) "
Michael Walsh61c12982019-03-28 12:38:01 -05001447 if type(cmd_buf) is list:
1448 # Assume this is a robot command in the form of a list.
1449 cmd_buf = ' '.join([str(element) for element in cmd_buf])
Michael Walshbec416d2016-11-10 08:54:52 -06001450 buffer += "Issuing: " + cmd_buf + "\n"
Michael Walshde791732016-09-06 14:25:24 -05001451
1452 return buffer
1453
Michael Walshde791732016-09-06 14:25:24 -05001454
Michael Walshde791732016-09-06 14:25:24 -05001455def sprint_pgm_footer():
Michael Walshde791732016-09-06 14:25:24 -05001456 r"""
1457 Return a standardized footer that programs should print at the end of the
1458 program run. It includes useful information like total run time, etc.
1459 """
1460
1461 buffer = "\n" + sprint_time() + "Finished running " + pgm_name + ".\n\n"
1462
1463 total_time = time.time() - start_time
1464 total_time_string = "%0.6f" % total_time
1465
Michael Walsh7423c012016-10-04 10:27:21 -05001466 buffer += sprint_varx(pgm_name_var_name + "_runtime", total_time_string)
Michael Walshbec416d2016-11-10 08:54:52 -06001467 buffer += "\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001468
1469 return buffer
1470
Michael Walsh7423c012016-10-04 10:27:21 -05001471
Michael Walsh7423c012016-10-04 10:27:21 -05001472def sprint(buffer=""):
Michael Walsh7423c012016-10-04 10:27:21 -05001473 r"""
1474 Simply return the user's buffer. This function is used by the qprint and
1475 dprint functions defined dynamically below, i.e. it would not normally be
1476 called for general use.
1477
1478 Description of arguments.
1479 buffer This will be returned to the caller.
1480 """
Michael Walshde791732016-09-06 14:25:24 -05001481
Michael Walsh95e45102018-02-09 12:44:43 -06001482 try:
1483 return str(buffer)
1484 except UnicodeEncodeError:
1485 return buffer
Michael Walshbec416d2016-11-10 08:54:52 -06001486
Michael Walshbec416d2016-11-10 08:54:52 -06001487
Michael Walshbec416d2016-11-10 08:54:52 -06001488def sprintn(buffer=""):
Michael Walshbec416d2016-11-10 08:54:52 -06001489 r"""
1490 Simply return the user's buffer with a line feed. This function is used
1491 by the qprint and dprint functions defined dynamically below, i.e. it
1492 would not normally be called for general use.
1493
1494 Description of arguments.
1495 buffer This will be returned to the caller.
1496 """
1497
Michael Walsh95e45102018-02-09 12:44:43 -06001498 try:
1499 buffer = str(buffer) + "\n"
1500 except UnicodeEncodeError:
1501 buffer = buffer + "\n"
Michael Walshbec416d2016-11-10 08:54:52 -06001502
Michael Walshde791732016-09-06 14:25:24 -05001503 return buffer
1504
Michael Walsh168eb0f2017-12-01 15:35:32 -06001505
Michael Walshfd2733c2017-11-13 11:36:20 -06001506def gp_print(buffer,
1507 stream='stdout'):
Michael Walshfd2733c2017-11-13 11:36:20 -06001508 r"""
1509 Print the buffer using either sys.stdout.write or BuiltIn().log_to_console
1510 depending on whether we are running in a robot environment.
1511
1512 This function is intended for use only by other functions in this module.
1513
1514 Description of arguments:
1515 buffer The string to be printed.
1516 stream Either "stdout" or "stderr".
1517 """
1518
1519 if robot_env:
1520 BuiltIn().log_to_console(buffer, stream=stream, no_newline=True)
1521 else:
1522 if stream == "stdout":
1523 sys.stdout.write(buffer)
1524 sys.stdout.flush()
1525 else:
1526 sys.stderr.write(buffer)
1527 sys.stderr.flush()
Michael Walshde791732016-09-06 14:25:24 -05001528
1529
Michael Walsh168eb0f2017-12-01 15:35:32 -06001530def gp_log(buffer):
Michael Walsh168eb0f2017-12-01 15:35:32 -06001531 r"""
1532 Log the buffer using either python logging or BuiltIn().log depending on
1533 whether we are running in a robot environment.
1534
1535 This function is intended for use only by other functions in this module.
1536
1537 Description of arguments:
1538 buffer The string to be logged.
1539 """
1540
1541 if robot_env:
1542 BuiltIn().log(buffer)
1543 else:
1544 logging.warning(buffer)
1545
1546
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001547def gp_debug_print(buffer):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001548 r"""
Michael Walshfd2733c2017-11-13 11:36:20 -06001549 Print with gp_print only if gen_print_debug is set.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001550
1551 This function is intended for use only by other functions in this module.
1552
1553 Description of arguments:
1554 buffer The string to be printed.
1555 """
1556
1557 if not gen_print_debug:
1558 return
1559
Michael Walshfd2733c2017-11-13 11:36:20 -06001560 gp_print(buffer)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001561
1562
Michael Walshb1500152017-04-12 15:42:43 -05001563def get_var_value(var_value=None,
1564 default=1,
1565 var_name=None):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001566 r"""
Michael Walshb1500152017-04-12 15:42:43 -05001567 Return either var_value, the corresponding global value or default.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001568
Michael Walshb1500152017-04-12 15:42:43 -05001569 If var_value is not None, it will simply be returned.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001570
Michael Walshb1500152017-04-12 15:42:43 -05001571 If var_value is None, this function will return the corresponding global
1572 value of the variable in question.
1573
1574 Note: For global values, if we are in a robot environment,
1575 get_variable_value will be used. Otherwise, the __builtin__ version of
1576 the variable is returned (which are set by gen_arg.py functions).
1577
1578 If there is no global value associated with the variable, default is
1579 returned.
1580
1581 This function is useful for other functions in setting default values for
1582 parameters.
1583
1584 Example use:
1585
1586 def my_func(quiet=None):
1587
1588 quiet = int(get_var_value(quiet, 0))
1589
1590 Example calls to my_func():
1591
1592 In the following example, the caller is explicitly asking to have quiet be
1593 set to 1.
1594
1595 my_func(quiet=1)
1596
1597 In the following example, quiet will be set to the global value of quiet,
1598 if defined, or to 0 (the default).
1599
1600 my_func()
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001601
1602 Description of arguments:
Michael Walshb1500152017-04-12 15:42:43 -05001603 var_value The value to be returned (if not equal to
1604 None).
1605 default The value that is returned if var_value is
1606 None and there is no corresponding global
1607 value defined.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001608 var_name The name of the variable whose value is to
Michael Walshb1500152017-04-12 15:42:43 -05001609 be returned. Under most circumstances,
1610 this value need not be provided. This
1611 function can figure out the name of the
1612 variable passed as var_value. One
1613 exception to this would be if this
1614 function is called directly from a .robot
1615 file.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001616 """
1617
Michael Walshb1500152017-04-12 15:42:43 -05001618 if var_value is not None:
1619 return var_value
1620
1621 if var_name is None:
1622 var_name = get_arg_name(None, 1, 2)
1623
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001624 if robot_env:
Michael Walshc6537442017-06-06 15:33:52 -05001625 var_value = BuiltIn().get_variable_value("${" + var_name + "}",
1626 default)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001627 else:
1628 var_value = getattr(__builtin__, var_name, default)
1629
1630 return var_value
1631
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001632
Michael Walsh052ff812018-05-18 16:09:09 -05001633def get_stack_var(var_name,
1634 default="",
1635 init_stack_ix=2):
Michael Walsh052ff812018-05-18 16:09:09 -05001636 r"""
1637 Starting with the caller's stack level, search upward in the call stack,
1638 for a variable named var_name and return its value. If the variable
1639 cannot be found, return default.
1640
1641 Example code:
1642
1643 def func12():
1644 my_loc_var1 = get_stack_var('my_var1', "default value")
1645
1646 def func11():
1647 my_var1 = 11
1648 func12()
1649
1650 In this example, get_stack_var will find the value of my_var1 in func11's
1651 stack and will therefore return the value 11. Therefore, my_loc_var1
1652 would get set to 11.
1653
1654 Description of argument(s):
1655 var_name The name of the variable to be searched
1656 for.
1657 default The value to return if the the variable
1658 cannot be found.
1659 init_stack_ix The initial stack index from which to
1660 begin the search. 0 would be the index of
1661 this func1tion ("get_stack_var"), 1 would
1662 be the index of the function calling this
1663 function, etc.
1664 """
1665
Michael Walsh6f0362c2019-03-25 14:05:14 -05001666 work_around_inspect_stack_cwd_failure()
Michael Walsh052ff812018-05-18 16:09:09 -05001667 return next((frame[0].f_locals[var_name]
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001668 for frame in inspect.stack()[init_stack_ix:]
1669 if var_name in frame[0].f_locals), default)
Michael Walsh052ff812018-05-18 16:09:09 -05001670
1671
Michael Walsh82acf002017-05-04 14:33:05 -05001672# hidden_text is a list of passwords which are to be replaced with asterisks
1673# by print functions defined in this module.
1674hidden_text = []
1675# password_regex is created based on the contents of hidden_text.
1676password_regex = ""
1677
1678
Michael Walsh82acf002017-05-04 14:33:05 -05001679def register_passwords(*args):
Michael Walsh82acf002017-05-04 14:33:05 -05001680 r"""
1681 Register one or more passwords which are to be hidden in output produced
1682 by the print functions in this module.
1683
1684 Note: Blank password values are NOT registered. They are simply ignored.
1685
1686 Description of argument(s):
1687 args One or more password values. If a given
1688 password value is already registered, this
1689 function will simply do nothing.
1690 """
1691
1692 global hidden_text
1693 global password_regex
1694
1695 for password in args:
1696 if password == "":
1697 break
1698 if password in hidden_text:
1699 break
1700
1701 # Place the password into the hidden_text list.
1702 hidden_text.append(password)
1703 # Create a corresponding password regular expression. Escape regex
1704 # special characters too.
1705 password_regex = '(' +\
1706 '|'.join([re.escape(x) for x in hidden_text]) + ')'
1707
Michael Walsh82acf002017-05-04 14:33:05 -05001708
Michael Walsh82acf002017-05-04 14:33:05 -05001709def replace_passwords(buffer):
Michael Walsh82acf002017-05-04 14:33:05 -05001710 r"""
1711 Return the buffer but with all registered passwords replaced by a string
1712 of asterisks.
1713
1714
1715 Description of argument(s):
1716 buffer The string to be returned but with
1717 passwords replaced.
1718 """
1719
1720 global password_regex
1721
1722 if int(os.environ.get("DEBUG_SHOW_PASSWORDS", "0")):
1723 return buffer
1724
1725 if password_regex == "":
1726 # No passwords to replace.
1727 return buffer
1728
1729 return re.sub(password_regex, "********", buffer)
1730
Michael Walshfd2733c2017-11-13 11:36:20 -06001731
1732def create_print_wrapper_funcs(func_names,
1733 stderr_func_names,
Michael Walsh61c12982019-03-28 12:38:01 -05001734 replace_dict,
1735 func_prefix=""):
Michael Walshfd2733c2017-11-13 11:36:20 -06001736 r"""
1737 Generate code for print wrapper functions and return the generated code as
1738 a string.
1739
1740 To illustrate, suppose there is a "print_foo_bar" function in the
1741 func_names list.
1742 This function will...
1743 - Expect that there is an sprint_foo_bar function already in existence.
1744 - Create a print_foo_bar function which calls sprint_foo_bar and prints
Michael Walshfaafa9c2018-06-27 16:39:31 -05001745 the result.
Michael Walshfd2733c2017-11-13 11:36:20 -06001746 - Create a qprint_foo_bar function which calls upon sprint_foo_bar only if
Michael Walshfaafa9c2018-06-27 16:39:31 -05001747 global value quiet is 0.
Michael Walshfd2733c2017-11-13 11:36:20 -06001748 - Create a dprint_foo_bar function which calls upon sprint_foo_bar only if
Michael Walshfaafa9c2018-06-27 16:39:31 -05001749 global value debug is 1.
Michael Walshfd2733c2017-11-13 11:36:20 -06001750
1751 Also, code will be generated to define aliases for each function as well.
1752 Each alias will be created by replacing "print_" in the function name with
1753 "p" For example, the alias for print_foo_bar will be pfoo_bar.
1754
1755 Description of argument(s):
1756 func_names A list of functions for which print
1757 wrapper function code is to be generated.
1758 stderr_func_names A list of functions whose generated code
1759 should print to stderr rather than to
1760 stdout.
1761 replace_dict Please see the create_func_def_string
1762 function in wrap_utils.py for details on
1763 this parameter. This parameter will be
1764 passed directly to create_func_def_string.
Michael Walsh61c12982019-03-28 12:38:01 -05001765 func_prefix Prefix to be pre-pended to the generated
1766 function name.
Michael Walshfd2733c2017-11-13 11:36:20 -06001767 """
1768
1769 buffer = ""
1770
1771 for func_name in func_names:
1772 if func_name in stderr_func_names:
1773 replace_dict['output_stream'] = "stderr"
1774 else:
1775 replace_dict['output_stream'] = "stdout"
1776
1777 s_func_name = "s" + func_name
1778 q_func_name = "q" + func_name
1779 d_func_name = "d" + func_name
1780
1781 # We don't want to try to redefine the "print" function, thus the
1782 # following if statement.
1783 if func_name != "print":
Michael Walsh61c12982019-03-28 12:38:01 -05001784 func_def = create_func_def_string(s_func_name,
1785 func_prefix + func_name,
Michael Walshfd2733c2017-11-13 11:36:20 -06001786 print_func_template,
1787 replace_dict)
1788 buffer += func_def
1789
Michael Walsh61c12982019-03-28 12:38:01 -05001790 func_def = create_func_def_string(s_func_name,
1791 func_prefix + "q" + func_name,
Michael Walshfd2733c2017-11-13 11:36:20 -06001792 qprint_func_template, replace_dict)
1793 buffer += func_def
1794
Michael Walsh61c12982019-03-28 12:38:01 -05001795 func_def = create_func_def_string(s_func_name,
1796 func_prefix + "d" + func_name,
Michael Walshfd2733c2017-11-13 11:36:20 -06001797 dprint_func_template, replace_dict)
1798 buffer += func_def
1799
Michael Walsh61c12982019-03-28 12:38:01 -05001800 func_def = create_func_def_string(s_func_name,
1801 func_prefix + "l" + func_name,
Michael Walsh168eb0f2017-12-01 15:35:32 -06001802 lprint_func_template, replace_dict)
1803 buffer += func_def
1804
Michael Walshfd2733c2017-11-13 11:36:20 -06001805 # Create abbreviated aliases (e.g. spvar is an alias for sprint_var).
1806 alias = re.sub("print_", "p", func_name)
1807 alias = re.sub("print", "p", alias)
Michael Walsh61c12982019-03-28 12:38:01 -05001808 prefixes = [func_prefix + "", "s", func_prefix + "q",
1809 func_prefix + "d", func_prefix + "l"]
Michael Walshfd2733c2017-11-13 11:36:20 -06001810 for prefix in prefixes:
1811 if alias == "p":
1812 continue
1813 func_def = prefix + alias + " = " + prefix + func_name
1814 buffer += func_def + "\n"
1815
1816 return buffer
Michael Walsh82acf002017-05-04 14:33:05 -05001817
1818
Michael Walshde791732016-09-06 14:25:24 -05001819# In the following section of code, we will dynamically create print versions
1820# for each of the sprint functions defined above. So, for example, where we
1821# have an sprint_time() function defined above that returns the time to the
Michael Walsh7423c012016-10-04 10:27:21 -05001822# caller in a string, we will create a corresponding print_time() function
1823# that will print that string directly to stdout.
Michael Walshde791732016-09-06 14:25:24 -05001824
Michael Walsh61c12982019-03-28 12:38:01 -05001825# It can be complicated to follow what's being created below. Here is an
Michael Walshfd2733c2017-11-13 11:36:20 -06001826# example of the print_time() function that will be created:
Michael Walshde791732016-09-06 14:25:24 -05001827
Michael Walshfd2733c2017-11-13 11:36:20 -06001828# def print_time(buffer=''):
Michael Walsh61c12982019-03-28 12:38:01 -05001829# gp_print(replace_passwords(sprint_time(buffer=buffer)), stream='stdout')
1830
1831# For each print function defined below, there will also be a qprint, a
1832# dprint and an lprint version defined (e.g. qprint_time, dprint_time,
1833# lprint_time).
1834
1835# The q version of each print function will only print if the quiet variable
1836# is 0.
1837# The d version of each print function will only print if the debug variable
1838# is 1.
1839# The l version of each print function will print the contents as log data.
1840# For conventional programs, this means use of the logging module. For robot
1841# programs it means use of the BuiltIn().log() function.
Michael Walshde791732016-09-06 14:25:24 -05001842
Michael Walshfd2733c2017-11-13 11:36:20 -06001843# Templates for the various print wrapper functions.
1844print_func_template = \
1845 [
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001846 " <mod_qualifier>gp_print(<mod_qualifier>replace_passwords("
1847 + "<call_line>), stream='<output_stream>')"
Michael Walshfd2733c2017-11-13 11:36:20 -06001848 ]
1849
1850qprint_func_template = \
1851 [
Michael Walsh589ae762019-03-19 13:22:39 -05001852 " quiet_default = <mod_qualifier>get_var_value(None, 0, \"quiet\")",
1853 " quiet = <mod_qualifier>get_stack_var(\"quiet\", quiet_default)",
1854 " if int(quiet): return"
Michael Walshfd2733c2017-11-13 11:36:20 -06001855 ] + print_func_template
1856
1857dprint_func_template = \
1858 [
Michael Walsh589ae762019-03-19 13:22:39 -05001859 " debug_default = <mod_qualifier>get_var_value(None, 0, \"debug\")",
1860 " debug = <mod_qualifier>get_stack_var(\"debug\", debug_default)",
1861 " if not int(debug): return"
Michael Walshfd2733c2017-11-13 11:36:20 -06001862 ] + print_func_template
1863
Michael Walsh168eb0f2017-12-01 15:35:32 -06001864lprint_func_template = \
1865 [
Michael Walsh61c12982019-03-28 12:38:01 -05001866 " <mod_qualifier>set_last_seconds_ix(<mod_qualifier>"
1867 + "lprint_last_seconds_ix())",
1868 " <mod_qualifier>gp_log(<mod_qualifier>replace_passwords"
1869 + "(<call_line>))",
1870 " <mod_qualifier>set_last_seconds_ix(<mod_qualifier>"
1871 + "standard_print_last_seconds_ix())"
Michael Walsh168eb0f2017-12-01 15:35:32 -06001872 ]
1873
Michael Walsh81c02342018-01-05 15:43:28 -06001874replace_dict = {'output_stream': 'stdout', 'mod_qualifier': ''}
Michael Walshfd2733c2017-11-13 11:36:20 -06001875
Michael Walsh61c12982019-03-28 12:38:01 -05001876gp_debug_print("robot_env: " + str(robot_env) + "\n")
Michael Walshde791732016-09-06 14:25:24 -05001877
1878# func_names contains a list of all print functions which should be created
1879# from their sprint counterparts.
1880func_names = ['print_time', 'print_timen', 'print_error', 'print_varx',
Michael Walsh18176322016-11-15 15:11:21 -06001881 'print_var', 'print_vars', 'print_dashes', 'indent',
1882 'print_call_stack', 'print_func_name', 'print_executing',
1883 'print_pgm_header', 'print_issuing', 'print_pgm_footer',
1884 'print_error_report', 'print', 'printn']
Michael Walshde791732016-09-06 14:25:24 -05001885
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001886# stderr_func_names is a list of functions whose output should go to stderr
1887# rather than stdout.
1888stderr_func_names = ['print_error', 'print_error_report']
Michael Walshde791732016-09-06 14:25:24 -05001889
Michael Walshfd2733c2017-11-13 11:36:20 -06001890func_defs = create_print_wrapper_funcs(func_names, stderr_func_names,
1891 replace_dict)
1892gp_debug_print(func_defs)
1893exec(func_defs)