blob: 8f7a6abb6e4603b1c828c5b8d68c077b81020fae [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',
859 'show_type']
860
861
862def create_fmt_definition():
863 r"""
864 Create a string consisting of function-definition code that can be
865 executed to create constant fmt definition functions.
866
867 These functions can be used by callers of sprint_var/sprint_varx to set
868 the fmt argument correctly.
869
870 Likewise, the sprint_varx function will use these generated functions to
871 correctly interpret the fmt argument.
872
873 Example output from this function:
874
875 def hexa():
876 return 0x00000001
877 def octal_fmt():
878 return 0x00000002
879 etc.
880 """
881
882 buffer = ""
883 bits = 0x00000001
884 for fmt_name in valid_fmts():
885 buffer += "def " + fmt_name + "():\n"
886 buffer += " return " + "0x%08x" % bits + "\n"
887 bits = bits << 1
888 return buffer
889
890
891# Dynamically create fmt definitions (for use with the fmt argument of
892# sprint_varx function):
893exec(create_fmt_definition())
894
895
Michael Walsh6bed4d32019-07-10 14:11:30 -0500896def terse():
897 r"""
898 Constant function to return fmt value of 0.
899
900 Now that sprint_varx defaults to printing in terse format, the terse
901 option is deprecated. This function is here for backward compatibility.
902
903 Once the repo has been purged of the use of terse, this function can be
904 removed.
905 """
906
907 return 0
908
909
Michael Walsh4dbb6002019-05-17 15:51:15 -0500910def list_pop(a_list, index=0, default=None):
911 r"""
912 Pop the list entry indicated by the index and return the entry. If no
913 such entry exists, return default.
914
915 Note that the list passed to this function will be modified.
916
917 Description of argument(s):
918 a_list The list from which an entry is to be
919 popped.
920 index The index indicating which entry is to be
921 popped.
922 default The value to be returned if there is no
923 entry at the given index location.
924 """
925 try:
926 return a_list.pop(index)
927 except IndexError:
928 return default
929
930
931def parse_fmt(fmt):
932 r"""
933 Parse the fmt argument and return a tuple consisting of a format and a
934 child format.
935
936 This function was written for use by the sprint_varx function defined in
937 this module.
938
939 When sprint_varx is processing a multi-level object such as a list or
940 dictionary (which in turn may contain other lists or dictionaries), it
941 will use the fmt value to dictate the print formatting of the current
942 level and the child_fmt value to dictate the print formatting of
943 subordinate levels. Consider the following example:
944
945 python code example:
946
947 ord_dict = \
948 collections.OrderedDict([
949 ('one', 1),
950 ('two', 2),
951 ('sub',
952 collections.OrderedDict([
953 ('three', 3), ('four', 4)]))])
954
955 print_var(ord_dict)
956
957 This would generate the following output:
958
959 ord_dict:
Michael Walsh6bed4d32019-07-10 14:11:30 -0500960 [one]: 1
961 [two]: 2
962 [sub]:
963 [three]: 3
964 [four]: 4
Michael Walsh4dbb6002019-05-17 15:51:15 -0500965
966 The first level in this example is the line that simply says "ord_dict".
967 The second level is comprised of the dictionary entries with the keys
968 'one', 'two' and 'sub'. The third level is comprised of the last 2 lines
969 (i.e. printed values 3 and 4).
970
971 Given the data structure shown above, the programmer could code the
Michael Walsh6bed4d32019-07-10 14:11:30 -0500972 following where fmt is a simple integer value set by calling the verbose()
Michael Walsh4dbb6002019-05-17 15:51:15 -0500973 function.
974
Michael Walsh6bed4d32019-07-10 14:11:30 -0500975 print_var(ord_dict, fmt=verbose())
Michael Walsh4dbb6002019-05-17 15:51:15 -0500976
977 The output would look like this:
978
979 ord_dict:
Michael Walsh6bed4d32019-07-10 14:11:30 -0500980 ord_dict[one]: 1
981 ord_dict[two]: 2
982 ord_dict[sub]:
983 ord_dict[sub][three]: 3
984 ord_dict[sub][four]: 4
Michael Walsh4dbb6002019-05-17 15:51:15 -0500985
Michael Walsh6bed4d32019-07-10 14:11:30 -0500986 Note the verbose format where the name of the object ("ord_dict") is
987 repeated on every line.
Michael Walsh4dbb6002019-05-17 15:51:15 -0500988
989 If the programmer wishes to get more granular with the fmt argument,
990 he/she can specify it as a list where each entry corresponds to a level of
991 the object being printed. The last such list entry governs the print
992 formatting of all subordinate parts of the given object.
993
994 Look at each of the following code examples and their corresponding
995 output. See how the show_type() formatting affects the printing depending
996 on which position it occupies in the fmt list argument:
997
998 print_var(ord_dict, fmt=[show_type()])
999
1000 ord_dict: <collections.OrderedDict>
1001 ord_dict[one]: 1 <int>
1002 ord_dict[two]: 2 <int>
1003 ord_dict[sub]: <collections.OrderedDict>
1004 ord_dict[sub][three]: 3 <int>
1005 ord_dict[sub][four]: 4 <int>
1006
1007 print_var(ord_dict, fmt=[0, show_type()])
1008
1009 ord_dict:
1010 ord_dict[one]: 1 <int>
1011 ord_dict[two]: 2 <int>
1012 ord_dict[sub]: <collections.OrderedDict>
1013 ord_dict[sub][three]: 3 <int>
1014 ord_dict[sub][four]: 4 <int>
1015
1016 print_var(ord_dict, fmt=[0, 0, show_type()])
1017
1018 ord_dict:
1019 ord_dict[one]: 1
1020 ord_dict[two]: 2
1021 ord_dict[sub]:
1022 ord_dict[sub][three]: 3 <int>
1023 ord_dict[sub][four]: 4 <int>
1024
1025 Description of argument(s):
1026 fmt The format argument such as is passed to
1027 sprint_varx. This argument may be an
1028 integer or a list of integers. See the
1029 prolog of sprint_varx for more details.
1030 """
1031
1032 # Make a deep copy of the fmt argument in order to avoid modifying the
1033 # caller's fmt value when it is a list.
1034 fmt = copy.deepcopy(fmt)
1035 try:
1036 # Assume fmt is a list. Pop the first element from the list.
1037 first_element = list_pop(fmt, index=0, default=0)
1038 # Return the first list element along with either 1) the remainder of
1039 # the fmt list if not null or 2) another copy of the first element.
1040 return first_element, fmt if len(fmt) else first_element
1041 except AttributeError:
1042 # fmt is not a list so treat it as a simple integer value.
1043 return fmt, fmt
1044
1045
Michael Walshde791732016-09-06 14:25:24 -05001046def sprint_varx(var_name,
1047 var_value,
Michael Walsh4dbb6002019-05-17 15:51:15 -05001048 fmt=0,
1049 indent=dft_indent,
1050 col1_width=dft_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -05001051 trailing_char="\n",
Michael Walsh4dbb6002019-05-17 15:51:15 -05001052 key_list=None,
1053 delim=":"):
Michael Walshde791732016-09-06 14:25:24 -05001054 r"""
Michael Walsh4dbb6002019-05-17 15:51:15 -05001055 Print the var name/value passed to it. If the caller lets col1_width
Michael Walshde791732016-09-06 14:25:24 -05001056 default, the printing lines up nicely with output generated by the
1057 print_time functions.
1058
1059 Note that the sprint_var function (defined below) can be used to call this
1060 function so that the programmer does not need to pass the var_name.
1061 sprint_var will figure out the var_name. The sprint_var function is the
1062 one that would normally be used by the general user.
1063
1064 For example, the following python code:
1065
1066 first_name = "Mike"
1067 print_time("Doing this...\n")
1068 print_varx("first_name", first_name)
1069 print_time("Doing that...\n")
1070
1071 Will generate output like this:
1072
1073 #(CDT) 2016/08/10 17:34:42.847374 - 0.001285 - Doing this...
1074 first_name: Mike
1075 #(CDT) 2016/08/10 17:34:42.847510 - 0.000136 - Doing that...
1076
1077 This function recognizes several complex types of data such as dict, list
1078 or tuple.
1079
1080 For example, the following python code:
1081
1082 my_dict = dict(one=1, two=2, three=3)
1083 print_var(my_dict)
1084
1085 Will generate the following output:
1086
1087 my_dict:
1088 my_dict[three]: 3
1089 my_dict[two]: 2
1090 my_dict[one]: 1
1091
Michael Walsh4dbb6002019-05-17 15:51:15 -05001092 Description of argument(s).
Michael Walshde791732016-09-06 14:25:24 -05001093 var_name The name of the variable to be printed.
1094 var_value The value of the variable to be printed.
Michael Walsh4dbb6002019-05-17 15:51:15 -05001095 fmt A bit map to dictate the format of the
1096 output. For printing multi-level objects
1097 like lists and dictionaries, this argument
1098 may also be a list of bit maps. The first
1099 list element pertains to the highest level
1100 of output, the second element pertains to
1101 the 2nd level of output, etc. The last
1102 element in the list pertains to all
1103 subordinate levels. The bits can be set
1104 using the dynamically created functionhs
1105 above. Example: sprint_varx("var1", var1,
Michael Walsh6bed4d32019-07-10 14:11:30 -05001106 fmt=verbose()). Note that these values
1107 can be OR'ed together: print_var(var1,
1108 hexa() | verbose()). If the caller ORs
1109 mutually exclusive bits (hexa() |
1110 octal()), behavior is not guaranteed. The
1111 following features are supported:
Michael Walsh4dbb6002019-05-17 15:51:15 -05001112 hexa Print all integer values in hexadecimal
1113 format.
1114 octal Print all integer values in octal format.
1115 binary Print all integer values in binary format.
1116 blank For blank string values, print "<blank>"
1117 instead of an actual blank.
Michael Walsh6bed4d32019-07-10 14:11:30 -05001118 verbose For structured values like dictionaries,
1119 lists, etc. repeat the name of the
Michael Walsh4dbb6002019-05-17 15:51:15 -05001120 variable on each line to the right of the
1121 key or subscript value. Example: print
Michael Walsh6bed4d32019-07-10 14:11:30 -05001122 "my_dict[key1]" instead of just "[key1]".
Michael Walsh4dbb6002019-05-17 15:51:15 -05001123 quote_keys Quote dictionary keys in the output.
1124 Example: my_dict['key1'] instead of
1125 my_dict[key1].
1126 show_type Show the type of the data in angled
1127 brackets just to the right of the data.
1128 indent The number of spaces to indent the output.
1129 col1_width The width of the output column containing
Michael Walshde791732016-09-06 14:25:24 -05001130 the variable name. The default value of
1131 this is adjusted so that the var_value
1132 lines up with text printed via the
1133 print_time function.
Michael Walsh7423c012016-10-04 10:27:21 -05001134 trailing_char The character to be used at the end of the
1135 returned string. The default value is a
1136 line feed.
Michael Walshd2869032018-03-22 16:12:11 -05001137 key_list A list of which dictionary keys should be
1138 printed. All others keys will be skipped.
1139 Each value in key_list will be regarded
1140 as a regular expression and it will be
1141 regarded as anchored to the beginning and
1142 ends of the dictionary key being
1143 referenced. For example if key_list is
1144 ["one", "two"], the resulting regex used
1145 will be "^one|two$", i.e. only keys "one"
1146 and "two" from the var_value dictionary
1147 will be printed. As another example, if
1148 the caller were to specify a key_list of
1149 ["one.*"], then only dictionary keys whose
1150 names begin with "one" will be printed.
1151 Note: This argument pertains only to
1152 var_values which are dictionaries.
Michael Walsh4dbb6002019-05-17 15:51:15 -05001153 delim The value to be used to delimit the
1154 variable name from the variable value in
1155 the output.
Michael Walsh7423c012016-10-04 10:27:21 -05001156 """
Michael Walshde791732016-09-06 14:25:24 -05001157
Michael Walsh4dbb6002019-05-17 15:51:15 -05001158 fmt, child_fmt = parse_fmt(fmt)
1159
1160 if fmt & show_type():
1161 type_str = "<" + str(type(var_value)).split("'")[1] + ">"
1162 # Compose object type categories.
1163 int_types = get_int_types()
1164 string_types = get_string_types()
1165 simple_types = int_types + string_types + (float, bool, type, type(None))
1166 # Determine the type.
1167 if type(var_value) in simple_types:
Michael Walshde791732016-09-06 14:25:24 -05001168 # The data type is simple in the sense that it has no subordinate
1169 # parts.
Michael Walsh4dbb6002019-05-17 15:51:15 -05001170 # Adjust col1_width.
1171 col1_width = col1_width - indent
1172 # Set default value for value_format.
1173 value_format = "%s"
1174 # Process format requests.
1175 if type(var_value) in int_types:
1176 # Process format values pertaining to int types.
1177 if fmt & hexa():
Michael Walsh3f248272018-06-01 13:59:35 -05001178 num_hex_digits = max(dft_num_hex_digits(),
1179 get_req_num_hex_digits(var_value))
1180 # Convert a negative number to its positive twos complement
1181 # for proper printing. For example, instead of printing -1 as
1182 # "0x-000000000000001" it will be printed as
1183 # "0xffffffffffffffff".
1184 var_value = var_value & (2 ** (num_hex_digits * 4) - 1)
1185 value_format = "0x%0" + str(num_hex_digits) + "x"
Michael Walsh4dbb6002019-05-17 15:51:15 -05001186 elif fmt & octal():
1187 value_format = "0o%016o"
1188 elif fmt & binary():
1189 num_digits, remainder = \
1190 divmod(max(bit_length(var_value), 1), 8)
1191 num_digits *= 8
1192 if remainder:
1193 num_digits += 8
1194 num_digits += 2
1195 value_format = '#0' + str(num_digits) + 'b'
1196 var_value = format(var_value, value_format)
1197 value_format = "%s"
1198 elif type(var_value) in string_types:
1199 # Process format values pertaining to string types.
1200 if fmt & blank() and var_value == "":
1201 value_format = "%s"
1202 var_value = "<blank>"
1203 elif type(var_value) is type:
1204 var_value = str(var_value).split("'")[1]
1205 format_string = "%" + str(indent) + "s%-" + str(col1_width) + "s" \
1206 + value_format
1207 if fmt & show_type():
1208 if var_value != "":
1209 format_string += " "
1210 format_string += type_str
1211 format_string += trailing_char
Michael Walsh6bed4d32019-07-10 14:11:30 -05001212 if not (fmt & verbose()):
Michael Walsh4dbb6002019-05-17 15:51:15 -05001213 # Strip everything leading up to the first left square brace.
1214 var_name = re.sub(r".*\[", "[", var_name)
Michael Walsh3383e652017-09-01 17:10:59 -05001215 if value_format == "0x%08x":
Michael Walsh4dbb6002019-05-17 15:51:15 -05001216 return format_string % ("", str(var_name) + delim,
Michael Walsh3383e652017-09-01 17:10:59 -05001217 var_value & 0xffffffff)
1218 else:
Michael Walsh4dbb6002019-05-17 15:51:15 -05001219 return format_string % ("", str(var_name) + delim, var_value)
Michael Walshde791732016-09-06 14:25:24 -05001220 else:
1221 # The data type is complex in the sense that it has subordinate parts.
Michael Walsh6bed4d32019-07-10 14:11:30 -05001222 if not (fmt & verbose()):
Michael Walsh4dbb6002019-05-17 15:51:15 -05001223 # Strip everything leading up to the first square brace.
1224 loc_var_name = re.sub(r".*\[", "[", var_name)
1225 else:
1226 loc_var_name = var_name
1227 format_string = "%" + str(indent) + "s%s\n"
1228 buffer = format_string % ("", loc_var_name + ":")
1229 if fmt & show_type():
1230 buffer = buffer.replace("\n", " " + type_str + "\n")
1231 indent += 2
Michael Walsh7423c012016-10-04 10:27:21 -05001232 try:
1233 length = len(var_value)
1234 except TypeError:
Michael Walsh23e7f492017-01-10 11:34:47 -06001235 length = 0
Michael Walsh7423c012016-10-04 10:27:21 -05001236 ix = 0
1237 loc_trailing_char = "\n"
Michael Walsh8646d962019-01-21 14:36:13 -06001238 if is_dict(var_value):
Michael Walsh4dbb6002019-05-17 15:51:15 -05001239 if type(child_fmt) is list:
1240 child_quote_keys = (child_fmt[0] & quote_keys())
1241 else:
1242 child_quote_keys = (child_fmt & quote_keys())
Michael Walsh37762f92018-08-07 14:59:18 -05001243 for key, value in var_value.items():
Michael Walshd2869032018-03-22 16:12:11 -05001244 if key_list is not None:
1245 key_list_regex = "^" + "|".join(key_list) + "$"
1246 if not re.match(key_list_regex, key):
1247 continue
Michael Walsh7423c012016-10-04 10:27:21 -05001248 ix += 1
1249 if ix == length:
1250 loc_trailing_char = trailing_char
Michael Walsh4dbb6002019-05-17 15:51:15 -05001251 if child_quote_keys:
1252 key = "'" + key + "'"
1253 key = "[" + str(key) + "]"
1254 buffer += sprint_varx(var_name + key, value, child_fmt, indent,
1255 col1_width, loc_trailing_char, key_list)
Michael Walsh7423c012016-10-04 10:27:21 -05001256 elif type(var_value) in (list, tuple, set):
Michael Walshde791732016-09-06 14:25:24 -05001257 for key, value in enumerate(var_value):
Michael Walsh7423c012016-10-04 10:27:21 -05001258 ix += 1
1259 if ix == length:
1260 loc_trailing_char = trailing_char
Michael Walsh4dbb6002019-05-17 15:51:15 -05001261 key = "[" + str(key) + "]"
1262 buffer += sprint_varx(var_name + key, value, child_fmt, indent,
1263 col1_width, loc_trailing_char, key_list)
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001264 elif isinstance(var_value, argparse.Namespace):
Michael Walshde791732016-09-06 14:25:24 -05001265 for key in var_value.__dict__:
Michael Walsh7423c012016-10-04 10:27:21 -05001266 ix += 1
1267 if ix == length:
1268 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -05001269 cmd_buf = "buffer += sprint_varx(var_name + \".\" + str(key)" \
Michael Walsh4dbb6002019-05-17 15:51:15 -05001270 + ", var_value." + key + ", child_fmt, indent," \
1271 + " col1_width, loc_trailing_char, key_list)"
Michael Walshde791732016-09-06 14:25:24 -05001272 exec(cmd_buf)
1273 else:
1274 var_type = type(var_value).__name__
1275 func_name = sys._getframe().f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -05001276 var_value = "<" + var_type + " type not supported by " + \
1277 func_name + "()>"
Michael Walshde791732016-09-06 14:25:24 -05001278 value_format = "%s"
Michael Walsh4dbb6002019-05-17 15:51:15 -05001279 indent -= 2
1280 # Adjust col1_width.
1281 col1_width = col1_width - indent
1282 format_string = "%" + str(indent) + "s%-" \
1283 + str(col1_width) + "s" + value_format + trailing_char
Michael Walsh0f2ea5f2017-02-20 15:55:00 -06001284 return format_string % ("", str(var_name) + ":", var_value)
Michael Walsh23e7f492017-01-10 11:34:47 -06001285
Michael Walshde791732016-09-06 14:25:24 -05001286 return buffer
1287
1288 return ""
1289
Michael Walshde791732016-09-06 14:25:24 -05001290
Michael Walsh4dbb6002019-05-17 15:51:15 -05001291def sprint_var(*args, **kwargs):
Michael Walshde791732016-09-06 14:25:24 -05001292 r"""
Michael Walsh4dbb6002019-05-17 15:51:15 -05001293 Figure out the name of the first argument for the caller and then call
Michael Walshde791732016-09-06 14:25:24 -05001294 sprint_varx with it. Therefore, the following 2 calls are equivalent:
1295 sprint_varx("var1", var1)
1296 sprint_var(var1)
Michael Walsh4dbb6002019-05-17 15:51:15 -05001297
1298 See sprint_varx for description of arguments.
Michael Walshde791732016-09-06 14:25:24 -05001299 """
1300
Michael Walshde791732016-09-06 14:25:24 -05001301 stack_frame = 2
Michael Walsh7423c012016-10-04 10:27:21 -05001302 caller_func_name = sprint_func_name(2)
1303 if caller_func_name.endswith("print_var"):
Michael Walshde791732016-09-06 14:25:24 -05001304 stack_frame += 1
Michael Walsh4dbb6002019-05-17 15:51:15 -05001305 # Get the name of the first variable passed to this function.
Michael Walshde791732016-09-06 14:25:24 -05001306 var_name = get_arg_name(None, 1, stack_frame)
Michael Walsh4dbb6002019-05-17 15:51:15 -05001307 return sprint_varx(var_name, *args, **kwargs)
Michael Walshde791732016-09-06 14:25:24 -05001308
1309
Michael Walsh4dbb6002019-05-17 15:51:15 -05001310def sprint_vars(*args, **kwargs):
Michael Walsh18176322016-11-15 15:11:21 -06001311 r"""
1312 Sprint the values of one or more variables.
1313
Michael Walsh4dbb6002019-05-17 15:51:15 -05001314 Description of argument(s):
1315 args The variable values which are to be
1316 printed.
1317 kwargs See sprint_varx (above) for description of
1318 additional arguments.
Michael Walsh18176322016-11-15 15:11:21 -06001319 """
1320
Michael Walsh18176322016-11-15 15:11:21 -06001321 stack_frame = 2
1322 caller_func_name = sprint_func_name(2)
1323 if caller_func_name.endswith("print_vars"):
1324 stack_frame += 1
1325
Michael Walsh18176322016-11-15 15:11:21 -06001326 buffer = ""
Michael Walsh4dbb6002019-05-17 15:51:15 -05001327 arg_num = 1
1328 for var_value in args:
1329 var_name = get_arg_name(None, arg_num, stack_frame)
1330 buffer += sprint_varx(var_name, var_value, **kwargs)
1331 arg_num += 1
Michael Walsh18176322016-11-15 15:11:21 -06001332
1333 return buffer
1334
Michael Walsh18176322016-11-15 15:11:21 -06001335
Michael Walsh4dbb6002019-05-17 15:51:15 -05001336def sprint_dashes(indent=dft_indent,
Michael Walsh7423c012016-10-04 10:27:21 -05001337 width=80,
1338 line_feed=1,
1339 char="-"):
Michael Walshde791732016-09-06 14:25:24 -05001340 r"""
1341 Return a string of dashes to the caller.
1342
Michael Walsh4dbb6002019-05-17 15:51:15 -05001343 Description of argument(s):
Michael Walshde791732016-09-06 14:25:24 -05001344 indent The number of characters to indent the
1345 output.
1346 width The width of the string of dashes.
1347 line_feed Indicates whether the output should end
1348 with a line feed.
Michael Walsh7423c012016-10-04 10:27:21 -05001349 char The character to be repeated in the output
1350 string.
Michael Walshde791732016-09-06 14:25:24 -05001351 """
1352
Michael Walsh7423c012016-10-04 10:27:21 -05001353 width = int(width)
Michael Walsh23e7f492017-01-10 11:34:47 -06001354 buffer = " " * int(indent) + char * width
Michael Walshde791732016-09-06 14:25:24 -05001355 if line_feed:
1356 buffer += "\n"
1357
1358 return buffer
1359
Michael Walshde791732016-09-06 14:25:24 -05001360
Michael Walsh7423c012016-10-04 10:27:21 -05001361def sindent(text="",
1362 indent=0):
Michael Walsh7423c012016-10-04 10:27:21 -05001363 r"""
1364 Pre-pend the specified number of characters to the text string (i.e.
1365 indent it) and return it.
1366
Michael Walsh4dbb6002019-05-17 15:51:15 -05001367 Description of argument(s):
Michael Walsh7423c012016-10-04 10:27:21 -05001368 text The string to be indented.
1369 indent The number of characters to indent the
1370 string.
1371 """
1372
1373 format_string = "%" + str(indent) + "s%s"
1374 buffer = format_string % ("", text)
1375
1376 return buffer
1377
Michael Walsh7423c012016-10-04 10:27:21 -05001378
Michael Walsh662e13b2019-03-01 15:54:08 -06001379func_line_style_std = None
1380func_line_style_short = 1
1381
1382
1383def sprint_func_line(stack_frame, style=None):
Michael Walsh47aa2a42018-12-10 15:06:02 -06001384 r"""
1385 For the given stack_frame, return a formatted string containing the
1386 function name and all its arguments.
1387
1388 Example:
1389
1390 func1(last_name = 'walsh', first_name = 'mikey')
1391
1392 Description of argument(s):
1393 stack_frame A stack frame (such as is returned by
1394 inspect.stack()).
Michael Walsh662e13b2019-03-01 15:54:08 -06001395 style Indicates the style or formatting of the
1396 result string. Acceptable values are
1397 shown above.
1398
1399 Description of styles:
1400 func_line_style_std The standard formatting.
1401 func_line_style_short 1) The self parm (associated with methods)
1402 will be dropped. 2) The args and kwargs
1403 values will be treated as special. In
1404 both cases the arg name ('args' or
1405 'kwargs') will be dropped and only the
1406 values will be shown.
Michael Walsh47aa2a42018-12-10 15:06:02 -06001407 """
1408
1409 func_name = str(stack_frame[3])
1410 if func_name == "?":
1411 # "?" is the name used when code is not in a function.
1412 func_name = "(none)"
1413
1414 if func_name == "<module>":
1415 # If the func_name is the "main" program, we simply get the command
1416 # line call string.
1417 func_and_args = ' '.join(sys.argv)
1418 else:
1419 # Get the program arguments.
1420 (args, varargs, keywords, locals) =\
1421 inspect.getargvalues(stack_frame[0])
1422
1423 args_list = []
1424 for arg_name in filter(None, args + [varargs, keywords]):
1425 # Get the arg value from frame locals.
1426 arg_value = locals[arg_name]
1427 if arg_name == 'self':
Michael Walsh662e13b2019-03-01 15:54:08 -06001428 if style == func_line_style_short:
1429 continue
Michael Walsh47aa2a42018-12-10 15:06:02 -06001430 # Manipulations to improve output for class methods.
1431 func_name = arg_value.__class__.__name__ + "." + func_name
1432 args_list.append(arg_name + " = <self>")
Michael Walsh662e13b2019-03-01 15:54:08 -06001433 elif (style == func_line_style_short
1434 and arg_name == 'args'
1435 and type(arg_value) in (list, tuple)):
1436 if len(arg_value) == 0:
1437 continue
1438 args_list.append(repr(', '.join(arg_value)))
1439 elif (style == func_line_style_short
1440 and arg_name == 'kwargs'
1441 and type(arg_value) is dict):
1442 for key, value in arg_value.items():
1443 args_list.append(key + "=" + repr(value))
Michael Walsh47aa2a42018-12-10 15:06:02 -06001444 else:
1445 args_list.append(arg_name + " = " + repr(arg_value))
1446 args_str = "(" + ', '.join(map(str, args_list)) + ")"
1447
1448 # Now we need to print this in a nicely-wrapped way.
1449 func_and_args = func_name + args_str
1450
1451 return func_and_args
1452
1453
Michael Walsh7423c012016-10-04 10:27:21 -05001454def sprint_call_stack(indent=0,
Michael Walsh662e13b2019-03-01 15:54:08 -06001455 stack_frame_ix=0,
1456 style=None):
Michael Walshde791732016-09-06 14:25:24 -05001457 r"""
1458 Return a call stack report for the given point in the program with line
1459 numbers, function names and function parameters and arguments.
1460
1461 Sample output:
1462
1463 -------------------------------------------------------------------------
1464 Python function call stack
1465
1466 Line # Function name and arguments
1467 ------ ------------------------------------------------------------------
Michael Walsh47aa2a42018-12-10 15:06:02 -06001468 424 sprint_call_stack()
1469 4 print_call_stack()
1470 31 func1(last_name = 'walsh', first_name = 'mikey')
Michael Walshde791732016-09-06 14:25:24 -05001471 59 /tmp/scr5.py
1472 -------------------------------------------------------------------------
1473
Michael Walsh4dbb6002019-05-17 15:51:15 -05001474 Description of argument(s):
Michael Walshde791732016-09-06 14:25:24 -05001475 indent The number of characters to indent each
1476 line of output.
1477 stack_frame_ix The index of the first stack frame which
1478 is to be returned.
Michael Walsh662e13b2019-03-01 15:54:08 -06001479 style See the sprint_line_func prolog above for
1480 details.
Michael Walshde791732016-09-06 14:25:24 -05001481 """
1482
1483 buffer = ""
Michael Walsh7423c012016-10-04 10:27:21 -05001484 buffer += sprint_dashes(indent)
1485 buffer += sindent("Python function call stack\n\n", indent)
1486 buffer += sindent("Line # Function name and arguments\n", indent)
1487 buffer += sprint_dashes(indent, 6, 0) + " " + sprint_dashes(0, 73)
Michael Walshde791732016-09-06 14:25:24 -05001488
1489 # Grab the current program stack.
Michael Walsh6f0362c2019-03-25 14:05:14 -05001490 work_around_inspect_stack_cwd_failure()
Michael Walshde791732016-09-06 14:25:24 -05001491 current_stack = inspect.stack()
1492
1493 # Process each frame in turn.
1494 format_string = "%6s %s\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001495 ix = 0
Michael Walshde791732016-09-06 14:25:24 -05001496 for stack_frame in current_stack:
Michael Walsh7423c012016-10-04 10:27:21 -05001497 if ix < stack_frame_ix:
1498 ix += 1
1499 continue
Michael Walsh4dbb6002019-05-17 15:51:15 -05001500 # Make the line number shown to be the line where one finds the line
Michael Walsh23e7f492017-01-10 11:34:47 -06001501 # shown.
1502 try:
1503 line_num = str(current_stack[ix + 1][2])
1504 except IndexError:
1505 line_num = ""
Michael Walsh662e13b2019-03-01 15:54:08 -06001506 func_and_args = sprint_func_line(stack_frame, style=style)
Michael Walshde791732016-09-06 14:25:24 -05001507
Michael Walsh23e7f492017-01-10 11:34:47 -06001508 buffer += sindent(format_string % (line_num, func_and_args), indent)
Michael Walsh7423c012016-10-04 10:27:21 -05001509 ix += 1
Michael Walshde791732016-09-06 14:25:24 -05001510
Michael Walsh7423c012016-10-04 10:27:21 -05001511 buffer += sprint_dashes(indent)
Michael Walshde791732016-09-06 14:25:24 -05001512
1513 return buffer
1514
Michael Walshde791732016-09-06 14:25:24 -05001515
Michael Walsh662e13b2019-03-01 15:54:08 -06001516def sprint_executing(stack_frame_ix=None, style=None):
Michael Walshde791732016-09-06 14:25:24 -05001517 r"""
1518 Print a line indicating what function is executing and with what parameter
1519 values. This is useful for debugging.
1520
1521 Sample output:
1522
Michael Walsh47aa2a42018-12-10 15:06:02 -06001523 #(CDT) 2016/08/25 17:54:27 - Executing: func1(x = 1)
Michael Walshde791732016-09-06 14:25:24 -05001524
Michael Walsh4dbb6002019-05-17 15:51:15 -05001525 Description of argument(s):
Michael Walshde791732016-09-06 14:25:24 -05001526 stack_frame_ix The index of the stack frame whose
1527 function info should be returned. If the
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001528 caller does not specify a value, this
Michael Walshde791732016-09-06 14:25:24 -05001529 function will set the value to 1 which is
1530 the index of the caller's stack frame. If
1531 the caller is the wrapper function
1532 "print_executing", this function will bump
1533 it up by 1.
Michael Walsh662e13b2019-03-01 15:54:08 -06001534 style See the sprint_line_func prolog above for
1535 details.
Michael Walshde791732016-09-06 14:25:24 -05001536 """
1537
1538 # If user wants default stack_frame_ix.
1539 if stack_frame_ix is None:
1540 func_name = sys._getframe().f_code.co_name
1541 caller_func_name = sys._getframe(1).f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -05001542 if caller_func_name.endswith(func_name[1:]):
Michael Walshde791732016-09-06 14:25:24 -05001543 stack_frame_ix = 2
1544 else:
1545 stack_frame_ix = 1
1546
Michael Walsh6f0362c2019-03-25 14:05:14 -05001547 work_around_inspect_stack_cwd_failure()
Michael Walshde791732016-09-06 14:25:24 -05001548 stack_frame = inspect.stack()[stack_frame_ix]
1549
Michael Walsh662e13b2019-03-01 15:54:08 -06001550 func_and_args = sprint_func_line(stack_frame, style)
Michael Walshde791732016-09-06 14:25:24 -05001551
1552 return sprint_time() + "Executing: " + func_and_args + "\n"
1553
Michael Walshde791732016-09-06 14:25:24 -05001554
Michael Walshbec416d2016-11-10 08:54:52 -06001555def sprint_pgm_header(indent=0,
1556 linefeed=1):
Michael Walshde791732016-09-06 14:25:24 -05001557 r"""
1558 Return a standardized header that programs should print at the beginning
1559 of the run. It includes useful information like command line, pid,
1560 userid, program parameters, etc.
1561
Michael Walsh4dbb6002019-05-17 15:51:15 -05001562 Description of argument(s):
Michael Walsh7423c012016-10-04 10:27:21 -05001563 indent The number of characters to indent each
1564 line of output.
Michael Walshbec416d2016-11-10 08:54:52 -06001565 linefeed Indicates whether a line feed be included
1566 at the beginning and end of the report.
Michael Walshde791732016-09-06 14:25:24 -05001567 """
1568
Michael Walsh4dbb6002019-05-17 15:51:15 -05001569 col1_width = dft_col1_width + indent
Michael Walshbec416d2016-11-10 08:54:52 -06001570
1571 buffer = ""
1572 if linefeed:
1573 buffer = "\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001574
Michael Walshdb6e68a2017-05-23 17:55:31 -05001575 if robot_env:
1576 suite_name = BuiltIn().get_variable_value("${suite_name}")
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001577 buffer += sindent(sprint_time("Running test suite \"" + suite_name
1578 + "\".\n"), indent)
Michael Walshdb6e68a2017-05-23 17:55:31 -05001579
Michael Walsh7423c012016-10-04 10:27:21 -05001580 buffer += sindent(sprint_time() + "Running " + pgm_name + ".\n", indent)
1581 buffer += sindent(sprint_time() + "Program parameter values, etc.:\n\n",
1582 indent)
Michael Walshbec416d2016-11-10 08:54:52 -06001583 buffer += sprint_varx("command_line", ' '.join(sys.argv), 0, indent,
Michael Walsh4dbb6002019-05-17 15:51:15 -05001584 col1_width)
Michael Walsh7423c012016-10-04 10:27:21 -05001585 # We want the output to show a customized name for the pid and pgid but
1586 # we want it to look like a valid variable name. Therefore, we'll use
Michael Walshde791732016-09-06 14:25:24 -05001587 # pgm_name_var_name which was set when this module was imported.
Michael Walshbec416d2016-11-10 08:54:52 -06001588 buffer += sprint_varx(pgm_name_var_name + "_pid", os.getpid(), 0, indent,
Michael Walsh4dbb6002019-05-17 15:51:15 -05001589 col1_width)
Michael Walshbec416d2016-11-10 08:54:52 -06001590 buffer += sprint_varx(pgm_name_var_name + "_pgid", os.getpgrp(), 0, indent,
Michael Walsh4dbb6002019-05-17 15:51:15 -05001591 col1_width)
Michael Walsh86de0d22016-12-05 10:13:15 -06001592 userid_num = str(os.geteuid())
1593 try:
1594 username = os.getlogin()
1595 except OSError:
1596 if userid_num == "0":
1597 username = "root"
1598 else:
1599 username = "?"
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001600 buffer += sprint_varx("uid", userid_num + " (" + username
Michael Walsh4dbb6002019-05-17 15:51:15 -05001601 + ")", 0, indent, col1_width)
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001602 buffer += sprint_varx("gid", str(os.getgid()) + " ("
1603 + str(grp.getgrgid(os.getgid()).gr_name) + ")", 0,
Michael Walsh4dbb6002019-05-17 15:51:15 -05001604 indent, col1_width)
Michael Walshbec416d2016-11-10 08:54:52 -06001605 buffer += sprint_varx("host_name", socket.gethostname(), 0, indent,
Michael Walsh4dbb6002019-05-17 15:51:15 -05001606 col1_width)
Michael Walsh86de0d22016-12-05 10:13:15 -06001607 try:
1608 DISPLAY = os.environ['DISPLAY']
1609 except KeyError:
1610 DISPLAY = ""
Michael Walsh91fc8822019-05-29 17:34:17 -05001611 buffer += sprint_var(DISPLAY, 0, indent, col1_width)
1612 PYTHON_VERSION = os.environ.get('PYTHON_VERSION', None)
1613 if PYTHON_VERSION is not None:
1614 buffer += sprint_var(PYTHON_VERSION)
1615 PYTHON_PGM_PATH = os.environ.get('PYTHON_PGM_PATH', None)
1616 if PYTHON_PGM_PATH is not None:
1617 buffer += sprint_var(PYTHON_PGM_PATH)
1618 python_version = sys.version.replace("\n", "")
1619 buffer += sprint_var(python_version)
1620 ROBOT_VERSION = os.environ.get('ROBOT_VERSION', None)
1621 if ROBOT_VERSION is not None:
1622 buffer += sprint_var(ROBOT_VERSION)
1623 ROBOT_PGM_PATH = os.environ.get('ROBOT_PGM_PATH', None)
1624 if ROBOT_PGM_PATH is not None:
1625 buffer += sprint_var(ROBOT_PGM_PATH)
1626
Michael Walsh4dbb6002019-05-17 15:51:15 -05001627 # TODO: Add code to print caller's parms.
Michael Walshde791732016-09-06 14:25:24 -05001628
Michael Walsh7423c012016-10-04 10:27:21 -05001629 # __builtin__.arg_obj is created by the get_arg module function,
1630 # gen_get_options.
1631 try:
1632 buffer += ga.sprint_args(__builtin__.arg_obj, indent)
1633 except AttributeError:
1634 pass
1635
Michael Walshdb6e68a2017-05-23 17:55:31 -05001636 if robot_env:
1637 # Get value of global parm_list.
1638 parm_list = BuiltIn().get_variable_value("${parm_list}")
1639
1640 for parm in parm_list:
1641 parm_value = BuiltIn().get_variable_value("${" + parm + "}")
Michael Walsh4dbb6002019-05-17 15:51:15 -05001642 buffer += sprint_varx(parm, parm_value, 0, indent, col1_width)
Michael Walshdb6e68a2017-05-23 17:55:31 -05001643
1644 # Setting global program_pid.
1645 BuiltIn().set_global_variable("${program_pid}", os.getpid())
1646
Michael Walshbec416d2016-11-10 08:54:52 -06001647 if linefeed:
1648 buffer += "\n"
Michael Walshde791732016-09-06 14:25:24 -05001649
1650 return buffer
1651
Michael Walshde791732016-09-06 14:25:24 -05001652
Michael Walsh7423c012016-10-04 10:27:21 -05001653def sprint_error_report(error_text="\n",
Michael Walshdb6e68a2017-05-23 17:55:31 -05001654 indent=2,
1655 format=None):
Michael Walsh7423c012016-10-04 10:27:21 -05001656 r"""
1657 Return a string with a standardized report which includes the caller's
1658 error text, the call stack and the program header.
1659
Michael Walsh4dbb6002019-05-17 15:51:15 -05001660 Description of argument(s):
Michael Walsh7423c012016-10-04 10:27:21 -05001661 error_text The error text to be included in the
1662 report. The caller should include any
1663 needed linefeeds.
1664 indent The number of characters to indent each
1665 line of output.
Michael Walshdb6e68a2017-05-23 17:55:31 -05001666 format Long or short format. Long includes
1667 extras like lines of dashes, call stack,
1668 etc.
Michael Walsh7423c012016-10-04 10:27:21 -05001669 """
1670
Michael Walshdb6e68a2017-05-23 17:55:31 -05001671 # Process input.
1672 indent = int(indent)
1673 if format is None:
1674 if robot_env:
1675 format = 'short'
1676 else:
1677 format = 'long'
1678 error_text = error_text.rstrip('\n') + '\n'
1679
1680 if format == 'short':
1681 return sprint_error(error_text)
1682
Michael Walsh7423c012016-10-04 10:27:21 -05001683 buffer = ""
1684 buffer += sprint_dashes(width=120, char="=")
1685 buffer += sprint_error(error_text)
1686 buffer += "\n"
1687 # Calling sprint_call_stack with stack_frame_ix of 0 causes it to show
1688 # itself and this function in the call stack. This is not helpful to a
1689 # debugger and is therefore clutter. We will adjust the stack_frame_ix to
1690 # hide that information.
Michael Walsh9c75f672017-09-12 17:11:35 -05001691 stack_frame_ix = 1
Michael Walsh7423c012016-10-04 10:27:21 -05001692 caller_func_name = sprint_func_name(2)
1693 if caller_func_name.endswith("print_error_report"):
1694 stack_frame_ix += 1
Michael Walsh7bfa9ab2018-11-16 15:24:26 -06001695 buffer += sprint_call_stack(indent, stack_frame_ix)
Michael Walsh7423c012016-10-04 10:27:21 -05001696 buffer += sprint_pgm_header(indent)
1697 buffer += sprint_dashes(width=120, char="=")
1698
1699 return buffer
1700
Michael Walsh7423c012016-10-04 10:27:21 -05001701
Michael Walsh18176322016-11-15 15:11:21 -06001702def sprint_issuing(cmd_buf,
1703 test_mode=0):
Michael Walshde791732016-09-06 14:25:24 -05001704 r"""
1705 Return a line indicating a command that the program is about to execute.
1706
1707 Sample output for a cmd_buf of "ls"
1708
1709 #(CDT) 2016/08/25 17:57:36 - Issuing: ls
Michael Walshbec416d2016-11-10 08:54:52 -06001710
Michael Walsh4dbb6002019-05-17 15:51:15 -05001711 Description of argument(s):
Michael Walshde791732016-09-06 14:25:24 -05001712 cmd_buf The command to be executed by caller.
Michael Walsh4dbb6002019-05-17 15:51:15 -05001713 test_mode With test_mode set, the output will look
Michael Walshbec416d2016-11-10 08:54:52 -06001714 like this:
1715
1716 #(CDT) 2016/08/25 17:57:36 - (test_mode) Issuing: ls
1717
Michael Walshde791732016-09-06 14:25:24 -05001718 """
1719
Michael Walshbec416d2016-11-10 08:54:52 -06001720 buffer = sprint_time()
1721 if test_mode:
1722 buffer += "(test_mode) "
Michael Walsh61c12982019-03-28 12:38:01 -05001723 if type(cmd_buf) is list:
1724 # Assume this is a robot command in the form of a list.
1725 cmd_buf = ' '.join([str(element) for element in cmd_buf])
Michael Walshbec416d2016-11-10 08:54:52 -06001726 buffer += "Issuing: " + cmd_buf + "\n"
Michael Walshde791732016-09-06 14:25:24 -05001727
1728 return buffer
1729
Michael Walshde791732016-09-06 14:25:24 -05001730
Michael Walshde791732016-09-06 14:25:24 -05001731def sprint_pgm_footer():
Michael Walshde791732016-09-06 14:25:24 -05001732 r"""
1733 Return a standardized footer that programs should print at the end of the
1734 program run. It includes useful information like total run time, etc.
1735 """
1736
1737 buffer = "\n" + sprint_time() + "Finished running " + pgm_name + ".\n\n"
1738
1739 total_time = time.time() - start_time
1740 total_time_string = "%0.6f" % total_time
1741
Michael Walsh7423c012016-10-04 10:27:21 -05001742 buffer += sprint_varx(pgm_name_var_name + "_runtime", total_time_string)
Michael Walshbec416d2016-11-10 08:54:52 -06001743 buffer += "\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001744
1745 return buffer
1746
Michael Walsh7423c012016-10-04 10:27:21 -05001747
Michael Walsh7423c012016-10-04 10:27:21 -05001748def sprint(buffer=""):
Michael Walsh7423c012016-10-04 10:27:21 -05001749 r"""
1750 Simply return the user's buffer. This function is used by the qprint and
1751 dprint functions defined dynamically below, i.e. it would not normally be
1752 called for general use.
1753
Michael Walsh4dbb6002019-05-17 15:51:15 -05001754 Description of argument(s).
Michael Walsh7423c012016-10-04 10:27:21 -05001755 buffer This will be returned to the caller.
1756 """
Michael Walshde791732016-09-06 14:25:24 -05001757
Michael Walsh95e45102018-02-09 12:44:43 -06001758 try:
1759 return str(buffer)
1760 except UnicodeEncodeError:
1761 return buffer
Michael Walshbec416d2016-11-10 08:54:52 -06001762
Michael Walshbec416d2016-11-10 08:54:52 -06001763
Michael Walshbec416d2016-11-10 08:54:52 -06001764def sprintn(buffer=""):
Michael Walshbec416d2016-11-10 08:54:52 -06001765 r"""
1766 Simply return the user's buffer with a line feed. This function is used
1767 by the qprint and dprint functions defined dynamically below, i.e. it
1768 would not normally be called for general use.
1769
Michael Walsh4dbb6002019-05-17 15:51:15 -05001770 Description of argument(s).
Michael Walshbec416d2016-11-10 08:54:52 -06001771 buffer This will be returned to the caller.
1772 """
1773
Michael Walsh95e45102018-02-09 12:44:43 -06001774 try:
1775 buffer = str(buffer) + "\n"
1776 except UnicodeEncodeError:
1777 buffer = buffer + "\n"
Michael Walshbec416d2016-11-10 08:54:52 -06001778
Michael Walshde791732016-09-06 14:25:24 -05001779 return buffer
1780
Michael Walsh168eb0f2017-12-01 15:35:32 -06001781
Michael Walshfd2733c2017-11-13 11:36:20 -06001782def gp_print(buffer,
1783 stream='stdout'):
Michael Walshfd2733c2017-11-13 11:36:20 -06001784 r"""
1785 Print the buffer using either sys.stdout.write or BuiltIn().log_to_console
1786 depending on whether we are running in a robot environment.
1787
1788 This function is intended for use only by other functions in this module.
1789
Michael Walsh4dbb6002019-05-17 15:51:15 -05001790 Description of argument(s):
Michael Walshfd2733c2017-11-13 11:36:20 -06001791 buffer The string to be printed.
1792 stream Either "stdout" or "stderr".
1793 """
1794
1795 if robot_env:
1796 BuiltIn().log_to_console(buffer, stream=stream, no_newline=True)
1797 else:
1798 if stream == "stdout":
1799 sys.stdout.write(buffer)
1800 sys.stdout.flush()
1801 else:
1802 sys.stderr.write(buffer)
1803 sys.stderr.flush()
Michael Walshde791732016-09-06 14:25:24 -05001804
1805
Michael Walsh168eb0f2017-12-01 15:35:32 -06001806def gp_log(buffer):
Michael Walsh168eb0f2017-12-01 15:35:32 -06001807 r"""
1808 Log the buffer using either python logging or BuiltIn().log depending on
1809 whether we are running in a robot environment.
1810
1811 This function is intended for use only by other functions in this module.
1812
Michael Walsh4dbb6002019-05-17 15:51:15 -05001813 Description of argument(s):
Michael Walsh168eb0f2017-12-01 15:35:32 -06001814 buffer The string to be logged.
1815 """
1816
1817 if robot_env:
1818 BuiltIn().log(buffer)
1819 else:
1820 logging.warning(buffer)
1821
1822
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001823def gp_debug_print(buffer):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001824 r"""
Michael Walshfd2733c2017-11-13 11:36:20 -06001825 Print with gp_print only if gen_print_debug is set.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001826
1827 This function is intended for use only by other functions in this module.
1828
Michael Walsh4dbb6002019-05-17 15:51:15 -05001829 Description of argument(s):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001830 buffer The string to be printed.
1831 """
1832
1833 if not gen_print_debug:
1834 return
1835
Michael Walshfd2733c2017-11-13 11:36:20 -06001836 gp_print(buffer)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001837
1838
Michael Walshb1500152017-04-12 15:42:43 -05001839def get_var_value(var_value=None,
1840 default=1,
1841 var_name=None):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001842 r"""
Michael Walshb1500152017-04-12 15:42:43 -05001843 Return either var_value, the corresponding global value or default.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001844
Michael Walshb1500152017-04-12 15:42:43 -05001845 If var_value is not None, it will simply be returned.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001846
Michael Walshb1500152017-04-12 15:42:43 -05001847 If var_value is None, this function will return the corresponding global
1848 value of the variable in question.
1849
1850 Note: For global values, if we are in a robot environment,
1851 get_variable_value will be used. Otherwise, the __builtin__ version of
1852 the variable is returned (which are set by gen_arg.py functions).
1853
1854 If there is no global value associated with the variable, default is
1855 returned.
1856
1857 This function is useful for other functions in setting default values for
1858 parameters.
1859
1860 Example use:
1861
1862 def my_func(quiet=None):
1863
1864 quiet = int(get_var_value(quiet, 0))
1865
1866 Example calls to my_func():
1867
1868 In the following example, the caller is explicitly asking to have quiet be
1869 set to 1.
1870
1871 my_func(quiet=1)
1872
1873 In the following example, quiet will be set to the global value of quiet,
1874 if defined, or to 0 (the default).
1875
1876 my_func()
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001877
Michael Walsh4dbb6002019-05-17 15:51:15 -05001878 Description of argument(s):
Michael Walshb1500152017-04-12 15:42:43 -05001879 var_value The value to be returned (if not equal to
1880 None).
1881 default The value that is returned if var_value is
1882 None and there is no corresponding global
1883 value defined.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001884 var_name The name of the variable whose value is to
Michael Walshb1500152017-04-12 15:42:43 -05001885 be returned. Under most circumstances,
1886 this value need not be provided. This
1887 function can figure out the name of the
1888 variable passed as var_value. One
1889 exception to this would be if this
1890 function is called directly from a .robot
1891 file.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001892 """
1893
Michael Walshb1500152017-04-12 15:42:43 -05001894 if var_value is not None:
1895 return var_value
1896
1897 if var_name is None:
1898 var_name = get_arg_name(None, 1, 2)
1899
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001900 if robot_env:
Michael Walshc6537442017-06-06 15:33:52 -05001901 var_value = BuiltIn().get_variable_value("${" + var_name + "}",
1902 default)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001903 else:
1904 var_value = getattr(__builtin__, var_name, default)
1905
1906 return var_value
1907
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001908
Michael Walsh052ff812018-05-18 16:09:09 -05001909def get_stack_var(var_name,
1910 default="",
1911 init_stack_ix=2):
Michael Walsh052ff812018-05-18 16:09:09 -05001912 r"""
1913 Starting with the caller's stack level, search upward in the call stack,
1914 for a variable named var_name and return its value. If the variable
1915 cannot be found, return default.
1916
1917 Example code:
1918
1919 def func12():
1920 my_loc_var1 = get_stack_var('my_var1', "default value")
1921
1922 def func11():
1923 my_var1 = 11
1924 func12()
1925
1926 In this example, get_stack_var will find the value of my_var1 in func11's
1927 stack and will therefore return the value 11. Therefore, my_loc_var1
1928 would get set to 11.
1929
1930 Description of argument(s):
1931 var_name The name of the variable to be searched
1932 for.
1933 default The value to return if the the variable
1934 cannot be found.
1935 init_stack_ix The initial stack index from which to
1936 begin the search. 0 would be the index of
1937 this func1tion ("get_stack_var"), 1 would
1938 be the index of the function calling this
1939 function, etc.
1940 """
1941
Michael Walsh6f0362c2019-03-25 14:05:14 -05001942 work_around_inspect_stack_cwd_failure()
Michael Walsh052ff812018-05-18 16:09:09 -05001943 return next((frame[0].f_locals[var_name]
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001944 for frame in inspect.stack()[init_stack_ix:]
1945 if var_name in frame[0].f_locals), default)
Michael Walsh052ff812018-05-18 16:09:09 -05001946
1947
Michael Walsh82acf002017-05-04 14:33:05 -05001948# hidden_text is a list of passwords which are to be replaced with asterisks
1949# by print functions defined in this module.
1950hidden_text = []
1951# password_regex is created based on the contents of hidden_text.
1952password_regex = ""
1953
1954
Michael Walsh82acf002017-05-04 14:33:05 -05001955def register_passwords(*args):
Michael Walsh82acf002017-05-04 14:33:05 -05001956 r"""
1957 Register one or more passwords which are to be hidden in output produced
1958 by the print functions in this module.
1959
1960 Note: Blank password values are NOT registered. They are simply ignored.
1961
1962 Description of argument(s):
1963 args One or more password values. If a given
1964 password value is already registered, this
1965 function will simply do nothing.
1966 """
1967
1968 global hidden_text
1969 global password_regex
1970
1971 for password in args:
1972 if password == "":
1973 break
1974 if password in hidden_text:
1975 break
1976
1977 # Place the password into the hidden_text list.
1978 hidden_text.append(password)
1979 # Create a corresponding password regular expression. Escape regex
1980 # special characters too.
1981 password_regex = '(' +\
1982 '|'.join([re.escape(x) for x in hidden_text]) + ')'
1983
Michael Walsh82acf002017-05-04 14:33:05 -05001984
Michael Walsh82acf002017-05-04 14:33:05 -05001985def replace_passwords(buffer):
Michael Walsh82acf002017-05-04 14:33:05 -05001986 r"""
1987 Return the buffer but with all registered passwords replaced by a string
1988 of asterisks.
1989
1990
1991 Description of argument(s):
1992 buffer The string to be returned but with
1993 passwords replaced.
1994 """
1995
1996 global password_regex
1997
1998 if int(os.environ.get("DEBUG_SHOW_PASSWORDS", "0")):
1999 return buffer
2000
2001 if password_regex == "":
2002 # No passwords to replace.
2003 return buffer
2004
2005 return re.sub(password_regex, "********", buffer)
2006
Michael Walshfd2733c2017-11-13 11:36:20 -06002007
2008def create_print_wrapper_funcs(func_names,
2009 stderr_func_names,
Michael Walsh61c12982019-03-28 12:38:01 -05002010 replace_dict,
2011 func_prefix=""):
Michael Walshfd2733c2017-11-13 11:36:20 -06002012 r"""
2013 Generate code for print wrapper functions and return the generated code as
2014 a string.
2015
2016 To illustrate, suppose there is a "print_foo_bar" function in the
2017 func_names list.
2018 This function will...
2019 - Expect that there is an sprint_foo_bar function already in existence.
2020 - Create a print_foo_bar function which calls sprint_foo_bar and prints
Michael Walshfaafa9c2018-06-27 16:39:31 -05002021 the result.
Michael Walshfd2733c2017-11-13 11:36:20 -06002022 - Create a qprint_foo_bar function which calls upon sprint_foo_bar only if
Michael Walshfaafa9c2018-06-27 16:39:31 -05002023 global value quiet is 0.
Michael Walshfd2733c2017-11-13 11:36:20 -06002024 - Create a dprint_foo_bar function which calls upon sprint_foo_bar only if
Michael Walshfaafa9c2018-06-27 16:39:31 -05002025 global value debug is 1.
Michael Walshfd2733c2017-11-13 11:36:20 -06002026
2027 Also, code will be generated to define aliases for each function as well.
2028 Each alias will be created by replacing "print_" in the function name with
2029 "p" For example, the alias for print_foo_bar will be pfoo_bar.
2030
2031 Description of argument(s):
2032 func_names A list of functions for which print
2033 wrapper function code is to be generated.
2034 stderr_func_names A list of functions whose generated code
2035 should print to stderr rather than to
2036 stdout.
2037 replace_dict Please see the create_func_def_string
2038 function in wrap_utils.py for details on
2039 this parameter. This parameter will be
2040 passed directly to create_func_def_string.
Michael Walsh61c12982019-03-28 12:38:01 -05002041 func_prefix Prefix to be pre-pended to the generated
2042 function name.
Michael Walshfd2733c2017-11-13 11:36:20 -06002043 """
2044
2045 buffer = ""
2046
2047 for func_name in func_names:
2048 if func_name in stderr_func_names:
2049 replace_dict['output_stream'] = "stderr"
2050 else:
2051 replace_dict['output_stream'] = "stdout"
2052
2053 s_func_name = "s" + func_name
2054 q_func_name = "q" + func_name
2055 d_func_name = "d" + func_name
2056
2057 # We don't want to try to redefine the "print" function, thus the
2058 # following if statement.
2059 if func_name != "print":
Michael Walsh61c12982019-03-28 12:38:01 -05002060 func_def = create_func_def_string(s_func_name,
2061 func_prefix + func_name,
Michael Walshfd2733c2017-11-13 11:36:20 -06002062 print_func_template,
2063 replace_dict)
2064 buffer += func_def
2065
Michael Walsh61c12982019-03-28 12:38:01 -05002066 func_def = create_func_def_string(s_func_name,
2067 func_prefix + "q" + func_name,
Michael Walshfd2733c2017-11-13 11:36:20 -06002068 qprint_func_template, replace_dict)
2069 buffer += func_def
2070
Michael Walsh61c12982019-03-28 12:38:01 -05002071 func_def = create_func_def_string(s_func_name,
2072 func_prefix + "d" + func_name,
Michael Walshfd2733c2017-11-13 11:36:20 -06002073 dprint_func_template, replace_dict)
2074 buffer += func_def
2075
Michael Walsh61c12982019-03-28 12:38:01 -05002076 func_def = create_func_def_string(s_func_name,
2077 func_prefix + "l" + func_name,
Michael Walsh168eb0f2017-12-01 15:35:32 -06002078 lprint_func_template, replace_dict)
2079 buffer += func_def
2080
Michael Walshfd2733c2017-11-13 11:36:20 -06002081 # Create abbreviated aliases (e.g. spvar is an alias for sprint_var).
2082 alias = re.sub("print_", "p", func_name)
2083 alias = re.sub("print", "p", alias)
Michael Walsh61c12982019-03-28 12:38:01 -05002084 prefixes = [func_prefix + "", "s", func_prefix + "q",
2085 func_prefix + "d", func_prefix + "l"]
Michael Walshfd2733c2017-11-13 11:36:20 -06002086 for prefix in prefixes:
2087 if alias == "p":
2088 continue
2089 func_def = prefix + alias + " = " + prefix + func_name
2090 buffer += func_def + "\n"
2091
2092 return buffer
Michael Walsh82acf002017-05-04 14:33:05 -05002093
2094
Michael Walshde791732016-09-06 14:25:24 -05002095# In the following section of code, we will dynamically create print versions
2096# for each of the sprint functions defined above. So, for example, where we
2097# have an sprint_time() function defined above that returns the time to the
Michael Walsh7423c012016-10-04 10:27:21 -05002098# caller in a string, we will create a corresponding print_time() function
2099# that will print that string directly to stdout.
Michael Walshde791732016-09-06 14:25:24 -05002100
Michael Walsh61c12982019-03-28 12:38:01 -05002101# It can be complicated to follow what's being created below. Here is an
Michael Walshfd2733c2017-11-13 11:36:20 -06002102# example of the print_time() function that will be created:
Michael Walshde791732016-09-06 14:25:24 -05002103
Michael Walshfd2733c2017-11-13 11:36:20 -06002104# def print_time(buffer=''):
Michael Walsh61c12982019-03-28 12:38:01 -05002105# gp_print(replace_passwords(sprint_time(buffer=buffer)), stream='stdout')
2106
2107# For each print function defined below, there will also be a qprint, a
2108# dprint and an lprint version defined (e.g. qprint_time, dprint_time,
2109# lprint_time).
2110
2111# The q version of each print function will only print if the quiet variable
2112# is 0.
2113# The d version of each print function will only print if the debug variable
2114# is 1.
2115# The l version of each print function will print the contents as log data.
2116# For conventional programs, this means use of the logging module. For robot
2117# programs it means use of the BuiltIn().log() function.
Michael Walshde791732016-09-06 14:25:24 -05002118
Michael Walshfd2733c2017-11-13 11:36:20 -06002119# Templates for the various print wrapper functions.
2120print_func_template = \
2121 [
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05002122 " <mod_qualifier>gp_print(<mod_qualifier>replace_passwords("
2123 + "<call_line>), stream='<output_stream>')"
Michael Walshfd2733c2017-11-13 11:36:20 -06002124 ]
2125
2126qprint_func_template = \
2127 [
Michael Walsh589ae762019-03-19 13:22:39 -05002128 " quiet_default = <mod_qualifier>get_var_value(None, 0, \"quiet\")",
2129 " quiet = <mod_qualifier>get_stack_var(\"quiet\", quiet_default)",
2130 " if int(quiet): return"
Michael Walshfd2733c2017-11-13 11:36:20 -06002131 ] + print_func_template
2132
2133dprint_func_template = \
2134 [
Michael Walsh589ae762019-03-19 13:22:39 -05002135 " debug_default = <mod_qualifier>get_var_value(None, 0, \"debug\")",
2136 " debug = <mod_qualifier>get_stack_var(\"debug\", debug_default)",
2137 " if not int(debug): return"
Michael Walshfd2733c2017-11-13 11:36:20 -06002138 ] + print_func_template
2139
Michael Walsh168eb0f2017-12-01 15:35:32 -06002140lprint_func_template = \
2141 [
Michael Walsh61c12982019-03-28 12:38:01 -05002142 " <mod_qualifier>set_last_seconds_ix(<mod_qualifier>"
2143 + "lprint_last_seconds_ix())",
2144 " <mod_qualifier>gp_log(<mod_qualifier>replace_passwords"
2145 + "(<call_line>))",
2146 " <mod_qualifier>set_last_seconds_ix(<mod_qualifier>"
2147 + "standard_print_last_seconds_ix())"
Michael Walsh168eb0f2017-12-01 15:35:32 -06002148 ]
2149
Michael Walsh81c02342018-01-05 15:43:28 -06002150replace_dict = {'output_stream': 'stdout', 'mod_qualifier': ''}
Michael Walshfd2733c2017-11-13 11:36:20 -06002151
Michael Walsh61c12982019-03-28 12:38:01 -05002152gp_debug_print("robot_env: " + str(robot_env) + "\n")
Michael Walshde791732016-09-06 14:25:24 -05002153
2154# func_names contains a list of all print functions which should be created
2155# from their sprint counterparts.
2156func_names = ['print_time', 'print_timen', 'print_error', 'print_varx',
Michael Walsh18176322016-11-15 15:11:21 -06002157 'print_var', 'print_vars', 'print_dashes', 'indent',
2158 'print_call_stack', 'print_func_name', 'print_executing',
2159 'print_pgm_header', 'print_issuing', 'print_pgm_footer',
2160 'print_error_report', 'print', 'printn']
Michael Walshde791732016-09-06 14:25:24 -05002161
Michael Walsh2ee77cd2017-03-08 11:50:17 -06002162# stderr_func_names is a list of functions whose output should go to stderr
2163# rather than stdout.
2164stderr_func_names = ['print_error', 'print_error_report']
Michael Walshde791732016-09-06 14:25:24 -05002165
Michael Walshfd2733c2017-11-13 11:36:20 -06002166func_defs = create_print_wrapper_funcs(func_names, stderr_func_names,
2167 replace_dict)
2168gp_debug_print(func_defs)
2169exec(func_defs)