blob: 945a9419048ff3a429bbed7d6ba28bef11e65d02 [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
1244 format_string = "%" + str(indent) + "s%s\n"
1245 buffer = format_string % ("", loc_var_name + ":")
1246 if fmt & show_type():
1247 buffer = buffer.replace("\n", " " + type_str + "\n")
1248 indent += 2
Michael Walsh7423c012016-10-04 10:27:21 -05001249 try:
1250 length = len(var_value)
1251 except TypeError:
Michael Walsh23e7f492017-01-10 11:34:47 -06001252 length = 0
Michael Walsh7423c012016-10-04 10:27:21 -05001253 ix = 0
1254 loc_trailing_char = "\n"
Michael Walsh8646d962019-01-21 14:36:13 -06001255 if is_dict(var_value):
Michael Walsh4dbb6002019-05-17 15:51:15 -05001256 if type(child_fmt) is list:
1257 child_quote_keys = (child_fmt[0] & quote_keys())
1258 else:
1259 child_quote_keys = (child_fmt & quote_keys())
Michael Walsh37762f92018-08-07 14:59:18 -05001260 for key, value in var_value.items():
Michael Walshd2869032018-03-22 16:12:11 -05001261 if key_list is not None:
1262 key_list_regex = "^" + "|".join(key_list) + "$"
1263 if not re.match(key_list_regex, key):
1264 continue
Michael Walsh7423c012016-10-04 10:27:21 -05001265 ix += 1
1266 if ix == length:
1267 loc_trailing_char = trailing_char
Michael Walsh4dbb6002019-05-17 15:51:15 -05001268 if child_quote_keys:
1269 key = "'" + key + "'"
1270 key = "[" + str(key) + "]"
1271 buffer += sprint_varx(var_name + key, value, child_fmt, indent,
Michael Walshc6acf742019-08-06 11:48:51 -05001272 col1_width, loc_trailing_char, key_list,
1273 delim)
Michael Walsh7423c012016-10-04 10:27:21 -05001274 elif type(var_value) in (list, tuple, set):
Michael Walshde791732016-09-06 14:25:24 -05001275 for key, value in enumerate(var_value):
Michael Walsh7423c012016-10-04 10:27:21 -05001276 ix += 1
1277 if ix == length:
1278 loc_trailing_char = trailing_char
Michael Walsh4dbb6002019-05-17 15:51:15 -05001279 key = "[" + str(key) + "]"
1280 buffer += sprint_varx(var_name + key, value, child_fmt, indent,
Michael Walshc6acf742019-08-06 11:48:51 -05001281 col1_width, loc_trailing_char, key_list,
1282 delim)
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001283 elif isinstance(var_value, argparse.Namespace):
Michael Walshde791732016-09-06 14:25:24 -05001284 for key in var_value.__dict__:
Michael Walsh7423c012016-10-04 10:27:21 -05001285 ix += 1
1286 if ix == length:
1287 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -05001288 cmd_buf = "buffer += sprint_varx(var_name + \".\" + str(key)" \
Michael Walsh4dbb6002019-05-17 15:51:15 -05001289 + ", var_value." + key + ", child_fmt, indent," \
Michael Walshc6acf742019-08-06 11:48:51 -05001290 + " col1_width, loc_trailing_char, key_list," \
1291 + " delim)"
Michael Walshde791732016-09-06 14:25:24 -05001292 exec(cmd_buf)
1293 else:
1294 var_type = type(var_value).__name__
1295 func_name = sys._getframe().f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -05001296 var_value = "<" + var_type + " type not supported by " + \
1297 func_name + "()>"
Michael Walshde791732016-09-06 14:25:24 -05001298 value_format = "%s"
Michael Walsh4dbb6002019-05-17 15:51:15 -05001299 indent -= 2
1300 # Adjust col1_width.
1301 col1_width = col1_width - indent
1302 format_string = "%" + str(indent) + "s%-" \
1303 + str(col1_width) + "s" + value_format + trailing_char
Michael Walsh0f2ea5f2017-02-20 15:55:00 -06001304 return format_string % ("", str(var_name) + ":", var_value)
Michael Walsh23e7f492017-01-10 11:34:47 -06001305
Michael Walshde791732016-09-06 14:25:24 -05001306 return buffer
1307
1308 return ""
1309
Michael Walshde791732016-09-06 14:25:24 -05001310
Michael Walsh4dbb6002019-05-17 15:51:15 -05001311def sprint_var(*args, **kwargs):
Michael Walshde791732016-09-06 14:25:24 -05001312 r"""
Michael Walsh4dbb6002019-05-17 15:51:15 -05001313 Figure out the name of the first argument for the caller and then call
Michael Walshde791732016-09-06 14:25:24 -05001314 sprint_varx with it. Therefore, the following 2 calls are equivalent:
1315 sprint_varx("var1", var1)
1316 sprint_var(var1)
Michael Walsh4dbb6002019-05-17 15:51:15 -05001317
1318 See sprint_varx for description of arguments.
Michael Walshde791732016-09-06 14:25:24 -05001319 """
1320
Michael Walshde791732016-09-06 14:25:24 -05001321 stack_frame = 2
Michael Walsh7423c012016-10-04 10:27:21 -05001322 caller_func_name = sprint_func_name(2)
1323 if caller_func_name.endswith("print_var"):
Michael Walshde791732016-09-06 14:25:24 -05001324 stack_frame += 1
Michael Walsh4dbb6002019-05-17 15:51:15 -05001325 # Get the name of the first variable passed to this function.
Michael Walshde791732016-09-06 14:25:24 -05001326 var_name = get_arg_name(None, 1, stack_frame)
Michael Walsh4dbb6002019-05-17 15:51:15 -05001327 return sprint_varx(var_name, *args, **kwargs)
Michael Walshde791732016-09-06 14:25:24 -05001328
1329
Michael Walsh4dbb6002019-05-17 15:51:15 -05001330def sprint_vars(*args, **kwargs):
Michael Walsh18176322016-11-15 15:11:21 -06001331 r"""
1332 Sprint the values of one or more variables.
1333
Michael Walsh4dbb6002019-05-17 15:51:15 -05001334 Description of argument(s):
1335 args The variable values which are to be
1336 printed.
1337 kwargs See sprint_varx (above) for description of
1338 additional arguments.
Michael Walsh18176322016-11-15 15:11:21 -06001339 """
1340
Michael Walsh18176322016-11-15 15:11:21 -06001341 stack_frame = 2
1342 caller_func_name = sprint_func_name(2)
1343 if caller_func_name.endswith("print_vars"):
1344 stack_frame += 1
1345
Michael Walsh18176322016-11-15 15:11:21 -06001346 buffer = ""
Michael Walsh4dbb6002019-05-17 15:51:15 -05001347 arg_num = 1
1348 for var_value in args:
1349 var_name = get_arg_name(None, arg_num, stack_frame)
1350 buffer += sprint_varx(var_name, var_value, **kwargs)
1351 arg_num += 1
Michael Walsh18176322016-11-15 15:11:21 -06001352
1353 return buffer
1354
Michael Walsh18176322016-11-15 15:11:21 -06001355
Michael Walsh4dbb6002019-05-17 15:51:15 -05001356def sprint_dashes(indent=dft_indent,
Michael Walsh7423c012016-10-04 10:27:21 -05001357 width=80,
1358 line_feed=1,
1359 char="-"):
Michael Walshde791732016-09-06 14:25:24 -05001360 r"""
1361 Return a string of dashes to the caller.
1362
Michael Walsh4dbb6002019-05-17 15:51:15 -05001363 Description of argument(s):
Michael Walshde791732016-09-06 14:25:24 -05001364 indent The number of characters to indent the
1365 output.
1366 width The width of the string of dashes.
1367 line_feed Indicates whether the output should end
1368 with a line feed.
Michael Walsh7423c012016-10-04 10:27:21 -05001369 char The character to be repeated in the output
1370 string.
Michael Walshde791732016-09-06 14:25:24 -05001371 """
1372
Michael Walsh7423c012016-10-04 10:27:21 -05001373 width = int(width)
Michael Walsh23e7f492017-01-10 11:34:47 -06001374 buffer = " " * int(indent) + char * width
Michael Walshde791732016-09-06 14:25:24 -05001375 if line_feed:
1376 buffer += "\n"
1377
1378 return buffer
1379
Michael Walshde791732016-09-06 14:25:24 -05001380
Michael Walsh7423c012016-10-04 10:27:21 -05001381def sindent(text="",
1382 indent=0):
Michael Walsh7423c012016-10-04 10:27:21 -05001383 r"""
1384 Pre-pend the specified number of characters to the text string (i.e.
1385 indent it) and return it.
1386
Michael Walsh4dbb6002019-05-17 15:51:15 -05001387 Description of argument(s):
Michael Walsh7423c012016-10-04 10:27:21 -05001388 text The string to be indented.
1389 indent The number of characters to indent the
1390 string.
1391 """
1392
1393 format_string = "%" + str(indent) + "s%s"
1394 buffer = format_string % ("", text)
1395
1396 return buffer
1397
Michael Walsh7423c012016-10-04 10:27:21 -05001398
Michael Walsh662e13b2019-03-01 15:54:08 -06001399func_line_style_std = None
1400func_line_style_short = 1
1401
1402
1403def sprint_func_line(stack_frame, style=None):
Michael Walsh47aa2a42018-12-10 15:06:02 -06001404 r"""
1405 For the given stack_frame, return a formatted string containing the
1406 function name and all its arguments.
1407
1408 Example:
1409
1410 func1(last_name = 'walsh', first_name = 'mikey')
1411
1412 Description of argument(s):
1413 stack_frame A stack frame (such as is returned by
1414 inspect.stack()).
Michael Walsh662e13b2019-03-01 15:54:08 -06001415 style Indicates the style or formatting of the
1416 result string. Acceptable values are
1417 shown above.
1418
1419 Description of styles:
1420 func_line_style_std The standard formatting.
1421 func_line_style_short 1) The self parm (associated with methods)
1422 will be dropped. 2) The args and kwargs
1423 values will be treated as special. In
1424 both cases the arg name ('args' or
1425 'kwargs') will be dropped and only the
1426 values will be shown.
Michael Walsh47aa2a42018-12-10 15:06:02 -06001427 """
1428
1429 func_name = str(stack_frame[3])
1430 if func_name == "?":
1431 # "?" is the name used when code is not in a function.
1432 func_name = "(none)"
1433
1434 if func_name == "<module>":
1435 # If the func_name is the "main" program, we simply get the command
1436 # line call string.
1437 func_and_args = ' '.join(sys.argv)
1438 else:
1439 # Get the program arguments.
1440 (args, varargs, keywords, locals) =\
1441 inspect.getargvalues(stack_frame[0])
1442
1443 args_list = []
1444 for arg_name in filter(None, args + [varargs, keywords]):
1445 # Get the arg value from frame locals.
1446 arg_value = locals[arg_name]
1447 if arg_name == 'self':
Michael Walsh662e13b2019-03-01 15:54:08 -06001448 if style == func_line_style_short:
1449 continue
Michael Walsh47aa2a42018-12-10 15:06:02 -06001450 # Manipulations to improve output for class methods.
1451 func_name = arg_value.__class__.__name__ + "." + func_name
1452 args_list.append(arg_name + " = <self>")
Michael Walsh662e13b2019-03-01 15:54:08 -06001453 elif (style == func_line_style_short
1454 and arg_name == 'args'
1455 and type(arg_value) in (list, tuple)):
1456 if len(arg_value) == 0:
1457 continue
1458 args_list.append(repr(', '.join(arg_value)))
1459 elif (style == func_line_style_short
1460 and arg_name == 'kwargs'
1461 and type(arg_value) is dict):
1462 for key, value in arg_value.items():
1463 args_list.append(key + "=" + repr(value))
Michael Walsh47aa2a42018-12-10 15:06:02 -06001464 else:
1465 args_list.append(arg_name + " = " + repr(arg_value))
1466 args_str = "(" + ', '.join(map(str, args_list)) + ")"
1467
1468 # Now we need to print this in a nicely-wrapped way.
1469 func_and_args = func_name + args_str
1470
1471 return func_and_args
1472
1473
Michael Walsh7423c012016-10-04 10:27:21 -05001474def sprint_call_stack(indent=0,
Michael Walsh662e13b2019-03-01 15:54:08 -06001475 stack_frame_ix=0,
1476 style=None):
Michael Walshde791732016-09-06 14:25:24 -05001477 r"""
1478 Return a call stack report for the given point in the program with line
1479 numbers, function names and function parameters and arguments.
1480
1481 Sample output:
1482
1483 -------------------------------------------------------------------------
1484 Python function call stack
1485
1486 Line # Function name and arguments
1487 ------ ------------------------------------------------------------------
Michael Walsh47aa2a42018-12-10 15:06:02 -06001488 424 sprint_call_stack()
1489 4 print_call_stack()
1490 31 func1(last_name = 'walsh', first_name = 'mikey')
Michael Walshde791732016-09-06 14:25:24 -05001491 59 /tmp/scr5.py
1492 -------------------------------------------------------------------------
1493
Michael Walsh4dbb6002019-05-17 15:51:15 -05001494 Description of argument(s):
Michael Walshde791732016-09-06 14:25:24 -05001495 indent The number of characters to indent each
1496 line of output.
1497 stack_frame_ix The index of the first stack frame which
1498 is to be returned.
Michael Walsh662e13b2019-03-01 15:54:08 -06001499 style See the sprint_line_func prolog above for
1500 details.
Michael Walshde791732016-09-06 14:25:24 -05001501 """
1502
1503 buffer = ""
Michael Walsh7423c012016-10-04 10:27:21 -05001504 buffer += sprint_dashes(indent)
1505 buffer += sindent("Python function call stack\n\n", indent)
1506 buffer += sindent("Line # Function name and arguments\n", indent)
1507 buffer += sprint_dashes(indent, 6, 0) + " " + sprint_dashes(0, 73)
Michael Walshde791732016-09-06 14:25:24 -05001508
1509 # Grab the current program stack.
Michael Walsh6f0362c2019-03-25 14:05:14 -05001510 work_around_inspect_stack_cwd_failure()
Michael Walshde791732016-09-06 14:25:24 -05001511 current_stack = inspect.stack()
1512
1513 # Process each frame in turn.
1514 format_string = "%6s %s\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001515 ix = 0
Michael Walshde791732016-09-06 14:25:24 -05001516 for stack_frame in current_stack:
Michael Walsh7423c012016-10-04 10:27:21 -05001517 if ix < stack_frame_ix:
1518 ix += 1
1519 continue
Michael Walsh4dbb6002019-05-17 15:51:15 -05001520 # Make the line number shown to be the line where one finds the line
Michael Walsh23e7f492017-01-10 11:34:47 -06001521 # shown.
1522 try:
1523 line_num = str(current_stack[ix + 1][2])
1524 except IndexError:
1525 line_num = ""
Michael Walsh662e13b2019-03-01 15:54:08 -06001526 func_and_args = sprint_func_line(stack_frame, style=style)
Michael Walshde791732016-09-06 14:25:24 -05001527
Michael Walsh23e7f492017-01-10 11:34:47 -06001528 buffer += sindent(format_string % (line_num, func_and_args), indent)
Michael Walsh7423c012016-10-04 10:27:21 -05001529 ix += 1
Michael Walshde791732016-09-06 14:25:24 -05001530
Michael Walsh7423c012016-10-04 10:27:21 -05001531 buffer += sprint_dashes(indent)
Michael Walshde791732016-09-06 14:25:24 -05001532
1533 return buffer
1534
Michael Walshde791732016-09-06 14:25:24 -05001535
Michael Walsh662e13b2019-03-01 15:54:08 -06001536def sprint_executing(stack_frame_ix=None, style=None):
Michael Walshde791732016-09-06 14:25:24 -05001537 r"""
1538 Print a line indicating what function is executing and with what parameter
1539 values. This is useful for debugging.
1540
1541 Sample output:
1542
Michael Walsh47aa2a42018-12-10 15:06:02 -06001543 #(CDT) 2016/08/25 17:54:27 - Executing: func1(x = 1)
Michael Walshde791732016-09-06 14:25:24 -05001544
Michael Walsh4dbb6002019-05-17 15:51:15 -05001545 Description of argument(s):
Michael Walshde791732016-09-06 14:25:24 -05001546 stack_frame_ix The index of the stack frame whose
1547 function info should be returned. If the
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001548 caller does not specify a value, this
Michael Walshde791732016-09-06 14:25:24 -05001549 function will set the value to 1 which is
1550 the index of the caller's stack frame. If
1551 the caller is the wrapper function
1552 "print_executing", this function will bump
1553 it up by 1.
Michael Walsh662e13b2019-03-01 15:54:08 -06001554 style See the sprint_line_func prolog above for
1555 details.
Michael Walshde791732016-09-06 14:25:24 -05001556 """
1557
1558 # If user wants default stack_frame_ix.
1559 if stack_frame_ix is None:
1560 func_name = sys._getframe().f_code.co_name
1561 caller_func_name = sys._getframe(1).f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -05001562 if caller_func_name.endswith(func_name[1:]):
Michael Walshde791732016-09-06 14:25:24 -05001563 stack_frame_ix = 2
1564 else:
1565 stack_frame_ix = 1
1566
Michael Walsh6f0362c2019-03-25 14:05:14 -05001567 work_around_inspect_stack_cwd_failure()
Michael Walshde791732016-09-06 14:25:24 -05001568 stack_frame = inspect.stack()[stack_frame_ix]
1569
Michael Walsh662e13b2019-03-01 15:54:08 -06001570 func_and_args = sprint_func_line(stack_frame, style)
Michael Walshde791732016-09-06 14:25:24 -05001571
1572 return sprint_time() + "Executing: " + func_and_args + "\n"
1573
Michael Walshde791732016-09-06 14:25:24 -05001574
Michael Walshbec416d2016-11-10 08:54:52 -06001575def sprint_pgm_header(indent=0,
1576 linefeed=1):
Michael Walshde791732016-09-06 14:25:24 -05001577 r"""
1578 Return a standardized header that programs should print at the beginning
1579 of the run. It includes useful information like command line, pid,
1580 userid, program parameters, etc.
1581
Michael Walsh4dbb6002019-05-17 15:51:15 -05001582 Description of argument(s):
Michael Walsh7423c012016-10-04 10:27:21 -05001583 indent The number of characters to indent each
1584 line of output.
Michael Walshbec416d2016-11-10 08:54:52 -06001585 linefeed Indicates whether a line feed be included
1586 at the beginning and end of the report.
Michael Walshde791732016-09-06 14:25:24 -05001587 """
1588
Michael Walsh4dbb6002019-05-17 15:51:15 -05001589 col1_width = dft_col1_width + indent
Michael Walshbec416d2016-11-10 08:54:52 -06001590
1591 buffer = ""
1592 if linefeed:
1593 buffer = "\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001594
Michael Walshdb6e68a2017-05-23 17:55:31 -05001595 if robot_env:
1596 suite_name = BuiltIn().get_variable_value("${suite_name}")
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001597 buffer += sindent(sprint_time("Running test suite \"" + suite_name
1598 + "\".\n"), indent)
Michael Walshdb6e68a2017-05-23 17:55:31 -05001599
Michael Walsh7423c012016-10-04 10:27:21 -05001600 buffer += sindent(sprint_time() + "Running " + pgm_name + ".\n", indent)
1601 buffer += sindent(sprint_time() + "Program parameter values, etc.:\n\n",
1602 indent)
Michael Walshbec416d2016-11-10 08:54:52 -06001603 buffer += sprint_varx("command_line", ' '.join(sys.argv), 0, indent,
Michael Walsh4dbb6002019-05-17 15:51:15 -05001604 col1_width)
Michael Walsh7423c012016-10-04 10:27:21 -05001605 # We want the output to show a customized name for the pid and pgid but
1606 # we want it to look like a valid variable name. Therefore, we'll use
Michael Walshde791732016-09-06 14:25:24 -05001607 # pgm_name_var_name which was set when this module was imported.
Michael Walshbec416d2016-11-10 08:54:52 -06001608 buffer += sprint_varx(pgm_name_var_name + "_pid", os.getpid(), 0, indent,
Michael Walsh4dbb6002019-05-17 15:51:15 -05001609 col1_width)
Michael Walshbec416d2016-11-10 08:54:52 -06001610 buffer += sprint_varx(pgm_name_var_name + "_pgid", os.getpgrp(), 0, indent,
Michael Walsh4dbb6002019-05-17 15:51:15 -05001611 col1_width)
Michael Walsh86de0d22016-12-05 10:13:15 -06001612 userid_num = str(os.geteuid())
1613 try:
1614 username = os.getlogin()
1615 except OSError:
1616 if userid_num == "0":
1617 username = "root"
1618 else:
1619 username = "?"
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001620 buffer += sprint_varx("uid", userid_num + " (" + username
Michael Walsh4dbb6002019-05-17 15:51:15 -05001621 + ")", 0, indent, col1_width)
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001622 buffer += sprint_varx("gid", str(os.getgid()) + " ("
1623 + str(grp.getgrgid(os.getgid()).gr_name) + ")", 0,
Michael Walsh4dbb6002019-05-17 15:51:15 -05001624 indent, col1_width)
Michael Walshbec416d2016-11-10 08:54:52 -06001625 buffer += sprint_varx("host_name", socket.gethostname(), 0, indent,
Michael Walsh4dbb6002019-05-17 15:51:15 -05001626 col1_width)
Michael Walsh86de0d22016-12-05 10:13:15 -06001627 try:
1628 DISPLAY = os.environ['DISPLAY']
1629 except KeyError:
1630 DISPLAY = ""
Michael Walsh91fc8822019-05-29 17:34:17 -05001631 buffer += sprint_var(DISPLAY, 0, indent, col1_width)
1632 PYTHON_VERSION = os.environ.get('PYTHON_VERSION', None)
1633 if PYTHON_VERSION is not None:
Michael Walshc9a7ee72019-08-28 16:45:50 -05001634 buffer += sprint_var(PYTHON_VERSION, 0, indent, col1_width)
Michael Walsh91fc8822019-05-29 17:34:17 -05001635 PYTHON_PGM_PATH = os.environ.get('PYTHON_PGM_PATH', None)
1636 if PYTHON_PGM_PATH is not None:
Michael Walshc9a7ee72019-08-28 16:45:50 -05001637 buffer += sprint_var(PYTHON_PGM_PATH, 0, indent, col1_width)
Michael Walsh91fc8822019-05-29 17:34:17 -05001638 python_version = sys.version.replace("\n", "")
Michael Walshc9a7ee72019-08-28 16:45:50 -05001639 buffer += sprint_var(python_version, 0, indent, col1_width)
Michael Walsh91fc8822019-05-29 17:34:17 -05001640 ROBOT_VERSION = os.environ.get('ROBOT_VERSION', None)
1641 if ROBOT_VERSION is not None:
Michael Walshc9a7ee72019-08-28 16:45:50 -05001642 buffer += sprint_var(ROBOT_VERSION, 0, indent, col1_width)
Michael Walsh91fc8822019-05-29 17:34:17 -05001643 ROBOT_PGM_PATH = os.environ.get('ROBOT_PGM_PATH', None)
1644 if ROBOT_PGM_PATH is not None:
Michael Walshc9a7ee72019-08-28 16:45:50 -05001645 buffer += sprint_var(ROBOT_PGM_PATH, 0, indent, col1_width)
Michael Walsh91fc8822019-05-29 17:34:17 -05001646
Michael Walsh4dbb6002019-05-17 15:51:15 -05001647 # TODO: Add code to print caller's parms.
Michael Walshde791732016-09-06 14:25:24 -05001648
Michael Walsh7423c012016-10-04 10:27:21 -05001649 # __builtin__.arg_obj is created by the get_arg module function,
1650 # gen_get_options.
1651 try:
1652 buffer += ga.sprint_args(__builtin__.arg_obj, indent)
1653 except AttributeError:
1654 pass
1655
Michael Walshdb6e68a2017-05-23 17:55:31 -05001656 if robot_env:
1657 # Get value of global parm_list.
1658 parm_list = BuiltIn().get_variable_value("${parm_list}")
1659
1660 for parm in parm_list:
1661 parm_value = BuiltIn().get_variable_value("${" + parm + "}")
Michael Walsh4dbb6002019-05-17 15:51:15 -05001662 buffer += sprint_varx(parm, parm_value, 0, indent, col1_width)
Michael Walshdb6e68a2017-05-23 17:55:31 -05001663
1664 # Setting global program_pid.
1665 BuiltIn().set_global_variable("${program_pid}", os.getpid())
1666
Michael Walshbec416d2016-11-10 08:54:52 -06001667 if linefeed:
1668 buffer += "\n"
Michael Walshde791732016-09-06 14:25:24 -05001669
1670 return buffer
1671
Michael Walshde791732016-09-06 14:25:24 -05001672
Michael Walsh7423c012016-10-04 10:27:21 -05001673def sprint_error_report(error_text="\n",
Michael Walshdb6e68a2017-05-23 17:55:31 -05001674 indent=2,
Michael Walshe7ca2b02019-08-01 11:09:45 -05001675 format=None,
1676 stack_frame_ix=None):
Michael Walsh7423c012016-10-04 10:27:21 -05001677 r"""
1678 Return a string with a standardized report which includes the caller's
1679 error text, the call stack and the program header.
1680
Michael Walsh4dbb6002019-05-17 15:51:15 -05001681 Description of argument(s):
Michael Walsh7423c012016-10-04 10:27:21 -05001682 error_text The error text to be included in the
1683 report. The caller should include any
1684 needed linefeeds.
1685 indent The number of characters to indent each
1686 line of output.
Michael Walshdb6e68a2017-05-23 17:55:31 -05001687 format Long or short format. Long includes
1688 extras like lines of dashes, call stack,
1689 etc.
Michael Walshe7ca2b02019-08-01 11:09:45 -05001690 stack_frame_ix The index of the first stack frame which
1691 is to be shown in the print_call_stack
1692 portion of the error report.
Michael Walsh7423c012016-10-04 10:27:21 -05001693 """
1694
Michael Walshdb6e68a2017-05-23 17:55:31 -05001695 # Process input.
1696 indent = int(indent)
1697 if format is None:
1698 if robot_env:
1699 format = 'short'
1700 else:
1701 format = 'long'
1702 error_text = error_text.rstrip('\n') + '\n'
1703
1704 if format == 'short':
1705 return sprint_error(error_text)
1706
Michael Walsh7423c012016-10-04 10:27:21 -05001707 buffer = ""
1708 buffer += sprint_dashes(width=120, char="=")
1709 buffer += sprint_error(error_text)
1710 buffer += "\n"
Michael Walshe7ca2b02019-08-01 11:09:45 -05001711 if not stack_frame_ix:
1712 # Calling sprint_call_stack with stack_frame_ix of 0 causes it to
1713 # show itself and this function in the call stack. This is not
1714 # helpful to a debugger and is therefore clutter. We will adjust the
1715 # stack_frame_ix to hide that information.
1716 stack_frame_ix = 1
1717 caller_func_name = sprint_func_name(1)
1718 if caller_func_name.endswith("print_error_report"):
1719 stack_frame_ix += 1
1720 caller_func_name = sprint_func_name(2)
1721 if caller_func_name.endswith("print_error_report"):
1722 stack_frame_ix += 1
Michael Walsh7bfa9ab2018-11-16 15:24:26 -06001723 buffer += sprint_call_stack(indent, stack_frame_ix)
Michael Walsh7423c012016-10-04 10:27:21 -05001724 buffer += sprint_pgm_header(indent)
1725 buffer += sprint_dashes(width=120, char="=")
1726
1727 return buffer
1728
Michael Walsh7423c012016-10-04 10:27:21 -05001729
Michael Walsh18176322016-11-15 15:11:21 -06001730def sprint_issuing(cmd_buf,
1731 test_mode=0):
Michael Walshde791732016-09-06 14:25:24 -05001732 r"""
1733 Return a line indicating a command that the program is about to execute.
1734
1735 Sample output for a cmd_buf of "ls"
1736
1737 #(CDT) 2016/08/25 17:57:36 - Issuing: ls
Michael Walshbec416d2016-11-10 08:54:52 -06001738
Michael Walsh4dbb6002019-05-17 15:51:15 -05001739 Description of argument(s):
Michael Walshde791732016-09-06 14:25:24 -05001740 cmd_buf The command to be executed by caller.
Michael Walsh4dbb6002019-05-17 15:51:15 -05001741 test_mode With test_mode set, the output will look
Michael Walshbec416d2016-11-10 08:54:52 -06001742 like this:
1743
1744 #(CDT) 2016/08/25 17:57:36 - (test_mode) Issuing: ls
1745
Michael Walshde791732016-09-06 14:25:24 -05001746 """
1747
Michael Walshbec416d2016-11-10 08:54:52 -06001748 buffer = sprint_time()
1749 if test_mode:
1750 buffer += "(test_mode) "
Michael Walsh61c12982019-03-28 12:38:01 -05001751 if type(cmd_buf) is list:
1752 # Assume this is a robot command in the form of a list.
1753 cmd_buf = ' '.join([str(element) for element in cmd_buf])
Michael Walshbec416d2016-11-10 08:54:52 -06001754 buffer += "Issuing: " + cmd_buf + "\n"
Michael Walshde791732016-09-06 14:25:24 -05001755
1756 return buffer
1757
Michael Walshde791732016-09-06 14:25:24 -05001758
Michael Walshde791732016-09-06 14:25:24 -05001759def sprint_pgm_footer():
Michael Walshde791732016-09-06 14:25:24 -05001760 r"""
1761 Return a standardized footer that programs should print at the end of the
1762 program run. It includes useful information like total run time, etc.
1763 """
1764
1765 buffer = "\n" + sprint_time() + "Finished running " + pgm_name + ".\n\n"
1766
1767 total_time = time.time() - start_time
1768 total_time_string = "%0.6f" % total_time
1769
Michael Walsh7423c012016-10-04 10:27:21 -05001770 buffer += sprint_varx(pgm_name_var_name + "_runtime", total_time_string)
Michael Walshbec416d2016-11-10 08:54:52 -06001771 buffer += "\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001772
1773 return buffer
1774
Michael Walsh7423c012016-10-04 10:27:21 -05001775
Michael Walsh7423c012016-10-04 10:27:21 -05001776def sprint(buffer=""):
Michael Walsh7423c012016-10-04 10:27:21 -05001777 r"""
1778 Simply return the user's buffer. This function is used by the qprint and
1779 dprint functions defined dynamically below, i.e. it would not normally be
1780 called for general use.
1781
Michael Walsh4dbb6002019-05-17 15:51:15 -05001782 Description of argument(s).
Michael Walsh7423c012016-10-04 10:27:21 -05001783 buffer This will be returned to the caller.
1784 """
Michael Walshde791732016-09-06 14:25:24 -05001785
Michael Walsh95e45102018-02-09 12:44:43 -06001786 try:
1787 return str(buffer)
1788 except UnicodeEncodeError:
1789 return buffer
Michael Walshbec416d2016-11-10 08:54:52 -06001790
Michael Walshbec416d2016-11-10 08:54:52 -06001791
Michael Walshbec416d2016-11-10 08:54:52 -06001792def sprintn(buffer=""):
Michael Walshbec416d2016-11-10 08:54:52 -06001793 r"""
1794 Simply return the user's buffer with a line feed. This function is used
1795 by the qprint and dprint functions defined dynamically below, i.e. it
1796 would not normally be called for general use.
1797
Michael Walsh4dbb6002019-05-17 15:51:15 -05001798 Description of argument(s).
Michael Walshbec416d2016-11-10 08:54:52 -06001799 buffer This will be returned to the caller.
1800 """
1801
Michael Walsh95e45102018-02-09 12:44:43 -06001802 try:
1803 buffer = str(buffer) + "\n"
1804 except UnicodeEncodeError:
1805 buffer = buffer + "\n"
Michael Walshbec416d2016-11-10 08:54:52 -06001806
Michael Walshde791732016-09-06 14:25:24 -05001807 return buffer
1808
Michael Walsh168eb0f2017-12-01 15:35:32 -06001809
Michael Walshfd2733c2017-11-13 11:36:20 -06001810def gp_print(buffer,
1811 stream='stdout'):
Michael Walshfd2733c2017-11-13 11:36:20 -06001812 r"""
1813 Print the buffer using either sys.stdout.write or BuiltIn().log_to_console
1814 depending on whether we are running in a robot environment.
1815
1816 This function is intended for use only by other functions in this module.
1817
Michael Walsh4dbb6002019-05-17 15:51:15 -05001818 Description of argument(s):
Michael Walshfd2733c2017-11-13 11:36:20 -06001819 buffer The string to be printed.
1820 stream Either "stdout" or "stderr".
1821 """
1822
1823 if robot_env:
1824 BuiltIn().log_to_console(buffer, stream=stream, no_newline=True)
1825 else:
1826 if stream == "stdout":
1827 sys.stdout.write(buffer)
1828 sys.stdout.flush()
1829 else:
1830 sys.stderr.write(buffer)
1831 sys.stderr.flush()
Michael Walshde791732016-09-06 14:25:24 -05001832
1833
Michael Walsh168eb0f2017-12-01 15:35:32 -06001834def gp_log(buffer):
Michael Walsh168eb0f2017-12-01 15:35:32 -06001835 r"""
1836 Log the buffer using either python logging or BuiltIn().log depending on
1837 whether we are running in a robot environment.
1838
1839 This function is intended for use only by other functions in this module.
1840
Michael Walsh4dbb6002019-05-17 15:51:15 -05001841 Description of argument(s):
Michael Walsh168eb0f2017-12-01 15:35:32 -06001842 buffer The string to be logged.
1843 """
1844
1845 if robot_env:
1846 BuiltIn().log(buffer)
1847 else:
1848 logging.warning(buffer)
1849
1850
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001851def gp_debug_print(buffer):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001852 r"""
Michael Walshfd2733c2017-11-13 11:36:20 -06001853 Print with gp_print only if gen_print_debug is set.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001854
1855 This function is intended for use only by other functions in this module.
1856
Michael Walsh4dbb6002019-05-17 15:51:15 -05001857 Description of argument(s):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001858 buffer The string to be printed.
1859 """
1860
1861 if not gen_print_debug:
1862 return
1863
Michael Walshfd2733c2017-11-13 11:36:20 -06001864 gp_print(buffer)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001865
1866
Michael Walshb1500152017-04-12 15:42:43 -05001867def get_var_value(var_value=None,
1868 default=1,
1869 var_name=None):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001870 r"""
Michael Walshb1500152017-04-12 15:42:43 -05001871 Return either var_value, the corresponding global value or default.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001872
Michael Walshb1500152017-04-12 15:42:43 -05001873 If var_value is not None, it will simply be returned.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001874
Michael Walshb1500152017-04-12 15:42:43 -05001875 If var_value is None, this function will return the corresponding global
1876 value of the variable in question.
1877
1878 Note: For global values, if we are in a robot environment,
1879 get_variable_value will be used. Otherwise, the __builtin__ version of
1880 the variable is returned (which are set by gen_arg.py functions).
1881
1882 If there is no global value associated with the variable, default is
1883 returned.
1884
1885 This function is useful for other functions in setting default values for
1886 parameters.
1887
1888 Example use:
1889
1890 def my_func(quiet=None):
1891
1892 quiet = int(get_var_value(quiet, 0))
1893
1894 Example calls to my_func():
1895
1896 In the following example, the caller is explicitly asking to have quiet be
1897 set to 1.
1898
1899 my_func(quiet=1)
1900
1901 In the following example, quiet will be set to the global value of quiet,
1902 if defined, or to 0 (the default).
1903
1904 my_func()
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001905
Michael Walsh4dbb6002019-05-17 15:51:15 -05001906 Description of argument(s):
Michael Walshb1500152017-04-12 15:42:43 -05001907 var_value The value to be returned (if not equal to
1908 None).
1909 default The value that is returned if var_value is
1910 None and there is no corresponding global
1911 value defined.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001912 var_name The name of the variable whose value is to
Michael Walshb1500152017-04-12 15:42:43 -05001913 be returned. Under most circumstances,
1914 this value need not be provided. This
1915 function can figure out the name of the
1916 variable passed as var_value. One
1917 exception to this would be if this
1918 function is called directly from a .robot
1919 file.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001920 """
1921
Michael Walshb1500152017-04-12 15:42:43 -05001922 if var_value is not None:
1923 return var_value
1924
1925 if var_name is None:
1926 var_name = get_arg_name(None, 1, 2)
1927
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001928 if robot_env:
Michael Walshc6537442017-06-06 15:33:52 -05001929 var_value = BuiltIn().get_variable_value("${" + var_name + "}",
1930 default)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001931 else:
1932 var_value = getattr(__builtin__, var_name, default)
1933
1934 return var_value
1935
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001936
Michael Walsh052ff812018-05-18 16:09:09 -05001937def get_stack_var(var_name,
1938 default="",
1939 init_stack_ix=2):
Michael Walsh052ff812018-05-18 16:09:09 -05001940 r"""
Michael Walshc9a7ee72019-08-28 16:45:50 -05001941 Starting with the caller's stack level, search upward in the call stack
Michael Walsh052ff812018-05-18 16:09:09 -05001942 for a variable named var_name and return its value. If the variable
Michael Walshc9a7ee72019-08-28 16:45:50 -05001943 cannot be found in the stack, attempt to get the global value. If the
1944 variable still cannot be found, return default.
Michael Walsh052ff812018-05-18 16:09:09 -05001945
1946 Example code:
1947
1948 def func12():
1949 my_loc_var1 = get_stack_var('my_var1', "default value")
1950
1951 def func11():
1952 my_var1 = 11
1953 func12()
1954
1955 In this example, get_stack_var will find the value of my_var1 in func11's
1956 stack and will therefore return the value 11. Therefore, my_loc_var1
1957 would get set to 11.
1958
1959 Description of argument(s):
1960 var_name The name of the variable to be searched
1961 for.
1962 default The value to return if the the variable
1963 cannot be found.
1964 init_stack_ix The initial stack index from which to
1965 begin the search. 0 would be the index of
1966 this func1tion ("get_stack_var"), 1 would
1967 be the index of the function calling this
1968 function, etc.
1969 """
1970
Michael Walsh6f0362c2019-03-25 14:05:14 -05001971 work_around_inspect_stack_cwd_failure()
Michael Walshc9a7ee72019-08-28 16:45:50 -05001972 default = get_var_value(var_name=var_name, default=default)
Michael Walsh052ff812018-05-18 16:09:09 -05001973 return next((frame[0].f_locals[var_name]
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001974 for frame in inspect.stack()[init_stack_ix:]
1975 if var_name in frame[0].f_locals), default)
Michael Walsh052ff812018-05-18 16:09:09 -05001976
1977
Michael Walsh82acf002017-05-04 14:33:05 -05001978# hidden_text is a list of passwords which are to be replaced with asterisks
1979# by print functions defined in this module.
1980hidden_text = []
1981# password_regex is created based on the contents of hidden_text.
1982password_regex = ""
1983
1984
Michael Walsh82acf002017-05-04 14:33:05 -05001985def register_passwords(*args):
Michael Walsh82acf002017-05-04 14:33:05 -05001986 r"""
1987 Register one or more passwords which are to be hidden in output produced
1988 by the print functions in this module.
1989
1990 Note: Blank password values are NOT registered. They are simply ignored.
1991
1992 Description of argument(s):
1993 args One or more password values. If a given
1994 password value is already registered, this
1995 function will simply do nothing.
1996 """
1997
1998 global hidden_text
1999 global password_regex
2000
2001 for password in args:
2002 if password == "":
2003 break
2004 if password in hidden_text:
2005 break
2006
2007 # Place the password into the hidden_text list.
2008 hidden_text.append(password)
2009 # Create a corresponding password regular expression. Escape regex
2010 # special characters too.
2011 password_regex = '(' +\
2012 '|'.join([re.escape(x) for x in hidden_text]) + ')'
2013
Michael Walsh82acf002017-05-04 14:33:05 -05002014
Michael Walsh82acf002017-05-04 14:33:05 -05002015def replace_passwords(buffer):
Michael Walsh82acf002017-05-04 14:33:05 -05002016 r"""
2017 Return the buffer but with all registered passwords replaced by a string
2018 of asterisks.
2019
2020
2021 Description of argument(s):
2022 buffer The string to be returned but with
2023 passwords replaced.
2024 """
2025
2026 global password_regex
2027
2028 if int(os.environ.get("DEBUG_SHOW_PASSWORDS", "0")):
2029 return buffer
2030
2031 if password_regex == "":
2032 # No passwords to replace.
2033 return buffer
2034
2035 return re.sub(password_regex, "********", buffer)
2036
Michael Walshfd2733c2017-11-13 11:36:20 -06002037
2038def create_print_wrapper_funcs(func_names,
2039 stderr_func_names,
Michael Walsh61c12982019-03-28 12:38:01 -05002040 replace_dict,
2041 func_prefix=""):
Michael Walshfd2733c2017-11-13 11:36:20 -06002042 r"""
2043 Generate code for print wrapper functions and return the generated code as
2044 a string.
2045
2046 To illustrate, suppose there is a "print_foo_bar" function in the
2047 func_names list.
2048 This function will...
2049 - Expect that there is an sprint_foo_bar function already in existence.
2050 - Create a print_foo_bar function which calls sprint_foo_bar and prints
Michael Walshfaafa9c2018-06-27 16:39:31 -05002051 the result.
Michael Walshfd2733c2017-11-13 11:36:20 -06002052 - Create a qprint_foo_bar function which calls upon sprint_foo_bar only if
Michael Walshfaafa9c2018-06-27 16:39:31 -05002053 global value quiet is 0.
Michael Walshfd2733c2017-11-13 11:36:20 -06002054 - Create a dprint_foo_bar function which calls upon sprint_foo_bar only if
Michael Walshfaafa9c2018-06-27 16:39:31 -05002055 global value debug is 1.
Michael Walshfd2733c2017-11-13 11:36:20 -06002056
2057 Also, code will be generated to define aliases for each function as well.
2058 Each alias will be created by replacing "print_" in the function name with
2059 "p" For example, the alias for print_foo_bar will be pfoo_bar.
2060
2061 Description of argument(s):
2062 func_names A list of functions for which print
2063 wrapper function code is to be generated.
2064 stderr_func_names A list of functions whose generated code
2065 should print to stderr rather than to
2066 stdout.
2067 replace_dict Please see the create_func_def_string
2068 function in wrap_utils.py for details on
2069 this parameter. This parameter will be
2070 passed directly to create_func_def_string.
Michael Walsh61c12982019-03-28 12:38:01 -05002071 func_prefix Prefix to be pre-pended to the generated
2072 function name.
Michael Walshfd2733c2017-11-13 11:36:20 -06002073 """
2074
2075 buffer = ""
2076
2077 for func_name in func_names:
2078 if func_name in stderr_func_names:
2079 replace_dict['output_stream'] = "stderr"
2080 else:
2081 replace_dict['output_stream'] = "stdout"
2082
2083 s_func_name = "s" + func_name
2084 q_func_name = "q" + func_name
2085 d_func_name = "d" + func_name
2086
2087 # We don't want to try to redefine the "print" function, thus the
2088 # following if statement.
2089 if func_name != "print":
Michael Walsh61c12982019-03-28 12:38:01 -05002090 func_def = create_func_def_string(s_func_name,
2091 func_prefix + func_name,
Michael Walshfd2733c2017-11-13 11:36:20 -06002092 print_func_template,
2093 replace_dict)
2094 buffer += func_def
2095
Michael Walsh61c12982019-03-28 12:38:01 -05002096 func_def = create_func_def_string(s_func_name,
2097 func_prefix + "q" + func_name,
Michael Walshfd2733c2017-11-13 11:36:20 -06002098 qprint_func_template, replace_dict)
2099 buffer += func_def
2100
Michael Walsh61c12982019-03-28 12:38:01 -05002101 func_def = create_func_def_string(s_func_name,
2102 func_prefix + "d" + func_name,
Michael Walshfd2733c2017-11-13 11:36:20 -06002103 dprint_func_template, replace_dict)
2104 buffer += func_def
2105
Michael Walsh61c12982019-03-28 12:38:01 -05002106 func_def = create_func_def_string(s_func_name,
2107 func_prefix + "l" + func_name,
Michael Walsh168eb0f2017-12-01 15:35:32 -06002108 lprint_func_template, replace_dict)
2109 buffer += func_def
2110
Michael Walshfd2733c2017-11-13 11:36:20 -06002111 # Create abbreviated aliases (e.g. spvar is an alias for sprint_var).
2112 alias = re.sub("print_", "p", func_name)
2113 alias = re.sub("print", "p", alias)
Michael Walsh61c12982019-03-28 12:38:01 -05002114 prefixes = [func_prefix + "", "s", func_prefix + "q",
2115 func_prefix + "d", func_prefix + "l"]
Michael Walshfd2733c2017-11-13 11:36:20 -06002116 for prefix in prefixes:
2117 if alias == "p":
2118 continue
2119 func_def = prefix + alias + " = " + prefix + func_name
2120 buffer += func_def + "\n"
2121
2122 return buffer
Michael Walsh82acf002017-05-04 14:33:05 -05002123
2124
Michael Walshde791732016-09-06 14:25:24 -05002125# In the following section of code, we will dynamically create print versions
2126# for each of the sprint functions defined above. So, for example, where we
2127# have an sprint_time() function defined above that returns the time to the
Michael Walsh7423c012016-10-04 10:27:21 -05002128# caller in a string, we will create a corresponding print_time() function
2129# that will print that string directly to stdout.
Michael Walshde791732016-09-06 14:25:24 -05002130
Michael Walsh61c12982019-03-28 12:38:01 -05002131# It can be complicated to follow what's being created below. Here is an
Michael Walshfd2733c2017-11-13 11:36:20 -06002132# example of the print_time() function that will be created:
Michael Walshde791732016-09-06 14:25:24 -05002133
Michael Walshfd2733c2017-11-13 11:36:20 -06002134# def print_time(buffer=''):
Michael Walsh61c12982019-03-28 12:38:01 -05002135# gp_print(replace_passwords(sprint_time(buffer=buffer)), stream='stdout')
2136
2137# For each print function defined below, there will also be a qprint, a
2138# dprint and an lprint version defined (e.g. qprint_time, dprint_time,
2139# lprint_time).
2140
2141# The q version of each print function will only print if the quiet variable
2142# is 0.
2143# The d version of each print function will only print if the debug variable
2144# is 1.
2145# The l version of each print function will print the contents as log data.
2146# For conventional programs, this means use of the logging module. For robot
2147# programs it means use of the BuiltIn().log() function.
Michael Walshde791732016-09-06 14:25:24 -05002148
Michael Walshfd2733c2017-11-13 11:36:20 -06002149# Templates for the various print wrapper functions.
2150print_func_template = \
2151 [
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05002152 " <mod_qualifier>gp_print(<mod_qualifier>replace_passwords("
2153 + "<call_line>), stream='<output_stream>')"
Michael Walshfd2733c2017-11-13 11:36:20 -06002154 ]
2155
2156qprint_func_template = \
2157 [
Michael Walshc9a7ee72019-08-28 16:45:50 -05002158 " quiet = <mod_qualifier>get_stack_var(\"quiet\", 0)",
Michael Walsh589ae762019-03-19 13:22:39 -05002159 " if int(quiet): return"
Michael Walshfd2733c2017-11-13 11:36:20 -06002160 ] + print_func_template
2161
2162dprint_func_template = \
2163 [
Michael Walshc9a7ee72019-08-28 16:45:50 -05002164 " debug = <mod_qualifier>get_stack_var(\"debug\", 0)",
Michael Walsh589ae762019-03-19 13:22:39 -05002165 " if not int(debug): return"
Michael Walshfd2733c2017-11-13 11:36:20 -06002166 ] + print_func_template
2167
Michael Walsh168eb0f2017-12-01 15:35:32 -06002168lprint_func_template = \
2169 [
Michael Walsh61c12982019-03-28 12:38:01 -05002170 " <mod_qualifier>set_last_seconds_ix(<mod_qualifier>"
2171 + "lprint_last_seconds_ix())",
2172 " <mod_qualifier>gp_log(<mod_qualifier>replace_passwords"
2173 + "(<call_line>))",
2174 " <mod_qualifier>set_last_seconds_ix(<mod_qualifier>"
2175 + "standard_print_last_seconds_ix())"
Michael Walsh168eb0f2017-12-01 15:35:32 -06002176 ]
2177
Michael Walsh81c02342018-01-05 15:43:28 -06002178replace_dict = {'output_stream': 'stdout', 'mod_qualifier': ''}
Michael Walshfd2733c2017-11-13 11:36:20 -06002179
Michael Walsh61c12982019-03-28 12:38:01 -05002180gp_debug_print("robot_env: " + str(robot_env) + "\n")
Michael Walshde791732016-09-06 14:25:24 -05002181
2182# func_names contains a list of all print functions which should be created
2183# from their sprint counterparts.
2184func_names = ['print_time', 'print_timen', 'print_error', 'print_varx',
Michael Walsh18176322016-11-15 15:11:21 -06002185 'print_var', 'print_vars', 'print_dashes', 'indent',
2186 'print_call_stack', 'print_func_name', 'print_executing',
2187 'print_pgm_header', 'print_issuing', 'print_pgm_footer',
2188 'print_error_report', 'print', 'printn']
Michael Walshde791732016-09-06 14:25:24 -05002189
Michael Walsh2ee77cd2017-03-08 11:50:17 -06002190# stderr_func_names is a list of functions whose output should go to stderr
2191# rather than stdout.
2192stderr_func_names = ['print_error', 'print_error_report']
Michael Walshde791732016-09-06 14:25:24 -05002193
Michael Walshfd2733c2017-11-13 11:36:20 -06002194func_defs = create_print_wrapper_funcs(func_names, stderr_func_names,
2195 replace_dict)
2196gp_debug_print(func_defs)
2197exec(func_defs)