blob: d65ed395d158cdc8d4264f12688a46cf1d40fad9 [file] [log] [blame]
Michael Walshde791732016-09-06 14:25:24 -05001#!/usr/bin/env python
2
3r"""
Michael Walsh46fcecb2019-10-16 17:11:59 -05004This module provides many print functions such as sprint_var, sprint_time, sprint_error, sprint_call_stack.
Michael Walshde791732016-09-06 14:25:24 -05005"""
6
7import sys
8import os
9import time
10import inspect
11import re
12import grp
13import socket
14import argparse
Michael Walsh4dbb6002019-05-17 15:51:15 -050015import copy
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 Walsh46fcecb2019-10-16 17:11:59 -050029 # Having access to the robot libraries alone does not indicate that we are in a robot environment. The
30 # following try block should confirm that.
Michael Walshb1500152017-04-12 15:42:43 -050031 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
Michael Walsh46fcecb2019-10-16 17:11:59 -050040# Setting these variables for use both inside this module and by programs importing this module.
Michael Walshbf605652017-09-01 12:33:26 -050041pgm_file_path = sys.argv[0]
42pgm_name = os.path.basename(pgm_file_path)
Michael Walsh3ba8ecd2018-04-24 11:33:25 -050043pgm_dir_path = os.path.normpath(re.sub("/" + pgm_name, "", pgm_file_path)) +\
44 os.path.sep
Michael Walsh7423c012016-10-04 10:27:21 -050045
Michael Walshde791732016-09-06 14:25:24 -050046
Michael Walsh46fcecb2019-10-16 17:11:59 -050047# Some functions (e.g. sprint_pgm_header) have need of a program name value that looks more like a valid
48# variable name. Therefore, we'll swap odd characters like "." out for underscores.
Michael Walshde791732016-09-06 14:25:24 -050049pgm_name_var_name = pgm_name.replace(".", "_")
50
51# Initialize global values used as defaults by print_time, print_var, etc.
Michael Walsh4dbb6002019-05-17 15:51:15 -050052dft_indent = 0
Michael Walshde791732016-09-06 14:25:24 -050053
Michael Walsh46fcecb2019-10-16 17:11:59 -050054# Calculate default column width for print_var functions based on environment variable settings. The
55# objective is to make the variable values line up nicely with the time stamps.
Michael Walsh4dbb6002019-05-17 15:51:15 -050056dft_col1_width = 29
Michael Walshb1500152017-04-12 15:42:43 -050057
58NANOSECONDS = os.environ.get('NANOSECONDS', '1')
59
Michael Walshde791732016-09-06 14:25:24 -050060if NANOSECONDS == "1":
Michael Walsh4dbb6002019-05-17 15:51:15 -050061 dft_col1_width = dft_col1_width + 7
Michael Walshde791732016-09-06 14:25:24 -050062
Michael Walshb1500152017-04-12 15:42:43 -050063SHOW_ELAPSED_TIME = os.environ.get('SHOW_ELAPSED_TIME', '1')
Michael Walshde791732016-09-06 14:25:24 -050064
65if SHOW_ELAPSED_TIME == "1":
66 if NANOSECONDS == "1":
Michael Walsh4dbb6002019-05-17 15:51:15 -050067 dft_col1_width = dft_col1_width + 14
Michael Walshde791732016-09-06 14:25:24 -050068 else:
Michael Walsh4dbb6002019-05-17 15:51:15 -050069 dft_col1_width = dft_col1_width + 7
Michael Walshde791732016-09-06 14:25:24 -050070
71# Initialize some time variables used in module functions.
72start_time = time.time()
Michael Walsh4fea2cf2018-08-22 17:48:18 -050073# sprint_time_last_seconds is used to calculate elapsed seconds.
Michael Walsh61c12982019-03-28 12:38:01 -050074sprint_time_last_seconds = [start_time, start_time]
Michael Walsh4fea2cf2018-08-22 17:48:18 -050075# Define global index for the sprint_time_last_seconds list.
76last_seconds_ix = 0
77
78
Michael Walsh61c12982019-03-28 12:38:01 -050079def set_last_seconds_ix(ix):
80 r"""
81 Set the "last_seconds_ix" module variable to the index value.
82
83 Description of argument(s):
Michael Walsh46fcecb2019-10-16 17:11:59 -050084 ix The index value to be set into the module global last_seconds_ix variable.
Michael Walsh61c12982019-03-28 12:38:01 -050085 """
86 global last_seconds_ix
87 last_seconds_ix = ix
88
89
Michael Walsh46fcecb2019-10-16 17:11:59 -050090# Since output from the lprint_ functions goes to a different location than the output from the print_
91# functions (e.g. a file vs. the console), sprint_time_last_seconds has been created as a list rather than a
92# simple integer so that it can store multiple sprint_time_last_seconds values. Standard print_ functions
93# defined in this file will use sprint_time_last_seconds[0] and the lprint_ functions will use
Michael Walsh4fea2cf2018-08-22 17:48:18 -050094# sprint_time_last_seconds[1].
Michael Walsh61c12982019-03-28 12:38:01 -050095def standard_print_last_seconds_ix():
96 r"""
97 Return the standard print last_seconds index value to the caller.
98 """
99 return 0
100
101
Michael Walsh4fea2cf2018-08-22 17:48:18 -0500102def lprint_last_seconds_ix():
103 r"""
104 Return lprint last_seconds index value to the caller.
105 """
106 return 1
107
Michael Walshde791732016-09-06 14:25:24 -0500108
Michael Walsh46fcecb2019-10-16 17:11:59 -0500109# The user can set environment variable "GEN_PRINT_DEBUG" to get debug output from this module.
Michael Walsh2ee77cd2017-03-08 11:50:17 -0600110gen_print_debug = int(os.environ.get('GEN_PRINT_DEBUG', 0))
Michael Walsh7423c012016-10-04 10:27:21 -0500111
Michael Walshde791732016-09-06 14:25:24 -0500112
Michael Walshde791732016-09-06 14:25:24 -0500113def sprint_func_name(stack_frame_ix=None):
Michael Walshde791732016-09-06 14:25:24 -0500114 r"""
115 Return the function name associated with the indicated stack frame.
116
Michael Walsh4dbb6002019-05-17 15:51:15 -0500117 Description of argument(s):
Michael Walsh46fcecb2019-10-16 17:11:59 -0500118 stack_frame_ix The index of the stack frame whose function name should be returned. If
119 the caller does not specify a value, this function will set the value to
120 1 which is the index of the caller's stack frame. If the caller is the
121 wrapper function "print_func_name", this function will bump it up by 1.
Michael Walshde791732016-09-06 14:25:24 -0500122 """
123
Michael Walsh46fcecb2019-10-16 17:11:59 -0500124 # If user specified no stack_frame_ix, we'll set it to a proper default value.
Michael Walshde791732016-09-06 14:25:24 -0500125 if stack_frame_ix is None:
126 func_name = sys._getframe().f_code.co_name
127 caller_func_name = sys._getframe(1).f_code.co_name
128 if func_name[1:] == caller_func_name:
129 stack_frame_ix = 2
130 else:
131 stack_frame_ix = 1
132
133 func_name = sys._getframe(stack_frame_ix).f_code.co_name
134
135 return func_name
136
Michael Walshde791732016-09-06 14:25:24 -0500137
Michael Walsh6f0362c2019-03-25 14:05:14 -0500138def work_around_inspect_stack_cwd_failure():
139 r"""
Michael Walsh46fcecb2019-10-16 17:11:59 -0500140 Work around the inspect.stack() getcwd() failure by making "/tmp" the current working directory.
141
142 NOTES: If the current working directory has been deleted, inspect.stack() will fail with "OSError: [Errno
143 2] No such file or directory" because it tries to do a getcwd().
144
145 This function will try to prevent this failure by detecting the scenario in advance and making "/tmp" the
Michael Walsh6f0362c2019-03-25 14:05:14 -0500146 current working directory.
Michael Walsh6f0362c2019-03-25 14:05:14 -0500147 """
148 try:
149 os.getcwd()
150 except OSError:
151 os.chdir("/tmp")
152
153
Michael Walsh1173a522018-05-21 17:24:51 -0500154def get_line_indent(line):
155 r"""
156 Return the number of spaces at the beginning of the line.
157 """
158
159 return len(line) - len(line.lstrip(' '))
160
161
Michael Walsh46fcecb2019-10-16 17:11:59 -0500162# get_arg_name is not a print function per se. It has been included in this module because it is used by
163# sprint_var which is defined in this module.
Michael Walshde791732016-09-06 14:25:24 -0500164def get_arg_name(var,
165 arg_num=1,
166 stack_frame_ix=1):
Michael Walshde791732016-09-06 14:25:24 -0500167 r"""
Michael Walsh46fcecb2019-10-16 17:11:59 -0500168 Return the "name" of an argument passed to a function. This could be a literal or a variable name.
Michael Walshde791732016-09-06 14:25:24 -0500169
Michael Walsh4dbb6002019-05-17 15:51:15 -0500170 Description of argument(s):
171 var The variable whose name is to be returned.
Michael Walsh46fcecb2019-10-16 17:11:59 -0500172 arg_num The arg number whose name is to be returned. To illustrate how arg_num
173 is processed, suppose that a programmer codes this line: "rc, outbuf =
174 my_func(var1, var2)" and suppose that my_func has this line of code:
175 "result = gp.get_arg_name(0, arg_num, 2)". If arg_num is positive, the
176 indicated argument is returned. For example, if arg_num is 1, "var1"
177 would be returned, If arg_num is 2, "var2" would be returned. If arg_num
178 exceeds the number of arguments, get_arg_name will simply return a
179 complete list of the arguments. If arg_num is 0, get_arg_name will
180 return the name of the target function as specified in the calling line
181 ("my_func" in this case). To clarify, if the caller of the target
182 function uses an alias function name, the alias name would be returned.
183 If arg_num is negative, an lvalue variable name is returned. Continuing
184 with the given example, if arg_num is -2 the 2nd parm to the left of the
185 "=" ("rc" in this case) should be returned. If arg_num is -1, the 1st
186 parm to the left of the "=" ("out_buf" in this case) should be returned.
187 If arg_num is less than -2, an entire dictionary is returned. The keys
188 to the dictionary for this example would be -2 and -1.
189 stack_frame_ix The stack frame index of the target function. This value must be 1 or
190 greater. 1 would indicate get_arg_name's stack frame. 2 would be the
191 caller of get_arg_name's stack frame, etc.
Michael Walshde791732016-09-06 14:25:24 -0500192
193 Example 1:
194
195 my_var = "mike"
196 var_name = get_arg_name(my_var)
197
198 In this example, var_name will receive the value "my_var".
199
200 Example 2:
201
202 def test1(var):
Michael Walsh46fcecb2019-10-16 17:11:59 -0500203 # Getting the var name of the first arg to this function, test1. Note, in this case, it doesn't
204 # matter what is passed as the first arg to get_arg_name since it is the caller's variable name that
Michael Walsh4dbb6002019-05-17 15:51:15 -0500205 # matters.
Michael Walshde791732016-09-06 14:25:24 -0500206 dummy = 1
207 arg_num = 1
208 stack_frame = 2
209 var_name = get_arg_name(dummy, arg_num, stack_frame)
210
211 # Mainline...
212
213 another_var = "whatever"
214 test1(another_var)
215
216 In this example, var_name will be set to "another_var".
217
218 """
219
Michael Walsh46fcecb2019-10-16 17:11:59 -0500220 # Note: To avoid infinite recursion, avoid calling any function that calls this function (e.g.
221 # sprint_var, valid_value, etc.).
Michael Walshde791732016-09-06 14:25:24 -0500222
Michael Walsh46fcecb2019-10-16 17:11:59 -0500223 # The user can set environment variable "GET_ARG_NAME_DEBUG" to get debug output from this function.
Michael Walsh23e7f492017-01-10 11:34:47 -0600224 local_debug = int(os.environ.get('GET_ARG_NAME_DEBUG', 0))
Michael Walsh46fcecb2019-10-16 17:11:59 -0500225 # In addition to GET_ARG_NAME_DEBUG, the user can set environment variable "GET_ARG_NAME_SHOW_SOURCE" to
226 # have this function include source code in the debug output.
Michael Walsh23e7f492017-01-10 11:34:47 -0600227 local_debug_show_source = int(
228 os.environ.get('GET_ARG_NAME_SHOW_SOURCE', 0))
Michael Walshde791732016-09-06 14:25:24 -0500229
Michael Walshde791732016-09-06 14:25:24 -0500230 if stack_frame_ix < 1:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500231 print_error("Programmer error - Variable \"stack_frame_ix\" has an"
232 + " invalid value of \"" + str(stack_frame_ix) + "\". The"
233 + " value must be an integer that is greater than or equal"
234 + " to 1.\n")
Michael Walshde791732016-09-06 14:25:24 -0500235 return
236
237 if local_debug:
238 debug_indent = 2
Michael Walsh23e7f492017-01-10 11:34:47 -0600239 print("")
240 print_dashes(0, 120)
Michael Walshde791732016-09-06 14:25:24 -0500241 print(sprint_func_name() + "() parms:")
Michael Walsh4dbb6002019-05-17 15:51:15 -0500242 print_varx("var", var, indent=debug_indent)
243 print_varx("arg_num", arg_num, indent=debug_indent)
244 print_varx("stack_frame_ix", stack_frame_ix, indent=debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600245 print("")
246 print_call_stack(debug_indent, 2)
Michael Walshde791732016-09-06 14:25:24 -0500247
Michael Walsh6f0362c2019-03-25 14:05:14 -0500248 work_around_inspect_stack_cwd_failure()
Michael Walsh23e7f492017-01-10 11:34:47 -0600249 for count in range(0, 2):
250 try:
251 frame, filename, cur_line_no, function_name, lines, index = \
252 inspect.stack()[stack_frame_ix]
253 except IndexError:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500254 print_error("Programmer error - The caller has asked for"
255 + " information about the stack frame at index \""
256 + str(stack_frame_ix) + "\". However, the stack"
257 + " only contains " + str(len(inspect.stack()))
258 + " entries. Therefore the stack frame index is out"
259 + " of range.\n")
Michael Walsh23e7f492017-01-10 11:34:47 -0600260 return
261 if filename != "<string>":
262 break
Michael Walsh46fcecb2019-10-16 17:11:59 -0500263 # filename of "<string>" may mean that the function in question was defined dynamically and
264 # therefore its code stack is inaccessible. This may happen with functions like "rqprint_var". In
265 # this case, we'll increment the stack_frame_ix and try again.
Michael Walsh23e7f492017-01-10 11:34:47 -0600266 stack_frame_ix += 1
267 if local_debug:
268 print("Adjusted stack_frame_ix...")
Michael Walsh4dbb6002019-05-17 15:51:15 -0500269 print_varx("stack_frame_ix", stack_frame_ix, indent=debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500270
Michael Walsh1173a522018-05-21 17:24:51 -0500271 real_called_func_name = sprint_func_name(stack_frame_ix)
Michael Walsh23e7f492017-01-10 11:34:47 -0600272
273 module = inspect.getmodule(frame)
274
Michael Walsh46fcecb2019-10-16 17:11:59 -0500275 # Though one would expect inspect.getsourcelines(frame) to get all module source lines if the frame is
276 # "<module>", it doesn't do that. Therefore, for this special case, do inspect.getsourcelines(module).
Michael Walsh23e7f492017-01-10 11:34:47 -0600277 if function_name == "<module>":
278 source_lines, source_line_num =\
279 inspect.getsourcelines(module)
280 line_ix = cur_line_no - source_line_num - 1
281 else:
282 source_lines, source_line_num =\
283 inspect.getsourcelines(frame)
284 line_ix = cur_line_no - source_line_num
285
286 if local_debug:
287 print("\n Variables retrieved from inspect.stack() function:")
Michael Walsh4dbb6002019-05-17 15:51:15 -0500288 print_varx("frame", frame, indent=debug_indent + 2)
289 print_varx("filename", filename, indent=debug_indent + 2)
290 print_varx("cur_line_no", cur_line_no, indent=debug_indent + 2)
291 print_varx("function_name", function_name, indent=debug_indent + 2)
292 print_varx("lines", lines, indent=debug_indent + 2)
293 print_varx("index", index, indent=debug_indent + 2)
294 print_varx("source_line_num", source_line_num, indent=debug_indent)
295 print_varx("line_ix", line_ix, indent=debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600296 if local_debug_show_source:
Michael Walsh4dbb6002019-05-17 15:51:15 -0500297 print_varx("source_lines", source_lines, indent=debug_indent)
298 print_varx("real_called_func_name", real_called_func_name,
299 indent=debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600300
Michael Walsh46fcecb2019-10-16 17:11:59 -0500301 # Get a list of all functions defined for the module. Note that this doesn't work consistently when
302 # _run_exitfuncs is at the top of the stack (i.e. if we're running an exit function). I've coded a
303 # work-around below for this deficiency.
Michael Walsh23e7f492017-01-10 11:34:47 -0600304 all_functions = inspect.getmembers(module, inspect.isfunction)
305
Michael Walsh46fcecb2019-10-16 17:11:59 -0500306 # Get called_func_id by searching for our function in the list of all functions.
Michael Walsh23e7f492017-01-10 11:34:47 -0600307 called_func_id = None
308 for func_name, function in all_functions:
Michael Walsh1173a522018-05-21 17:24:51 -0500309 if func_name == real_called_func_name:
Michael Walsh23e7f492017-01-10 11:34:47 -0600310 called_func_id = id(function)
311 break
Michael Walsh46fcecb2019-10-16 17:11:59 -0500312 # NOTE: The only time I've found that called_func_id can't be found is when we're running from an exit
313 # function.
Michael Walsh23e7f492017-01-10 11:34:47 -0600314
315 # Look for other functions in module with matching id.
Michael Walsh1173a522018-05-21 17:24:51 -0500316 aliases = set([real_called_func_name])
Michael Walsh23e7f492017-01-10 11:34:47 -0600317 for func_name, function in all_functions:
Michael Walsh1173a522018-05-21 17:24:51 -0500318 if func_name == real_called_func_name:
Michael Walsh23e7f492017-01-10 11:34:47 -0600319 continue
320 func_id = id(function)
321 if func_id == called_func_id:
322 aliases.add(func_name)
323
Michael Walsh46fcecb2019-10-16 17:11:59 -0500324 # In most cases, my general purpose code above will find all aliases. However, for the odd case (i.e.
325 # running from exit function), I've added code to handle pvar, qpvar, dpvar, etc. aliases explicitly
326 # since they are defined in this module and used frequently.
Michael Walsh23e7f492017-01-10 11:34:47 -0600327 # 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 Walsh46fcecb2019-10-16 17:11:59 -0500330 # The call to the function could be encased in a recast (e.g. int(func_name())).
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500331 recast_regex = "([^ ]+\\([ ]*)?"
332 import_name_regex = "([a-zA-Z0-9_]+\\.)?"
Michael Walsh3f248272018-06-01 13:59:35 -0500333 func_name_regex = recast_regex + import_name_regex + "(" +\
334 '|'.join(aliases) + ")"
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500335 pre_args_regex = ".*" + func_name_regex + "[ ]*\\("
Michael Walsh23e7f492017-01-10 11:34:47 -0600336
Michael Walsh46fcecb2019-10-16 17:11:59 -0500337 # Search backward through source lines looking for the calling function name.
Michael Walsh23e7f492017-01-10 11:34:47 -0600338 found = False
339 for start_line_ix in range(line_ix, 0, -1):
340 # Skip comment lines.
341 if re.match(r"[ ]*#", source_lines[start_line_ix]):
342 continue
Michael Walsh1173a522018-05-21 17:24:51 -0500343 if re.match(pre_args_regex, source_lines[start_line_ix]):
Michael Walsh23e7f492017-01-10 11:34:47 -0600344 found = True
345 break
346 if not found:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500347 print_error("Programmer error - Could not find the source line with"
348 + " a reference to function \"" + real_called_func_name
349 + "\".\n")
Michael Walsh23e7f492017-01-10 11:34:47 -0600350 return
351
Michael Walsh46fcecb2019-10-16 17:11:59 -0500352 # Search forward through the source lines looking for a line whose indentation is the same or less than
353 # the start line. The end of our composite line should be the line preceding that line.
Michael Walsh1173a522018-05-21 17:24:51 -0500354 start_indent = get_line_indent(source_lines[start_line_ix])
Michael Walsh37cd29d2018-05-24 13:19:18 -0500355 end_line_ix = line_ix
Michael Walsh23e7f492017-01-10 11:34:47 -0600356 for end_line_ix in range(line_ix + 1, len(source_lines)):
357 if source_lines[end_line_ix].strip() == "":
358 continue
Michael Walsh1173a522018-05-21 17:24:51 -0500359 line_indent = get_line_indent(source_lines[end_line_ix])
Michael Walsh82acf002017-05-04 14:33:05 -0500360 if line_indent <= start_indent:
Michael Walsh23e7f492017-01-10 11:34:47 -0600361 end_line_ix -= 1
362 break
Michael Walsh1173a522018-05-21 17:24:51 -0500363 if start_line_ix != 0:
Michael Walsh46fcecb2019-10-16 17:11:59 -0500364 # Check to see whether the start line is a continuation of the prior line.
Michael Walsha52e9eb2018-09-10 13:56:01 -0500365 prior_line = source_lines[start_line_ix - 1]
366 prior_line_stripped = re.sub(r"[ ]*\\([\r\n]$)", " \\1", prior_line)
367 prior_line_indent = get_line_indent(prior_line)
368 if prior_line != prior_line_stripped and\
369 prior_line_indent < start_indent:
Michael Walsh1173a522018-05-21 17:24:51 -0500370 start_line_ix -= 1
Michael Walsha52e9eb2018-09-10 13:56:01 -0500371 # Remove the backslash (continuation char) from prior line.
372 source_lines[start_line_ix] = prior_line_stripped
Michael Walsh23e7f492017-01-10 11:34:47 -0600373
374 # Join the start line through the end line into a composite line.
375 composite_line = ''.join(map(str.strip,
Gunnar Mills096cd562018-03-26 10:19:12 -0500376 source_lines[start_line_ix:end_line_ix + 1]))
Michael Walsh1173a522018-05-21 17:24:51 -0500377 # Insert one space after first "=" if there isn't one already.
378 composite_line = re.sub("=[ ]*([^ ])", "= \\1", composite_line, 1)
Michael Walsh7423c012016-10-04 10:27:21 -0500379
Michael Walsh3f248272018-06-01 13:59:35 -0500380 lvalue_regex = "[ ]*=[ ]+" + func_name_regex + ".*"
Michael Walsh1173a522018-05-21 17:24:51 -0500381 lvalue_string = re.sub(lvalue_regex, "", composite_line)
Michael Walsh3f248272018-06-01 13:59:35 -0500382 if lvalue_string == composite_line:
383 # i.e. the regex did not match so there are no lvalues.
384 lvalue_string = ""
Michael Walsh37762f92018-08-07 14:59:18 -0500385 lvalues_list = list(filter(None, map(str.strip, lvalue_string.split(","))))
Michael Walsh3f248272018-06-01 13:59:35 -0500386 try:
387 lvalues = collections.OrderedDict()
388 except AttributeError:
389 # A non-ordered dict doesn't look as nice when printed but it will do.
390 lvalues = {}
Michael Walsh1173a522018-05-21 17:24:51 -0500391 ix = len(lvalues_list) * -1
392 for lvalue in lvalues_list:
393 lvalues[ix] = lvalue
394 ix += 1
Michael Walsh3f248272018-06-01 13:59:35 -0500395 lvalue_prefix_regex = "(.*=[ ]+)?"
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500396 called_func_name_regex = lvalue_prefix_regex + func_name_regex +\
397 "[ ]*\\(.*"
Michael Walsh3f248272018-06-01 13:59:35 -0500398 called_func_name = re.sub(called_func_name_regex, "\\4", composite_line)
Michael Walsh1173a522018-05-21 17:24:51 -0500399 arg_list_etc = "(" + re.sub(pre_args_regex, "", composite_line)
Michael Walshde791732016-09-06 14:25:24 -0500400 if local_debug:
Michael Walsh4dbb6002019-05-17 15:51:15 -0500401 print_varx("aliases", aliases, indent=debug_indent)
402 print_varx("import_name_regex", import_name_regex, indent=debug_indent)
403 print_varx("func_name_regex", func_name_regex, indent=debug_indent)
404 print_varx("pre_args_regex", pre_args_regex, indent=debug_indent)
405 print_varx("start_line_ix", start_line_ix, indent=debug_indent)
406 print_varx("end_line_ix", end_line_ix, indent=debug_indent)
407 print_varx("composite_line", composite_line, indent=debug_indent)
408 print_varx("lvalue_regex", lvalue_regex, indent=debug_indent)
409 print_varx("lvalue_string", lvalue_string, indent=debug_indent)
410 print_varx("lvalues", lvalues, indent=debug_indent)
411 print_varx("called_func_name_regex", called_func_name_regex,
412 indent=debug_indent)
413 print_varx("called_func_name", called_func_name, indent=debug_indent)
414 print_varx("arg_list_etc", arg_list_etc, indent=debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500415
416 # Parse arg list...
417 # Initialize...
418 nest_level = -1
419 arg_ix = 0
Michael Walsh7423c012016-10-04 10:27:21 -0500420 args_list = [""]
Michael Walshde791732016-09-06 14:25:24 -0500421 for ix in range(0, len(arg_list_etc)):
422 char = arg_list_etc[ix]
423 # Set the nest_level based on whether we've encounted a parenthesis.
424 if char == "(":
425 nest_level += 1
426 if nest_level == 0:
427 continue
428 elif char == ")":
429 nest_level -= 1
430 if nest_level < 0:
431 break
432
Michael Walsh46fcecb2019-10-16 17:11:59 -0500433 # If we reach a comma at base nest level, we are done processing an argument so we increment arg_ix
434 # and initialize a new args_list entry.
Michael Walshde791732016-09-06 14:25:24 -0500435 if char == "," and nest_level == 0:
436 arg_ix += 1
Michael Walsh7423c012016-10-04 10:27:21 -0500437 args_list.append("")
Michael Walshde791732016-09-06 14:25:24 -0500438 continue
439
Michael Walsh46fcecb2019-10-16 17:11:59 -0500440 # For any other character, we append it it to the current arg list entry.
Michael Walsh7423c012016-10-04 10:27:21 -0500441 args_list[arg_ix] += char
Michael Walshde791732016-09-06 14:25:24 -0500442
443 # Trim whitespace from each list entry.
Michael Walsh7423c012016-10-04 10:27:21 -0500444 args_list = [arg.strip() for arg in args_list]
Michael Walshde791732016-09-06 14:25:24 -0500445
Michael Walsh1173a522018-05-21 17:24:51 -0500446 if arg_num < 0:
447 if abs(arg_num) > len(lvalues):
448 argument = lvalues
449 else:
450 argument = lvalues[arg_num]
451 elif arg_num == 0:
452 argument = called_func_name
Michael Walsh2750b442018-05-18 14:49:11 -0500453 else:
Michael Walsh1173a522018-05-21 17:24:51 -0500454 if arg_num > len(args_list):
455 argument = args_list
456 else:
457 argument = args_list[arg_num - 1]
Michael Walshde791732016-09-06 14:25:24 -0500458
459 if local_debug:
Michael Walsh4dbb6002019-05-17 15:51:15 -0500460 print_varx("args_list", args_list, indent=debug_indent)
461 print_varx("argument", argument, indent=debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600462 print_dashes(0, 120)
Michael Walshde791732016-09-06 14:25:24 -0500463
464 return argument
465
Michael Walshde791732016-09-06 14:25:24 -0500466
Michael Walshde791732016-09-06 14:25:24 -0500467def sprint_time(buffer=""):
Michael Walshde791732016-09-06 14:25:24 -0500468 r"""
469 Return the time in the following format.
470
471 Example:
472
473 The following python code...
474
475 sys.stdout.write(sprint_time())
476 sys.stdout.write("Hi.\n")
477
478 Will result in the following type of output:
479
480 #(CDT) 2016/07/08 15:25:35 - Hi.
481
482 Example:
483
484 The following python code...
485
486 sys.stdout.write(sprint_time("Hi.\n"))
487
488 Will result in the following type of output:
489
490 #(CDT) 2016/08/03 17:12:05 - Hi.
491
Michael Walsh46fcecb2019-10-16 17:11:59 -0500492 The following environment variables will affect the formatting as described:
493 NANOSECONDS This will cause the time stamps to be precise to the microsecond (Yes, it
494 probably should have been named MICROSECONDS but the convention was set
495 long ago so we're sticking with it). Example of the output when
496 environment variable NANOSECONDS=1.
Michael Walshde791732016-09-06 14:25:24 -0500497
498 #(CDT) 2016/08/03 17:16:25.510469 - Hi.
499
Michael Walsh46fcecb2019-10-16 17:11:59 -0500500 SHOW_ELAPSED_TIME This will cause the elapsed time to be included in the output. This is
501 the amount of time that has elapsed since the last time this function was
502 called. The precision of the elapsed time field is also affected by the
503 value of the NANOSECONDS environment variable. Example of the output
504 when environment variable NANOSECONDS=0 and SHOW_ELAPSED_TIME=1.
Michael Walshde791732016-09-06 14:25:24 -0500505
506 #(CDT) 2016/08/03 17:17:40 - 0 - Hi.
507
Michael Walsh46fcecb2019-10-16 17:11:59 -0500508 Example of the output when environment variable NANOSECONDS=1 and SHOW_ELAPSED_TIME=1.
Michael Walshde791732016-09-06 14:25:24 -0500509
510 #(CDT) 2016/08/03 17:18:47.317339 - 0.000046 - Hi.
511
Michael Walsh4dbb6002019-05-17 15:51:15 -0500512 Description of argument(s).
Michael Walsh46fcecb2019-10-16 17:11:59 -0500513 buffer This will be appended to the formatted time string.
Michael Walshde791732016-09-06 14:25:24 -0500514 """
515
516 global NANOSECONDS
517 global SHOW_ELAPSED_TIME
518 global sprint_time_last_seconds
Michael Walsh4fea2cf2018-08-22 17:48:18 -0500519 global last_seconds_ix
Michael Walshde791732016-09-06 14:25:24 -0500520
521 seconds = time.time()
522 loc_time = time.localtime(seconds)
523 nanoseconds = "%0.6f" % seconds
524 pos = nanoseconds.find(".")
525 nanoseconds = nanoseconds[pos:]
526
527 time_string = time.strftime("#(%Z) %Y/%m/%d %H:%M:%S", loc_time)
528 if NANOSECONDS == "1":
529 time_string = time_string + nanoseconds
530
531 if SHOW_ELAPSED_TIME == "1":
532 cur_time_seconds = seconds
533 math_string = "%9.9f" % cur_time_seconds + " - " + "%9.9f" % \
Michael Walsh4fea2cf2018-08-22 17:48:18 -0500534 sprint_time_last_seconds[last_seconds_ix]
Michael Walshde791732016-09-06 14:25:24 -0500535 elapsed_seconds = eval(math_string)
536 if NANOSECONDS == "1":
537 elapsed_seconds = "%11.6f" % elapsed_seconds
538 else:
539 elapsed_seconds = "%4i" % elapsed_seconds
Michael Walsh4fea2cf2018-08-22 17:48:18 -0500540 sprint_time_last_seconds[last_seconds_ix] = cur_time_seconds
Michael Walshde791732016-09-06 14:25:24 -0500541 time_string = time_string + " - " + elapsed_seconds
542
543 return time_string + " - " + buffer
544
Michael Walshde791732016-09-06 14:25:24 -0500545
Michael Walshde791732016-09-06 14:25:24 -0500546def sprint_timen(buffer=""):
Michael Walshde791732016-09-06 14:25:24 -0500547 r"""
Michael Walsh46fcecb2019-10-16 17:11:59 -0500548 Append a line feed to the buffer, pass it to sprint_time and return the result.
Michael Walshde791732016-09-06 14:25:24 -0500549 """
550
551 return sprint_time(buffer + "\n")
552
Michael Walshde791732016-09-06 14:25:24 -0500553
Michael Walshde791732016-09-06 14:25:24 -0500554def sprint_error(buffer=""):
Michael Walshde791732016-09-06 14:25:24 -0500555 r"""
556 Return a standardized error string. This includes:
557 - A time stamp
558 - The "**ERROR**" string
559 - The caller's buffer string.
560
561 Example:
562
563 The following python code...
564
565 print(sprint_error("Oops.\n"))
566
567 Will result in the following type of output:
568
569 #(CDT) 2016/08/03 17:12:05 - **ERROR** Oops.
570
Michael Walsh4dbb6002019-05-17 15:51:15 -0500571 Description of argument(s).
Michael Walsh46fcecb2019-10-16 17:11:59 -0500572 buffer This will be appended to the formatted error string.
Michael Walshde791732016-09-06 14:25:24 -0500573 """
574
575 return sprint_time() + "**ERROR** " + buffer
576
Michael Walshde791732016-09-06 14:25:24 -0500577
Michael Walsh3f248272018-06-01 13:59:35 -0500578# Implement "constants" with functions.
579def digit_length_in_bits():
580 r"""
581 Return the digit length in bits.
582 """
583
584 return 4
585
586
587def word_length_in_digits():
588 r"""
589 Return the word length in digits.
590 """
591
592 return 8
593
594
595def bit_length(number):
596 r"""
597 Return the bit length of the number.
598
599 Description of argument(s):
600 number The number to be analyzed.
601 """
602
603 if number < 0:
Michael Walsh46fcecb2019-10-16 17:11:59 -0500604 # Convert negative numbers to positive and subtract one. The following example illustrates the
605 # reason for this:
606 # Consider a single nibble whose signed values can range from -8 to 7 (0x8 to 0x7). A value of 0x7
607 # equals 0b0111. Therefore, its length in bits is 3. Since the negative bit (i.e. 0b1000) is not
608 # set, the value 7 clearly will fit in one nibble. With -8 = 0x8 = 0b1000, one has the smallest
609 # negative value that will fit. Note that it requires 3 bits of 0. So by converting a number value
610 # of -8 to a working_number of 7, this function can accurately calculate the number of bits and
611 # therefore nibbles required to represent the number in print.
Michael Walsh3f248272018-06-01 13:59:35 -0500612 working_number = abs(number) - 1
613 else:
614 working_number = number
615
616 # Handle the special case of the number 0.
617 if working_number == 0:
618 return 0
619
620 return len(bin(working_number)) - 2
621
622
623def get_req_num_hex_digits(number):
624 r"""
Michael Walsh46fcecb2019-10-16 17:11:59 -0500625 Return the required number of hex digits required to display the given number.
Michael Walsh3f248272018-06-01 13:59:35 -0500626
627 The returned value will always be rounded up to the nearest multiple of 8.
628
629 Description of argument(s):
630 number The number to be analyzed.
631 """
632
633 if number < 0:
Michael Walsh46fcecb2019-10-16 17:11:59 -0500634 # Convert negative numbers to positive and subtract one. The following example illustrates the
635 # reason for this:
636 # Consider a single nibble whose signed values can range from -8 to 7 (0x8 to 0x7). A value of 0x7
637 # equals 0b0111. Therefore, its length in bits is 3. Since the negative bit (i.e. 0b1000) is not
638 # set, the value 7 clearly will fit in one nibble. With -8 = 0x8 = 0b1000, one has the smallest
639 # negative value that will fit. Note that it requires 3 bits of 0. So by converting a number value
640 # of -8 to a working_number of 7, this function can accurately calculate the number of bits and
641 # therefore nibbles required to represent the number in print.
Michael Walsh3f248272018-06-01 13:59:35 -0500642 working_number = abs(number) - 1
643 else:
644 working_number = number
645
646 # Handle the special case of the number 0.
647 if working_number == 0:
648 return word_length_in_digits()
649
650 num_length_in_bits = bit_length(working_number)
651 num_hex_digits, remainder = divmod(num_length_in_bits,
652 digit_length_in_bits())
653 if remainder > 0:
Michael Walsh46fcecb2019-10-16 17:11:59 -0500654 # Example: the number 7 requires 3 bits. The divmod above produces, 0 with remainder of 3. So
655 # because we have a remainder, we increment num_hex_digits from 0 to 1.
Michael Walsh3f248272018-06-01 13:59:35 -0500656 num_hex_digits += 1
657
Michael Walsh46fcecb2019-10-16 17:11:59 -0500658 # Check to see whether the negative bit is set. This is the left-most bit in the highest order digit.
Michael Walsh3f248272018-06-01 13:59:35 -0500659 negative_mask = 2 ** (num_hex_digits * 4 - 1)
660 if working_number & negative_mask:
Michael Walsh46fcecb2019-10-16 17:11:59 -0500661 # If a number that is intended to be positive has its negative bit on, an additional digit will be
662 # required to represent it correctly in print.
Michael Walsh3f248272018-06-01 13:59:35 -0500663 num_hex_digits += 1
664
665 num_words, remainder = divmod(num_hex_digits, word_length_in_digits())
666 if remainder > 0 or num_words == 0:
667 num_words += 1
668
669 # Round up to the next word length in digits.
670 return num_words * word_length_in_digits()
671
672
673def dft_num_hex_digits():
674 r"""
Michael Walsh46fcecb2019-10-16 17:11:59 -0500675 Return the default number of hex digits to be used to represent a hex number in print.
Michael Walsh3f248272018-06-01 13:59:35 -0500676
677 The value returned is a function of sys.maxsize.
678 """
679
680 global _gen_print_dft_num_hex_digits_
681 try:
682 return _gen_print_dft_num_hex_digits_
683 except NameError:
684 _gen_print_dft_num_hex_digits_ = get_req_num_hex_digits(sys.maxsize)
685 return _gen_print_dft_num_hex_digits_
686
687
Michael Walsh4dbb6002019-05-17 15:51:15 -0500688# Create constant functions to describe various types of dictionaries.
689def dict_type():
690 return 1
691
692
693def ordered_dict_type():
694 return 2
695
696
697def dot_dict_type():
698 return 3
699
700
701def normalized_dict_type():
702 return 4
703
704
Michael Walsh91fc8822019-05-29 17:34:17 -0500705def proxy_dict_type():
706 return 5
707
708
Michael Walsh8646d962019-01-21 14:36:13 -0600709def is_dict(var_value):
710 r"""
Michael Walsh4dbb6002019-05-17 15:51:15 -0500711 Return non-zero if var_value is a type of dictionary and 0 if it is not.
712
Michael Walsh46fcecb2019-10-16 17:11:59 -0500713 The specific non-zero value returned will indicate what type of dictionary var_value is (see constant
714 functions above).
Michael Walsh4dbb6002019-05-17 15:51:15 -0500715
716 Description of argument(s):
Michael Walsh46fcecb2019-10-16 17:11:59 -0500717 var_value The object to be analyzed to determine whether it is a dictionary and if
718 so, what type of dictionary.
Michael Walsh8646d962019-01-21 14:36:13 -0600719 """
720
Michael Walsh8646d962019-01-21 14:36:13 -0600721 if isinstance(var_value, dict):
Michael Walsh91fc8822019-05-29 17:34:17 -0500722 return dict_type()
Michael Walsh8646d962019-01-21 14:36:13 -0600723 try:
724 if isinstance(var_value, collections.OrderedDict):
Michael Walsh91fc8822019-05-29 17:34:17 -0500725 return ordered_dict_type()
Michael Walsh8646d962019-01-21 14:36:13 -0600726 except AttributeError:
727 pass
728 try:
729 if isinstance(var_value, DotDict):
Michael Walsh91fc8822019-05-29 17:34:17 -0500730 return dot_dict_type()
Michael Walsh8646d962019-01-21 14:36:13 -0600731 except NameError:
732 pass
733 try:
734 if isinstance(var_value, NormalizedDict):
Michael Walsh91fc8822019-05-29 17:34:17 -0500735 return normalized_dict_type()
Michael Walsh8646d962019-01-21 14:36:13 -0600736 except NameError:
737 pass
Michael Walsh91fc8822019-05-29 17:34:17 -0500738 try:
739 if str(type(var_value)).split("'")[1] == "dictproxy":
740 return proxy_dict_type()
741 except NameError:
742 pass
743 return 0
Michael Walsh8646d962019-01-21 14:36:13 -0600744
745
Michael Walsh4dbb6002019-05-17 15:51:15 -0500746def get_int_types():
747 r"""
Michael Walsh46fcecb2019-10-16 17:11:59 -0500748 Return a tuple consisting of the valid integer data types for the system and version of python being run.
Michael Walsh4dbb6002019-05-17 15:51:15 -0500749
750 Example:
751 (int, long)
752 """
753
754 try:
755 int_types = (int, long)
756 except NameError:
757 int_types = (int,)
758 return int_types
759
760
761def get_string_types():
762 r"""
Michael Walsh46fcecb2019-10-16 17:11:59 -0500763 Return a tuple consisting of the valid string data types for the system and version of python being run.
Michael Walsh4dbb6002019-05-17 15:51:15 -0500764
765 Example:
766 (str, unicode)
767 """
768
769 try:
770 string_types = (str, unicode)
771 except NameError:
772 string_types = (bytes, str)
773 return string_types
774
775
776def valid_fmts():
777 r"""
Michael Walsh46fcecb2019-10-16 17:11:59 -0500778 Return a list of the valid formats that can be specified for the fmt argument of the sprint_varx function
779 (defined below).
Michael Walsh4dbb6002019-05-17 15:51:15 -0500780 """
781
782 return [
783 'hexa',
784 'octal',
785 'binary',
786 'blank',
Michael Walsh6bed4d32019-07-10 14:11:30 -0500787 'verbose',
Michael Walsh4dbb6002019-05-17 15:51:15 -0500788 'quote_keys',
Michael Walshc6acf742019-08-06 11:48:51 -0500789 'show_type',
790 'strip_brackets',
Michael Walsh46fcecb2019-10-16 17:11:59 -0500791 'no_header',
792 'quote_values']
Michael Walsh4dbb6002019-05-17 15:51:15 -0500793
794
795def create_fmt_definition():
796 r"""
Michael Walsh46fcecb2019-10-16 17:11:59 -0500797 Create a string consisting of function-definition code that can be executed to create constant fmt
798 definition functions.
Michael Walsh4dbb6002019-05-17 15:51:15 -0500799
Michael Walsh46fcecb2019-10-16 17:11:59 -0500800 These functions can be used by callers of sprint_var/sprint_varx to set the fmt argument correctly.
Michael Walsh4dbb6002019-05-17 15:51:15 -0500801
Michael Walsh46fcecb2019-10-16 17:11:59 -0500802 Likewise, the sprint_varx function will use these generated functions to correctly interpret the fmt
803 argument.
Michael Walsh4dbb6002019-05-17 15:51:15 -0500804
805 Example output from this function:
806
807 def hexa():
808 return 0x00000001
809 def octal_fmt():
810 return 0x00000002
811 etc.
812 """
813
814 buffer = ""
815 bits = 0x00000001
816 for fmt_name in valid_fmts():
817 buffer += "def " + fmt_name + "():\n"
818 buffer += " return " + "0x%08x" % bits + "\n"
819 bits = bits << 1
820 return buffer
821
822
Michael Walsh46fcecb2019-10-16 17:11:59 -0500823# Dynamically create fmt definitions (for use with the fmt argument of sprint_varx function):
Michael Walsh4dbb6002019-05-17 15:51:15 -0500824exec(create_fmt_definition())
825
826
Michael Walsh6bed4d32019-07-10 14:11:30 -0500827def terse():
828 r"""
829 Constant function to return fmt value of 0.
830
Michael Walsh46fcecb2019-10-16 17:11:59 -0500831 Now that sprint_varx defaults to printing in terse format, the terse option is deprecated. This function
832 is here for backward compatibility.
Michael Walsh6bed4d32019-07-10 14:11:30 -0500833
Michael Walsh46fcecb2019-10-16 17:11:59 -0500834 Once the repo has been purged of the use of terse, this function can be removed.
Michael Walsh6bed4d32019-07-10 14:11:30 -0500835 """
836
837 return 0
838
839
Michael Walsh4dbb6002019-05-17 15:51:15 -0500840def list_pop(a_list, index=0, default=None):
841 r"""
Michael Walsh46fcecb2019-10-16 17:11:59 -0500842 Pop the list entry indicated by the index and return the entry. If no such entry exists, return default.
Michael Walsh4dbb6002019-05-17 15:51:15 -0500843
844 Note that the list passed to this function will be modified.
845
846 Description of argument(s):
Michael Walsh46fcecb2019-10-16 17:11:59 -0500847 a_list The list from which an entry is to be popped.
848 index The index indicating which entry is to be popped.
849 default The value to be returned if there is no entry at the given index location.
Michael Walsh4dbb6002019-05-17 15:51:15 -0500850 """
851 try:
852 return a_list.pop(index)
853 except IndexError:
854 return default
855
856
857def parse_fmt(fmt):
858 r"""
Michael Walsh46fcecb2019-10-16 17:11:59 -0500859 Parse the fmt argument and return a tuple consisting of a format and a child format.
Michael Walsh4dbb6002019-05-17 15:51:15 -0500860
Michael Walsh46fcecb2019-10-16 17:11:59 -0500861 This function was written for use by the sprint_varx function defined in this module.
Michael Walsh4dbb6002019-05-17 15:51:15 -0500862
Michael Walsh46fcecb2019-10-16 17:11:59 -0500863 When sprint_varx is processing a multi-level object such as a list or dictionary (which in turn may
864 contain other lists or dictionaries), it will use the fmt value to dictate the print formatting of the
865 current level and the child_fmt value to dictate the print formatting of subordinate levels. Consider
866 the following example:
Michael Walsh4dbb6002019-05-17 15:51:15 -0500867
868 python code example:
869
870 ord_dict = \
871 collections.OrderedDict([
872 ('one', 1),
873 ('two', 2),
874 ('sub',
875 collections.OrderedDict([
876 ('three', 3), ('four', 4)]))])
877
878 print_var(ord_dict)
879
880 This would generate the following output:
881
882 ord_dict:
Michael Walsh6bed4d32019-07-10 14:11:30 -0500883 [one]: 1
884 [two]: 2
885 [sub]:
886 [three]: 3
887 [four]: 4
Michael Walsh4dbb6002019-05-17 15:51:15 -0500888
Michael Walsh46fcecb2019-10-16 17:11:59 -0500889 The first level in this example is the line that simply says "ord_dict". The second level is comprised
890 of the dictionary entries with the keys 'one', 'two' and 'sub'. The third level is comprised of the last
891 2 lines (i.e. printed values 3 and 4).
Michael Walsh4dbb6002019-05-17 15:51:15 -0500892
Michael Walsh46fcecb2019-10-16 17:11:59 -0500893 Given the data structure shown above, the programmer could code the following where fmt is a simple
894 integer value set by calling the verbose() function.
Michael Walsh4dbb6002019-05-17 15:51:15 -0500895
Michael Walsh6bed4d32019-07-10 14:11:30 -0500896 print_var(ord_dict, fmt=verbose())
Michael Walsh4dbb6002019-05-17 15:51:15 -0500897
898 The output would look like this:
899
900 ord_dict:
Michael Walsh6bed4d32019-07-10 14:11:30 -0500901 ord_dict[one]: 1
902 ord_dict[two]: 2
903 ord_dict[sub]:
904 ord_dict[sub][three]: 3
905 ord_dict[sub][four]: 4
Michael Walsh4dbb6002019-05-17 15:51:15 -0500906
Michael Walsh46fcecb2019-10-16 17:11:59 -0500907 Note the verbose format where the name of the object ("ord_dict") is repeated on every line.
Michael Walsh4dbb6002019-05-17 15:51:15 -0500908
Michael Walsh46fcecb2019-10-16 17:11:59 -0500909 If the programmer wishes to get more granular with the fmt argument, he/she can specify it as a list
910 where each entry corresponds to a level of the object being printed. The last such list entry governs
911 the print formatting of all subordinate parts of the given object.
Michael Walsh4dbb6002019-05-17 15:51:15 -0500912
Michael Walsh46fcecb2019-10-16 17:11:59 -0500913 Look at each of the following code examples and their corresponding output. See how the show_type()
914 formatting affects the printing depending on which position it occupies in the fmt list argument:
Michael Walsh4dbb6002019-05-17 15:51:15 -0500915
916 print_var(ord_dict, fmt=[show_type()])
917
918 ord_dict: <collections.OrderedDict>
919 ord_dict[one]: 1 <int>
920 ord_dict[two]: 2 <int>
921 ord_dict[sub]: <collections.OrderedDict>
922 ord_dict[sub][three]: 3 <int>
923 ord_dict[sub][four]: 4 <int>
924
925 print_var(ord_dict, fmt=[0, show_type()])
926
927 ord_dict:
928 ord_dict[one]: 1 <int>
929 ord_dict[two]: 2 <int>
930 ord_dict[sub]: <collections.OrderedDict>
931 ord_dict[sub][three]: 3 <int>
932 ord_dict[sub][four]: 4 <int>
933
934 print_var(ord_dict, fmt=[0, 0, show_type()])
935
936 ord_dict:
937 ord_dict[one]: 1
938 ord_dict[two]: 2
939 ord_dict[sub]:
940 ord_dict[sub][three]: 3 <int>
941 ord_dict[sub][four]: 4 <int>
942
943 Description of argument(s):
Michael Walsh46fcecb2019-10-16 17:11:59 -0500944 fmt The format argument such as is passed to sprint_varx. This argument may
945 be an integer or a list of integers. See the prolog of sprint_varx for
946 more details.
Michael Walsh4dbb6002019-05-17 15:51:15 -0500947 """
948
Michael Walsh46fcecb2019-10-16 17:11:59 -0500949 # Make a deep copy of the fmt argument in order to avoid modifying the caller's fmt value when it is a
950 # list.
Michael Walsh4dbb6002019-05-17 15:51:15 -0500951 fmt = copy.deepcopy(fmt)
952 try:
953 # Assume fmt is a list. Pop the first element from the list.
954 first_element = list_pop(fmt, index=0, default=0)
Michael Walsh46fcecb2019-10-16 17:11:59 -0500955 # Return the first list element along with either 1) the remainder of the fmt list if not null or 2)
956 # another copy of the first element.
Michael Walsh4dbb6002019-05-17 15:51:15 -0500957 return first_element, fmt if len(fmt) else first_element
958 except AttributeError:
959 # fmt is not a list so treat it as a simple integer value.
960 return fmt, fmt
961
962
Michael Walshde791732016-09-06 14:25:24 -0500963def sprint_varx(var_name,
964 var_value,
Michael Walsh4dbb6002019-05-17 15:51:15 -0500965 fmt=0,
966 indent=dft_indent,
967 col1_width=dft_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -0500968 trailing_char="\n",
Michael Walsh4dbb6002019-05-17 15:51:15 -0500969 key_list=None,
970 delim=":"):
Michael Walshde791732016-09-06 14:25:24 -0500971 r"""
Michael Walsh46fcecb2019-10-16 17:11:59 -0500972 Print the var name/value passed to it. If the caller lets col1_width default, the printing lines up
973 nicely with output generated by the print_time functions.
Michael Walshde791732016-09-06 14:25:24 -0500974
Michael Walsh46fcecb2019-10-16 17:11:59 -0500975 Note that the sprint_var function (defined below) can be used to call this function so that the
976 programmer does not need to pass the var_name. sprint_var will figure out the var_name. The sprint_var
977 function is the one that would normally be used by the general user.
Michael Walshde791732016-09-06 14:25:24 -0500978
979 For example, the following python code:
980
981 first_name = "Mike"
982 print_time("Doing this...\n")
983 print_varx("first_name", first_name)
984 print_time("Doing that...\n")
985
986 Will generate output like this:
987
988 #(CDT) 2016/08/10 17:34:42.847374 - 0.001285 - Doing this...
989 first_name: Mike
990 #(CDT) 2016/08/10 17:34:42.847510 - 0.000136 - Doing that...
991
Michael Walsh46fcecb2019-10-16 17:11:59 -0500992 This function recognizes several complex types of data such as dict, list or tuple.
Michael Walshde791732016-09-06 14:25:24 -0500993
994 For example, the following python code:
995
996 my_dict = dict(one=1, two=2, three=3)
997 print_var(my_dict)
998
999 Will generate the following output:
1000
1001 my_dict:
1002 my_dict[three]: 3
1003 my_dict[two]: 2
1004 my_dict[one]: 1
1005
Michael Walsh4dbb6002019-05-17 15:51:15 -05001006 Description of argument(s).
Michael Walshde791732016-09-06 14:25:24 -05001007 var_name The name of the variable to be printed.
1008 var_value The value of the variable to be printed.
Michael Walsh46fcecb2019-10-16 17:11:59 -05001009 fmt A bit map to dictate the format of the output. For printing multi-level
1010 objects like lists and dictionaries, this argument may also be a list of
1011 bit maps. The first list element pertains to the highest level of
1012 output, the second element pertains to the 2nd level of output, etc. The
1013 last element in the list pertains to all subordinate levels. The bits
1014 can be set using the dynamically created functionhs above. Example:
1015 sprint_varx("var1", var1, fmt=verbose()). Note that these values can be
1016 OR'ed together: print_var(var1, hexa() | verbose()). If the caller ORs
1017 mutually exclusive bits (hexa() | octal()), behavior is not guaranteed.
1018 The following features are supported:
1019 hexa Print all integer values in hexadecimal format.
Michael Walsh4dbb6002019-05-17 15:51:15 -05001020 octal Print all integer values in octal format.
1021 binary Print all integer values in binary format.
Michael Walsh46fcecb2019-10-16 17:11:59 -05001022 blank For blank string values, print "<blank>" instead of an actual blank.
1023 verbose For structured values like dictionaries, lists, etc. repeat the name of
1024 the variable on each line to the right of the key or subscript value.
1025 Example: print "my_dict[key1]" instead of just "[key1]".
1026 quote_keys Quote dictionary keys in the output. Example: my_dict['key1'] instead of
Michael Walsh4dbb6002019-05-17 15:51:15 -05001027 my_dict[key1].
Michael Walsh46fcecb2019-10-16 17:11:59 -05001028 show_type Show the type of the data in angled brackets just to the right of the
1029 data.
1030 strip_brackets Strip the brackets from the variable name portion of the output. This is
1031 applicable when printing complex objects like lists or dictionaries.
1032 no_header For complex objects like dictionaries, do not include a header line.
1033 This necessarily means that the member lines will be indented 2
1034 characters less than they otherwise would have been.
1035 quote_values Quote the values printed.
Michael Walsh4dbb6002019-05-17 15:51:15 -05001036 indent The number of spaces to indent the output.
Michael Walsh46fcecb2019-10-16 17:11:59 -05001037 col1_width The width of the output column containing the variable name. The default
1038 value of this is adjusted so that the var_value lines up with text
1039 printed via the print_time function.
1040 trailing_char The character to be used at the end of the returned string. The default
1041 value is a line feed.
1042 key_list A list of which dictionary keys should be printed. All others keys will
1043 be skipped. Each value in key_list will be regarded as a regular
1044 expression and it will be regarded as anchored to the beginning and ends
1045 of the dictionary key being referenced. For example if key_list is
1046 ["one", "two"], the resulting regex used will be "^one|two$", i.e. only
1047 keys "one" and "two" from the var_value dictionary will be printed. As
1048 another example, if the caller were to specify a key_list of ["one.*"],
1049 then only dictionary keys whose names begin with "one" will be printed.
1050 Note: This argument pertains only to var_values which are dictionaries.
1051 delim The value to be used to delimit the variable name from the variable value
1052 in the output.
Michael Walsh7423c012016-10-04 10:27:21 -05001053 """
Michael Walshde791732016-09-06 14:25:24 -05001054
Michael Walsh4dbb6002019-05-17 15:51:15 -05001055 fmt, child_fmt = parse_fmt(fmt)
1056
1057 if fmt & show_type():
1058 type_str = "<" + str(type(var_value)).split("'")[1] + ">"
1059 # Compose object type categories.
1060 int_types = get_int_types()
1061 string_types = get_string_types()
1062 simple_types = int_types + string_types + (float, bool, type, type(None))
1063 # Determine the type.
1064 if type(var_value) in simple_types:
Michael Walsh46fcecb2019-10-16 17:11:59 -05001065 # The data type is simple in the sense that it has no subordinate parts.
Michael Walsh4dbb6002019-05-17 15:51:15 -05001066 # Adjust col1_width.
1067 col1_width = col1_width - indent
1068 # Set default value for value_format.
1069 value_format = "%s"
1070 # Process format requests.
1071 if type(var_value) in int_types:
1072 # Process format values pertaining to int types.
1073 if fmt & hexa():
Michael Walsh3f248272018-06-01 13:59:35 -05001074 num_hex_digits = max(dft_num_hex_digits(),
1075 get_req_num_hex_digits(var_value))
Michael Walsh46fcecb2019-10-16 17:11:59 -05001076 # Convert a negative number to its positive twos complement for proper printing. For
1077 # example, instead of printing -1 as "0x-000000000000001" it will be printed as
Michael Walsh3f248272018-06-01 13:59:35 -05001078 # "0xffffffffffffffff".
1079 var_value = var_value & (2 ** (num_hex_digits * 4) - 1)
1080 value_format = "0x%0" + str(num_hex_digits) + "x"
Michael Walsh4dbb6002019-05-17 15:51:15 -05001081 elif fmt & octal():
1082 value_format = "0o%016o"
1083 elif fmt & binary():
1084 num_digits, remainder = \
1085 divmod(max(bit_length(var_value), 1), 8)
1086 num_digits *= 8
1087 if remainder:
1088 num_digits += 8
1089 num_digits += 2
1090 value_format = '#0' + str(num_digits) + 'b'
1091 var_value = format(var_value, value_format)
1092 value_format = "%s"
1093 elif type(var_value) in string_types:
1094 # Process format values pertaining to string types.
1095 if fmt & blank() and var_value == "":
1096 value_format = "%s"
1097 var_value = "<blank>"
1098 elif type(var_value) is type:
1099 var_value = str(var_value).split("'")[1]
1100 format_string = "%" + str(indent) + "s%-" + str(col1_width) + "s" \
1101 + value_format
1102 if fmt & show_type():
1103 if var_value != "":
1104 format_string += " "
1105 format_string += type_str
1106 format_string += trailing_char
Michael Walsh46fcecb2019-10-16 17:11:59 -05001107 if fmt & quote_values():
1108 var_value = "'" + var_value + "'"
Michael Walsh6bed4d32019-07-10 14:11:30 -05001109 if not (fmt & verbose()):
Michael Walsh4dbb6002019-05-17 15:51:15 -05001110 # Strip everything leading up to the first left square brace.
1111 var_name = re.sub(r".*\[", "[", var_name)
Michael Walshc6acf742019-08-06 11:48:51 -05001112 if (fmt & strip_brackets()):
1113 var_name = re.sub(r"[\[\]]", "", var_name)
Michael Walsh3383e652017-09-01 17:10:59 -05001114 if value_format == "0x%08x":
Michael Walsh4dbb6002019-05-17 15:51:15 -05001115 return format_string % ("", str(var_name) + delim,
Michael Walsh3383e652017-09-01 17:10:59 -05001116 var_value & 0xffffffff)
1117 else:
Michael Walsh4dbb6002019-05-17 15:51:15 -05001118 return format_string % ("", str(var_name) + delim, var_value)
Michael Walshde791732016-09-06 14:25:24 -05001119 else:
1120 # The data type is complex in the sense that it has subordinate parts.
Michael Walshc6acf742019-08-06 11:48:51 -05001121 if (fmt & no_header()):
1122 buffer = ""
Michael Walsh4dbb6002019-05-17 15:51:15 -05001123 else:
Michael Walshc6acf742019-08-06 11:48:51 -05001124 # Create header line.
1125 if not (fmt & verbose()):
1126 # Strip everything leading up to the first square brace.
1127 loc_var_name = re.sub(r".*\[", "[", var_name)
1128 else:
1129 loc_var_name = var_name
Michael Walshadf4ab22019-09-06 16:23:41 -05001130 if (fmt & strip_brackets()):
1131 loc_var_name = re.sub(r"[\[\]]", "", loc_var_name)
Michael Walshc6acf742019-08-06 11:48:51 -05001132 format_string = "%" + str(indent) + "s%s\n"
1133 buffer = format_string % ("", loc_var_name + ":")
1134 if fmt & show_type():
1135 buffer = buffer.replace("\n", " " + type_str + "\n")
1136 indent += 2
Michael Walsh7423c012016-10-04 10:27:21 -05001137 try:
1138 length = len(var_value)
1139 except TypeError:
Michael Walsh23e7f492017-01-10 11:34:47 -06001140 length = 0
Michael Walsh7423c012016-10-04 10:27:21 -05001141 ix = 0
1142 loc_trailing_char = "\n"
Michael Walsh8646d962019-01-21 14:36:13 -06001143 if is_dict(var_value):
Michael Walsh4dbb6002019-05-17 15:51:15 -05001144 if type(child_fmt) is list:
1145 child_quote_keys = (child_fmt[0] & quote_keys())
1146 else:
1147 child_quote_keys = (child_fmt & quote_keys())
Michael Walsh37762f92018-08-07 14:59:18 -05001148 for key, value in var_value.items():
Michael Walshd2869032018-03-22 16:12:11 -05001149 if key_list is not None:
1150 key_list_regex = "^" + "|".join(key_list) + "$"
1151 if not re.match(key_list_regex, key):
1152 continue
Michael Walsh7423c012016-10-04 10:27:21 -05001153 ix += 1
1154 if ix == length:
1155 loc_trailing_char = trailing_char
Michael Walsh4dbb6002019-05-17 15:51:15 -05001156 if child_quote_keys:
1157 key = "'" + key + "'"
1158 key = "[" + str(key) + "]"
1159 buffer += sprint_varx(var_name + key, value, child_fmt, indent,
Michael Walshc6acf742019-08-06 11:48:51 -05001160 col1_width, loc_trailing_char, key_list,
1161 delim)
Michael Walsh7423c012016-10-04 10:27:21 -05001162 elif type(var_value) in (list, tuple, set):
Michael Walshde791732016-09-06 14:25:24 -05001163 for key, value in enumerate(var_value):
Michael Walsh7423c012016-10-04 10:27:21 -05001164 ix += 1
1165 if ix == length:
1166 loc_trailing_char = trailing_char
Michael Walsh4dbb6002019-05-17 15:51:15 -05001167 key = "[" + str(key) + "]"
1168 buffer += sprint_varx(var_name + key, value, child_fmt, indent,
Michael Walshc6acf742019-08-06 11:48:51 -05001169 col1_width, loc_trailing_char, key_list,
1170 delim)
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001171 elif isinstance(var_value, argparse.Namespace):
Michael Walshde791732016-09-06 14:25:24 -05001172 for key in var_value.__dict__:
Michael Walsh7423c012016-10-04 10:27:21 -05001173 ix += 1
1174 if ix == length:
1175 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -05001176 cmd_buf = "buffer += sprint_varx(var_name + \".\" + str(key)" \
Michael Walsh4dbb6002019-05-17 15:51:15 -05001177 + ", var_value." + key + ", child_fmt, indent," \
Michael Walshc6acf742019-08-06 11:48:51 -05001178 + " col1_width, loc_trailing_char, key_list," \
1179 + " delim)"
Michael Walshde791732016-09-06 14:25:24 -05001180 exec(cmd_buf)
1181 else:
1182 var_type = type(var_value).__name__
1183 func_name = sys._getframe().f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -05001184 var_value = "<" + var_type + " type not supported by " + \
1185 func_name + "()>"
Michael Walshde791732016-09-06 14:25:24 -05001186 value_format = "%s"
Michael Walsh4dbb6002019-05-17 15:51:15 -05001187 indent -= 2
1188 # Adjust col1_width.
1189 col1_width = col1_width - indent
1190 format_string = "%" + str(indent) + "s%-" \
1191 + str(col1_width) + "s" + value_format + trailing_char
Michael Walsh0f2ea5f2017-02-20 15:55:00 -06001192 return format_string % ("", str(var_name) + ":", var_value)
Michael Walsh23e7f492017-01-10 11:34:47 -06001193
Michael Walshde791732016-09-06 14:25:24 -05001194 return buffer
1195
1196 return ""
1197
Michael Walshde791732016-09-06 14:25:24 -05001198
Michael Walsh4dbb6002019-05-17 15:51:15 -05001199def sprint_var(*args, **kwargs):
Michael Walshde791732016-09-06 14:25:24 -05001200 r"""
Michael Walsh46fcecb2019-10-16 17:11:59 -05001201 Figure out the name of the first argument for the caller and then call sprint_varx with it. Therefore,
1202 the following 2 calls are equivalent:
Michael Walshde791732016-09-06 14:25:24 -05001203 sprint_varx("var1", var1)
1204 sprint_var(var1)
Michael Walsh4dbb6002019-05-17 15:51:15 -05001205
1206 See sprint_varx for description of arguments.
Michael Walshde791732016-09-06 14:25:24 -05001207 """
1208
Michael Walshde791732016-09-06 14:25:24 -05001209 stack_frame = 2
Michael Walsh7423c012016-10-04 10:27:21 -05001210 caller_func_name = sprint_func_name(2)
1211 if caller_func_name.endswith("print_var"):
Michael Walshde791732016-09-06 14:25:24 -05001212 stack_frame += 1
Michael Walsh4dbb6002019-05-17 15:51:15 -05001213 # Get the name of the first variable passed to this function.
Michael Walshde791732016-09-06 14:25:24 -05001214 var_name = get_arg_name(None, 1, stack_frame)
Michael Walsh4dbb6002019-05-17 15:51:15 -05001215 return sprint_varx(var_name, *args, **kwargs)
Michael Walshde791732016-09-06 14:25:24 -05001216
1217
Michael Walsh4dbb6002019-05-17 15:51:15 -05001218def sprint_vars(*args, **kwargs):
Michael Walsh18176322016-11-15 15:11:21 -06001219 r"""
1220 Sprint the values of one or more variables.
1221
Michael Walsh4dbb6002019-05-17 15:51:15 -05001222 Description of argument(s):
Michael Walsh46fcecb2019-10-16 17:11:59 -05001223 args The variable values which are to be printed.
1224 kwargs See sprint_varx (above) for description of additional arguments.
Michael Walsh18176322016-11-15 15:11:21 -06001225 """
1226
Michael Walsh18176322016-11-15 15:11:21 -06001227 stack_frame = 2
1228 caller_func_name = sprint_func_name(2)
1229 if caller_func_name.endswith("print_vars"):
1230 stack_frame += 1
1231
Michael Walsh18176322016-11-15 15:11:21 -06001232 buffer = ""
Michael Walsh4dbb6002019-05-17 15:51:15 -05001233 arg_num = 1
1234 for var_value in args:
1235 var_name = get_arg_name(None, arg_num, stack_frame)
1236 buffer += sprint_varx(var_name, var_value, **kwargs)
1237 arg_num += 1
Michael Walsh18176322016-11-15 15:11:21 -06001238
1239 return buffer
1240
Michael Walsh18176322016-11-15 15:11:21 -06001241
Michael Walsh4dbb6002019-05-17 15:51:15 -05001242def sprint_dashes(indent=dft_indent,
Michael Walsh7423c012016-10-04 10:27:21 -05001243 width=80,
1244 line_feed=1,
1245 char="-"):
Michael Walshde791732016-09-06 14:25:24 -05001246 r"""
1247 Return a string of dashes to the caller.
1248
Michael Walsh4dbb6002019-05-17 15:51:15 -05001249 Description of argument(s):
Michael Walsh46fcecb2019-10-16 17:11:59 -05001250 indent The number of characters to indent the output.
Michael Walshde791732016-09-06 14:25:24 -05001251 width The width of the string of dashes.
Michael Walsh46fcecb2019-10-16 17:11:59 -05001252 line_feed Indicates whether the output should end with a line feed.
1253 char The character to be repeated in the output string.
Michael Walshde791732016-09-06 14:25:24 -05001254 """
1255
Michael Walsh7423c012016-10-04 10:27:21 -05001256 width = int(width)
Michael Walsh23e7f492017-01-10 11:34:47 -06001257 buffer = " " * int(indent) + char * width
Michael Walshde791732016-09-06 14:25:24 -05001258 if line_feed:
1259 buffer += "\n"
1260
1261 return buffer
1262
Michael Walshde791732016-09-06 14:25:24 -05001263
Michael Walsh7423c012016-10-04 10:27:21 -05001264def sindent(text="",
1265 indent=0):
Michael Walsh7423c012016-10-04 10:27:21 -05001266 r"""
Michael Walsh46fcecb2019-10-16 17:11:59 -05001267 Pre-pend the specified number of characters to the text string (i.e. indent it) and return it.
Michael Walsh7423c012016-10-04 10:27:21 -05001268
Michael Walsh4dbb6002019-05-17 15:51:15 -05001269 Description of argument(s):
Michael Walsh7423c012016-10-04 10:27:21 -05001270 text The string to be indented.
Michael Walsh46fcecb2019-10-16 17:11:59 -05001271 indent The number of characters to indent the string.
Michael Walsh7423c012016-10-04 10:27:21 -05001272 """
1273
1274 format_string = "%" + str(indent) + "s%s"
1275 buffer = format_string % ("", text)
1276
1277 return buffer
1278
Michael Walsh7423c012016-10-04 10:27:21 -05001279
Michael Walsh662e13b2019-03-01 15:54:08 -06001280func_line_style_std = None
1281func_line_style_short = 1
1282
1283
Michael Walshb26d2c72019-11-04 10:48:04 -06001284def sprint_func_line(stack_frame, style=None, max_width=160):
Michael Walsh47aa2a42018-12-10 15:06:02 -06001285 r"""
Michael Walsh46fcecb2019-10-16 17:11:59 -05001286 For the given stack_frame, return a formatted string containing the function name and all its arguments.
Michael Walsh47aa2a42018-12-10 15:06:02 -06001287
1288 Example:
1289
1290 func1(last_name = 'walsh', first_name = 'mikey')
1291
1292 Description of argument(s):
Michael Walsh46fcecb2019-10-16 17:11:59 -05001293 stack_frame A stack frame (such as is returned by inspect.stack()).
1294 style Indicates the style or formatting of the result string. Acceptable
1295 values are shown above.
Michael Walshd40115e2020-02-19 14:22:35 -06001296 max_width The max width of the result. If it exceeds this length, it will be
1297 truncated on the right.
Michael Walsh662e13b2019-03-01 15:54:08 -06001298
1299 Description of styles:
1300 func_line_style_std The standard formatting.
Michael Walsh46fcecb2019-10-16 17:11:59 -05001301 func_line_style_short 1) The self parm (associated with methods) will be dropped. 2) The args
1302 and kwargs values will be treated as special. In both cases the arg name
1303 ('args' or 'kwargs') will be dropped and only the values will be shown.
Michael Walsh47aa2a42018-12-10 15:06:02 -06001304 """
1305
1306 func_name = str(stack_frame[3])
1307 if func_name == "?":
1308 # "?" is the name used when code is not in a function.
1309 func_name = "(none)"
1310
1311 if func_name == "<module>":
Michael Walsh46fcecb2019-10-16 17:11:59 -05001312 # If the func_name is the "main" program, we simply get the command line call string.
Michael Walsh47aa2a42018-12-10 15:06:02 -06001313 func_and_args = ' '.join(sys.argv)
1314 else:
1315 # Get the program arguments.
1316 (args, varargs, keywords, locals) =\
1317 inspect.getargvalues(stack_frame[0])
1318
1319 args_list = []
1320 for arg_name in filter(None, args + [varargs, keywords]):
1321 # Get the arg value from frame locals.
1322 arg_value = locals[arg_name]
1323 if arg_name == 'self':
Michael Walsh662e13b2019-03-01 15:54:08 -06001324 if style == func_line_style_short:
1325 continue
Michael Walsh47aa2a42018-12-10 15:06:02 -06001326 # Manipulations to improve output for class methods.
1327 func_name = arg_value.__class__.__name__ + "." + func_name
1328 args_list.append(arg_name + " = <self>")
Michael Walsh662e13b2019-03-01 15:54:08 -06001329 elif (style == func_line_style_short
1330 and arg_name == 'args'
1331 and type(arg_value) in (list, tuple)):
1332 if len(arg_value) == 0:
1333 continue
1334 args_list.append(repr(', '.join(arg_value)))
1335 elif (style == func_line_style_short
1336 and arg_name == 'kwargs'
1337 and type(arg_value) is dict):
1338 for key, value in arg_value.items():
1339 args_list.append(key + "=" + repr(value))
Michael Walsh47aa2a42018-12-10 15:06:02 -06001340 else:
1341 args_list.append(arg_name + " = " + repr(arg_value))
1342 args_str = "(" + ', '.join(map(str, args_list)) + ")"
1343
1344 # Now we need to print this in a nicely-wrapped way.
1345 func_and_args = func_name + args_str
1346
Michael Walshb26d2c72019-11-04 10:48:04 -06001347 if len(func_and_args) > max_width:
1348 func_and_args = func_and_args[0:max_width] + "..."
Michael Walsh47aa2a42018-12-10 15:06:02 -06001349 return func_and_args
1350
1351
Michael Walsh7423c012016-10-04 10:27:21 -05001352def sprint_call_stack(indent=0,
Michael Walsh662e13b2019-03-01 15:54:08 -06001353 stack_frame_ix=0,
1354 style=None):
Michael Walshde791732016-09-06 14:25:24 -05001355 r"""
Michael Walsh46fcecb2019-10-16 17:11:59 -05001356 Return a call stack report for the given point in the program with line numbers, function names and
1357 function parameters and arguments.
Michael Walshde791732016-09-06 14:25:24 -05001358
1359 Sample output:
1360
1361 -------------------------------------------------------------------------
1362 Python function call stack
1363
1364 Line # Function name and arguments
1365 ------ ------------------------------------------------------------------
Michael Walsh47aa2a42018-12-10 15:06:02 -06001366 424 sprint_call_stack()
1367 4 print_call_stack()
1368 31 func1(last_name = 'walsh', first_name = 'mikey')
Michael Walshde791732016-09-06 14:25:24 -05001369 59 /tmp/scr5.py
1370 -------------------------------------------------------------------------
1371
Michael Walsh4dbb6002019-05-17 15:51:15 -05001372 Description of argument(s):
Michael Walsh46fcecb2019-10-16 17:11:59 -05001373 indent The number of characters to indent each line of output.
1374 stack_frame_ix The index of the first stack frame which is to be returned.
1375 style See the sprint_line_func prolog above for details.
Michael Walshde791732016-09-06 14:25:24 -05001376 """
1377
1378 buffer = ""
Michael Walsh7423c012016-10-04 10:27:21 -05001379 buffer += sprint_dashes(indent)
1380 buffer += sindent("Python function call stack\n\n", indent)
1381 buffer += sindent("Line # Function name and arguments\n", indent)
1382 buffer += sprint_dashes(indent, 6, 0) + " " + sprint_dashes(0, 73)
Michael Walshde791732016-09-06 14:25:24 -05001383
1384 # Grab the current program stack.
Michael Walsh6f0362c2019-03-25 14:05:14 -05001385 work_around_inspect_stack_cwd_failure()
Michael Walshde791732016-09-06 14:25:24 -05001386 current_stack = inspect.stack()
1387
1388 # Process each frame in turn.
1389 format_string = "%6s %s\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001390 ix = 0
Michael Walshde791732016-09-06 14:25:24 -05001391 for stack_frame in current_stack:
Michael Walsh7423c012016-10-04 10:27:21 -05001392 if ix < stack_frame_ix:
1393 ix += 1
1394 continue
Michael Walsh46fcecb2019-10-16 17:11:59 -05001395 # Make the line number shown to be the line where one finds the line shown.
Michael Walsh23e7f492017-01-10 11:34:47 -06001396 try:
1397 line_num = str(current_stack[ix + 1][2])
1398 except IndexError:
1399 line_num = ""
Michael Walsh662e13b2019-03-01 15:54:08 -06001400 func_and_args = sprint_func_line(stack_frame, style=style)
Michael Walshde791732016-09-06 14:25:24 -05001401
Michael Walsh23e7f492017-01-10 11:34:47 -06001402 buffer += sindent(format_string % (line_num, func_and_args), indent)
Michael Walsh7423c012016-10-04 10:27:21 -05001403 ix += 1
Michael Walshde791732016-09-06 14:25:24 -05001404
Michael Walsh7423c012016-10-04 10:27:21 -05001405 buffer += sprint_dashes(indent)
Michael Walshde791732016-09-06 14:25:24 -05001406
1407 return buffer
1408
Michael Walshde791732016-09-06 14:25:24 -05001409
Michael Walshd40115e2020-02-19 14:22:35 -06001410def sprint_executing(stack_frame_ix=None, style=None, max_width=None):
Michael Walshde791732016-09-06 14:25:24 -05001411 r"""
Michael Walsh46fcecb2019-10-16 17:11:59 -05001412 Print a line indicating what function is executing and with what parameter values. This is useful for
1413 debugging.
Michael Walshde791732016-09-06 14:25:24 -05001414
1415 Sample output:
1416
Michael Walsh47aa2a42018-12-10 15:06:02 -06001417 #(CDT) 2016/08/25 17:54:27 - Executing: func1(x = 1)
Michael Walshde791732016-09-06 14:25:24 -05001418
Michael Walsh4dbb6002019-05-17 15:51:15 -05001419 Description of argument(s):
Michael Walsh46fcecb2019-10-16 17:11:59 -05001420 stack_frame_ix The index of the stack frame whose function info should be returned. If
1421 the caller does not specify a value, this function will set the value to
1422 1 which is the index of the caller's stack frame. If the caller is the
1423 wrapper function "print_executing", this function will bump it up by 1.
1424 style See the sprint_line_func prolog above for details.
Michael Walshd40115e2020-02-19 14:22:35 -06001425 max_width See the sprint_line_func prolog above for details.
Michael Walshde791732016-09-06 14:25:24 -05001426 """
1427
1428 # If user wants default stack_frame_ix.
1429 if stack_frame_ix is None:
1430 func_name = sys._getframe().f_code.co_name
1431 caller_func_name = sys._getframe(1).f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -05001432 if caller_func_name.endswith(func_name[1:]):
Michael Walshde791732016-09-06 14:25:24 -05001433 stack_frame_ix = 2
1434 else:
1435 stack_frame_ix = 1
1436
Michael Walsh6f0362c2019-03-25 14:05:14 -05001437 work_around_inspect_stack_cwd_failure()
Michael Walshde791732016-09-06 14:25:24 -05001438 stack_frame = inspect.stack()[stack_frame_ix]
1439
Michael Walshd40115e2020-02-19 14:22:35 -06001440 if max_width is None:
1441 max_width = 160 - (dft_col1_width + 11)
1442 func_and_args = sprint_func_line(stack_frame, style, max_width=max_width)
Michael Walshde791732016-09-06 14:25:24 -05001443
1444 return sprint_time() + "Executing: " + func_and_args + "\n"
1445
Michael Walshde791732016-09-06 14:25:24 -05001446
Michael Walshbec416d2016-11-10 08:54:52 -06001447def sprint_pgm_header(indent=0,
1448 linefeed=1):
Michael Walshde791732016-09-06 14:25:24 -05001449 r"""
Michael Walsh46fcecb2019-10-16 17:11:59 -05001450 Return a standardized header that programs should print at the beginning of the run. It includes useful
1451 information like command line, pid, userid, program parameters, etc.
Michael Walshde791732016-09-06 14:25:24 -05001452
Michael Walsh4dbb6002019-05-17 15:51:15 -05001453 Description of argument(s):
Michael Walsh46fcecb2019-10-16 17:11:59 -05001454 indent The number of characters to indent each line of output.
1455 linefeed Indicates whether a line feed be included at the beginning and end of the
1456 report.
Michael Walshde791732016-09-06 14:25:24 -05001457 """
1458
Michael Walsh4dbb6002019-05-17 15:51:15 -05001459 col1_width = dft_col1_width + indent
Michael Walshbec416d2016-11-10 08:54:52 -06001460
1461 buffer = ""
1462 if linefeed:
1463 buffer = "\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001464
Michael Walshdb6e68a2017-05-23 17:55:31 -05001465 if robot_env:
1466 suite_name = BuiltIn().get_variable_value("${suite_name}")
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001467 buffer += sindent(sprint_time("Running test suite \"" + suite_name
1468 + "\".\n"), indent)
Michael Walshdb6e68a2017-05-23 17:55:31 -05001469
Michael Walsh7423c012016-10-04 10:27:21 -05001470 buffer += sindent(sprint_time() + "Running " + pgm_name + ".\n", indent)
1471 buffer += sindent(sprint_time() + "Program parameter values, etc.:\n\n",
1472 indent)
Michael Walshbec416d2016-11-10 08:54:52 -06001473 buffer += sprint_varx("command_line", ' '.join(sys.argv), 0, indent,
Michael Walsh4dbb6002019-05-17 15:51:15 -05001474 col1_width)
Michael Walsh46fcecb2019-10-16 17:11:59 -05001475 # We want the output to show a customized name for the pid and pgid but we want it to look like a valid
1476 # variable name. Therefore, we'll use pgm_name_var_name which was set when this module was imported.
Michael Walshbec416d2016-11-10 08:54:52 -06001477 buffer += sprint_varx(pgm_name_var_name + "_pid", os.getpid(), 0, indent,
Michael Walsh4dbb6002019-05-17 15:51:15 -05001478 col1_width)
Michael Walshbec416d2016-11-10 08:54:52 -06001479 buffer += sprint_varx(pgm_name_var_name + "_pgid", os.getpgrp(), 0, indent,
Michael Walsh4dbb6002019-05-17 15:51:15 -05001480 col1_width)
Michael Walsh86de0d22016-12-05 10:13:15 -06001481 userid_num = str(os.geteuid())
1482 try:
1483 username = os.getlogin()
1484 except OSError:
1485 if userid_num == "0":
1486 username = "root"
1487 else:
1488 username = "?"
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001489 buffer += sprint_varx("uid", userid_num + " (" + username
Michael Walsh4dbb6002019-05-17 15:51:15 -05001490 + ")", 0, indent, col1_width)
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001491 buffer += sprint_varx("gid", str(os.getgid()) + " ("
1492 + str(grp.getgrgid(os.getgid()).gr_name) + ")", 0,
Michael Walsh4dbb6002019-05-17 15:51:15 -05001493 indent, col1_width)
Michael Walshbec416d2016-11-10 08:54:52 -06001494 buffer += sprint_varx("host_name", socket.gethostname(), 0, indent,
Michael Walsh4dbb6002019-05-17 15:51:15 -05001495 col1_width)
Michael Walsh86de0d22016-12-05 10:13:15 -06001496 try:
1497 DISPLAY = os.environ['DISPLAY']
1498 except KeyError:
1499 DISPLAY = ""
Michael Walsh91fc8822019-05-29 17:34:17 -05001500 buffer += sprint_var(DISPLAY, 0, indent, col1_width)
1501 PYTHON_VERSION = os.environ.get('PYTHON_VERSION', None)
1502 if PYTHON_VERSION is not None:
Michael Walshc9a7ee72019-08-28 16:45:50 -05001503 buffer += sprint_var(PYTHON_VERSION, 0, indent, col1_width)
Michael Walsh91fc8822019-05-29 17:34:17 -05001504 PYTHON_PGM_PATH = os.environ.get('PYTHON_PGM_PATH', None)
1505 if PYTHON_PGM_PATH is not None:
Michael Walshc9a7ee72019-08-28 16:45:50 -05001506 buffer += sprint_var(PYTHON_PGM_PATH, 0, indent, col1_width)
Michael Walsh91fc8822019-05-29 17:34:17 -05001507 python_version = sys.version.replace("\n", "")
Michael Walshc9a7ee72019-08-28 16:45:50 -05001508 buffer += sprint_var(python_version, 0, indent, col1_width)
Michael Walsh91fc8822019-05-29 17:34:17 -05001509 ROBOT_VERSION = os.environ.get('ROBOT_VERSION', None)
1510 if ROBOT_VERSION is not None:
Michael Walshc9a7ee72019-08-28 16:45:50 -05001511 buffer += sprint_var(ROBOT_VERSION, 0, indent, col1_width)
Michael Walsh91fc8822019-05-29 17:34:17 -05001512 ROBOT_PGM_PATH = os.environ.get('ROBOT_PGM_PATH', None)
1513 if ROBOT_PGM_PATH is not None:
Michael Walshc9a7ee72019-08-28 16:45:50 -05001514 buffer += sprint_var(ROBOT_PGM_PATH, 0, indent, col1_width)
Michael Walsh91fc8822019-05-29 17:34:17 -05001515
Michael Walsh46fcecb2019-10-16 17:11:59 -05001516 # __builtin__.arg_obj is created by the get_arg module function, gen_get_options.
Michael Walsh7423c012016-10-04 10:27:21 -05001517 try:
1518 buffer += ga.sprint_args(__builtin__.arg_obj, indent)
1519 except AttributeError:
1520 pass
1521
Michael Walshdb6e68a2017-05-23 17:55:31 -05001522 if robot_env:
1523 # Get value of global parm_list.
1524 parm_list = BuiltIn().get_variable_value("${parm_list}")
1525
1526 for parm in parm_list:
1527 parm_value = BuiltIn().get_variable_value("${" + parm + "}")
Michael Walsh4dbb6002019-05-17 15:51:15 -05001528 buffer += sprint_varx(parm, parm_value, 0, indent, col1_width)
Michael Walshdb6e68a2017-05-23 17:55:31 -05001529
1530 # Setting global program_pid.
1531 BuiltIn().set_global_variable("${program_pid}", os.getpid())
1532
Michael Walshbec416d2016-11-10 08:54:52 -06001533 if linefeed:
1534 buffer += "\n"
Michael Walshde791732016-09-06 14:25:24 -05001535
1536 return buffer
1537
Michael Walshde791732016-09-06 14:25:24 -05001538
Michael Walsh7423c012016-10-04 10:27:21 -05001539def sprint_error_report(error_text="\n",
Michael Walshdb6e68a2017-05-23 17:55:31 -05001540 indent=2,
Michael Walshe7ca2b02019-08-01 11:09:45 -05001541 format=None,
1542 stack_frame_ix=None):
Michael Walsh7423c012016-10-04 10:27:21 -05001543 r"""
Michael Walsh46fcecb2019-10-16 17:11:59 -05001544 Return a string with a standardized report which includes the caller's error text, the call stack and the
1545 program header.
Michael Walsh7423c012016-10-04 10:27:21 -05001546
Michael Walsh4dbb6002019-05-17 15:51:15 -05001547 Description of argument(s):
Michael Walsh46fcecb2019-10-16 17:11:59 -05001548 error_text The error text to be included in the report. The caller should include
1549 any needed linefeeds.
1550 indent The number of characters to indent each line of output.
1551 format Long or short format. Long includes extras like lines of dashes, call
1552 stack, etc.
1553 stack_frame_ix The index of the first stack frame which is to be shown in the
1554 print_call_stack portion of the error report.
Michael Walsh7423c012016-10-04 10:27:21 -05001555 """
1556
Michael Walshdb6e68a2017-05-23 17:55:31 -05001557 # Process input.
1558 indent = int(indent)
1559 if format is None:
1560 if robot_env:
1561 format = 'short'
1562 else:
1563 format = 'long'
1564 error_text = error_text.rstrip('\n') + '\n'
1565
1566 if format == 'short':
1567 return sprint_error(error_text)
1568
Michael Walsh7423c012016-10-04 10:27:21 -05001569 buffer = ""
1570 buffer += sprint_dashes(width=120, char="=")
1571 buffer += sprint_error(error_text)
1572 buffer += "\n"
Michael Walshe7ca2b02019-08-01 11:09:45 -05001573 if not stack_frame_ix:
Michael Walsh46fcecb2019-10-16 17:11:59 -05001574 # Calling sprint_call_stack with stack_frame_ix of 0 causes it to show itself and this function in
1575 # the call stack. This is not helpful to a debugger and is therefore clutter. We will adjust the
Michael Walshe7ca2b02019-08-01 11:09:45 -05001576 # stack_frame_ix to hide that information.
1577 stack_frame_ix = 1
1578 caller_func_name = sprint_func_name(1)
1579 if caller_func_name.endswith("print_error_report"):
1580 stack_frame_ix += 1
1581 caller_func_name = sprint_func_name(2)
1582 if caller_func_name.endswith("print_error_report"):
1583 stack_frame_ix += 1
Michael Walsh7bfa9ab2018-11-16 15:24:26 -06001584 buffer += sprint_call_stack(indent, stack_frame_ix)
Michael Walsh7423c012016-10-04 10:27:21 -05001585 buffer += sprint_pgm_header(indent)
1586 buffer += sprint_dashes(width=120, char="=")
1587
1588 return buffer
1589
Michael Walsh7423c012016-10-04 10:27:21 -05001590
Michael Walsh18176322016-11-15 15:11:21 -06001591def sprint_issuing(cmd_buf,
1592 test_mode=0):
Michael Walshde791732016-09-06 14:25:24 -05001593 r"""
1594 Return a line indicating a command that the program is about to execute.
1595
1596 Sample output for a cmd_buf of "ls"
1597
1598 #(CDT) 2016/08/25 17:57:36 - Issuing: ls
Michael Walshbec416d2016-11-10 08:54:52 -06001599
Michael Walsh4dbb6002019-05-17 15:51:15 -05001600 Description of argument(s):
Michael Walshde791732016-09-06 14:25:24 -05001601 cmd_buf The command to be executed by caller.
Michael Walsh46fcecb2019-10-16 17:11:59 -05001602 test_mode With test_mode set, the output will look like this:
Michael Walshbec416d2016-11-10 08:54:52 -06001603
1604 #(CDT) 2016/08/25 17:57:36 - (test_mode) Issuing: ls
1605
Michael Walshde791732016-09-06 14:25:24 -05001606 """
1607
Michael Walshbec416d2016-11-10 08:54:52 -06001608 buffer = sprint_time()
1609 if test_mode:
1610 buffer += "(test_mode) "
Michael Walsh61c12982019-03-28 12:38:01 -05001611 if type(cmd_buf) is list:
1612 # Assume this is a robot command in the form of a list.
1613 cmd_buf = ' '.join([str(element) for element in cmd_buf])
Michael Walshbec416d2016-11-10 08:54:52 -06001614 buffer += "Issuing: " + cmd_buf + "\n"
Michael Walshde791732016-09-06 14:25:24 -05001615
1616 return buffer
1617
Michael Walshde791732016-09-06 14:25:24 -05001618
Michael Walshde791732016-09-06 14:25:24 -05001619def sprint_pgm_footer():
Michael Walshde791732016-09-06 14:25:24 -05001620 r"""
Michael Walsh46fcecb2019-10-16 17:11:59 -05001621 Return a standardized footer that programs should print at the end of the program run. It includes
1622 useful information like total run time, etc.
Michael Walshde791732016-09-06 14:25:24 -05001623 """
1624
1625 buffer = "\n" + sprint_time() + "Finished running " + pgm_name + ".\n\n"
1626
1627 total_time = time.time() - start_time
1628 total_time_string = "%0.6f" % total_time
1629
Michael Walsh7423c012016-10-04 10:27:21 -05001630 buffer += sprint_varx(pgm_name_var_name + "_runtime", total_time_string)
Michael Walshbec416d2016-11-10 08:54:52 -06001631 buffer += "\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001632
1633 return buffer
1634
Michael Walsh7423c012016-10-04 10:27:21 -05001635
Michael Walsh45ee00b2019-12-05 16:27:04 -06001636def sprint_file(file_path):
1637 r"""
1638 Return the file data as a string.
1639
1640 Description of argument(s):
1641 file_path The path to a file (e.g. "/tmp/file1").
1642 """
1643
1644 with open(file_path, 'r') as file:
1645 buffer = file.read()
1646 return buffer
1647
1648
Michael Walsh7423c012016-10-04 10:27:21 -05001649def sprint(buffer=""):
Michael Walsh7423c012016-10-04 10:27:21 -05001650 r"""
Michael Walsh46fcecb2019-10-16 17:11:59 -05001651 Simply return the user's buffer. This function is used by the qprint and dprint functions defined
1652 dynamically below, i.e. it would not normally be called for general use.
Michael Walsh7423c012016-10-04 10:27:21 -05001653
Michael Walsh4dbb6002019-05-17 15:51:15 -05001654 Description of argument(s).
Michael Walsh7423c012016-10-04 10:27:21 -05001655 buffer This will be returned to the caller.
1656 """
Michael Walshde791732016-09-06 14:25:24 -05001657
Michael Walsh95e45102018-02-09 12:44:43 -06001658 try:
1659 return str(buffer)
1660 except UnicodeEncodeError:
1661 return buffer
Michael Walshbec416d2016-11-10 08:54:52 -06001662
Michael Walshbec416d2016-11-10 08:54:52 -06001663
Michael Walshbec416d2016-11-10 08:54:52 -06001664def sprintn(buffer=""):
Michael Walshbec416d2016-11-10 08:54:52 -06001665 r"""
Michael Walsh46fcecb2019-10-16 17:11:59 -05001666 Simply return the user's buffer with a line feed. This function is used by the qprint and dprint
1667 functions defined dynamically below, i.e. it would not normally be called for general use.
Michael Walshbec416d2016-11-10 08:54:52 -06001668
Michael Walsh4dbb6002019-05-17 15:51:15 -05001669 Description of argument(s).
Michael Walshbec416d2016-11-10 08:54:52 -06001670 buffer This will be returned to the caller.
1671 """
1672
Michael Walsh95e45102018-02-09 12:44:43 -06001673 try:
1674 buffer = str(buffer) + "\n"
1675 except UnicodeEncodeError:
1676 buffer = buffer + "\n"
Michael Walshbec416d2016-11-10 08:54:52 -06001677
Michael Walshde791732016-09-06 14:25:24 -05001678 return buffer
1679
Michael Walsh168eb0f2017-12-01 15:35:32 -06001680
Michael Walshfd2733c2017-11-13 11:36:20 -06001681def gp_print(buffer,
1682 stream='stdout'):
Michael Walshfd2733c2017-11-13 11:36:20 -06001683 r"""
Michael Walsh46fcecb2019-10-16 17:11:59 -05001684 Print the buffer using either sys.stdout.write or BuiltIn().log_to_console depending on whether we are
1685 running in a robot environment.
Michael Walshfd2733c2017-11-13 11:36:20 -06001686
1687 This function is intended for use only by other functions in this module.
1688
Michael Walsh4dbb6002019-05-17 15:51:15 -05001689 Description of argument(s):
Michael Walshfd2733c2017-11-13 11:36:20 -06001690 buffer The string to be printed.
1691 stream Either "stdout" or "stderr".
1692 """
1693
1694 if robot_env:
1695 BuiltIn().log_to_console(buffer, stream=stream, no_newline=True)
1696 else:
1697 if stream == "stdout":
1698 sys.stdout.write(buffer)
1699 sys.stdout.flush()
1700 else:
1701 sys.stderr.write(buffer)
1702 sys.stderr.flush()
Michael Walshde791732016-09-06 14:25:24 -05001703
1704
Michael Walsh168eb0f2017-12-01 15:35:32 -06001705def gp_log(buffer):
Michael Walsh168eb0f2017-12-01 15:35:32 -06001706 r"""
Michael Walsh46fcecb2019-10-16 17:11:59 -05001707 Log the buffer using either python logging or BuiltIn().log depending on whether we are running in a
1708 robot environment.
Michael Walsh168eb0f2017-12-01 15:35:32 -06001709
1710 This function is intended for use only by other functions in this module.
1711
Michael Walsh4dbb6002019-05-17 15:51:15 -05001712 Description of argument(s):
Michael Walsh168eb0f2017-12-01 15:35:32 -06001713 buffer The string to be logged.
1714 """
1715
1716 if robot_env:
1717 BuiltIn().log(buffer)
1718 else:
1719 logging.warning(buffer)
1720
1721
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001722def gp_debug_print(buffer):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001723 r"""
Michael Walshfd2733c2017-11-13 11:36:20 -06001724 Print with gp_print only if gen_print_debug is set.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001725
1726 This function is intended for use only by other functions in this module.
1727
Michael Walsh4dbb6002019-05-17 15:51:15 -05001728 Description of argument(s):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001729 buffer The string to be printed.
1730 """
1731
1732 if not gen_print_debug:
1733 return
1734
Michael Walshfd2733c2017-11-13 11:36:20 -06001735 gp_print(buffer)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001736
1737
Michael Walshb1500152017-04-12 15:42:43 -05001738def get_var_value(var_value=None,
1739 default=1,
1740 var_name=None):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001741 r"""
Michael Walshb1500152017-04-12 15:42:43 -05001742 Return either var_value, the corresponding global value or default.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001743
Michael Walshb1500152017-04-12 15:42:43 -05001744 If var_value is not None, it will simply be returned.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001745
Michael Walsh46fcecb2019-10-16 17:11:59 -05001746 If var_value is None, this function will return the corresponding global value of the variable in
1747 question.
Michael Walshb1500152017-04-12 15:42:43 -05001748
Michael Walsh46fcecb2019-10-16 17:11:59 -05001749 Note: For global values, if we are in a robot environment, get_variable_value will be used. Otherwise,
1750 the __builtin__ version of the variable is returned (which are set by gen_arg.py functions).
Michael Walshb1500152017-04-12 15:42:43 -05001751
Michael Walsh46fcecb2019-10-16 17:11:59 -05001752 If there is no global value associated with the variable, default is returned.
Michael Walshb1500152017-04-12 15:42:43 -05001753
Michael Walsh46fcecb2019-10-16 17:11:59 -05001754 This function is useful for other functions in setting default values for parameters.
Michael Walshb1500152017-04-12 15:42:43 -05001755
1756 Example use:
1757
1758 def my_func(quiet=None):
1759
1760 quiet = int(get_var_value(quiet, 0))
1761
1762 Example calls to my_func():
1763
Michael Walsh46fcecb2019-10-16 17:11:59 -05001764 In the following example, the caller is explicitly asking to have quiet be set to 1.
Michael Walshb1500152017-04-12 15:42:43 -05001765
1766 my_func(quiet=1)
1767
Michael Walsh46fcecb2019-10-16 17:11:59 -05001768 In the following example, quiet will be set to the global value of quiet, if defined, or to 0 (the
1769 default).
Michael Walshb1500152017-04-12 15:42:43 -05001770
1771 my_func()
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001772
Michael Walsh4dbb6002019-05-17 15:51:15 -05001773 Description of argument(s):
Michael Walsh46fcecb2019-10-16 17:11:59 -05001774 var_value The value to be returned (if not equal to None).
1775 default The value that is returned if var_value is None and there is no
1776 corresponding global value defined.
1777 var_name The name of the variable whose value is to be returned. Under most
1778 circumstances, this value need not be provided. This function can figure
1779 out the name of the variable passed as var_value. One exception to this
1780 would be if this function is called directly from a .robot file.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001781 """
1782
Michael Walshb1500152017-04-12 15:42:43 -05001783 if var_value is not None:
1784 return var_value
1785
1786 if var_name is None:
1787 var_name = get_arg_name(None, 1, 2)
1788
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001789 if robot_env:
Michael Walshc6537442017-06-06 15:33:52 -05001790 var_value = BuiltIn().get_variable_value("${" + var_name + "}",
1791 default)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001792 else:
1793 var_value = getattr(__builtin__, var_name, default)
1794
1795 return var_value
1796
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001797
Michael Walsh052ff812018-05-18 16:09:09 -05001798def get_stack_var(var_name,
1799 default="",
1800 init_stack_ix=2):
Michael Walsh052ff812018-05-18 16:09:09 -05001801 r"""
Michael Walsh46fcecb2019-10-16 17:11:59 -05001802 Starting with the caller's stack level, search upward in the call stack for a variable named var_name and
1803 return its value. If the variable cannot be found in the stack, attempt to get the global value. If the
Michael Walshc9a7ee72019-08-28 16:45:50 -05001804 variable still cannot be found, return default.
Michael Walsh052ff812018-05-18 16:09:09 -05001805
1806 Example code:
1807
1808 def func12():
1809 my_loc_var1 = get_stack_var('my_var1', "default value")
1810
1811 def func11():
1812 my_var1 = 11
1813 func12()
1814
Michael Walsh46fcecb2019-10-16 17:11:59 -05001815 In this example, get_stack_var will find the value of my_var1 in func11's stack and will therefore return
1816 the value 11. Therefore, my_loc_var1 would get set to 11.
Michael Walsh052ff812018-05-18 16:09:09 -05001817
1818 Description of argument(s):
Michael Walsh46fcecb2019-10-16 17:11:59 -05001819 var_name The name of the variable to be searched for.
1820 default The value to return if the the variable cannot be found.
1821 init_stack_ix The initial stack index from which to begin the search. 0 would be the
1822 index of this func1tion ("get_stack_var"), 1 would be the index of the
1823 function calling this function, etc.
Michael Walsh052ff812018-05-18 16:09:09 -05001824 """
1825
Michael Walsh6f0362c2019-03-25 14:05:14 -05001826 work_around_inspect_stack_cwd_failure()
Michael Walshc9a7ee72019-08-28 16:45:50 -05001827 default = get_var_value(var_name=var_name, default=default)
Michael Walsh052ff812018-05-18 16:09:09 -05001828 return next((frame[0].f_locals[var_name]
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001829 for frame in inspect.stack()[init_stack_ix:]
1830 if var_name in frame[0].f_locals), default)
Michael Walsh052ff812018-05-18 16:09:09 -05001831
1832
Michael Walsh46fcecb2019-10-16 17:11:59 -05001833# hidden_text is a list of passwords which are to be replaced with asterisks by print functions defined in
1834# this module.
Michael Walsh82acf002017-05-04 14:33:05 -05001835hidden_text = []
1836# password_regex is created based on the contents of hidden_text.
1837password_regex = ""
1838
1839
Michael Walsh82acf002017-05-04 14:33:05 -05001840def register_passwords(*args):
Michael Walsh82acf002017-05-04 14:33:05 -05001841 r"""
Michael Walsh46fcecb2019-10-16 17:11:59 -05001842 Register one or more passwords which are to be hidden in output produced by the print functions in this
1843 module.
Michael Walsh82acf002017-05-04 14:33:05 -05001844
1845 Note: Blank password values are NOT registered. They are simply ignored.
1846
1847 Description of argument(s):
Michael Walsh46fcecb2019-10-16 17:11:59 -05001848 args One or more password values. If a given password value is already
1849 registered, this function will simply do nothing.
Michael Walsh82acf002017-05-04 14:33:05 -05001850 """
1851
1852 global hidden_text
1853 global password_regex
1854
1855 for password in args:
1856 if password == "":
1857 break
1858 if password in hidden_text:
1859 break
1860
1861 # Place the password into the hidden_text list.
1862 hidden_text.append(password)
Michael Walsh46fcecb2019-10-16 17:11:59 -05001863 # Create a corresponding password regular expression. Escape regex special characters too.
Michael Walsh82acf002017-05-04 14:33:05 -05001864 password_regex = '(' +\
1865 '|'.join([re.escape(x) for x in hidden_text]) + ')'
1866
Michael Walsh82acf002017-05-04 14:33:05 -05001867
Michael Walsh82acf002017-05-04 14:33:05 -05001868def replace_passwords(buffer):
Michael Walsh82acf002017-05-04 14:33:05 -05001869 r"""
Michael Walsh46fcecb2019-10-16 17:11:59 -05001870 Return the buffer but with all registered passwords replaced by a string of asterisks.
Michael Walsh82acf002017-05-04 14:33:05 -05001871
1872
1873 Description of argument(s):
Michael Walsh46fcecb2019-10-16 17:11:59 -05001874 buffer The string to be returned but with passwords replaced.
Michael Walsh82acf002017-05-04 14:33:05 -05001875 """
1876
1877 global password_regex
1878
1879 if int(os.environ.get("DEBUG_SHOW_PASSWORDS", "0")):
1880 return buffer
1881
1882 if password_regex == "":
1883 # No passwords to replace.
1884 return buffer
1885
1886 return re.sub(password_regex, "********", buffer)
1887
Michael Walshfd2733c2017-11-13 11:36:20 -06001888
1889def create_print_wrapper_funcs(func_names,
1890 stderr_func_names,
Michael Walsh61c12982019-03-28 12:38:01 -05001891 replace_dict,
1892 func_prefix=""):
Michael Walshfd2733c2017-11-13 11:36:20 -06001893 r"""
Michael Walsh46fcecb2019-10-16 17:11:59 -05001894 Generate code for print wrapper functions and return the generated code as a string.
Michael Walshfd2733c2017-11-13 11:36:20 -06001895
Michael Walsh46fcecb2019-10-16 17:11:59 -05001896 To illustrate, suppose there is a "print_foo_bar" function in the func_names list.
Michael Walshfd2733c2017-11-13 11:36:20 -06001897 This function will...
1898 - Expect that there is an sprint_foo_bar function already in existence.
Michael Walsh46fcecb2019-10-16 17:11:59 -05001899 - Create a print_foo_bar function which calls sprint_foo_bar and prints the result.
1900 - Create a qprint_foo_bar function which calls upon sprint_foo_bar only if global value quiet is 0.
1901 - Create a dprint_foo_bar function which calls upon sprint_foo_bar only if global value debug is 1.
Michael Walshfd2733c2017-11-13 11:36:20 -06001902
Michael Walsh46fcecb2019-10-16 17:11:59 -05001903 Also, code will be generated to define aliases for each function as well. Each alias will be created by
1904 replacing "print_" in the function name with "p" For example, the alias for print_foo_bar will be
1905 pfoo_bar.
Michael Walshfd2733c2017-11-13 11:36:20 -06001906
1907 Description of argument(s):
Michael Walsh46fcecb2019-10-16 17:11:59 -05001908 func_names A list of functions for which print wrapper function code is to be
1909 generated.
1910 stderr_func_names A list of functions whose generated code should print to stderr rather
1911 than to stdout.
1912 replace_dict Please see the create_func_def_string function in wrap_utils.py for
1913 details on this parameter. This parameter will be passed directly to
1914 create_func_def_string.
1915 func_prefix Prefix to be pre-pended to the generated function name.
Michael Walshfd2733c2017-11-13 11:36:20 -06001916 """
1917
1918 buffer = ""
1919
1920 for func_name in func_names:
1921 if func_name in stderr_func_names:
1922 replace_dict['output_stream'] = "stderr"
1923 else:
1924 replace_dict['output_stream'] = "stdout"
1925
1926 s_func_name = "s" + func_name
1927 q_func_name = "q" + func_name
1928 d_func_name = "d" + func_name
1929
Michael Walsh46fcecb2019-10-16 17:11:59 -05001930 # We don't want to try to redefine the "print" function, thus the following if statement.
Michael Walshfd2733c2017-11-13 11:36:20 -06001931 if func_name != "print":
Michael Walsh61c12982019-03-28 12:38:01 -05001932 func_def = create_func_def_string(s_func_name,
1933 func_prefix + func_name,
Michael Walshfd2733c2017-11-13 11:36:20 -06001934 print_func_template,
1935 replace_dict)
1936 buffer += func_def
1937
Michael Walsh61c12982019-03-28 12:38:01 -05001938 func_def = create_func_def_string(s_func_name,
1939 func_prefix + "q" + func_name,
Michael Walshfd2733c2017-11-13 11:36:20 -06001940 qprint_func_template, replace_dict)
1941 buffer += func_def
1942
Michael Walsh61c12982019-03-28 12:38:01 -05001943 func_def = create_func_def_string(s_func_name,
1944 func_prefix + "d" + func_name,
Michael Walshfd2733c2017-11-13 11:36:20 -06001945 dprint_func_template, replace_dict)
1946 buffer += func_def
1947
Michael Walsh61c12982019-03-28 12:38:01 -05001948 func_def = create_func_def_string(s_func_name,
1949 func_prefix + "l" + func_name,
Michael Walsh168eb0f2017-12-01 15:35:32 -06001950 lprint_func_template, replace_dict)
1951 buffer += func_def
1952
Michael Walshfd2733c2017-11-13 11:36:20 -06001953 # Create abbreviated aliases (e.g. spvar is an alias for sprint_var).
1954 alias = re.sub("print_", "p", func_name)
1955 alias = re.sub("print", "p", alias)
Michael Walsh61c12982019-03-28 12:38:01 -05001956 prefixes = [func_prefix + "", "s", func_prefix + "q",
1957 func_prefix + "d", func_prefix + "l"]
Michael Walshfd2733c2017-11-13 11:36:20 -06001958 for prefix in prefixes:
1959 if alias == "p":
1960 continue
1961 func_def = prefix + alias + " = " + prefix + func_name
1962 buffer += func_def + "\n"
1963
1964 return buffer
Michael Walsh82acf002017-05-04 14:33:05 -05001965
1966
Michael Walsh46fcecb2019-10-16 17:11:59 -05001967# In the following section of code, we will dynamically create print versions for each of the sprint
1968# functions defined above. So, for example, where we have an sprint_time() function defined above that
1969# returns the time to the caller in a string, we will create a corresponding print_time() function that will
1970# print that string directly to stdout.
Michael Walshde791732016-09-06 14:25:24 -05001971
Michael Walsh46fcecb2019-10-16 17:11:59 -05001972# It can be complicated to follow what's being created below. Here is an example of the print_time()
1973# function that will be created:
Michael Walshde791732016-09-06 14:25:24 -05001974
Michael Walshfd2733c2017-11-13 11:36:20 -06001975# def print_time(buffer=''):
Michael Walsh61c12982019-03-28 12:38:01 -05001976# gp_print(replace_passwords(sprint_time(buffer=buffer)), stream='stdout')
1977
Michael Walsh46fcecb2019-10-16 17:11:59 -05001978# For each print function defined below, there will also be a qprint, a dprint and an lprint version defined
1979# (e.g. qprint_time, dprint_time, lprint_time).
Michael Walsh61c12982019-03-28 12:38:01 -05001980
Michael Walsh46fcecb2019-10-16 17:11:59 -05001981# The q version of each print function will only print if the quiet variable is 0.
1982# The d version of each print function will only print if the debug variable is 1.
1983# The l version of each print function will print the contents as log data. For conventional programs, this
1984# means use of the logging module. For robot programs it means use of the BuiltIn().log() function.
Michael Walshde791732016-09-06 14:25:24 -05001985
Michael Walshfd2733c2017-11-13 11:36:20 -06001986# Templates for the various print wrapper functions.
1987print_func_template = \
1988 [
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001989 " <mod_qualifier>gp_print(<mod_qualifier>replace_passwords("
1990 + "<call_line>), stream='<output_stream>')"
Michael Walshfd2733c2017-11-13 11:36:20 -06001991 ]
1992
1993qprint_func_template = \
1994 [
Michael Walshc9a7ee72019-08-28 16:45:50 -05001995 " quiet = <mod_qualifier>get_stack_var(\"quiet\", 0)",
Michael Walsh589ae762019-03-19 13:22:39 -05001996 " if int(quiet): return"
Michael Walshfd2733c2017-11-13 11:36:20 -06001997 ] + print_func_template
1998
1999dprint_func_template = \
2000 [
Michael Walshc9a7ee72019-08-28 16:45:50 -05002001 " debug = <mod_qualifier>get_stack_var(\"debug\", 0)",
Michael Walsh589ae762019-03-19 13:22:39 -05002002 " if not int(debug): return"
Michael Walshfd2733c2017-11-13 11:36:20 -06002003 ] + print_func_template
2004
Michael Walsh168eb0f2017-12-01 15:35:32 -06002005lprint_func_template = \
2006 [
Michael Walsh61c12982019-03-28 12:38:01 -05002007 " <mod_qualifier>set_last_seconds_ix(<mod_qualifier>"
2008 + "lprint_last_seconds_ix())",
2009 " <mod_qualifier>gp_log(<mod_qualifier>replace_passwords"
2010 + "(<call_line>))",
2011 " <mod_qualifier>set_last_seconds_ix(<mod_qualifier>"
2012 + "standard_print_last_seconds_ix())"
Michael Walsh168eb0f2017-12-01 15:35:32 -06002013 ]
2014
Michael Walsh81c02342018-01-05 15:43:28 -06002015replace_dict = {'output_stream': 'stdout', 'mod_qualifier': ''}
Michael Walshfd2733c2017-11-13 11:36:20 -06002016
Michael Walsh61c12982019-03-28 12:38:01 -05002017gp_debug_print("robot_env: " + str(robot_env) + "\n")
Michael Walshde791732016-09-06 14:25:24 -05002018
Michael Walsh46fcecb2019-10-16 17:11:59 -05002019# func_names contains a list of all print functions which should be created from their sprint counterparts.
Michael Walshde791732016-09-06 14:25:24 -05002020func_names = ['print_time', 'print_timen', 'print_error', 'print_varx',
Michael Walsh18176322016-11-15 15:11:21 -06002021 'print_var', 'print_vars', 'print_dashes', 'indent',
2022 'print_call_stack', 'print_func_name', 'print_executing',
2023 'print_pgm_header', 'print_issuing', 'print_pgm_footer',
Michael Walsh45ee00b2019-12-05 16:27:04 -06002024 'print_file', 'print_error_report', 'print', 'printn']
Michael Walshde791732016-09-06 14:25:24 -05002025
Michael Walsh46fcecb2019-10-16 17:11:59 -05002026# stderr_func_names is a list of functions whose output should go to stderr rather than stdout.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06002027stderr_func_names = ['print_error', 'print_error_report']
Michael Walshde791732016-09-06 14:25:24 -05002028
Michael Walshfd2733c2017-11-13 11:36:20 -06002029func_defs = create_print_wrapper_funcs(func_names, stderr_func_names,
2030 replace_dict)
2031gp_debug_print(func_defs)
2032exec(func_defs)