blob: 92dec7b9c5e15523129f84ff3806c408885ca943 [file] [log] [blame]
Michael Walshde791732016-09-06 14:25:24 -05001#!/usr/bin/env python
2
3r"""
Michael Walsh4dbb6002019-05-17 15:51:15 -05004This module provides many print functions such as sprint_var, sprint_time,
5sprint_error, sprint_call_stack.
Michael Walshde791732016-09-06 14:25:24 -05006"""
7
8import sys
9import os
10import time
11import inspect
12import re
13import grp
14import socket
15import argparse
Michael Walsh4dbb6002019-05-17 15:51:15 -050016import copy
George Keishing3b7115a2018-08-02 10:48:17 -050017try:
18 import __builtin__
19except ImportError:
20 import builtins as __builtin__
Michael Walsh7423c012016-10-04 10:27:21 -050021import logging
Michael Walshbec416d2016-11-10 08:54:52 -060022import collections
Michael Walshfd2733c2017-11-13 11:36:20 -060023from wrap_utils import *
Michael Walshbec416d2016-11-10 08:54:52 -060024
Michael Walshbec416d2016-11-10 08:54:52 -060025try:
Michael Walsh2ee77cd2017-03-08 11:50:17 -060026 robot_env = 1
Michael Walshbec416d2016-11-10 08:54:52 -060027 from robot.utils import DotDict
Michael Walsh8e6deb42017-01-27 14:22:41 -060028 from robot.utils import NormalizedDict
Michael Walsh2ee77cd2017-03-08 11:50:17 -060029 from robot.libraries.BuiltIn import BuiltIn
Michael Walshb1500152017-04-12 15:42:43 -050030 # Having access to the robot libraries alone does not indicate that we
31 # are in a robot environment. The following try block should confirm that.
32 try:
33 var_value = BuiltIn().get_variable_value("${SUITE_NAME}", "")
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -050034 except BaseException:
Michael Walshb1500152017-04-12 15:42:43 -050035 robot_env = 0
Michael Walshbec416d2016-11-10 08:54:52 -060036except ImportError:
Michael Walsh2ee77cd2017-03-08 11:50:17 -060037 robot_env = 0
Michael Walsh7423c012016-10-04 10:27:21 -050038
39import gen_arg as ga
Michael Walshde791732016-09-06 14:25:24 -050040
41# Setting these variables for use both inside this module and by programs
42# importing this module.
Michael Walshbf605652017-09-01 12:33:26 -050043pgm_file_path = sys.argv[0]
44pgm_name = os.path.basename(pgm_file_path)
Michael Walsh3ba8ecd2018-04-24 11:33:25 -050045pgm_dir_path = os.path.normpath(re.sub("/" + pgm_name, "", pgm_file_path)) +\
46 os.path.sep
Michael Walsh7423c012016-10-04 10:27:21 -050047
Michael Walshde791732016-09-06 14:25:24 -050048
49# Some functions (e.g. sprint_pgm_header) have need of a program name value
50# that looks more like a valid variable name. Therefore, we'll swap odd
51# characters like "." out for underscores.
52pgm_name_var_name = pgm_name.replace(".", "_")
53
54# Initialize global values used as defaults by print_time, print_var, etc.
Michael Walsh4dbb6002019-05-17 15:51:15 -050055dft_indent = 0
Michael Walshde791732016-09-06 14:25:24 -050056
57# Calculate default column width for print_var functions based on environment
58# variable settings. The objective is to make the variable values line up
59# nicely with the time stamps.
Michael Walsh4dbb6002019-05-17 15:51:15 -050060dft_col1_width = 29
Michael Walshb1500152017-04-12 15:42:43 -050061
62NANOSECONDS = os.environ.get('NANOSECONDS', '1')
63
Michael Walshde791732016-09-06 14:25:24 -050064if NANOSECONDS == "1":
Michael Walsh4dbb6002019-05-17 15:51:15 -050065 dft_col1_width = dft_col1_width + 7
Michael Walshde791732016-09-06 14:25:24 -050066
Michael Walshb1500152017-04-12 15:42:43 -050067SHOW_ELAPSED_TIME = os.environ.get('SHOW_ELAPSED_TIME', '1')
Michael Walshde791732016-09-06 14:25:24 -050068
69if SHOW_ELAPSED_TIME == "1":
70 if NANOSECONDS == "1":
Michael Walsh4dbb6002019-05-17 15:51:15 -050071 dft_col1_width = dft_col1_width + 14
Michael Walshde791732016-09-06 14:25:24 -050072 else:
Michael Walsh4dbb6002019-05-17 15:51:15 -050073 dft_col1_width = dft_col1_width + 7
Michael Walshde791732016-09-06 14:25:24 -050074
75# Initialize some time variables used in module functions.
76start_time = time.time()
Michael Walsh4fea2cf2018-08-22 17:48:18 -050077# sprint_time_last_seconds is used to calculate elapsed seconds.
Michael Walsh61c12982019-03-28 12:38:01 -050078sprint_time_last_seconds = [start_time, start_time]
Michael Walsh4fea2cf2018-08-22 17:48:18 -050079# Define global index for the sprint_time_last_seconds list.
80last_seconds_ix = 0
81
82
Michael Walsh61c12982019-03-28 12:38:01 -050083def set_last_seconds_ix(ix):
84 r"""
85 Set the "last_seconds_ix" module variable to the index value.
86
87 Description of argument(s):
88 ix The index value to be set into the module
89 global last_seconds_ix variable.
90 """
91 global last_seconds_ix
92 last_seconds_ix = ix
93
94
Michael Walsh4fea2cf2018-08-22 17:48:18 -050095# Since output from the lprint_ functions goes to a different location than
96# the output from the print_ functions (e.g. a file vs. the console),
97# sprint_time_last_seconds has been created as a list rather than a simple
98# integer so that it can store multiple sprint_time_last_seconds values.
99# Standard print_ functions defined in this file will use
100# sprint_time_last_seconds[0] and the lprint_ functions will use
101# sprint_time_last_seconds[1].
Michael Walsh61c12982019-03-28 12:38:01 -0500102def standard_print_last_seconds_ix():
103 r"""
104 Return the standard print last_seconds index value to the caller.
105 """
106 return 0
107
108
Michael Walsh4fea2cf2018-08-22 17:48:18 -0500109def lprint_last_seconds_ix():
110 r"""
111 Return lprint last_seconds index value to the caller.
112 """
113 return 1
114
Michael Walshde791732016-09-06 14:25:24 -0500115
Michael Walsh2ee77cd2017-03-08 11:50:17 -0600116# The user can set environment variable "GEN_PRINT_DEBUG" to get debug output
117# from this module.
118gen_print_debug = int(os.environ.get('GEN_PRINT_DEBUG', 0))
Michael Walsh7423c012016-10-04 10:27:21 -0500119
Michael Walshde791732016-09-06 14:25:24 -0500120
Michael Walshde791732016-09-06 14:25:24 -0500121def sprint_func_name(stack_frame_ix=None):
Michael Walshde791732016-09-06 14:25:24 -0500122 r"""
123 Return the function name associated with the indicated stack frame.
124
Michael Walsh4dbb6002019-05-17 15:51:15 -0500125 Description of argument(s):
Michael Walshde791732016-09-06 14:25:24 -0500126 stack_frame_ix The index of the stack frame whose
127 function name should be returned. If the
Michael Walsh2ee77cd2017-03-08 11:50:17 -0600128 caller does not specify a value, this
Michael Walshde791732016-09-06 14:25:24 -0500129 function will set the value to 1 which is
130 the index of the caller's stack frame. If
131 the caller is the wrapper function
132 "print_func_name", this function will bump
133 it up by 1.
134 """
135
136 # If user specified no stack_frame_ix, we'll set it to a proper default
137 # value.
138 if stack_frame_ix is None:
139 func_name = sys._getframe().f_code.co_name
140 caller_func_name = sys._getframe(1).f_code.co_name
141 if func_name[1:] == caller_func_name:
142 stack_frame_ix = 2
143 else:
144 stack_frame_ix = 1
145
146 func_name = sys._getframe(stack_frame_ix).f_code.co_name
147
148 return func_name
149
Michael Walshde791732016-09-06 14:25:24 -0500150
Michael Walsh6f0362c2019-03-25 14:05:14 -0500151def work_around_inspect_stack_cwd_failure():
152 r"""
153 Work around the inspect.stack() getcwd() failure by making "/tmp" the
154 current working directory.
155
Michael Walsh4dbb6002019-05-17 15:51:15 -0500156 NOTES: If the current working directory has been deleted, inspect.stack()
157 will fail with "OSError: [Errno 2] No such file or directory" because it
158 tries to do a getcwd().
Michael Walsh6f0362c2019-03-25 14:05:14 -0500159
160 This function will try to prevent this failure by detecting the scenario
161 in advance and making "/tmp" the current working directory.
162 """
163 try:
164 os.getcwd()
165 except OSError:
166 os.chdir("/tmp")
167
168
Michael Walsh1173a522018-05-21 17:24:51 -0500169def get_line_indent(line):
170 r"""
171 Return the number of spaces at the beginning of the line.
172 """
173
174 return len(line) - len(line.lstrip(' '))
175
176
Michael Walsh4dbb6002019-05-17 15:51:15 -0500177# get_arg_name is not a print function per se. It has been included in this
178# module because it is used by sprint_var which is defined in this module.
Michael Walshde791732016-09-06 14:25:24 -0500179def get_arg_name(var,
180 arg_num=1,
181 stack_frame_ix=1):
Michael Walshde791732016-09-06 14:25:24 -0500182 r"""
183 Return the "name" of an argument passed to a function. This could be a
184 literal or a variable name.
185
Michael Walsh4dbb6002019-05-17 15:51:15 -0500186 Description of argument(s):
187 var The variable whose name is to be returned.
Michael Walsh1173a522018-05-21 17:24:51 -0500188 arg_num The arg number whose name is to be
189 returned. To illustrate how arg_num is
190 processed, suppose that a programmer codes
191 this line: "rc, outbuf = my_func(var1,
192 var2)" and suppose that my_func has this
193 line of code: "result = gp.get_arg_name(0,
194 arg_num, 2)". If arg_num is positive, the
195 indicated argument is returned. For
196 example, if arg_num is 1, "var1" would be
197 returned, If arg_num is 2, "var2" would be
198 returned. If arg_num exceeds the number
199 of arguments, get_arg_name will simply
200 return a complete list of the arguments.
201 If arg_num is 0, get_arg_name will return
202 the name of the target function as
203 specified in the calling line ("my_func"
204 in this case). To clarify, if the caller
205 of the target function uses an alias
206 function name, the alias name would be
207 returned. If arg_num is negative, an
208 lvalue variable name is returned.
209 Continuing with the given example, if
210 arg_num is -2 the 2nd parm to the left of
211 the "=" ("rc" in this case) should be
212 returned. If arg_num is -1, the 1st parm
213 to the left of the "=" ("out_buf" in this
214 case) should be returned. If arg_num is
215 less than -2, an entire dictionary is
216 returned. The keys to the dictionary for
217 this example would be -2 and -1.
Michael Walshde791732016-09-06 14:25:24 -0500218 stack_frame_ix The stack frame index of the target
219 function. This value must be 1 or
220 greater. 1 would indicate get_arg_name's
221 stack frame. 2 would be the caller of
222 get_arg_name's stack frame, etc.
223
224 Example 1:
225
226 my_var = "mike"
227 var_name = get_arg_name(my_var)
228
229 In this example, var_name will receive the value "my_var".
230
231 Example 2:
232
233 def test1(var):
234 # Getting the var name of the first arg to this function, test1.
Michael Walsh4dbb6002019-05-17 15:51:15 -0500235 # Note, in this case, it doesn't matter what is passed as the first
236 # arg to get_arg_name since it is the caller's variable name that
237 # matters.
Michael Walshde791732016-09-06 14:25:24 -0500238 dummy = 1
239 arg_num = 1
240 stack_frame = 2
241 var_name = get_arg_name(dummy, arg_num, stack_frame)
242
243 # Mainline...
244
245 another_var = "whatever"
246 test1(another_var)
247
248 In this example, var_name will be set to "another_var".
249
250 """
251
Michael Walsh4dbb6002019-05-17 15:51:15 -0500252 # Note: To avoid infinite recursion, avoid calling any function that
253 # calls this function (e.g. sprint_var, valid_value, etc.).
Michael Walshde791732016-09-06 14:25:24 -0500254
Michael Walsh23e7f492017-01-10 11:34:47 -0600255 # The user can set environment variable "GET_ARG_NAME_DEBUG" to get debug
256 # output from this function.
257 local_debug = int(os.environ.get('GET_ARG_NAME_DEBUG', 0))
258 # In addition to GET_ARG_NAME_DEBUG, the user can set environment
259 # variable "GET_ARG_NAME_SHOW_SOURCE" to have this function include source
260 # code in the debug output.
261 local_debug_show_source = int(
262 os.environ.get('GET_ARG_NAME_SHOW_SOURCE', 0))
Michael Walshde791732016-09-06 14:25:24 -0500263
Michael Walshde791732016-09-06 14:25:24 -0500264 if stack_frame_ix < 1:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500265 print_error("Programmer error - Variable \"stack_frame_ix\" has an"
266 + " invalid value of \"" + str(stack_frame_ix) + "\". The"
267 + " value must be an integer that is greater than or equal"
268 + " to 1.\n")
Michael Walshde791732016-09-06 14:25:24 -0500269 return
270
271 if local_debug:
272 debug_indent = 2
Michael Walsh23e7f492017-01-10 11:34:47 -0600273 print("")
274 print_dashes(0, 120)
Michael Walshde791732016-09-06 14:25:24 -0500275 print(sprint_func_name() + "() parms:")
Michael Walsh4dbb6002019-05-17 15:51:15 -0500276 print_varx("var", var, indent=debug_indent)
277 print_varx("arg_num", arg_num, indent=debug_indent)
278 print_varx("stack_frame_ix", stack_frame_ix, indent=debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600279 print("")
280 print_call_stack(debug_indent, 2)
Michael Walshde791732016-09-06 14:25:24 -0500281
Michael Walsh6f0362c2019-03-25 14:05:14 -0500282 work_around_inspect_stack_cwd_failure()
Michael Walsh23e7f492017-01-10 11:34:47 -0600283 for count in range(0, 2):
284 try:
285 frame, filename, cur_line_no, function_name, lines, index = \
286 inspect.stack()[stack_frame_ix]
287 except IndexError:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500288 print_error("Programmer error - The caller has asked for"
289 + " information about the stack frame at index \""
290 + str(stack_frame_ix) + "\". However, the stack"
291 + " only contains " + str(len(inspect.stack()))
292 + " entries. Therefore the stack frame index is out"
293 + " of range.\n")
Michael Walsh23e7f492017-01-10 11:34:47 -0600294 return
295 if filename != "<string>":
296 break
297 # filename of "<string>" may mean that the function in question was
298 # defined dynamically and therefore its code stack is inaccessible.
299 # This may happen with functions like "rqprint_var". In this case,
300 # we'll increment the stack_frame_ix and try again.
301 stack_frame_ix += 1
302 if local_debug:
303 print("Adjusted stack_frame_ix...")
Michael Walsh4dbb6002019-05-17 15:51:15 -0500304 print_varx("stack_frame_ix", stack_frame_ix, indent=debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500305
Michael Walsh1173a522018-05-21 17:24:51 -0500306 real_called_func_name = sprint_func_name(stack_frame_ix)
Michael Walsh23e7f492017-01-10 11:34:47 -0600307
308 module = inspect.getmodule(frame)
309
Michael Walsh4dbb6002019-05-17 15:51:15 -0500310 # Though one would expect inspect.getsourcelines(frame) to get all module
Michael Walsh23e7f492017-01-10 11:34:47 -0600311 # source lines if the frame is "<module>", it doesn't do that. Therefore,
Michael Walsh4dbb6002019-05-17 15:51:15 -0500312 # for this special case, do inspect.getsourcelines(module).
Michael Walsh23e7f492017-01-10 11:34:47 -0600313 if function_name == "<module>":
314 source_lines, source_line_num =\
315 inspect.getsourcelines(module)
316 line_ix = cur_line_no - source_line_num - 1
317 else:
318 source_lines, source_line_num =\
319 inspect.getsourcelines(frame)
320 line_ix = cur_line_no - source_line_num
321
322 if local_debug:
323 print("\n Variables retrieved from inspect.stack() function:")
Michael Walsh4dbb6002019-05-17 15:51:15 -0500324 print_varx("frame", frame, indent=debug_indent + 2)
325 print_varx("filename", filename, indent=debug_indent + 2)
326 print_varx("cur_line_no", cur_line_no, indent=debug_indent + 2)
327 print_varx("function_name", function_name, indent=debug_indent + 2)
328 print_varx("lines", lines, indent=debug_indent + 2)
329 print_varx("index", index, indent=debug_indent + 2)
330 print_varx("source_line_num", source_line_num, indent=debug_indent)
331 print_varx("line_ix", line_ix, indent=debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600332 if local_debug_show_source:
Michael Walsh4dbb6002019-05-17 15:51:15 -0500333 print_varx("source_lines", source_lines, indent=debug_indent)
334 print_varx("real_called_func_name", real_called_func_name,
335 indent=debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600336
337 # Get a list of all functions defined for the module. Note that this
338 # doesn't work consistently when _run_exitfuncs is at the top of the stack
339 # (i.e. if we're running an exit function). I've coded a work-around
340 # below for this deficiency.
341 all_functions = inspect.getmembers(module, inspect.isfunction)
342
343 # Get called_func_id by searching for our function in the list of all
344 # functions.
345 called_func_id = None
346 for func_name, function in all_functions:
Michael Walsh1173a522018-05-21 17:24:51 -0500347 if func_name == real_called_func_name:
Michael Walsh23e7f492017-01-10 11:34:47 -0600348 called_func_id = id(function)
349 break
350 # NOTE: The only time I've found that called_func_id can't be found is
351 # when we're running from an exit function.
352
353 # Look for other functions in module with matching id.
Michael Walsh1173a522018-05-21 17:24:51 -0500354 aliases = set([real_called_func_name])
Michael Walsh23e7f492017-01-10 11:34:47 -0600355 for func_name, function in all_functions:
Michael Walsh1173a522018-05-21 17:24:51 -0500356 if func_name == real_called_func_name:
Michael Walsh23e7f492017-01-10 11:34:47 -0600357 continue
358 func_id = id(function)
359 if func_id == called_func_id:
360 aliases.add(func_name)
361
362 # In most cases, my general purpose code above will find all aliases.
363 # However, for the odd case (i.e. running from exit function), I've added
364 # code to handle pvar, qpvar, dpvar, etc. aliases explicitly since they
365 # are defined in this module and used frequently.
366 # pvar is an alias for print_var.
Michael Walsh1173a522018-05-21 17:24:51 -0500367 aliases.add(re.sub("print_var", "pvar", real_called_func_name))
Michael Walsh23e7f492017-01-10 11:34:47 -0600368
Michael Walsh3f248272018-06-01 13:59:35 -0500369 # The call to the function could be encased in a recast (e.g.
370 # int(func_name())).
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500371 recast_regex = "([^ ]+\\([ ]*)?"
372 import_name_regex = "([a-zA-Z0-9_]+\\.)?"
Michael Walsh3f248272018-06-01 13:59:35 -0500373 func_name_regex = recast_regex + import_name_regex + "(" +\
374 '|'.join(aliases) + ")"
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500375 pre_args_regex = ".*" + func_name_regex + "[ ]*\\("
Michael Walsh23e7f492017-01-10 11:34:47 -0600376
377 # Search backward through source lines looking for the calling function
378 # name.
379 found = False
380 for start_line_ix in range(line_ix, 0, -1):
381 # Skip comment lines.
382 if re.match(r"[ ]*#", source_lines[start_line_ix]):
383 continue
Michael Walsh1173a522018-05-21 17:24:51 -0500384 if re.match(pre_args_regex, source_lines[start_line_ix]):
Michael Walsh23e7f492017-01-10 11:34:47 -0600385 found = True
386 break
387 if not found:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500388 print_error("Programmer error - Could not find the source line with"
389 + " a reference to function \"" + real_called_func_name
390 + "\".\n")
Michael Walsh23e7f492017-01-10 11:34:47 -0600391 return
392
Michael Walsh82acf002017-05-04 14:33:05 -0500393 # Search forward through the source lines looking for a line whose
394 # indentation is the same or less than the start line. The end of our
395 # composite line should be the line preceding that line.
Michael Walsh1173a522018-05-21 17:24:51 -0500396 start_indent = get_line_indent(source_lines[start_line_ix])
Michael Walsh37cd29d2018-05-24 13:19:18 -0500397 end_line_ix = line_ix
Michael Walsh23e7f492017-01-10 11:34:47 -0600398 for end_line_ix in range(line_ix + 1, len(source_lines)):
399 if source_lines[end_line_ix].strip() == "":
400 continue
Michael Walsh1173a522018-05-21 17:24:51 -0500401 line_indent = get_line_indent(source_lines[end_line_ix])
Michael Walsh82acf002017-05-04 14:33:05 -0500402 if line_indent <= start_indent:
Michael Walsh23e7f492017-01-10 11:34:47 -0600403 end_line_ix -= 1
404 break
Michael Walsh1173a522018-05-21 17:24:51 -0500405 if start_line_ix != 0:
406 # Check to see whether the start line is a continuation of the prior
Michael Walsha52e9eb2018-09-10 13:56:01 -0500407 # line.
408 prior_line = source_lines[start_line_ix - 1]
409 prior_line_stripped = re.sub(r"[ ]*\\([\r\n]$)", " \\1", prior_line)
410 prior_line_indent = get_line_indent(prior_line)
411 if prior_line != prior_line_stripped and\
412 prior_line_indent < start_indent:
Michael Walsh1173a522018-05-21 17:24:51 -0500413 start_line_ix -= 1
Michael Walsha52e9eb2018-09-10 13:56:01 -0500414 # Remove the backslash (continuation char) from prior line.
415 source_lines[start_line_ix] = prior_line_stripped
Michael Walsh23e7f492017-01-10 11:34:47 -0600416
417 # Join the start line through the end line into a composite line.
418 composite_line = ''.join(map(str.strip,
Gunnar Mills096cd562018-03-26 10:19:12 -0500419 source_lines[start_line_ix:end_line_ix + 1]))
Michael Walsh1173a522018-05-21 17:24:51 -0500420 # Insert one space after first "=" if there isn't one already.
421 composite_line = re.sub("=[ ]*([^ ])", "= \\1", composite_line, 1)
Michael Walsh7423c012016-10-04 10:27:21 -0500422
Michael Walsh3f248272018-06-01 13:59:35 -0500423 lvalue_regex = "[ ]*=[ ]+" + func_name_regex + ".*"
Michael Walsh1173a522018-05-21 17:24:51 -0500424 lvalue_string = re.sub(lvalue_regex, "", composite_line)
Michael Walsh3f248272018-06-01 13:59:35 -0500425 if lvalue_string == composite_line:
426 # i.e. the regex did not match so there are no lvalues.
427 lvalue_string = ""
Michael Walsh37762f92018-08-07 14:59:18 -0500428 lvalues_list = list(filter(None, map(str.strip, lvalue_string.split(","))))
Michael Walsh3f248272018-06-01 13:59:35 -0500429 try:
430 lvalues = collections.OrderedDict()
431 except AttributeError:
432 # A non-ordered dict doesn't look as nice when printed but it will do.
433 lvalues = {}
Michael Walsh1173a522018-05-21 17:24:51 -0500434 ix = len(lvalues_list) * -1
435 for lvalue in lvalues_list:
436 lvalues[ix] = lvalue
437 ix += 1
Michael Walsh3f248272018-06-01 13:59:35 -0500438 lvalue_prefix_regex = "(.*=[ ]+)?"
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500439 called_func_name_regex = lvalue_prefix_regex + func_name_regex +\
440 "[ ]*\\(.*"
Michael Walsh3f248272018-06-01 13:59:35 -0500441 called_func_name = re.sub(called_func_name_regex, "\\4", composite_line)
Michael Walsh1173a522018-05-21 17:24:51 -0500442 arg_list_etc = "(" + re.sub(pre_args_regex, "", composite_line)
Michael Walshde791732016-09-06 14:25:24 -0500443 if local_debug:
Michael Walsh4dbb6002019-05-17 15:51:15 -0500444 print_varx("aliases", aliases, indent=debug_indent)
445 print_varx("import_name_regex", import_name_regex, indent=debug_indent)
446 print_varx("func_name_regex", func_name_regex, indent=debug_indent)
447 print_varx("pre_args_regex", pre_args_regex, indent=debug_indent)
448 print_varx("start_line_ix", start_line_ix, indent=debug_indent)
449 print_varx("end_line_ix", end_line_ix, indent=debug_indent)
450 print_varx("composite_line", composite_line, indent=debug_indent)
451 print_varx("lvalue_regex", lvalue_regex, indent=debug_indent)
452 print_varx("lvalue_string", lvalue_string, indent=debug_indent)
453 print_varx("lvalues", lvalues, indent=debug_indent)
454 print_varx("called_func_name_regex", called_func_name_regex,
455 indent=debug_indent)
456 print_varx("called_func_name", called_func_name, indent=debug_indent)
457 print_varx("arg_list_etc", arg_list_etc, indent=debug_indent)
Michael Walshde791732016-09-06 14:25:24 -0500458
459 # Parse arg list...
460 # Initialize...
461 nest_level = -1
462 arg_ix = 0
Michael Walsh7423c012016-10-04 10:27:21 -0500463 args_list = [""]
Michael Walshde791732016-09-06 14:25:24 -0500464 for ix in range(0, len(arg_list_etc)):
465 char = arg_list_etc[ix]
466 # Set the nest_level based on whether we've encounted a parenthesis.
467 if char == "(":
468 nest_level += 1
469 if nest_level == 0:
470 continue
471 elif char == ")":
472 nest_level -= 1
473 if nest_level < 0:
474 break
475
476 # If we reach a comma at base nest level, we are done processing an
Michael Walsh7423c012016-10-04 10:27:21 -0500477 # argument so we increment arg_ix and initialize a new args_list entry.
Michael Walshde791732016-09-06 14:25:24 -0500478 if char == "," and nest_level == 0:
479 arg_ix += 1
Michael Walsh7423c012016-10-04 10:27:21 -0500480 args_list.append("")
Michael Walshde791732016-09-06 14:25:24 -0500481 continue
482
Michael Walsh7423c012016-10-04 10:27:21 -0500483 # For any other character, we append it it to the current arg list
Michael Walshde791732016-09-06 14:25:24 -0500484 # entry.
Michael Walsh7423c012016-10-04 10:27:21 -0500485 args_list[arg_ix] += char
Michael Walshde791732016-09-06 14:25:24 -0500486
487 # Trim whitespace from each list entry.
Michael Walsh7423c012016-10-04 10:27:21 -0500488 args_list = [arg.strip() for arg in args_list]
Michael Walshde791732016-09-06 14:25:24 -0500489
Michael Walsh1173a522018-05-21 17:24:51 -0500490 if arg_num < 0:
491 if abs(arg_num) > len(lvalues):
492 argument = lvalues
493 else:
494 argument = lvalues[arg_num]
495 elif arg_num == 0:
496 argument = called_func_name
Michael Walsh2750b442018-05-18 14:49:11 -0500497 else:
Michael Walsh1173a522018-05-21 17:24:51 -0500498 if arg_num > len(args_list):
499 argument = args_list
500 else:
501 argument = args_list[arg_num - 1]
Michael Walshde791732016-09-06 14:25:24 -0500502
503 if local_debug:
Michael Walsh4dbb6002019-05-17 15:51:15 -0500504 print_varx("args_list", args_list, indent=debug_indent)
505 print_varx("argument", argument, indent=debug_indent)
Michael Walsh23e7f492017-01-10 11:34:47 -0600506 print_dashes(0, 120)
Michael Walshde791732016-09-06 14:25:24 -0500507
508 return argument
509
Michael Walshde791732016-09-06 14:25:24 -0500510
Michael Walshde791732016-09-06 14:25:24 -0500511def sprint_time(buffer=""):
Michael Walshde791732016-09-06 14:25:24 -0500512 r"""
513 Return the time in the following format.
514
515 Example:
516
517 The following python code...
518
519 sys.stdout.write(sprint_time())
520 sys.stdout.write("Hi.\n")
521
522 Will result in the following type of output:
523
524 #(CDT) 2016/07/08 15:25:35 - Hi.
525
526 Example:
527
528 The following python code...
529
530 sys.stdout.write(sprint_time("Hi.\n"))
531
532 Will result in the following type of output:
533
534 #(CDT) 2016/08/03 17:12:05 - Hi.
535
536 The following environment variables will affect the formatting as
537 described:
538 NANOSECONDS This will cause the time stamps to be
539 precise to the microsecond (Yes, it
540 probably should have been named
541 MICROSECONDS but the convention was set
542 long ago so we're sticking with it).
543 Example of the output when environment
544 variable NANOSECONDS=1.
545
546 #(CDT) 2016/08/03 17:16:25.510469 - Hi.
547
548 SHOW_ELAPSED_TIME This will cause the elapsed time to be
549 included in the output. This is the
550 amount of time that has elapsed since the
551 last time this function was called. The
552 precision of the elapsed time field is
553 also affected by the value of the
554 NANOSECONDS environment variable. Example
555 of the output when environment variable
556 NANOSECONDS=0 and SHOW_ELAPSED_TIME=1.
557
558 #(CDT) 2016/08/03 17:17:40 - 0 - Hi.
559
560 Example of the output when environment variable NANOSECONDS=1 and
561 SHOW_ELAPSED_TIME=1.
562
563 #(CDT) 2016/08/03 17:18:47.317339 - 0.000046 - Hi.
564
Michael Walsh4dbb6002019-05-17 15:51:15 -0500565 Description of argument(s).
Michael Walshde791732016-09-06 14:25:24 -0500566 buffer This will be appended to the formatted
567 time string.
568 """
569
570 global NANOSECONDS
571 global SHOW_ELAPSED_TIME
572 global sprint_time_last_seconds
Michael Walsh4fea2cf2018-08-22 17:48:18 -0500573 global last_seconds_ix
Michael Walshde791732016-09-06 14:25:24 -0500574
575 seconds = time.time()
576 loc_time = time.localtime(seconds)
577 nanoseconds = "%0.6f" % seconds
578 pos = nanoseconds.find(".")
579 nanoseconds = nanoseconds[pos:]
580
581 time_string = time.strftime("#(%Z) %Y/%m/%d %H:%M:%S", loc_time)
582 if NANOSECONDS == "1":
583 time_string = time_string + nanoseconds
584
585 if SHOW_ELAPSED_TIME == "1":
586 cur_time_seconds = seconds
587 math_string = "%9.9f" % cur_time_seconds + " - " + "%9.9f" % \
Michael Walsh4fea2cf2018-08-22 17:48:18 -0500588 sprint_time_last_seconds[last_seconds_ix]
Michael Walshde791732016-09-06 14:25:24 -0500589 elapsed_seconds = eval(math_string)
590 if NANOSECONDS == "1":
591 elapsed_seconds = "%11.6f" % elapsed_seconds
592 else:
593 elapsed_seconds = "%4i" % elapsed_seconds
Michael Walsh4fea2cf2018-08-22 17:48:18 -0500594 sprint_time_last_seconds[last_seconds_ix] = cur_time_seconds
Michael Walshde791732016-09-06 14:25:24 -0500595 time_string = time_string + " - " + elapsed_seconds
596
597 return time_string + " - " + buffer
598
Michael Walshde791732016-09-06 14:25:24 -0500599
Michael Walshde791732016-09-06 14:25:24 -0500600def sprint_timen(buffer=""):
Michael Walshde791732016-09-06 14:25:24 -0500601 r"""
602 Append a line feed to the buffer, pass it to sprint_time and return the
603 result.
604 """
605
606 return sprint_time(buffer + "\n")
607
Michael Walshde791732016-09-06 14:25:24 -0500608
Michael Walshde791732016-09-06 14:25:24 -0500609def sprint_error(buffer=""):
Michael Walshde791732016-09-06 14:25:24 -0500610 r"""
611 Return a standardized error string. This includes:
612 - A time stamp
613 - The "**ERROR**" string
614 - The caller's buffer string.
615
616 Example:
617
618 The following python code...
619
620 print(sprint_error("Oops.\n"))
621
622 Will result in the following type of output:
623
624 #(CDT) 2016/08/03 17:12:05 - **ERROR** Oops.
625
Michael Walsh4dbb6002019-05-17 15:51:15 -0500626 Description of argument(s).
Michael Walshde791732016-09-06 14:25:24 -0500627 buffer This will be appended to the formatted
628 error string.
629 """
630
631 return sprint_time() + "**ERROR** " + buffer
632
Michael Walshde791732016-09-06 14:25:24 -0500633
Michael Walsh3f248272018-06-01 13:59:35 -0500634# Implement "constants" with functions.
635def digit_length_in_bits():
636 r"""
637 Return the digit length in bits.
638 """
639
640 return 4
641
642
643def word_length_in_digits():
644 r"""
645 Return the word length in digits.
646 """
647
648 return 8
649
650
651def bit_length(number):
652 r"""
653 Return the bit length of the number.
654
655 Description of argument(s):
656 number The number to be analyzed.
657 """
658
659 if number < 0:
660 # Convert negative numbers to positive and subtract one. The
661 # following example illustrates the reason for this:
662 # Consider a single nibble whose signed values can range from -8 to 7
663 # (0x8 to 0x7). A value of 0x7 equals 0b0111. Therefore, its length
664 # in bits is 3. Since the negative bit (i.e. 0b1000) is not set, the
Michael Walsh4dbb6002019-05-17 15:51:15 -0500665 # value 7 clearly will fit in one nibble. With -8 = 0x8 = 0b1000, one
666 # has the smallest negative value that will fit. Note that it
Michael Walsh3f248272018-06-01 13:59:35 -0500667 # requires 3 bits of 0. So by converting a number value of -8 to a
668 # working_number of 7, this function can accurately calculate the
669 # number of bits and therefore nibbles required to represent the
670 # number in print.
671 working_number = abs(number) - 1
672 else:
673 working_number = number
674
675 # Handle the special case of the number 0.
676 if working_number == 0:
677 return 0
678
679 return len(bin(working_number)) - 2
680
681
682def get_req_num_hex_digits(number):
683 r"""
684 Return the required number of hex digits required to display the given
685 number.
686
687 The returned value will always be rounded up to the nearest multiple of 8.
688
689 Description of argument(s):
690 number The number to be analyzed.
691 """
692
693 if number < 0:
694 # Convert negative numbers to positive and subtract one. The
695 # following example illustrates the reason for this:
696 # Consider a single nibble whose signed values can range from -8 to 7
697 # (0x8 to 0x7). A value of 0x7 equals 0b0111. Therefore, its length
698 # in bits is 3. Since the negative bit (i.e. 0b1000) is not set, the
Michael Walsh4dbb6002019-05-17 15:51:15 -0500699 # value 7 clearly will fit in one nibble. With -8 = 0x8 = 0b1000, one
700 # has the smallest negative value that will fit. Note that it
Michael Walsh3f248272018-06-01 13:59:35 -0500701 # requires 3 bits of 0. So by converting a number value of -8 to a
702 # working_number of 7, this function can accurately calculate the
703 # number of bits and therefore nibbles required to represent the
704 # number in print.
705 working_number = abs(number) - 1
706 else:
707 working_number = number
708
709 # Handle the special case of the number 0.
710 if working_number == 0:
711 return word_length_in_digits()
712
713 num_length_in_bits = bit_length(working_number)
714 num_hex_digits, remainder = divmod(num_length_in_bits,
715 digit_length_in_bits())
716 if remainder > 0:
717 # Example: the number 7 requires 3 bits. The divmod above produces,
718 # 0 with remainder of 3. So because we have a remainder, we increment
719 # num_hex_digits from 0 to 1.
720 num_hex_digits += 1
721
722 # Check to see whether the negative bit is set. This is the left-most
723 # bit in the highest order digit.
724 negative_mask = 2 ** (num_hex_digits * 4 - 1)
725 if working_number & negative_mask:
726 # If a number that is intended to be positive has its negative bit
727 # on, an additional digit will be required to represent it correctly
728 # in print.
729 num_hex_digits += 1
730
731 num_words, remainder = divmod(num_hex_digits, word_length_in_digits())
732 if remainder > 0 or num_words == 0:
733 num_words += 1
734
735 # Round up to the next word length in digits.
736 return num_words * word_length_in_digits()
737
738
739def dft_num_hex_digits():
740 r"""
741 Return the default number of hex digits to be used to represent a hex
742 number in print.
743
744 The value returned is a function of sys.maxsize.
745 """
746
747 global _gen_print_dft_num_hex_digits_
748 try:
749 return _gen_print_dft_num_hex_digits_
750 except NameError:
751 _gen_print_dft_num_hex_digits_ = get_req_num_hex_digits(sys.maxsize)
752 return _gen_print_dft_num_hex_digits_
753
754
Michael Walsh4dbb6002019-05-17 15:51:15 -0500755# Create constant functions to describe various types of dictionaries.
756def dict_type():
757 return 1
758
759
760def ordered_dict_type():
761 return 2
762
763
764def dot_dict_type():
765 return 3
766
767
768def normalized_dict_type():
769 return 4
770
771
Michael Walsh91fc8822019-05-29 17:34:17 -0500772def proxy_dict_type():
773 return 5
774
775
Michael Walsh8646d962019-01-21 14:36:13 -0600776def is_dict(var_value):
777 r"""
Michael Walsh4dbb6002019-05-17 15:51:15 -0500778 Return non-zero if var_value is a type of dictionary and 0 if it is not.
779
780 The specific non-zero value returned will indicate what type of dictionary
781 var_value is (see constant functions above).
782
783 Description of argument(s):
784 var_value The object to be analyzed to determine
785 whether it is a dictionary and if so, what
786 type of dictionary.
Michael Walsh8646d962019-01-21 14:36:13 -0600787 """
788
Michael Walsh8646d962019-01-21 14:36:13 -0600789 if isinstance(var_value, dict):
Michael Walsh91fc8822019-05-29 17:34:17 -0500790 return dict_type()
Michael Walsh8646d962019-01-21 14:36:13 -0600791 try:
792 if isinstance(var_value, collections.OrderedDict):
Michael Walsh91fc8822019-05-29 17:34:17 -0500793 return ordered_dict_type()
Michael Walsh8646d962019-01-21 14:36:13 -0600794 except AttributeError:
795 pass
796 try:
797 if isinstance(var_value, DotDict):
Michael Walsh91fc8822019-05-29 17:34:17 -0500798 return dot_dict_type()
Michael Walsh8646d962019-01-21 14:36:13 -0600799 except NameError:
800 pass
801 try:
802 if isinstance(var_value, NormalizedDict):
Michael Walsh91fc8822019-05-29 17:34:17 -0500803 return normalized_dict_type()
Michael Walsh8646d962019-01-21 14:36:13 -0600804 except NameError:
805 pass
Michael Walsh91fc8822019-05-29 17:34:17 -0500806 try:
807 if str(type(var_value)).split("'")[1] == "dictproxy":
808 return proxy_dict_type()
809 except NameError:
810 pass
811 return 0
Michael Walsh8646d962019-01-21 14:36:13 -0600812
813
Michael Walsh4dbb6002019-05-17 15:51:15 -0500814def get_int_types():
815 r"""
816 Return a tuple consisting of the valid integer data types for the system
817 and version of python being run.
818
819 Example:
820 (int, long)
821 """
822
823 try:
824 int_types = (int, long)
825 except NameError:
826 int_types = (int,)
827 return int_types
828
829
830def get_string_types():
831 r"""
832 Return a tuple consisting of the valid string data types for the system
833 and version of python being run.
834
835 Example:
836 (str, unicode)
837 """
838
839 try:
840 string_types = (str, unicode)
841 except NameError:
842 string_types = (bytes, str)
843 return string_types
844
845
846def valid_fmts():
847 r"""
848 Return a list of the valid formats that can be specified for the fmt
849 argument of the sprint_varx function (defined below).
850 """
851
852 return [
853 'hexa',
854 'octal',
855 'binary',
856 'blank',
Michael Walsh6bed4d32019-07-10 14:11:30 -0500857 'verbose',
Michael Walsh4dbb6002019-05-17 15:51:15 -0500858 'quote_keys',
Michael Walshc6acf742019-08-06 11:48:51 -0500859 'show_type',
860 'strip_brackets',
861 'no_header']
Michael Walsh4dbb6002019-05-17 15:51:15 -0500862
863
864def create_fmt_definition():
865 r"""
866 Create a string consisting of function-definition code that can be
867 executed to create constant fmt definition functions.
868
869 These functions can be used by callers of sprint_var/sprint_varx to set
870 the fmt argument correctly.
871
872 Likewise, the sprint_varx function will use these generated functions to
873 correctly interpret the fmt argument.
874
875 Example output from this function:
876
877 def hexa():
878 return 0x00000001
879 def octal_fmt():
880 return 0x00000002
881 etc.
882 """
883
884 buffer = ""
885 bits = 0x00000001
886 for fmt_name in valid_fmts():
887 buffer += "def " + fmt_name + "():\n"
888 buffer += " return " + "0x%08x" % bits + "\n"
889 bits = bits << 1
890 return buffer
891
892
893# Dynamically create fmt definitions (for use with the fmt argument of
894# sprint_varx function):
895exec(create_fmt_definition())
896
897
Michael Walsh6bed4d32019-07-10 14:11:30 -0500898def terse():
899 r"""
900 Constant function to return fmt value of 0.
901
902 Now that sprint_varx defaults to printing in terse format, the terse
903 option is deprecated. This function is here for backward compatibility.
904
905 Once the repo has been purged of the use of terse, this function can be
906 removed.
907 """
908
909 return 0
910
911
Michael Walsh4dbb6002019-05-17 15:51:15 -0500912def list_pop(a_list, index=0, default=None):
913 r"""
914 Pop the list entry indicated by the index and return the entry. If no
915 such entry exists, return default.
916
917 Note that the list passed to this function will be modified.
918
919 Description of argument(s):
920 a_list The list from which an entry is to be
921 popped.
922 index The index indicating which entry is to be
923 popped.
924 default The value to be returned if there is no
925 entry at the given index location.
926 """
927 try:
928 return a_list.pop(index)
929 except IndexError:
930 return default
931
932
933def parse_fmt(fmt):
934 r"""
935 Parse the fmt argument and return a tuple consisting of a format and a
936 child format.
937
938 This function was written for use by the sprint_varx function defined in
939 this module.
940
941 When sprint_varx is processing a multi-level object such as a list or
942 dictionary (which in turn may contain other lists or dictionaries), it
943 will use the fmt value to dictate the print formatting of the current
944 level and the child_fmt value to dictate the print formatting of
945 subordinate levels. Consider the following example:
946
947 python code example:
948
949 ord_dict = \
950 collections.OrderedDict([
951 ('one', 1),
952 ('two', 2),
953 ('sub',
954 collections.OrderedDict([
955 ('three', 3), ('four', 4)]))])
956
957 print_var(ord_dict)
958
959 This would generate the following output:
960
961 ord_dict:
Michael Walsh6bed4d32019-07-10 14:11:30 -0500962 [one]: 1
963 [two]: 2
964 [sub]:
965 [three]: 3
966 [four]: 4
Michael Walsh4dbb6002019-05-17 15:51:15 -0500967
968 The first level in this example is the line that simply says "ord_dict".
969 The second level is comprised of the dictionary entries with the keys
970 'one', 'two' and 'sub'. The third level is comprised of the last 2 lines
971 (i.e. printed values 3 and 4).
972
973 Given the data structure shown above, the programmer could code the
Michael Walsh6bed4d32019-07-10 14:11:30 -0500974 following where fmt is a simple integer value set by calling the verbose()
Michael Walsh4dbb6002019-05-17 15:51:15 -0500975 function.
976
Michael Walsh6bed4d32019-07-10 14:11:30 -0500977 print_var(ord_dict, fmt=verbose())
Michael Walsh4dbb6002019-05-17 15:51:15 -0500978
979 The output would look like this:
980
981 ord_dict:
Michael Walsh6bed4d32019-07-10 14:11:30 -0500982 ord_dict[one]: 1
983 ord_dict[two]: 2
984 ord_dict[sub]:
985 ord_dict[sub][three]: 3
986 ord_dict[sub][four]: 4
Michael Walsh4dbb6002019-05-17 15:51:15 -0500987
Michael Walsh6bed4d32019-07-10 14:11:30 -0500988 Note the verbose format where the name of the object ("ord_dict") is
989 repeated on every line.
Michael Walsh4dbb6002019-05-17 15:51:15 -0500990
991 If the programmer wishes to get more granular with the fmt argument,
992 he/she can specify it as a list where each entry corresponds to a level of
993 the object being printed. The last such list entry governs the print
994 formatting of all subordinate parts of the given object.
995
996 Look at each of the following code examples and their corresponding
997 output. See how the show_type() formatting affects the printing depending
998 on which position it occupies in the fmt list argument:
999
1000 print_var(ord_dict, fmt=[show_type()])
1001
1002 ord_dict: <collections.OrderedDict>
1003 ord_dict[one]: 1 <int>
1004 ord_dict[two]: 2 <int>
1005 ord_dict[sub]: <collections.OrderedDict>
1006 ord_dict[sub][three]: 3 <int>
1007 ord_dict[sub][four]: 4 <int>
1008
1009 print_var(ord_dict, fmt=[0, show_type()])
1010
1011 ord_dict:
1012 ord_dict[one]: 1 <int>
1013 ord_dict[two]: 2 <int>
1014 ord_dict[sub]: <collections.OrderedDict>
1015 ord_dict[sub][three]: 3 <int>
1016 ord_dict[sub][four]: 4 <int>
1017
1018 print_var(ord_dict, fmt=[0, 0, show_type()])
1019
1020 ord_dict:
1021 ord_dict[one]: 1
1022 ord_dict[two]: 2
1023 ord_dict[sub]:
1024 ord_dict[sub][three]: 3 <int>
1025 ord_dict[sub][four]: 4 <int>
1026
1027 Description of argument(s):
1028 fmt The format argument such as is passed to
1029 sprint_varx. This argument may be an
1030 integer or a list of integers. See the
1031 prolog of sprint_varx for more details.
1032 """
1033
1034 # Make a deep copy of the fmt argument in order to avoid modifying the
1035 # caller's fmt value when it is a list.
1036 fmt = copy.deepcopy(fmt)
1037 try:
1038 # Assume fmt is a list. Pop the first element from the list.
1039 first_element = list_pop(fmt, index=0, default=0)
1040 # Return the first list element along with either 1) the remainder of
1041 # the fmt list if not null or 2) another copy of the first element.
1042 return first_element, fmt if len(fmt) else first_element
1043 except AttributeError:
1044 # fmt is not a list so treat it as a simple integer value.
1045 return fmt, fmt
1046
1047
Michael Walshde791732016-09-06 14:25:24 -05001048def sprint_varx(var_name,
1049 var_value,
Michael Walsh4dbb6002019-05-17 15:51:15 -05001050 fmt=0,
1051 indent=dft_indent,
1052 col1_width=dft_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -05001053 trailing_char="\n",
Michael Walsh4dbb6002019-05-17 15:51:15 -05001054 key_list=None,
1055 delim=":"):
Michael Walshde791732016-09-06 14:25:24 -05001056 r"""
Michael Walsh4dbb6002019-05-17 15:51:15 -05001057 Print the var name/value passed to it. If the caller lets col1_width
Michael Walshde791732016-09-06 14:25:24 -05001058 default, the printing lines up nicely with output generated by the
1059 print_time functions.
1060
1061 Note that the sprint_var function (defined below) can be used to call this
1062 function so that the programmer does not need to pass the var_name.
1063 sprint_var will figure out the var_name. The sprint_var function is the
1064 one that would normally be used by the general user.
1065
1066 For example, the following python code:
1067
1068 first_name = "Mike"
1069 print_time("Doing this...\n")
1070 print_varx("first_name", first_name)
1071 print_time("Doing that...\n")
1072
1073 Will generate output like this:
1074
1075 #(CDT) 2016/08/10 17:34:42.847374 - 0.001285 - Doing this...
1076 first_name: Mike
1077 #(CDT) 2016/08/10 17:34:42.847510 - 0.000136 - Doing that...
1078
1079 This function recognizes several complex types of data such as dict, list
1080 or tuple.
1081
1082 For example, the following python code:
1083
1084 my_dict = dict(one=1, two=2, three=3)
1085 print_var(my_dict)
1086
1087 Will generate the following output:
1088
1089 my_dict:
1090 my_dict[three]: 3
1091 my_dict[two]: 2
1092 my_dict[one]: 1
1093
Michael Walsh4dbb6002019-05-17 15:51:15 -05001094 Description of argument(s).
Michael Walshde791732016-09-06 14:25:24 -05001095 var_name The name of the variable to be printed.
1096 var_value The value of the variable to be printed.
Michael Walsh4dbb6002019-05-17 15:51:15 -05001097 fmt A bit map to dictate the format of the
1098 output. For printing multi-level objects
1099 like lists and dictionaries, this argument
1100 may also be a list of bit maps. The first
1101 list element pertains to the highest level
1102 of output, the second element pertains to
1103 the 2nd level of output, etc. The last
1104 element in the list pertains to all
1105 subordinate levels. The bits can be set
1106 using the dynamically created functionhs
1107 above. Example: sprint_varx("var1", var1,
Michael Walsh6bed4d32019-07-10 14:11:30 -05001108 fmt=verbose()). Note that these values
1109 can be OR'ed together: print_var(var1,
1110 hexa() | verbose()). If the caller ORs
1111 mutually exclusive bits (hexa() |
1112 octal()), behavior is not guaranteed. The
1113 following features are supported:
Michael Walsh4dbb6002019-05-17 15:51:15 -05001114 hexa Print all integer values in hexadecimal
1115 format.
1116 octal Print all integer values in octal format.
1117 binary Print all integer values in binary format.
1118 blank For blank string values, print "<blank>"
1119 instead of an actual blank.
Michael Walsh6bed4d32019-07-10 14:11:30 -05001120 verbose For structured values like dictionaries,
1121 lists, etc. repeat the name of the
Michael Walsh4dbb6002019-05-17 15:51:15 -05001122 variable on each line to the right of the
1123 key or subscript value. Example: print
Michael Walsh6bed4d32019-07-10 14:11:30 -05001124 "my_dict[key1]" instead of just "[key1]".
Michael Walsh4dbb6002019-05-17 15:51:15 -05001125 quote_keys Quote dictionary keys in the output.
1126 Example: my_dict['key1'] instead of
1127 my_dict[key1].
1128 show_type Show the type of the data in angled
1129 brackets just to the right of the data.
Michael Walshc6acf742019-08-06 11:48:51 -05001130 strip_brackets Strip the brackets from the variable name
1131 portion of the output. This is applicable
1132 when printing complex objects like lists
1133 or dictionaries.
1134 no_header For complex objects like dictionaries, do
1135 not include a header line. This
1136 necessarily means that the member lines
1137 will be indented 2 characters less than
1138 they otherwise would have been.
Michael Walsh4dbb6002019-05-17 15:51:15 -05001139 indent The number of spaces to indent the output.
1140 col1_width The width of the output column containing
Michael Walshde791732016-09-06 14:25:24 -05001141 the variable name. The default value of
1142 this is adjusted so that the var_value
1143 lines up with text printed via the
1144 print_time function.
Michael Walsh7423c012016-10-04 10:27:21 -05001145 trailing_char The character to be used at the end of the
1146 returned string. The default value is a
1147 line feed.
Michael Walshd2869032018-03-22 16:12:11 -05001148 key_list A list of which dictionary keys should be
1149 printed. All others keys will be skipped.
1150 Each value in key_list will be regarded
1151 as a regular expression and it will be
1152 regarded as anchored to the beginning and
1153 ends of the dictionary key being
1154 referenced. For example if key_list is
1155 ["one", "two"], the resulting regex used
1156 will be "^one|two$", i.e. only keys "one"
1157 and "two" from the var_value dictionary
1158 will be printed. As another example, if
1159 the caller were to specify a key_list of
1160 ["one.*"], then only dictionary keys whose
1161 names begin with "one" will be printed.
1162 Note: This argument pertains only to
1163 var_values which are dictionaries.
Michael Walsh4dbb6002019-05-17 15:51:15 -05001164 delim The value to be used to delimit the
1165 variable name from the variable value in
1166 the output.
Michael Walsh7423c012016-10-04 10:27:21 -05001167 """
Michael Walshde791732016-09-06 14:25:24 -05001168
Michael Walsh4dbb6002019-05-17 15:51:15 -05001169 fmt, child_fmt = parse_fmt(fmt)
1170
1171 if fmt & show_type():
1172 type_str = "<" + str(type(var_value)).split("'")[1] + ">"
1173 # Compose object type categories.
1174 int_types = get_int_types()
1175 string_types = get_string_types()
1176 simple_types = int_types + string_types + (float, bool, type, type(None))
1177 # Determine the type.
1178 if type(var_value) in simple_types:
Michael Walshde791732016-09-06 14:25:24 -05001179 # The data type is simple in the sense that it has no subordinate
1180 # parts.
Michael Walsh4dbb6002019-05-17 15:51:15 -05001181 # Adjust col1_width.
1182 col1_width = col1_width - indent
1183 # Set default value for value_format.
1184 value_format = "%s"
1185 # Process format requests.
1186 if type(var_value) in int_types:
1187 # Process format values pertaining to int types.
1188 if fmt & hexa():
Michael Walsh3f248272018-06-01 13:59:35 -05001189 num_hex_digits = max(dft_num_hex_digits(),
1190 get_req_num_hex_digits(var_value))
1191 # Convert a negative number to its positive twos complement
1192 # for proper printing. For example, instead of printing -1 as
1193 # "0x-000000000000001" it will be printed as
1194 # "0xffffffffffffffff".
1195 var_value = var_value & (2 ** (num_hex_digits * 4) - 1)
1196 value_format = "0x%0" + str(num_hex_digits) + "x"
Michael Walsh4dbb6002019-05-17 15:51:15 -05001197 elif fmt & octal():
1198 value_format = "0o%016o"
1199 elif fmt & binary():
1200 num_digits, remainder = \
1201 divmod(max(bit_length(var_value), 1), 8)
1202 num_digits *= 8
1203 if remainder:
1204 num_digits += 8
1205 num_digits += 2
1206 value_format = '#0' + str(num_digits) + 'b'
1207 var_value = format(var_value, value_format)
1208 value_format = "%s"
1209 elif type(var_value) in string_types:
1210 # Process format values pertaining to string types.
1211 if fmt & blank() and var_value == "":
1212 value_format = "%s"
1213 var_value = "<blank>"
1214 elif type(var_value) is type:
1215 var_value = str(var_value).split("'")[1]
1216 format_string = "%" + str(indent) + "s%-" + str(col1_width) + "s" \
1217 + value_format
1218 if fmt & show_type():
1219 if var_value != "":
1220 format_string += " "
1221 format_string += type_str
1222 format_string += trailing_char
Michael Walsh6bed4d32019-07-10 14:11:30 -05001223 if not (fmt & verbose()):
Michael Walsh4dbb6002019-05-17 15:51:15 -05001224 # Strip everything leading up to the first left square brace.
1225 var_name = re.sub(r".*\[", "[", var_name)
Michael Walshc6acf742019-08-06 11:48:51 -05001226 if (fmt & strip_brackets()):
1227 var_name = re.sub(r"[\[\]]", "", var_name)
Michael Walsh3383e652017-09-01 17:10:59 -05001228 if value_format == "0x%08x":
Michael Walsh4dbb6002019-05-17 15:51:15 -05001229 return format_string % ("", str(var_name) + delim,
Michael Walsh3383e652017-09-01 17:10:59 -05001230 var_value & 0xffffffff)
1231 else:
Michael Walsh4dbb6002019-05-17 15:51:15 -05001232 return format_string % ("", str(var_name) + delim, var_value)
Michael Walshde791732016-09-06 14:25:24 -05001233 else:
1234 # The data type is complex in the sense that it has subordinate parts.
Michael Walshc6acf742019-08-06 11:48:51 -05001235 if (fmt & no_header()):
1236 buffer = ""
Michael Walsh4dbb6002019-05-17 15:51:15 -05001237 else:
Michael Walshc6acf742019-08-06 11:48:51 -05001238 # Create header line.
1239 if not (fmt & verbose()):
1240 # Strip everything leading up to the first square brace.
1241 loc_var_name = re.sub(r".*\[", "[", var_name)
1242 else:
1243 loc_var_name = var_name
Michael Walshadf4ab22019-09-06 16:23:41 -05001244 if (fmt & strip_brackets()):
1245 loc_var_name = re.sub(r"[\[\]]", "", loc_var_name)
Michael Walshc6acf742019-08-06 11:48:51 -05001246 format_string = "%" + str(indent) + "s%s\n"
1247 buffer = format_string % ("", loc_var_name + ":")
1248 if fmt & show_type():
1249 buffer = buffer.replace("\n", " " + type_str + "\n")
1250 indent += 2
Michael Walsh7423c012016-10-04 10:27:21 -05001251 try:
1252 length = len(var_value)
1253 except TypeError:
Michael Walsh23e7f492017-01-10 11:34:47 -06001254 length = 0
Michael Walsh7423c012016-10-04 10:27:21 -05001255 ix = 0
1256 loc_trailing_char = "\n"
Michael Walsh8646d962019-01-21 14:36:13 -06001257 if is_dict(var_value):
Michael Walsh4dbb6002019-05-17 15:51:15 -05001258 if type(child_fmt) is list:
1259 child_quote_keys = (child_fmt[0] & quote_keys())
1260 else:
1261 child_quote_keys = (child_fmt & quote_keys())
Michael Walsh37762f92018-08-07 14:59:18 -05001262 for key, value in var_value.items():
Michael Walshd2869032018-03-22 16:12:11 -05001263 if key_list is not None:
1264 key_list_regex = "^" + "|".join(key_list) + "$"
1265 if not re.match(key_list_regex, key):
1266 continue
Michael Walsh7423c012016-10-04 10:27:21 -05001267 ix += 1
1268 if ix == length:
1269 loc_trailing_char = trailing_char
Michael Walsh4dbb6002019-05-17 15:51:15 -05001270 if child_quote_keys:
1271 key = "'" + key + "'"
1272 key = "[" + str(key) + "]"
1273 buffer += sprint_varx(var_name + key, value, child_fmt, indent,
Michael Walshc6acf742019-08-06 11:48:51 -05001274 col1_width, loc_trailing_char, key_list,
1275 delim)
Michael Walsh7423c012016-10-04 10:27:21 -05001276 elif type(var_value) in (list, tuple, set):
Michael Walshde791732016-09-06 14:25:24 -05001277 for key, value in enumerate(var_value):
Michael Walsh7423c012016-10-04 10:27:21 -05001278 ix += 1
1279 if ix == length:
1280 loc_trailing_char = trailing_char
Michael Walsh4dbb6002019-05-17 15:51:15 -05001281 key = "[" + str(key) + "]"
1282 buffer += sprint_varx(var_name + key, value, child_fmt, indent,
Michael Walshc6acf742019-08-06 11:48:51 -05001283 col1_width, loc_trailing_char, key_list,
1284 delim)
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001285 elif isinstance(var_value, argparse.Namespace):
Michael Walshde791732016-09-06 14:25:24 -05001286 for key in var_value.__dict__:
Michael Walsh7423c012016-10-04 10:27:21 -05001287 ix += 1
1288 if ix == length:
1289 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -05001290 cmd_buf = "buffer += sprint_varx(var_name + \".\" + str(key)" \
Michael Walsh4dbb6002019-05-17 15:51:15 -05001291 + ", var_value." + key + ", child_fmt, indent," \
Michael Walshc6acf742019-08-06 11:48:51 -05001292 + " col1_width, loc_trailing_char, key_list," \
1293 + " delim)"
Michael Walshde791732016-09-06 14:25:24 -05001294 exec(cmd_buf)
1295 else:
1296 var_type = type(var_value).__name__
1297 func_name = sys._getframe().f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -05001298 var_value = "<" + var_type + " type not supported by " + \
1299 func_name + "()>"
Michael Walshde791732016-09-06 14:25:24 -05001300 value_format = "%s"
Michael Walsh4dbb6002019-05-17 15:51:15 -05001301 indent -= 2
1302 # Adjust col1_width.
1303 col1_width = col1_width - indent
1304 format_string = "%" + str(indent) + "s%-" \
1305 + str(col1_width) + "s" + value_format + trailing_char
Michael Walsh0f2ea5f2017-02-20 15:55:00 -06001306 return format_string % ("", str(var_name) + ":", var_value)
Michael Walsh23e7f492017-01-10 11:34:47 -06001307
Michael Walshde791732016-09-06 14:25:24 -05001308 return buffer
1309
1310 return ""
1311
Michael Walshde791732016-09-06 14:25:24 -05001312
Michael Walsh4dbb6002019-05-17 15:51:15 -05001313def sprint_var(*args, **kwargs):
Michael Walshde791732016-09-06 14:25:24 -05001314 r"""
Michael Walsh4dbb6002019-05-17 15:51:15 -05001315 Figure out the name of the first argument for the caller and then call
Michael Walshde791732016-09-06 14:25:24 -05001316 sprint_varx with it. Therefore, the following 2 calls are equivalent:
1317 sprint_varx("var1", var1)
1318 sprint_var(var1)
Michael Walsh4dbb6002019-05-17 15:51:15 -05001319
1320 See sprint_varx for description of arguments.
Michael Walshde791732016-09-06 14:25:24 -05001321 """
1322
Michael Walshde791732016-09-06 14:25:24 -05001323 stack_frame = 2
Michael Walsh7423c012016-10-04 10:27:21 -05001324 caller_func_name = sprint_func_name(2)
1325 if caller_func_name.endswith("print_var"):
Michael Walshde791732016-09-06 14:25:24 -05001326 stack_frame += 1
Michael Walsh4dbb6002019-05-17 15:51:15 -05001327 # Get the name of the first variable passed to this function.
Michael Walshde791732016-09-06 14:25:24 -05001328 var_name = get_arg_name(None, 1, stack_frame)
Michael Walsh4dbb6002019-05-17 15:51:15 -05001329 return sprint_varx(var_name, *args, **kwargs)
Michael Walshde791732016-09-06 14:25:24 -05001330
1331
Michael Walsh4dbb6002019-05-17 15:51:15 -05001332def sprint_vars(*args, **kwargs):
Michael Walsh18176322016-11-15 15:11:21 -06001333 r"""
1334 Sprint the values of one or more variables.
1335
Michael Walsh4dbb6002019-05-17 15:51:15 -05001336 Description of argument(s):
1337 args The variable values which are to be
1338 printed.
1339 kwargs See sprint_varx (above) for description of
1340 additional arguments.
Michael Walsh18176322016-11-15 15:11:21 -06001341 """
1342
Michael Walsh18176322016-11-15 15:11:21 -06001343 stack_frame = 2
1344 caller_func_name = sprint_func_name(2)
1345 if caller_func_name.endswith("print_vars"):
1346 stack_frame += 1
1347
Michael Walsh18176322016-11-15 15:11:21 -06001348 buffer = ""
Michael Walsh4dbb6002019-05-17 15:51:15 -05001349 arg_num = 1
1350 for var_value in args:
1351 var_name = get_arg_name(None, arg_num, stack_frame)
1352 buffer += sprint_varx(var_name, var_value, **kwargs)
1353 arg_num += 1
Michael Walsh18176322016-11-15 15:11:21 -06001354
1355 return buffer
1356
Michael Walsh18176322016-11-15 15:11:21 -06001357
Michael Walsh4dbb6002019-05-17 15:51:15 -05001358def sprint_dashes(indent=dft_indent,
Michael Walsh7423c012016-10-04 10:27:21 -05001359 width=80,
1360 line_feed=1,
1361 char="-"):
Michael Walshde791732016-09-06 14:25:24 -05001362 r"""
1363 Return a string of dashes to the caller.
1364
Michael Walsh4dbb6002019-05-17 15:51:15 -05001365 Description of argument(s):
Michael Walshde791732016-09-06 14:25:24 -05001366 indent The number of characters to indent the
1367 output.
1368 width The width of the string of dashes.
1369 line_feed Indicates whether the output should end
1370 with a line feed.
Michael Walsh7423c012016-10-04 10:27:21 -05001371 char The character to be repeated in the output
1372 string.
Michael Walshde791732016-09-06 14:25:24 -05001373 """
1374
Michael Walsh7423c012016-10-04 10:27:21 -05001375 width = int(width)
Michael Walsh23e7f492017-01-10 11:34:47 -06001376 buffer = " " * int(indent) + char * width
Michael Walshde791732016-09-06 14:25:24 -05001377 if line_feed:
1378 buffer += "\n"
1379
1380 return buffer
1381
Michael Walshde791732016-09-06 14:25:24 -05001382
Michael Walsh7423c012016-10-04 10:27:21 -05001383def sindent(text="",
1384 indent=0):
Michael Walsh7423c012016-10-04 10:27:21 -05001385 r"""
1386 Pre-pend the specified number of characters to the text string (i.e.
1387 indent it) and return it.
1388
Michael Walsh4dbb6002019-05-17 15:51:15 -05001389 Description of argument(s):
Michael Walsh7423c012016-10-04 10:27:21 -05001390 text The string to be indented.
1391 indent The number of characters to indent the
1392 string.
1393 """
1394
1395 format_string = "%" + str(indent) + "s%s"
1396 buffer = format_string % ("", text)
1397
1398 return buffer
1399
Michael Walsh7423c012016-10-04 10:27:21 -05001400
Michael Walsh662e13b2019-03-01 15:54:08 -06001401func_line_style_std = None
1402func_line_style_short = 1
1403
1404
1405def sprint_func_line(stack_frame, style=None):
Michael Walsh47aa2a42018-12-10 15:06:02 -06001406 r"""
1407 For the given stack_frame, return a formatted string containing the
1408 function name and all its arguments.
1409
1410 Example:
1411
1412 func1(last_name = 'walsh', first_name = 'mikey')
1413
1414 Description of argument(s):
1415 stack_frame A stack frame (such as is returned by
1416 inspect.stack()).
Michael Walsh662e13b2019-03-01 15:54:08 -06001417 style Indicates the style or formatting of the
1418 result string. Acceptable values are
1419 shown above.
1420
1421 Description of styles:
1422 func_line_style_std The standard formatting.
1423 func_line_style_short 1) The self parm (associated with methods)
1424 will be dropped. 2) The args and kwargs
1425 values will be treated as special. In
1426 both cases the arg name ('args' or
1427 'kwargs') will be dropped and only the
1428 values will be shown.
Michael Walsh47aa2a42018-12-10 15:06:02 -06001429 """
1430
1431 func_name = str(stack_frame[3])
1432 if func_name == "?":
1433 # "?" is the name used when code is not in a function.
1434 func_name = "(none)"
1435
1436 if func_name == "<module>":
1437 # If the func_name is the "main" program, we simply get the command
1438 # line call string.
1439 func_and_args = ' '.join(sys.argv)
1440 else:
1441 # Get the program arguments.
1442 (args, varargs, keywords, locals) =\
1443 inspect.getargvalues(stack_frame[0])
1444
1445 args_list = []
1446 for arg_name in filter(None, args + [varargs, keywords]):
1447 # Get the arg value from frame locals.
1448 arg_value = locals[arg_name]
1449 if arg_name == 'self':
Michael Walsh662e13b2019-03-01 15:54:08 -06001450 if style == func_line_style_short:
1451 continue
Michael Walsh47aa2a42018-12-10 15:06:02 -06001452 # Manipulations to improve output for class methods.
1453 func_name = arg_value.__class__.__name__ + "." + func_name
1454 args_list.append(arg_name + " = <self>")
Michael Walsh662e13b2019-03-01 15:54:08 -06001455 elif (style == func_line_style_short
1456 and arg_name == 'args'
1457 and type(arg_value) in (list, tuple)):
1458 if len(arg_value) == 0:
1459 continue
1460 args_list.append(repr(', '.join(arg_value)))
1461 elif (style == func_line_style_short
1462 and arg_name == 'kwargs'
1463 and type(arg_value) is dict):
1464 for key, value in arg_value.items():
1465 args_list.append(key + "=" + repr(value))
Michael Walsh47aa2a42018-12-10 15:06:02 -06001466 else:
1467 args_list.append(arg_name + " = " + repr(arg_value))
1468 args_str = "(" + ', '.join(map(str, args_list)) + ")"
1469
1470 # Now we need to print this in a nicely-wrapped way.
1471 func_and_args = func_name + args_str
1472
1473 return func_and_args
1474
1475
Michael Walsh7423c012016-10-04 10:27:21 -05001476def sprint_call_stack(indent=0,
Michael Walsh662e13b2019-03-01 15:54:08 -06001477 stack_frame_ix=0,
1478 style=None):
Michael Walshde791732016-09-06 14:25:24 -05001479 r"""
1480 Return a call stack report for the given point in the program with line
1481 numbers, function names and function parameters and arguments.
1482
1483 Sample output:
1484
1485 -------------------------------------------------------------------------
1486 Python function call stack
1487
1488 Line # Function name and arguments
1489 ------ ------------------------------------------------------------------
Michael Walsh47aa2a42018-12-10 15:06:02 -06001490 424 sprint_call_stack()
1491 4 print_call_stack()
1492 31 func1(last_name = 'walsh', first_name = 'mikey')
Michael Walshde791732016-09-06 14:25:24 -05001493 59 /tmp/scr5.py
1494 -------------------------------------------------------------------------
1495
Michael Walsh4dbb6002019-05-17 15:51:15 -05001496 Description of argument(s):
Michael Walshde791732016-09-06 14:25:24 -05001497 indent The number of characters to indent each
1498 line of output.
1499 stack_frame_ix The index of the first stack frame which
1500 is to be returned.
Michael Walsh662e13b2019-03-01 15:54:08 -06001501 style See the sprint_line_func prolog above for
1502 details.
Michael Walshde791732016-09-06 14:25:24 -05001503 """
1504
1505 buffer = ""
Michael Walsh7423c012016-10-04 10:27:21 -05001506 buffer += sprint_dashes(indent)
1507 buffer += sindent("Python function call stack\n\n", indent)
1508 buffer += sindent("Line # Function name and arguments\n", indent)
1509 buffer += sprint_dashes(indent, 6, 0) + " " + sprint_dashes(0, 73)
Michael Walshde791732016-09-06 14:25:24 -05001510
1511 # Grab the current program stack.
Michael Walsh6f0362c2019-03-25 14:05:14 -05001512 work_around_inspect_stack_cwd_failure()
Michael Walshde791732016-09-06 14:25:24 -05001513 current_stack = inspect.stack()
1514
1515 # Process each frame in turn.
1516 format_string = "%6s %s\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001517 ix = 0
Michael Walshde791732016-09-06 14:25:24 -05001518 for stack_frame in current_stack:
Michael Walsh7423c012016-10-04 10:27:21 -05001519 if ix < stack_frame_ix:
1520 ix += 1
1521 continue
Michael Walsh4dbb6002019-05-17 15:51:15 -05001522 # Make the line number shown to be the line where one finds the line
Michael Walsh23e7f492017-01-10 11:34:47 -06001523 # shown.
1524 try:
1525 line_num = str(current_stack[ix + 1][2])
1526 except IndexError:
1527 line_num = ""
Michael Walsh662e13b2019-03-01 15:54:08 -06001528 func_and_args = sprint_func_line(stack_frame, style=style)
Michael Walshde791732016-09-06 14:25:24 -05001529
Michael Walsh23e7f492017-01-10 11:34:47 -06001530 buffer += sindent(format_string % (line_num, func_and_args), indent)
Michael Walsh7423c012016-10-04 10:27:21 -05001531 ix += 1
Michael Walshde791732016-09-06 14:25:24 -05001532
Michael Walsh7423c012016-10-04 10:27:21 -05001533 buffer += sprint_dashes(indent)
Michael Walshde791732016-09-06 14:25:24 -05001534
1535 return buffer
1536
Michael Walshde791732016-09-06 14:25:24 -05001537
Michael Walsh662e13b2019-03-01 15:54:08 -06001538def sprint_executing(stack_frame_ix=None, style=None):
Michael Walshde791732016-09-06 14:25:24 -05001539 r"""
1540 Print a line indicating what function is executing and with what parameter
1541 values. This is useful for debugging.
1542
1543 Sample output:
1544
Michael Walsh47aa2a42018-12-10 15:06:02 -06001545 #(CDT) 2016/08/25 17:54:27 - Executing: func1(x = 1)
Michael Walshde791732016-09-06 14:25:24 -05001546
Michael Walsh4dbb6002019-05-17 15:51:15 -05001547 Description of argument(s):
Michael Walshde791732016-09-06 14:25:24 -05001548 stack_frame_ix The index of the stack frame whose
1549 function info should be returned. If the
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001550 caller does not specify a value, this
Michael Walshde791732016-09-06 14:25:24 -05001551 function will set the value to 1 which is
1552 the index of the caller's stack frame. If
1553 the caller is the wrapper function
1554 "print_executing", this function will bump
1555 it up by 1.
Michael Walsh662e13b2019-03-01 15:54:08 -06001556 style See the sprint_line_func prolog above for
1557 details.
Michael Walshde791732016-09-06 14:25:24 -05001558 """
1559
1560 # If user wants default stack_frame_ix.
1561 if stack_frame_ix is None:
1562 func_name = sys._getframe().f_code.co_name
1563 caller_func_name = sys._getframe(1).f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -05001564 if caller_func_name.endswith(func_name[1:]):
Michael Walshde791732016-09-06 14:25:24 -05001565 stack_frame_ix = 2
1566 else:
1567 stack_frame_ix = 1
1568
Michael Walsh6f0362c2019-03-25 14:05:14 -05001569 work_around_inspect_stack_cwd_failure()
Michael Walshde791732016-09-06 14:25:24 -05001570 stack_frame = inspect.stack()[stack_frame_ix]
1571
Michael Walsh662e13b2019-03-01 15:54:08 -06001572 func_and_args = sprint_func_line(stack_frame, style)
Michael Walshde791732016-09-06 14:25:24 -05001573
1574 return sprint_time() + "Executing: " + func_and_args + "\n"
1575
Michael Walshde791732016-09-06 14:25:24 -05001576
Michael Walshbec416d2016-11-10 08:54:52 -06001577def sprint_pgm_header(indent=0,
1578 linefeed=1):
Michael Walshde791732016-09-06 14:25:24 -05001579 r"""
1580 Return a standardized header that programs should print at the beginning
1581 of the run. It includes useful information like command line, pid,
1582 userid, program parameters, etc.
1583
Michael Walsh4dbb6002019-05-17 15:51:15 -05001584 Description of argument(s):
Michael Walsh7423c012016-10-04 10:27:21 -05001585 indent The number of characters to indent each
1586 line of output.
Michael Walshbec416d2016-11-10 08:54:52 -06001587 linefeed Indicates whether a line feed be included
1588 at the beginning and end of the report.
Michael Walshde791732016-09-06 14:25:24 -05001589 """
1590
Michael Walsh4dbb6002019-05-17 15:51:15 -05001591 col1_width = dft_col1_width + indent
Michael Walshbec416d2016-11-10 08:54:52 -06001592
1593 buffer = ""
1594 if linefeed:
1595 buffer = "\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001596
Michael Walshdb6e68a2017-05-23 17:55:31 -05001597 if robot_env:
1598 suite_name = BuiltIn().get_variable_value("${suite_name}")
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001599 buffer += sindent(sprint_time("Running test suite \"" + suite_name
1600 + "\".\n"), indent)
Michael Walshdb6e68a2017-05-23 17:55:31 -05001601
Michael Walsh7423c012016-10-04 10:27:21 -05001602 buffer += sindent(sprint_time() + "Running " + pgm_name + ".\n", indent)
1603 buffer += sindent(sprint_time() + "Program parameter values, etc.:\n\n",
1604 indent)
Michael Walshbec416d2016-11-10 08:54:52 -06001605 buffer += sprint_varx("command_line", ' '.join(sys.argv), 0, indent,
Michael Walsh4dbb6002019-05-17 15:51:15 -05001606 col1_width)
Michael Walsh7423c012016-10-04 10:27:21 -05001607 # We want the output to show a customized name for the pid and pgid but
1608 # we want it to look like a valid variable name. Therefore, we'll use
Michael Walshde791732016-09-06 14:25:24 -05001609 # pgm_name_var_name which was set when this module was imported.
Michael Walshbec416d2016-11-10 08:54:52 -06001610 buffer += sprint_varx(pgm_name_var_name + "_pid", os.getpid(), 0, indent,
Michael Walsh4dbb6002019-05-17 15:51:15 -05001611 col1_width)
Michael Walshbec416d2016-11-10 08:54:52 -06001612 buffer += sprint_varx(pgm_name_var_name + "_pgid", os.getpgrp(), 0, indent,
Michael Walsh4dbb6002019-05-17 15:51:15 -05001613 col1_width)
Michael Walsh86de0d22016-12-05 10:13:15 -06001614 userid_num = str(os.geteuid())
1615 try:
1616 username = os.getlogin()
1617 except OSError:
1618 if userid_num == "0":
1619 username = "root"
1620 else:
1621 username = "?"
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001622 buffer += sprint_varx("uid", userid_num + " (" + username
Michael Walsh4dbb6002019-05-17 15:51:15 -05001623 + ")", 0, indent, col1_width)
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001624 buffer += sprint_varx("gid", str(os.getgid()) + " ("
1625 + str(grp.getgrgid(os.getgid()).gr_name) + ")", 0,
Michael Walsh4dbb6002019-05-17 15:51:15 -05001626 indent, col1_width)
Michael Walshbec416d2016-11-10 08:54:52 -06001627 buffer += sprint_varx("host_name", socket.gethostname(), 0, indent,
Michael Walsh4dbb6002019-05-17 15:51:15 -05001628 col1_width)
Michael Walsh86de0d22016-12-05 10:13:15 -06001629 try:
1630 DISPLAY = os.environ['DISPLAY']
1631 except KeyError:
1632 DISPLAY = ""
Michael Walsh91fc8822019-05-29 17:34:17 -05001633 buffer += sprint_var(DISPLAY, 0, indent, col1_width)
1634 PYTHON_VERSION = os.environ.get('PYTHON_VERSION', None)
1635 if PYTHON_VERSION is not None:
Michael Walshc9a7ee72019-08-28 16:45:50 -05001636 buffer += sprint_var(PYTHON_VERSION, 0, indent, col1_width)
Michael Walsh91fc8822019-05-29 17:34:17 -05001637 PYTHON_PGM_PATH = os.environ.get('PYTHON_PGM_PATH', None)
1638 if PYTHON_PGM_PATH is not None:
Michael Walshc9a7ee72019-08-28 16:45:50 -05001639 buffer += sprint_var(PYTHON_PGM_PATH, 0, indent, col1_width)
Michael Walsh91fc8822019-05-29 17:34:17 -05001640 python_version = sys.version.replace("\n", "")
Michael Walshc9a7ee72019-08-28 16:45:50 -05001641 buffer += sprint_var(python_version, 0, indent, col1_width)
Michael Walsh91fc8822019-05-29 17:34:17 -05001642 ROBOT_VERSION = os.environ.get('ROBOT_VERSION', None)
1643 if ROBOT_VERSION is not None:
Michael Walshc9a7ee72019-08-28 16:45:50 -05001644 buffer += sprint_var(ROBOT_VERSION, 0, indent, col1_width)
Michael Walsh91fc8822019-05-29 17:34:17 -05001645 ROBOT_PGM_PATH = os.environ.get('ROBOT_PGM_PATH', None)
1646 if ROBOT_PGM_PATH is not None:
Michael Walshc9a7ee72019-08-28 16:45:50 -05001647 buffer += sprint_var(ROBOT_PGM_PATH, 0, indent, col1_width)
Michael Walsh91fc8822019-05-29 17:34:17 -05001648
Michael Walsh4dbb6002019-05-17 15:51:15 -05001649 # TODO: Add code to print caller's parms.
Michael Walshde791732016-09-06 14:25:24 -05001650
Michael Walsh7423c012016-10-04 10:27:21 -05001651 # __builtin__.arg_obj is created by the get_arg module function,
1652 # gen_get_options.
1653 try:
1654 buffer += ga.sprint_args(__builtin__.arg_obj, indent)
1655 except AttributeError:
1656 pass
1657
Michael Walshdb6e68a2017-05-23 17:55:31 -05001658 if robot_env:
1659 # Get value of global parm_list.
1660 parm_list = BuiltIn().get_variable_value("${parm_list}")
1661
1662 for parm in parm_list:
1663 parm_value = BuiltIn().get_variable_value("${" + parm + "}")
Michael Walsh4dbb6002019-05-17 15:51:15 -05001664 buffer += sprint_varx(parm, parm_value, 0, indent, col1_width)
Michael Walshdb6e68a2017-05-23 17:55:31 -05001665
1666 # Setting global program_pid.
1667 BuiltIn().set_global_variable("${program_pid}", os.getpid())
1668
Michael Walshbec416d2016-11-10 08:54:52 -06001669 if linefeed:
1670 buffer += "\n"
Michael Walshde791732016-09-06 14:25:24 -05001671
1672 return buffer
1673
Michael Walshde791732016-09-06 14:25:24 -05001674
Michael Walsh7423c012016-10-04 10:27:21 -05001675def sprint_error_report(error_text="\n",
Michael Walshdb6e68a2017-05-23 17:55:31 -05001676 indent=2,
Michael Walshe7ca2b02019-08-01 11:09:45 -05001677 format=None,
1678 stack_frame_ix=None):
Michael Walsh7423c012016-10-04 10:27:21 -05001679 r"""
1680 Return a string with a standardized report which includes the caller's
1681 error text, the call stack and the program header.
1682
Michael Walsh4dbb6002019-05-17 15:51:15 -05001683 Description of argument(s):
Michael Walsh7423c012016-10-04 10:27:21 -05001684 error_text The error text to be included in the
1685 report. The caller should include any
1686 needed linefeeds.
1687 indent The number of characters to indent each
1688 line of output.
Michael Walshdb6e68a2017-05-23 17:55:31 -05001689 format Long or short format. Long includes
1690 extras like lines of dashes, call stack,
1691 etc.
Michael Walshe7ca2b02019-08-01 11:09:45 -05001692 stack_frame_ix The index of the first stack frame which
1693 is to be shown in the print_call_stack
1694 portion of the error report.
Michael Walsh7423c012016-10-04 10:27:21 -05001695 """
1696
Michael Walshdb6e68a2017-05-23 17:55:31 -05001697 # Process input.
1698 indent = int(indent)
1699 if format is None:
1700 if robot_env:
1701 format = 'short'
1702 else:
1703 format = 'long'
1704 error_text = error_text.rstrip('\n') + '\n'
1705
1706 if format == 'short':
1707 return sprint_error(error_text)
1708
Michael Walsh7423c012016-10-04 10:27:21 -05001709 buffer = ""
1710 buffer += sprint_dashes(width=120, char="=")
1711 buffer += sprint_error(error_text)
1712 buffer += "\n"
Michael Walshe7ca2b02019-08-01 11:09:45 -05001713 if not stack_frame_ix:
1714 # Calling sprint_call_stack with stack_frame_ix of 0 causes it to
1715 # show itself and this function in the call stack. This is not
1716 # helpful to a debugger and is therefore clutter. We will adjust the
1717 # stack_frame_ix to hide that information.
1718 stack_frame_ix = 1
1719 caller_func_name = sprint_func_name(1)
1720 if caller_func_name.endswith("print_error_report"):
1721 stack_frame_ix += 1
1722 caller_func_name = sprint_func_name(2)
1723 if caller_func_name.endswith("print_error_report"):
1724 stack_frame_ix += 1
Michael Walsh7bfa9ab2018-11-16 15:24:26 -06001725 buffer += sprint_call_stack(indent, stack_frame_ix)
Michael Walsh7423c012016-10-04 10:27:21 -05001726 buffer += sprint_pgm_header(indent)
1727 buffer += sprint_dashes(width=120, char="=")
1728
1729 return buffer
1730
Michael Walsh7423c012016-10-04 10:27:21 -05001731
Michael Walsh18176322016-11-15 15:11:21 -06001732def sprint_issuing(cmd_buf,
1733 test_mode=0):
Michael Walshde791732016-09-06 14:25:24 -05001734 r"""
1735 Return a line indicating a command that the program is about to execute.
1736
1737 Sample output for a cmd_buf of "ls"
1738
1739 #(CDT) 2016/08/25 17:57:36 - Issuing: ls
Michael Walshbec416d2016-11-10 08:54:52 -06001740
Michael Walsh4dbb6002019-05-17 15:51:15 -05001741 Description of argument(s):
Michael Walshde791732016-09-06 14:25:24 -05001742 cmd_buf The command to be executed by caller.
Michael Walsh4dbb6002019-05-17 15:51:15 -05001743 test_mode With test_mode set, the output will look
Michael Walshbec416d2016-11-10 08:54:52 -06001744 like this:
1745
1746 #(CDT) 2016/08/25 17:57:36 - (test_mode) Issuing: ls
1747
Michael Walshde791732016-09-06 14:25:24 -05001748 """
1749
Michael Walshbec416d2016-11-10 08:54:52 -06001750 buffer = sprint_time()
1751 if test_mode:
1752 buffer += "(test_mode) "
Michael Walsh61c12982019-03-28 12:38:01 -05001753 if type(cmd_buf) is list:
1754 # Assume this is a robot command in the form of a list.
1755 cmd_buf = ' '.join([str(element) for element in cmd_buf])
Michael Walshbec416d2016-11-10 08:54:52 -06001756 buffer += "Issuing: " + cmd_buf + "\n"
Michael Walshde791732016-09-06 14:25:24 -05001757
1758 return buffer
1759
Michael Walshde791732016-09-06 14:25:24 -05001760
Michael Walshde791732016-09-06 14:25:24 -05001761def sprint_pgm_footer():
Michael Walshde791732016-09-06 14:25:24 -05001762 r"""
1763 Return a standardized footer that programs should print at the end of the
1764 program run. It includes useful information like total run time, etc.
1765 """
1766
1767 buffer = "\n" + sprint_time() + "Finished running " + pgm_name + ".\n\n"
1768
1769 total_time = time.time() - start_time
1770 total_time_string = "%0.6f" % total_time
1771
Michael Walsh7423c012016-10-04 10:27:21 -05001772 buffer += sprint_varx(pgm_name_var_name + "_runtime", total_time_string)
Michael Walshbec416d2016-11-10 08:54:52 -06001773 buffer += "\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001774
1775 return buffer
1776
Michael Walsh7423c012016-10-04 10:27:21 -05001777
Michael Walsh7423c012016-10-04 10:27:21 -05001778def sprint(buffer=""):
Michael Walsh7423c012016-10-04 10:27:21 -05001779 r"""
1780 Simply return the user's buffer. This function is used by the qprint and
1781 dprint functions defined dynamically below, i.e. it would not normally be
1782 called for general use.
1783
Michael Walsh4dbb6002019-05-17 15:51:15 -05001784 Description of argument(s).
Michael Walsh7423c012016-10-04 10:27:21 -05001785 buffer This will be returned to the caller.
1786 """
Michael Walshde791732016-09-06 14:25:24 -05001787
Michael Walsh95e45102018-02-09 12:44:43 -06001788 try:
1789 return str(buffer)
1790 except UnicodeEncodeError:
1791 return buffer
Michael Walshbec416d2016-11-10 08:54:52 -06001792
Michael Walshbec416d2016-11-10 08:54:52 -06001793
Michael Walshbec416d2016-11-10 08:54:52 -06001794def sprintn(buffer=""):
Michael Walshbec416d2016-11-10 08:54:52 -06001795 r"""
1796 Simply return the user's buffer with a line feed. This function is used
1797 by the qprint and dprint functions defined dynamically below, i.e. it
1798 would not normally be called for general use.
1799
Michael Walsh4dbb6002019-05-17 15:51:15 -05001800 Description of argument(s).
Michael Walshbec416d2016-11-10 08:54:52 -06001801 buffer This will be returned to the caller.
1802 """
1803
Michael Walsh95e45102018-02-09 12:44:43 -06001804 try:
1805 buffer = str(buffer) + "\n"
1806 except UnicodeEncodeError:
1807 buffer = buffer + "\n"
Michael Walshbec416d2016-11-10 08:54:52 -06001808
Michael Walshde791732016-09-06 14:25:24 -05001809 return buffer
1810
Michael Walsh168eb0f2017-12-01 15:35:32 -06001811
Michael Walshfd2733c2017-11-13 11:36:20 -06001812def gp_print(buffer,
1813 stream='stdout'):
Michael Walshfd2733c2017-11-13 11:36:20 -06001814 r"""
1815 Print the buffer using either sys.stdout.write or BuiltIn().log_to_console
1816 depending on whether we are running in a robot environment.
1817
1818 This function is intended for use only by other functions in this module.
1819
Michael Walsh4dbb6002019-05-17 15:51:15 -05001820 Description of argument(s):
Michael Walshfd2733c2017-11-13 11:36:20 -06001821 buffer The string to be printed.
1822 stream Either "stdout" or "stderr".
1823 """
1824
1825 if robot_env:
1826 BuiltIn().log_to_console(buffer, stream=stream, no_newline=True)
1827 else:
1828 if stream == "stdout":
1829 sys.stdout.write(buffer)
1830 sys.stdout.flush()
1831 else:
1832 sys.stderr.write(buffer)
1833 sys.stderr.flush()
Michael Walshde791732016-09-06 14:25:24 -05001834
1835
Michael Walsh168eb0f2017-12-01 15:35:32 -06001836def gp_log(buffer):
Michael Walsh168eb0f2017-12-01 15:35:32 -06001837 r"""
1838 Log the buffer using either python logging or BuiltIn().log depending on
1839 whether we are running in a robot environment.
1840
1841 This function is intended for use only by other functions in this module.
1842
Michael Walsh4dbb6002019-05-17 15:51:15 -05001843 Description of argument(s):
Michael Walsh168eb0f2017-12-01 15:35:32 -06001844 buffer The string to be logged.
1845 """
1846
1847 if robot_env:
1848 BuiltIn().log(buffer)
1849 else:
1850 logging.warning(buffer)
1851
1852
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001853def gp_debug_print(buffer):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001854 r"""
Michael Walshfd2733c2017-11-13 11:36:20 -06001855 Print with gp_print only if gen_print_debug is set.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001856
1857 This function is intended for use only by other functions in this module.
1858
Michael Walsh4dbb6002019-05-17 15:51:15 -05001859 Description of argument(s):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001860 buffer The string to be printed.
1861 """
1862
1863 if not gen_print_debug:
1864 return
1865
Michael Walshfd2733c2017-11-13 11:36:20 -06001866 gp_print(buffer)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001867
1868
Michael Walshb1500152017-04-12 15:42:43 -05001869def get_var_value(var_value=None,
1870 default=1,
1871 var_name=None):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001872 r"""
Michael Walshb1500152017-04-12 15:42:43 -05001873 Return either var_value, the corresponding global value or default.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001874
Michael Walshb1500152017-04-12 15:42:43 -05001875 If var_value is not None, it will simply be returned.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001876
Michael Walshb1500152017-04-12 15:42:43 -05001877 If var_value is None, this function will return the corresponding global
1878 value of the variable in question.
1879
1880 Note: For global values, if we are in a robot environment,
1881 get_variable_value will be used. Otherwise, the __builtin__ version of
1882 the variable is returned (which are set by gen_arg.py functions).
1883
1884 If there is no global value associated with the variable, default is
1885 returned.
1886
1887 This function is useful for other functions in setting default values for
1888 parameters.
1889
1890 Example use:
1891
1892 def my_func(quiet=None):
1893
1894 quiet = int(get_var_value(quiet, 0))
1895
1896 Example calls to my_func():
1897
1898 In the following example, the caller is explicitly asking to have quiet be
1899 set to 1.
1900
1901 my_func(quiet=1)
1902
1903 In the following example, quiet will be set to the global value of quiet,
1904 if defined, or to 0 (the default).
1905
1906 my_func()
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001907
Michael Walsh4dbb6002019-05-17 15:51:15 -05001908 Description of argument(s):
Michael Walshb1500152017-04-12 15:42:43 -05001909 var_value The value to be returned (if not equal to
1910 None).
1911 default The value that is returned if var_value is
1912 None and there is no corresponding global
1913 value defined.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001914 var_name The name of the variable whose value is to
Michael Walshb1500152017-04-12 15:42:43 -05001915 be returned. Under most circumstances,
1916 this value need not be provided. This
1917 function can figure out the name of the
1918 variable passed as var_value. One
1919 exception to this would be if this
1920 function is called directly from a .robot
1921 file.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001922 """
1923
Michael Walshb1500152017-04-12 15:42:43 -05001924 if var_value is not None:
1925 return var_value
1926
1927 if var_name is None:
1928 var_name = get_arg_name(None, 1, 2)
1929
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001930 if robot_env:
Michael Walshc6537442017-06-06 15:33:52 -05001931 var_value = BuiltIn().get_variable_value("${" + var_name + "}",
1932 default)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001933 else:
1934 var_value = getattr(__builtin__, var_name, default)
1935
1936 return var_value
1937
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001938
Michael Walsh052ff812018-05-18 16:09:09 -05001939def get_stack_var(var_name,
1940 default="",
1941 init_stack_ix=2):
Michael Walsh052ff812018-05-18 16:09:09 -05001942 r"""
Michael Walshc9a7ee72019-08-28 16:45:50 -05001943 Starting with the caller's stack level, search upward in the call stack
Michael Walsh052ff812018-05-18 16:09:09 -05001944 for a variable named var_name and return its value. If the variable
Michael Walshc9a7ee72019-08-28 16:45:50 -05001945 cannot be found in the stack, attempt to get the global value. If the
1946 variable still cannot be found, return default.
Michael Walsh052ff812018-05-18 16:09:09 -05001947
1948 Example code:
1949
1950 def func12():
1951 my_loc_var1 = get_stack_var('my_var1', "default value")
1952
1953 def func11():
1954 my_var1 = 11
1955 func12()
1956
1957 In this example, get_stack_var will find the value of my_var1 in func11's
1958 stack and will therefore return the value 11. Therefore, my_loc_var1
1959 would get set to 11.
1960
1961 Description of argument(s):
1962 var_name The name of the variable to be searched
1963 for.
1964 default The value to return if the the variable
1965 cannot be found.
1966 init_stack_ix The initial stack index from which to
1967 begin the search. 0 would be the index of
1968 this func1tion ("get_stack_var"), 1 would
1969 be the index of the function calling this
1970 function, etc.
1971 """
1972
Michael Walsh6f0362c2019-03-25 14:05:14 -05001973 work_around_inspect_stack_cwd_failure()
Michael Walshc9a7ee72019-08-28 16:45:50 -05001974 default = get_var_value(var_name=var_name, default=default)
Michael Walsh052ff812018-05-18 16:09:09 -05001975 return next((frame[0].f_locals[var_name]
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001976 for frame in inspect.stack()[init_stack_ix:]
1977 if var_name in frame[0].f_locals), default)
Michael Walsh052ff812018-05-18 16:09:09 -05001978
1979
Michael Walsh82acf002017-05-04 14:33:05 -05001980# hidden_text is a list of passwords which are to be replaced with asterisks
1981# by print functions defined in this module.
1982hidden_text = []
1983# password_regex is created based on the contents of hidden_text.
1984password_regex = ""
1985
1986
Michael Walsh82acf002017-05-04 14:33:05 -05001987def register_passwords(*args):
Michael Walsh82acf002017-05-04 14:33:05 -05001988 r"""
1989 Register one or more passwords which are to be hidden in output produced
1990 by the print functions in this module.
1991
1992 Note: Blank password values are NOT registered. They are simply ignored.
1993
1994 Description of argument(s):
1995 args One or more password values. If a given
1996 password value is already registered, this
1997 function will simply do nothing.
1998 """
1999
2000 global hidden_text
2001 global password_regex
2002
2003 for password in args:
2004 if password == "":
2005 break
2006 if password in hidden_text:
2007 break
2008
2009 # Place the password into the hidden_text list.
2010 hidden_text.append(password)
2011 # Create a corresponding password regular expression. Escape regex
2012 # special characters too.
2013 password_regex = '(' +\
2014 '|'.join([re.escape(x) for x in hidden_text]) + ')'
2015
Michael Walsh82acf002017-05-04 14:33:05 -05002016
Michael Walsh82acf002017-05-04 14:33:05 -05002017def replace_passwords(buffer):
Michael Walsh82acf002017-05-04 14:33:05 -05002018 r"""
2019 Return the buffer but with all registered passwords replaced by a string
2020 of asterisks.
2021
2022
2023 Description of argument(s):
2024 buffer The string to be returned but with
2025 passwords replaced.
2026 """
2027
2028 global password_regex
2029
2030 if int(os.environ.get("DEBUG_SHOW_PASSWORDS", "0")):
2031 return buffer
2032
2033 if password_regex == "":
2034 # No passwords to replace.
2035 return buffer
2036
2037 return re.sub(password_regex, "********", buffer)
2038
Michael Walshfd2733c2017-11-13 11:36:20 -06002039
2040def create_print_wrapper_funcs(func_names,
2041 stderr_func_names,
Michael Walsh61c12982019-03-28 12:38:01 -05002042 replace_dict,
2043 func_prefix=""):
Michael Walshfd2733c2017-11-13 11:36:20 -06002044 r"""
2045 Generate code for print wrapper functions and return the generated code as
2046 a string.
2047
2048 To illustrate, suppose there is a "print_foo_bar" function in the
2049 func_names list.
2050 This function will...
2051 - Expect that there is an sprint_foo_bar function already in existence.
2052 - Create a print_foo_bar function which calls sprint_foo_bar and prints
Michael Walshfaafa9c2018-06-27 16:39:31 -05002053 the result.
Michael Walshfd2733c2017-11-13 11:36:20 -06002054 - Create a qprint_foo_bar function which calls upon sprint_foo_bar only if
Michael Walshfaafa9c2018-06-27 16:39:31 -05002055 global value quiet is 0.
Michael Walshfd2733c2017-11-13 11:36:20 -06002056 - Create a dprint_foo_bar function which calls upon sprint_foo_bar only if
Michael Walshfaafa9c2018-06-27 16:39:31 -05002057 global value debug is 1.
Michael Walshfd2733c2017-11-13 11:36:20 -06002058
2059 Also, code will be generated to define aliases for each function as well.
2060 Each alias will be created by replacing "print_" in the function name with
2061 "p" For example, the alias for print_foo_bar will be pfoo_bar.
2062
2063 Description of argument(s):
2064 func_names A list of functions for which print
2065 wrapper function code is to be generated.
2066 stderr_func_names A list of functions whose generated code
2067 should print to stderr rather than to
2068 stdout.
2069 replace_dict Please see the create_func_def_string
2070 function in wrap_utils.py for details on
2071 this parameter. This parameter will be
2072 passed directly to create_func_def_string.
Michael Walsh61c12982019-03-28 12:38:01 -05002073 func_prefix Prefix to be pre-pended to the generated
2074 function name.
Michael Walshfd2733c2017-11-13 11:36:20 -06002075 """
2076
2077 buffer = ""
2078
2079 for func_name in func_names:
2080 if func_name in stderr_func_names:
2081 replace_dict['output_stream'] = "stderr"
2082 else:
2083 replace_dict['output_stream'] = "stdout"
2084
2085 s_func_name = "s" + func_name
2086 q_func_name = "q" + func_name
2087 d_func_name = "d" + func_name
2088
2089 # We don't want to try to redefine the "print" function, thus the
2090 # following if statement.
2091 if func_name != "print":
Michael Walsh61c12982019-03-28 12:38:01 -05002092 func_def = create_func_def_string(s_func_name,
2093 func_prefix + func_name,
Michael Walshfd2733c2017-11-13 11:36:20 -06002094 print_func_template,
2095 replace_dict)
2096 buffer += func_def
2097
Michael Walsh61c12982019-03-28 12:38:01 -05002098 func_def = create_func_def_string(s_func_name,
2099 func_prefix + "q" + func_name,
Michael Walshfd2733c2017-11-13 11:36:20 -06002100 qprint_func_template, replace_dict)
2101 buffer += func_def
2102
Michael Walsh61c12982019-03-28 12:38:01 -05002103 func_def = create_func_def_string(s_func_name,
2104 func_prefix + "d" + func_name,
Michael Walshfd2733c2017-11-13 11:36:20 -06002105 dprint_func_template, replace_dict)
2106 buffer += func_def
2107
Michael Walsh61c12982019-03-28 12:38:01 -05002108 func_def = create_func_def_string(s_func_name,
2109 func_prefix + "l" + func_name,
Michael Walsh168eb0f2017-12-01 15:35:32 -06002110 lprint_func_template, replace_dict)
2111 buffer += func_def
2112
Michael Walshfd2733c2017-11-13 11:36:20 -06002113 # Create abbreviated aliases (e.g. spvar is an alias for sprint_var).
2114 alias = re.sub("print_", "p", func_name)
2115 alias = re.sub("print", "p", alias)
Michael Walsh61c12982019-03-28 12:38:01 -05002116 prefixes = [func_prefix + "", "s", func_prefix + "q",
2117 func_prefix + "d", func_prefix + "l"]
Michael Walshfd2733c2017-11-13 11:36:20 -06002118 for prefix in prefixes:
2119 if alias == "p":
2120 continue
2121 func_def = prefix + alias + " = " + prefix + func_name
2122 buffer += func_def + "\n"
2123
2124 return buffer
Michael Walsh82acf002017-05-04 14:33:05 -05002125
2126
Michael Walshde791732016-09-06 14:25:24 -05002127# In the following section of code, we will dynamically create print versions
2128# for each of the sprint functions defined above. So, for example, where we
2129# have an sprint_time() function defined above that returns the time to the
Michael Walsh7423c012016-10-04 10:27:21 -05002130# caller in a string, we will create a corresponding print_time() function
2131# that will print that string directly to stdout.
Michael Walshde791732016-09-06 14:25:24 -05002132
Michael Walsh61c12982019-03-28 12:38:01 -05002133# It can be complicated to follow what's being created below. Here is an
Michael Walshfd2733c2017-11-13 11:36:20 -06002134# example of the print_time() function that will be created:
Michael Walshde791732016-09-06 14:25:24 -05002135
Michael Walshfd2733c2017-11-13 11:36:20 -06002136# def print_time(buffer=''):
Michael Walsh61c12982019-03-28 12:38:01 -05002137# gp_print(replace_passwords(sprint_time(buffer=buffer)), stream='stdout')
2138
2139# For each print function defined below, there will also be a qprint, a
2140# dprint and an lprint version defined (e.g. qprint_time, dprint_time,
2141# lprint_time).
2142
2143# The q version of each print function will only print if the quiet variable
2144# is 0.
2145# The d version of each print function will only print if the debug variable
2146# is 1.
2147# The l version of each print function will print the contents as log data.
2148# For conventional programs, this means use of the logging module. For robot
2149# programs it means use of the BuiltIn().log() function.
Michael Walshde791732016-09-06 14:25:24 -05002150
Michael Walshfd2733c2017-11-13 11:36:20 -06002151# Templates for the various print wrapper functions.
2152print_func_template = \
2153 [
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05002154 " <mod_qualifier>gp_print(<mod_qualifier>replace_passwords("
2155 + "<call_line>), stream='<output_stream>')"
Michael Walshfd2733c2017-11-13 11:36:20 -06002156 ]
2157
2158qprint_func_template = \
2159 [
Michael Walshc9a7ee72019-08-28 16:45:50 -05002160 " quiet = <mod_qualifier>get_stack_var(\"quiet\", 0)",
Michael Walsh589ae762019-03-19 13:22:39 -05002161 " if int(quiet): return"
Michael Walshfd2733c2017-11-13 11:36:20 -06002162 ] + print_func_template
2163
2164dprint_func_template = \
2165 [
Michael Walshc9a7ee72019-08-28 16:45:50 -05002166 " debug = <mod_qualifier>get_stack_var(\"debug\", 0)",
Michael Walsh589ae762019-03-19 13:22:39 -05002167 " if not int(debug): return"
Michael Walshfd2733c2017-11-13 11:36:20 -06002168 ] + print_func_template
2169
Michael Walsh168eb0f2017-12-01 15:35:32 -06002170lprint_func_template = \
2171 [
Michael Walsh61c12982019-03-28 12:38:01 -05002172 " <mod_qualifier>set_last_seconds_ix(<mod_qualifier>"
2173 + "lprint_last_seconds_ix())",
2174 " <mod_qualifier>gp_log(<mod_qualifier>replace_passwords"
2175 + "(<call_line>))",
2176 " <mod_qualifier>set_last_seconds_ix(<mod_qualifier>"
2177 + "standard_print_last_seconds_ix())"
Michael Walsh168eb0f2017-12-01 15:35:32 -06002178 ]
2179
Michael Walsh81c02342018-01-05 15:43:28 -06002180replace_dict = {'output_stream': 'stdout', 'mod_qualifier': ''}
Michael Walshfd2733c2017-11-13 11:36:20 -06002181
Michael Walsh61c12982019-03-28 12:38:01 -05002182gp_debug_print("robot_env: " + str(robot_env) + "\n")
Michael Walshde791732016-09-06 14:25:24 -05002183
2184# func_names contains a list of all print functions which should be created
2185# from their sprint counterparts.
2186func_names = ['print_time', 'print_timen', 'print_error', 'print_varx',
Michael Walsh18176322016-11-15 15:11:21 -06002187 'print_var', 'print_vars', 'print_dashes', 'indent',
2188 'print_call_stack', 'print_func_name', 'print_executing',
2189 'print_pgm_header', 'print_issuing', 'print_pgm_footer',
2190 'print_error_report', 'print', 'printn']
Michael Walshde791732016-09-06 14:25:24 -05002191
Michael Walsh2ee77cd2017-03-08 11:50:17 -06002192# stderr_func_names is a list of functions whose output should go to stderr
2193# rather than stdout.
2194stderr_func_names = ['print_error', 'print_error_report']
Michael Walshde791732016-09-06 14:25:24 -05002195
Michael Walshfd2733c2017-11-13 11:36:20 -06002196func_defs = create_print_wrapper_funcs(func_names, stderr_func_names,
2197 replace_dict)
2198gp_debug_print(func_defs)
2199exec(func_defs)