blob: b51b51e45b028008cb8a015f148c8295ba78514f [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',
857 'terse',
858 '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
896def list_pop(a_list, index=0, default=None):
897 r"""
898 Pop the list entry indicated by the index and return the entry. If no
899 such entry exists, return default.
900
901 Note that the list passed to this function will be modified.
902
903 Description of argument(s):
904 a_list The list from which an entry is to be
905 popped.
906 index The index indicating which entry is to be
907 popped.
908 default The value to be returned if there is no
909 entry at the given index location.
910 """
911 try:
912 return a_list.pop(index)
913 except IndexError:
914 return default
915
916
917def parse_fmt(fmt):
918 r"""
919 Parse the fmt argument and return a tuple consisting of a format and a
920 child format.
921
922 This function was written for use by the sprint_varx function defined in
923 this module.
924
925 When sprint_varx is processing a multi-level object such as a list or
926 dictionary (which in turn may contain other lists or dictionaries), it
927 will use the fmt value to dictate the print formatting of the current
928 level and the child_fmt value to dictate the print formatting of
929 subordinate levels. Consider the following example:
930
931 python code example:
932
933 ord_dict = \
934 collections.OrderedDict([
935 ('one', 1),
936 ('two', 2),
937 ('sub',
938 collections.OrderedDict([
939 ('three', 3), ('four', 4)]))])
940
941 print_var(ord_dict)
942
943 This would generate the following output:
944
945 ord_dict:
946 ord_dict[one]: 1
947 ord_dict[two]: 2
948 ord_dict[sub]:
949 ord_dict[sub][three]: 3
950 ord_dict[sub][four]: 4
951
952 The first level in this example is the line that simply says "ord_dict".
953 The second level is comprised of the dictionary entries with the keys
954 'one', 'two' and 'sub'. The third level is comprised of the last 2 lines
955 (i.e. printed values 3 and 4).
956
957 Given the data structure shown above, the programmer could code the
958 following where fmt is a simple integer value set by calling the terse()
959 function.
960
961 print_var(ord_dict, fmt=terse())
962
963 The output would look like this:
964
965 ord_dict:
966 [one]: 1
967 [two]: 2
968 [sub]:
969 [three]: 3
970 [four]: 4
971
972 Note the terse format where the name of the object ("ord_dict") is not
973 repeated on every line as it was in example #1.
974
975 If the programmer wishes to get more granular with the fmt argument,
976 he/she can specify it as a list where each entry corresponds to a level of
977 the object being printed. The last such list entry governs the print
978 formatting of all subordinate parts of the given object.
979
980 Look at each of the following code examples and their corresponding
981 output. See how the show_type() formatting affects the printing depending
982 on which position it occupies in the fmt list argument:
983
984 print_var(ord_dict, fmt=[show_type()])
985
986 ord_dict: <collections.OrderedDict>
987 ord_dict[one]: 1 <int>
988 ord_dict[two]: 2 <int>
989 ord_dict[sub]: <collections.OrderedDict>
990 ord_dict[sub][three]: 3 <int>
991 ord_dict[sub][four]: 4 <int>
992
993 print_var(ord_dict, fmt=[0, show_type()])
994
995 ord_dict:
996 ord_dict[one]: 1 <int>
997 ord_dict[two]: 2 <int>
998 ord_dict[sub]: <collections.OrderedDict>
999 ord_dict[sub][three]: 3 <int>
1000 ord_dict[sub][four]: 4 <int>
1001
1002 print_var(ord_dict, fmt=[0, 0, show_type()])
1003
1004 ord_dict:
1005 ord_dict[one]: 1
1006 ord_dict[two]: 2
1007 ord_dict[sub]:
1008 ord_dict[sub][three]: 3 <int>
1009 ord_dict[sub][four]: 4 <int>
1010
1011 Description of argument(s):
1012 fmt The format argument such as is passed to
1013 sprint_varx. This argument may be an
1014 integer or a list of integers. See the
1015 prolog of sprint_varx for more details.
1016 """
1017
1018 # Make a deep copy of the fmt argument in order to avoid modifying the
1019 # caller's fmt value when it is a list.
1020 fmt = copy.deepcopy(fmt)
1021 try:
1022 # Assume fmt is a list. Pop the first element from the list.
1023 first_element = list_pop(fmt, index=0, default=0)
1024 # Return the first list element along with either 1) the remainder of
1025 # the fmt list if not null or 2) another copy of the first element.
1026 return first_element, fmt if len(fmt) else first_element
1027 except AttributeError:
1028 # fmt is not a list so treat it as a simple integer value.
1029 return fmt, fmt
1030
1031
Michael Walshde791732016-09-06 14:25:24 -05001032def sprint_varx(var_name,
1033 var_value,
Michael Walsh4dbb6002019-05-17 15:51:15 -05001034 fmt=0,
1035 indent=dft_indent,
1036 col1_width=dft_col1_width,
Michael Walshd2869032018-03-22 16:12:11 -05001037 trailing_char="\n",
Michael Walsh4dbb6002019-05-17 15:51:15 -05001038 key_list=None,
1039 delim=":"):
Michael Walshde791732016-09-06 14:25:24 -05001040 r"""
Michael Walsh4dbb6002019-05-17 15:51:15 -05001041 Print the var name/value passed to it. If the caller lets col1_width
Michael Walshde791732016-09-06 14:25:24 -05001042 default, the printing lines up nicely with output generated by the
1043 print_time functions.
1044
1045 Note that the sprint_var function (defined below) can be used to call this
1046 function so that the programmer does not need to pass the var_name.
1047 sprint_var will figure out the var_name. The sprint_var function is the
1048 one that would normally be used by the general user.
1049
1050 For example, the following python code:
1051
1052 first_name = "Mike"
1053 print_time("Doing this...\n")
1054 print_varx("first_name", first_name)
1055 print_time("Doing that...\n")
1056
1057 Will generate output like this:
1058
1059 #(CDT) 2016/08/10 17:34:42.847374 - 0.001285 - Doing this...
1060 first_name: Mike
1061 #(CDT) 2016/08/10 17:34:42.847510 - 0.000136 - Doing that...
1062
1063 This function recognizes several complex types of data such as dict, list
1064 or tuple.
1065
1066 For example, the following python code:
1067
1068 my_dict = dict(one=1, two=2, three=3)
1069 print_var(my_dict)
1070
1071 Will generate the following output:
1072
1073 my_dict:
1074 my_dict[three]: 3
1075 my_dict[two]: 2
1076 my_dict[one]: 1
1077
Michael Walsh4dbb6002019-05-17 15:51:15 -05001078 Description of argument(s).
Michael Walshde791732016-09-06 14:25:24 -05001079 var_name The name of the variable to be printed.
1080 var_value The value of the variable to be printed.
Michael Walsh4dbb6002019-05-17 15:51:15 -05001081 fmt A bit map to dictate the format of the
1082 output. For printing multi-level objects
1083 like lists and dictionaries, this argument
1084 may also be a list of bit maps. The first
1085 list element pertains to the highest level
1086 of output, the second element pertains to
1087 the 2nd level of output, etc. The last
1088 element in the list pertains to all
1089 subordinate levels. The bits can be set
1090 using the dynamically created functionhs
1091 above. Example: sprint_varx("var1", var1,
1092 fmt=terse()). Note that these values can
1093 be OR'ed together: print_var(var1, hexa()
1094 | terse()). If the caller ORs mutually
1095 exclusive bits (hexa() | octal()),
1096 behavior is not guaranteed. The following
1097 features are supported:
1098 hexa Print all integer values in hexadecimal
1099 format.
1100 octal Print all integer values in octal format.
1101 binary Print all integer values in binary format.
1102 blank For blank string values, print "<blank>"
1103 instead of an actual blank.
1104 terse For structured values like dictionaries,
1105 lists, etc. do not repeat the name of the
1106 variable on each line to the right of the
1107 key or subscript value. Example: print
1108 "[key1]" instead of "my_dict[key1]".
1109 quote_keys Quote dictionary keys in the output.
1110 Example: my_dict['key1'] instead of
1111 my_dict[key1].
1112 show_type Show the type of the data in angled
1113 brackets just to the right of the data.
1114 indent The number of spaces to indent the output.
1115 col1_width The width of the output column containing
Michael Walshde791732016-09-06 14:25:24 -05001116 the variable name. The default value of
1117 this is adjusted so that the var_value
1118 lines up with text printed via the
1119 print_time function.
Michael Walsh7423c012016-10-04 10:27:21 -05001120 trailing_char The character to be used at the end of the
1121 returned string. The default value is a
1122 line feed.
Michael Walshd2869032018-03-22 16:12:11 -05001123 key_list A list of which dictionary keys should be
1124 printed. All others keys will be skipped.
1125 Each value in key_list will be regarded
1126 as a regular expression and it will be
1127 regarded as anchored to the beginning and
1128 ends of the dictionary key being
1129 referenced. For example if key_list is
1130 ["one", "two"], the resulting regex used
1131 will be "^one|two$", i.e. only keys "one"
1132 and "two" from the var_value dictionary
1133 will be printed. As another example, if
1134 the caller were to specify a key_list of
1135 ["one.*"], then only dictionary keys whose
1136 names begin with "one" will be printed.
1137 Note: This argument pertains only to
1138 var_values which are dictionaries.
Michael Walsh4dbb6002019-05-17 15:51:15 -05001139 delim The value to be used to delimit the
1140 variable name from the variable value in
1141 the output.
Michael Walsh7423c012016-10-04 10:27:21 -05001142 """
Michael Walshde791732016-09-06 14:25:24 -05001143
Michael Walsh4dbb6002019-05-17 15:51:15 -05001144 fmt, child_fmt = parse_fmt(fmt)
1145
1146 if fmt & show_type():
1147 type_str = "<" + str(type(var_value)).split("'")[1] + ">"
1148 # Compose object type categories.
1149 int_types = get_int_types()
1150 string_types = get_string_types()
1151 simple_types = int_types + string_types + (float, bool, type, type(None))
1152 # Determine the type.
1153 if type(var_value) in simple_types:
Michael Walshde791732016-09-06 14:25:24 -05001154 # The data type is simple in the sense that it has no subordinate
1155 # parts.
Michael Walsh4dbb6002019-05-17 15:51:15 -05001156 # Adjust col1_width.
1157 col1_width = col1_width - indent
1158 # Set default value for value_format.
1159 value_format = "%s"
1160 # Process format requests.
1161 if type(var_value) in int_types:
1162 # Process format values pertaining to int types.
1163 if fmt & hexa():
Michael Walsh3f248272018-06-01 13:59:35 -05001164 num_hex_digits = max(dft_num_hex_digits(),
1165 get_req_num_hex_digits(var_value))
1166 # Convert a negative number to its positive twos complement
1167 # for proper printing. For example, instead of printing -1 as
1168 # "0x-000000000000001" it will be printed as
1169 # "0xffffffffffffffff".
1170 var_value = var_value & (2 ** (num_hex_digits * 4) - 1)
1171 value_format = "0x%0" + str(num_hex_digits) + "x"
Michael Walsh4dbb6002019-05-17 15:51:15 -05001172 elif fmt & octal():
1173 value_format = "0o%016o"
1174 elif fmt & binary():
1175 num_digits, remainder = \
1176 divmod(max(bit_length(var_value), 1), 8)
1177 num_digits *= 8
1178 if remainder:
1179 num_digits += 8
1180 num_digits += 2
1181 value_format = '#0' + str(num_digits) + 'b'
1182 var_value = format(var_value, value_format)
1183 value_format = "%s"
1184 elif type(var_value) in string_types:
1185 # Process format values pertaining to string types.
1186 if fmt & blank() and var_value == "":
1187 value_format = "%s"
1188 var_value = "<blank>"
1189 elif type(var_value) is type:
1190 var_value = str(var_value).split("'")[1]
1191 format_string = "%" + str(indent) + "s%-" + str(col1_width) + "s" \
1192 + value_format
1193 if fmt & show_type():
1194 if var_value != "":
1195 format_string += " "
1196 format_string += type_str
1197 format_string += trailing_char
1198 if fmt & terse():
1199 # Strip everything leading up to the first left square brace.
1200 var_name = re.sub(r".*\[", "[", var_name)
Michael Walsh3383e652017-09-01 17:10:59 -05001201 if value_format == "0x%08x":
Michael Walsh4dbb6002019-05-17 15:51:15 -05001202 return format_string % ("", str(var_name) + delim,
Michael Walsh3383e652017-09-01 17:10:59 -05001203 var_value & 0xffffffff)
1204 else:
Michael Walsh4dbb6002019-05-17 15:51:15 -05001205 return format_string % ("", str(var_name) + delim, var_value)
Michael Walshde791732016-09-06 14:25:24 -05001206 else:
1207 # The data type is complex in the sense that it has subordinate parts.
Michael Walsh4dbb6002019-05-17 15:51:15 -05001208 if fmt & terse():
1209 # Strip everything leading up to the first square brace.
1210 loc_var_name = re.sub(r".*\[", "[", var_name)
1211 else:
1212 loc_var_name = var_name
1213 format_string = "%" + str(indent) + "s%s\n"
1214 buffer = format_string % ("", loc_var_name + ":")
1215 if fmt & show_type():
1216 buffer = buffer.replace("\n", " " + type_str + "\n")
1217 indent += 2
Michael Walsh7423c012016-10-04 10:27:21 -05001218 try:
1219 length = len(var_value)
1220 except TypeError:
Michael Walsh23e7f492017-01-10 11:34:47 -06001221 length = 0
Michael Walsh7423c012016-10-04 10:27:21 -05001222 ix = 0
1223 loc_trailing_char = "\n"
Michael Walsh8646d962019-01-21 14:36:13 -06001224 if is_dict(var_value):
Michael Walsh4dbb6002019-05-17 15:51:15 -05001225 if type(child_fmt) is list:
1226 child_quote_keys = (child_fmt[0] & quote_keys())
1227 else:
1228 child_quote_keys = (child_fmt & quote_keys())
Michael Walsh37762f92018-08-07 14:59:18 -05001229 for key, value in var_value.items():
Michael Walshd2869032018-03-22 16:12:11 -05001230 if key_list is not None:
1231 key_list_regex = "^" + "|".join(key_list) + "$"
1232 if not re.match(key_list_regex, key):
1233 continue
Michael Walsh7423c012016-10-04 10:27:21 -05001234 ix += 1
1235 if ix == length:
1236 loc_trailing_char = trailing_char
Michael Walsh4dbb6002019-05-17 15:51:15 -05001237 if child_quote_keys:
1238 key = "'" + key + "'"
1239 key = "[" + str(key) + "]"
1240 buffer += sprint_varx(var_name + key, value, child_fmt, indent,
1241 col1_width, loc_trailing_char, key_list)
Michael Walsh7423c012016-10-04 10:27:21 -05001242 elif type(var_value) in (list, tuple, set):
Michael Walshde791732016-09-06 14:25:24 -05001243 for key, value in enumerate(var_value):
Michael Walsh7423c012016-10-04 10:27:21 -05001244 ix += 1
1245 if ix == length:
1246 loc_trailing_char = trailing_char
Michael Walsh4dbb6002019-05-17 15:51:15 -05001247 key = "[" + str(key) + "]"
1248 buffer += sprint_varx(var_name + key, value, child_fmt, indent,
1249 col1_width, loc_trailing_char, key_list)
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001250 elif isinstance(var_value, argparse.Namespace):
Michael Walshde791732016-09-06 14:25:24 -05001251 for key in var_value.__dict__:
Michael Walsh7423c012016-10-04 10:27:21 -05001252 ix += 1
1253 if ix == length:
1254 loc_trailing_char = trailing_char
Michael Walshde791732016-09-06 14:25:24 -05001255 cmd_buf = "buffer += sprint_varx(var_name + \".\" + str(key)" \
Michael Walsh4dbb6002019-05-17 15:51:15 -05001256 + ", var_value." + key + ", child_fmt, indent," \
1257 + " col1_width, loc_trailing_char, key_list)"
Michael Walshde791732016-09-06 14:25:24 -05001258 exec(cmd_buf)
1259 else:
1260 var_type = type(var_value).__name__
1261 func_name = sys._getframe().f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -05001262 var_value = "<" + var_type + " type not supported by " + \
1263 func_name + "()>"
Michael Walshde791732016-09-06 14:25:24 -05001264 value_format = "%s"
Michael Walsh4dbb6002019-05-17 15:51:15 -05001265 indent -= 2
1266 # Adjust col1_width.
1267 col1_width = col1_width - indent
1268 format_string = "%" + str(indent) + "s%-" \
1269 + str(col1_width) + "s" + value_format + trailing_char
Michael Walsh0f2ea5f2017-02-20 15:55:00 -06001270 return format_string % ("", str(var_name) + ":", var_value)
Michael Walsh23e7f492017-01-10 11:34:47 -06001271
Michael Walshde791732016-09-06 14:25:24 -05001272 return buffer
1273
1274 return ""
1275
Michael Walshde791732016-09-06 14:25:24 -05001276
Michael Walsh4dbb6002019-05-17 15:51:15 -05001277def sprint_var(*args, **kwargs):
Michael Walshde791732016-09-06 14:25:24 -05001278 r"""
Michael Walsh4dbb6002019-05-17 15:51:15 -05001279 Figure out the name of the first argument for the caller and then call
Michael Walshde791732016-09-06 14:25:24 -05001280 sprint_varx with it. Therefore, the following 2 calls are equivalent:
1281 sprint_varx("var1", var1)
1282 sprint_var(var1)
Michael Walsh4dbb6002019-05-17 15:51:15 -05001283
1284 See sprint_varx for description of arguments.
Michael Walshde791732016-09-06 14:25:24 -05001285 """
1286
Michael Walshde791732016-09-06 14:25:24 -05001287 stack_frame = 2
Michael Walsh7423c012016-10-04 10:27:21 -05001288 caller_func_name = sprint_func_name(2)
1289 if caller_func_name.endswith("print_var"):
Michael Walshde791732016-09-06 14:25:24 -05001290 stack_frame += 1
Michael Walsh4dbb6002019-05-17 15:51:15 -05001291 # Get the name of the first variable passed to this function.
Michael Walshde791732016-09-06 14:25:24 -05001292 var_name = get_arg_name(None, 1, stack_frame)
Michael Walsh4dbb6002019-05-17 15:51:15 -05001293 return sprint_varx(var_name, *args, **kwargs)
Michael Walshde791732016-09-06 14:25:24 -05001294
1295
Michael Walsh4dbb6002019-05-17 15:51:15 -05001296def sprint_vars(*args, **kwargs):
Michael Walsh18176322016-11-15 15:11:21 -06001297 r"""
1298 Sprint the values of one or more variables.
1299
Michael Walsh4dbb6002019-05-17 15:51:15 -05001300 Description of argument(s):
1301 args The variable values which are to be
1302 printed.
1303 kwargs See sprint_varx (above) for description of
1304 additional arguments.
Michael Walsh18176322016-11-15 15:11:21 -06001305 """
1306
Michael Walsh18176322016-11-15 15:11:21 -06001307 stack_frame = 2
1308 caller_func_name = sprint_func_name(2)
1309 if caller_func_name.endswith("print_vars"):
1310 stack_frame += 1
1311
Michael Walsh18176322016-11-15 15:11:21 -06001312 buffer = ""
Michael Walsh4dbb6002019-05-17 15:51:15 -05001313 arg_num = 1
1314 for var_value in args:
1315 var_name = get_arg_name(None, arg_num, stack_frame)
1316 buffer += sprint_varx(var_name, var_value, **kwargs)
1317 arg_num += 1
Michael Walsh18176322016-11-15 15:11:21 -06001318
1319 return buffer
1320
Michael Walsh18176322016-11-15 15:11:21 -06001321
Michael Walsh4dbb6002019-05-17 15:51:15 -05001322def sprint_dashes(indent=dft_indent,
Michael Walsh7423c012016-10-04 10:27:21 -05001323 width=80,
1324 line_feed=1,
1325 char="-"):
Michael Walshde791732016-09-06 14:25:24 -05001326 r"""
1327 Return a string of dashes to the caller.
1328
Michael Walsh4dbb6002019-05-17 15:51:15 -05001329 Description of argument(s):
Michael Walshde791732016-09-06 14:25:24 -05001330 indent The number of characters to indent the
1331 output.
1332 width The width of the string of dashes.
1333 line_feed Indicates whether the output should end
1334 with a line feed.
Michael Walsh7423c012016-10-04 10:27:21 -05001335 char The character to be repeated in the output
1336 string.
Michael Walshde791732016-09-06 14:25:24 -05001337 """
1338
Michael Walsh7423c012016-10-04 10:27:21 -05001339 width = int(width)
Michael Walsh23e7f492017-01-10 11:34:47 -06001340 buffer = " " * int(indent) + char * width
Michael Walshde791732016-09-06 14:25:24 -05001341 if line_feed:
1342 buffer += "\n"
1343
1344 return buffer
1345
Michael Walshde791732016-09-06 14:25:24 -05001346
Michael Walsh7423c012016-10-04 10:27:21 -05001347def sindent(text="",
1348 indent=0):
Michael Walsh7423c012016-10-04 10:27:21 -05001349 r"""
1350 Pre-pend the specified number of characters to the text string (i.e.
1351 indent it) and return it.
1352
Michael Walsh4dbb6002019-05-17 15:51:15 -05001353 Description of argument(s):
Michael Walsh7423c012016-10-04 10:27:21 -05001354 text The string to be indented.
1355 indent The number of characters to indent the
1356 string.
1357 """
1358
1359 format_string = "%" + str(indent) + "s%s"
1360 buffer = format_string % ("", text)
1361
1362 return buffer
1363
Michael Walsh7423c012016-10-04 10:27:21 -05001364
Michael Walsh662e13b2019-03-01 15:54:08 -06001365func_line_style_std = None
1366func_line_style_short = 1
1367
1368
1369def sprint_func_line(stack_frame, style=None):
Michael Walsh47aa2a42018-12-10 15:06:02 -06001370 r"""
1371 For the given stack_frame, return a formatted string containing the
1372 function name and all its arguments.
1373
1374 Example:
1375
1376 func1(last_name = 'walsh', first_name = 'mikey')
1377
1378 Description of argument(s):
1379 stack_frame A stack frame (such as is returned by
1380 inspect.stack()).
Michael Walsh662e13b2019-03-01 15:54:08 -06001381 style Indicates the style or formatting of the
1382 result string. Acceptable values are
1383 shown above.
1384
1385 Description of styles:
1386 func_line_style_std The standard formatting.
1387 func_line_style_short 1) The self parm (associated with methods)
1388 will be dropped. 2) The args and kwargs
1389 values will be treated as special. In
1390 both cases the arg name ('args' or
1391 'kwargs') will be dropped and only the
1392 values will be shown.
Michael Walsh47aa2a42018-12-10 15:06:02 -06001393 """
1394
1395 func_name = str(stack_frame[3])
1396 if func_name == "?":
1397 # "?" is the name used when code is not in a function.
1398 func_name = "(none)"
1399
1400 if func_name == "<module>":
1401 # If the func_name is the "main" program, we simply get the command
1402 # line call string.
1403 func_and_args = ' '.join(sys.argv)
1404 else:
1405 # Get the program arguments.
1406 (args, varargs, keywords, locals) =\
1407 inspect.getargvalues(stack_frame[0])
1408
1409 args_list = []
1410 for arg_name in filter(None, args + [varargs, keywords]):
1411 # Get the arg value from frame locals.
1412 arg_value = locals[arg_name]
1413 if arg_name == 'self':
Michael Walsh662e13b2019-03-01 15:54:08 -06001414 if style == func_line_style_short:
1415 continue
Michael Walsh47aa2a42018-12-10 15:06:02 -06001416 # Manipulations to improve output for class methods.
1417 func_name = arg_value.__class__.__name__ + "." + func_name
1418 args_list.append(arg_name + " = <self>")
Michael Walsh662e13b2019-03-01 15:54:08 -06001419 elif (style == func_line_style_short
1420 and arg_name == 'args'
1421 and type(arg_value) in (list, tuple)):
1422 if len(arg_value) == 0:
1423 continue
1424 args_list.append(repr(', '.join(arg_value)))
1425 elif (style == func_line_style_short
1426 and arg_name == 'kwargs'
1427 and type(arg_value) is dict):
1428 for key, value in arg_value.items():
1429 args_list.append(key + "=" + repr(value))
Michael Walsh47aa2a42018-12-10 15:06:02 -06001430 else:
1431 args_list.append(arg_name + " = " + repr(arg_value))
1432 args_str = "(" + ', '.join(map(str, args_list)) + ")"
1433
1434 # Now we need to print this in a nicely-wrapped way.
1435 func_and_args = func_name + args_str
1436
1437 return func_and_args
1438
1439
Michael Walsh7423c012016-10-04 10:27:21 -05001440def sprint_call_stack(indent=0,
Michael Walsh662e13b2019-03-01 15:54:08 -06001441 stack_frame_ix=0,
1442 style=None):
Michael Walshde791732016-09-06 14:25:24 -05001443 r"""
1444 Return a call stack report for the given point in the program with line
1445 numbers, function names and function parameters and arguments.
1446
1447 Sample output:
1448
1449 -------------------------------------------------------------------------
1450 Python function call stack
1451
1452 Line # Function name and arguments
1453 ------ ------------------------------------------------------------------
Michael Walsh47aa2a42018-12-10 15:06:02 -06001454 424 sprint_call_stack()
1455 4 print_call_stack()
1456 31 func1(last_name = 'walsh', first_name = 'mikey')
Michael Walshde791732016-09-06 14:25:24 -05001457 59 /tmp/scr5.py
1458 -------------------------------------------------------------------------
1459
Michael Walsh4dbb6002019-05-17 15:51:15 -05001460 Description of argument(s):
Michael Walshde791732016-09-06 14:25:24 -05001461 indent The number of characters to indent each
1462 line of output.
1463 stack_frame_ix The index of the first stack frame which
1464 is to be returned.
Michael Walsh662e13b2019-03-01 15:54:08 -06001465 style See the sprint_line_func prolog above for
1466 details.
Michael Walshde791732016-09-06 14:25:24 -05001467 """
1468
1469 buffer = ""
Michael Walsh7423c012016-10-04 10:27:21 -05001470 buffer += sprint_dashes(indent)
1471 buffer += sindent("Python function call stack\n\n", indent)
1472 buffer += sindent("Line # Function name and arguments\n", indent)
1473 buffer += sprint_dashes(indent, 6, 0) + " " + sprint_dashes(0, 73)
Michael Walshde791732016-09-06 14:25:24 -05001474
1475 # Grab the current program stack.
Michael Walsh6f0362c2019-03-25 14:05:14 -05001476 work_around_inspect_stack_cwd_failure()
Michael Walshde791732016-09-06 14:25:24 -05001477 current_stack = inspect.stack()
1478
1479 # Process each frame in turn.
1480 format_string = "%6s %s\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001481 ix = 0
Michael Walshde791732016-09-06 14:25:24 -05001482 for stack_frame in current_stack:
Michael Walsh7423c012016-10-04 10:27:21 -05001483 if ix < stack_frame_ix:
1484 ix += 1
1485 continue
Michael Walsh4dbb6002019-05-17 15:51:15 -05001486 # Make the line number shown to be the line where one finds the line
Michael Walsh23e7f492017-01-10 11:34:47 -06001487 # shown.
1488 try:
1489 line_num = str(current_stack[ix + 1][2])
1490 except IndexError:
1491 line_num = ""
Michael Walsh662e13b2019-03-01 15:54:08 -06001492 func_and_args = sprint_func_line(stack_frame, style=style)
Michael Walshde791732016-09-06 14:25:24 -05001493
Michael Walsh23e7f492017-01-10 11:34:47 -06001494 buffer += sindent(format_string % (line_num, func_and_args), indent)
Michael Walsh7423c012016-10-04 10:27:21 -05001495 ix += 1
Michael Walshde791732016-09-06 14:25:24 -05001496
Michael Walsh7423c012016-10-04 10:27:21 -05001497 buffer += sprint_dashes(indent)
Michael Walshde791732016-09-06 14:25:24 -05001498
1499 return buffer
1500
Michael Walshde791732016-09-06 14:25:24 -05001501
Michael Walsh662e13b2019-03-01 15:54:08 -06001502def sprint_executing(stack_frame_ix=None, style=None):
Michael Walshde791732016-09-06 14:25:24 -05001503 r"""
1504 Print a line indicating what function is executing and with what parameter
1505 values. This is useful for debugging.
1506
1507 Sample output:
1508
Michael Walsh47aa2a42018-12-10 15:06:02 -06001509 #(CDT) 2016/08/25 17:54:27 - Executing: func1(x = 1)
Michael Walshde791732016-09-06 14:25:24 -05001510
Michael Walsh4dbb6002019-05-17 15:51:15 -05001511 Description of argument(s):
Michael Walshde791732016-09-06 14:25:24 -05001512 stack_frame_ix The index of the stack frame whose
1513 function info should be returned. If the
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001514 caller does not specify a value, this
Michael Walshde791732016-09-06 14:25:24 -05001515 function will set the value to 1 which is
1516 the index of the caller's stack frame. If
1517 the caller is the wrapper function
1518 "print_executing", this function will bump
1519 it up by 1.
Michael Walsh662e13b2019-03-01 15:54:08 -06001520 style See the sprint_line_func prolog above for
1521 details.
Michael Walshde791732016-09-06 14:25:24 -05001522 """
1523
1524 # If user wants default stack_frame_ix.
1525 if stack_frame_ix is None:
1526 func_name = sys._getframe().f_code.co_name
1527 caller_func_name = sys._getframe(1).f_code.co_name
Michael Walsh7423c012016-10-04 10:27:21 -05001528 if caller_func_name.endswith(func_name[1:]):
Michael Walshde791732016-09-06 14:25:24 -05001529 stack_frame_ix = 2
1530 else:
1531 stack_frame_ix = 1
1532
Michael Walsh6f0362c2019-03-25 14:05:14 -05001533 work_around_inspect_stack_cwd_failure()
Michael Walshde791732016-09-06 14:25:24 -05001534 stack_frame = inspect.stack()[stack_frame_ix]
1535
Michael Walsh662e13b2019-03-01 15:54:08 -06001536 func_and_args = sprint_func_line(stack_frame, style)
Michael Walshde791732016-09-06 14:25:24 -05001537
1538 return sprint_time() + "Executing: " + func_and_args + "\n"
1539
Michael Walshde791732016-09-06 14:25:24 -05001540
Michael Walshbec416d2016-11-10 08:54:52 -06001541def sprint_pgm_header(indent=0,
1542 linefeed=1):
Michael Walshde791732016-09-06 14:25:24 -05001543 r"""
1544 Return a standardized header that programs should print at the beginning
1545 of the run. It includes useful information like command line, pid,
1546 userid, program parameters, etc.
1547
Michael Walsh4dbb6002019-05-17 15:51:15 -05001548 Description of argument(s):
Michael Walsh7423c012016-10-04 10:27:21 -05001549 indent The number of characters to indent each
1550 line of output.
Michael Walshbec416d2016-11-10 08:54:52 -06001551 linefeed Indicates whether a line feed be included
1552 at the beginning and end of the report.
Michael Walshde791732016-09-06 14:25:24 -05001553 """
1554
Michael Walsh4dbb6002019-05-17 15:51:15 -05001555 col1_width = dft_col1_width + indent
Michael Walshbec416d2016-11-10 08:54:52 -06001556
1557 buffer = ""
1558 if linefeed:
1559 buffer = "\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001560
Michael Walshdb6e68a2017-05-23 17:55:31 -05001561 if robot_env:
1562 suite_name = BuiltIn().get_variable_value("${suite_name}")
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001563 buffer += sindent(sprint_time("Running test suite \"" + suite_name
1564 + "\".\n"), indent)
Michael Walshdb6e68a2017-05-23 17:55:31 -05001565
Michael Walsh7423c012016-10-04 10:27:21 -05001566 buffer += sindent(sprint_time() + "Running " + pgm_name + ".\n", indent)
1567 buffer += sindent(sprint_time() + "Program parameter values, etc.:\n\n",
1568 indent)
Michael Walshbec416d2016-11-10 08:54:52 -06001569 buffer += sprint_varx("command_line", ' '.join(sys.argv), 0, indent,
Michael Walsh4dbb6002019-05-17 15:51:15 -05001570 col1_width)
Michael Walsh7423c012016-10-04 10:27:21 -05001571 # We want the output to show a customized name for the pid and pgid but
1572 # we want it to look like a valid variable name. Therefore, we'll use
Michael Walshde791732016-09-06 14:25:24 -05001573 # pgm_name_var_name which was set when this module was imported.
Michael Walshbec416d2016-11-10 08:54:52 -06001574 buffer += sprint_varx(pgm_name_var_name + "_pid", os.getpid(), 0, indent,
Michael Walsh4dbb6002019-05-17 15:51:15 -05001575 col1_width)
Michael Walshbec416d2016-11-10 08:54:52 -06001576 buffer += sprint_varx(pgm_name_var_name + "_pgid", os.getpgrp(), 0, indent,
Michael Walsh4dbb6002019-05-17 15:51:15 -05001577 col1_width)
Michael Walsh86de0d22016-12-05 10:13:15 -06001578 userid_num = str(os.geteuid())
1579 try:
1580 username = os.getlogin()
1581 except OSError:
1582 if userid_num == "0":
1583 username = "root"
1584 else:
1585 username = "?"
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001586 buffer += sprint_varx("uid", userid_num + " (" + username
Michael Walsh4dbb6002019-05-17 15:51:15 -05001587 + ")", 0, indent, col1_width)
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001588 buffer += sprint_varx("gid", str(os.getgid()) + " ("
1589 + str(grp.getgrgid(os.getgid()).gr_name) + ")", 0,
Michael Walsh4dbb6002019-05-17 15:51:15 -05001590 indent, col1_width)
Michael Walshbec416d2016-11-10 08:54:52 -06001591 buffer += sprint_varx("host_name", socket.gethostname(), 0, indent,
Michael Walsh4dbb6002019-05-17 15:51:15 -05001592 col1_width)
Michael Walsh86de0d22016-12-05 10:13:15 -06001593 try:
1594 DISPLAY = os.environ['DISPLAY']
1595 except KeyError:
1596 DISPLAY = ""
Michael Walsh91fc8822019-05-29 17:34:17 -05001597 buffer += sprint_var(DISPLAY, 0, indent, col1_width)
1598 PYTHON_VERSION = os.environ.get('PYTHON_VERSION', None)
1599 if PYTHON_VERSION is not None:
1600 buffer += sprint_var(PYTHON_VERSION)
1601 PYTHON_PGM_PATH = os.environ.get('PYTHON_PGM_PATH', None)
1602 if PYTHON_PGM_PATH is not None:
1603 buffer += sprint_var(PYTHON_PGM_PATH)
1604 python_version = sys.version.replace("\n", "")
1605 buffer += sprint_var(python_version)
1606 ROBOT_VERSION = os.environ.get('ROBOT_VERSION', None)
1607 if ROBOT_VERSION is not None:
1608 buffer += sprint_var(ROBOT_VERSION)
1609 ROBOT_PGM_PATH = os.environ.get('ROBOT_PGM_PATH', None)
1610 if ROBOT_PGM_PATH is not None:
1611 buffer += sprint_var(ROBOT_PGM_PATH)
1612
Michael Walsh4dbb6002019-05-17 15:51:15 -05001613 # TODO: Add code to print caller's parms.
Michael Walshde791732016-09-06 14:25:24 -05001614
Michael Walsh7423c012016-10-04 10:27:21 -05001615 # __builtin__.arg_obj is created by the get_arg module function,
1616 # gen_get_options.
1617 try:
1618 buffer += ga.sprint_args(__builtin__.arg_obj, indent)
1619 except AttributeError:
1620 pass
1621
Michael Walshdb6e68a2017-05-23 17:55:31 -05001622 if robot_env:
1623 # Get value of global parm_list.
1624 parm_list = BuiltIn().get_variable_value("${parm_list}")
1625
1626 for parm in parm_list:
1627 parm_value = BuiltIn().get_variable_value("${" + parm + "}")
Michael Walsh4dbb6002019-05-17 15:51:15 -05001628 buffer += sprint_varx(parm, parm_value, 0, indent, col1_width)
Michael Walshdb6e68a2017-05-23 17:55:31 -05001629
1630 # Setting global program_pid.
1631 BuiltIn().set_global_variable("${program_pid}", os.getpid())
1632
Michael Walshbec416d2016-11-10 08:54:52 -06001633 if linefeed:
1634 buffer += "\n"
Michael Walshde791732016-09-06 14:25:24 -05001635
1636 return buffer
1637
Michael Walshde791732016-09-06 14:25:24 -05001638
Michael Walsh7423c012016-10-04 10:27:21 -05001639def sprint_error_report(error_text="\n",
Michael Walshdb6e68a2017-05-23 17:55:31 -05001640 indent=2,
1641 format=None):
Michael Walsh7423c012016-10-04 10:27:21 -05001642 r"""
1643 Return a string with a standardized report which includes the caller's
1644 error text, the call stack and the program header.
1645
Michael Walsh4dbb6002019-05-17 15:51:15 -05001646 Description of argument(s):
Michael Walsh7423c012016-10-04 10:27:21 -05001647 error_text The error text to be included in the
1648 report. The caller should include any
1649 needed linefeeds.
1650 indent The number of characters to indent each
1651 line of output.
Michael Walshdb6e68a2017-05-23 17:55:31 -05001652 format Long or short format. Long includes
1653 extras like lines of dashes, call stack,
1654 etc.
Michael Walsh7423c012016-10-04 10:27:21 -05001655 """
1656
Michael Walshdb6e68a2017-05-23 17:55:31 -05001657 # Process input.
1658 indent = int(indent)
1659 if format is None:
1660 if robot_env:
1661 format = 'short'
1662 else:
1663 format = 'long'
1664 error_text = error_text.rstrip('\n') + '\n'
1665
1666 if format == 'short':
1667 return sprint_error(error_text)
1668
Michael Walsh7423c012016-10-04 10:27:21 -05001669 buffer = ""
1670 buffer += sprint_dashes(width=120, char="=")
1671 buffer += sprint_error(error_text)
1672 buffer += "\n"
1673 # Calling sprint_call_stack with stack_frame_ix of 0 causes it to show
1674 # itself and this function in the call stack. This is not helpful to a
1675 # debugger and is therefore clutter. We will adjust the stack_frame_ix to
1676 # hide that information.
Michael Walsh9c75f672017-09-12 17:11:35 -05001677 stack_frame_ix = 1
Michael Walsh7423c012016-10-04 10:27:21 -05001678 caller_func_name = sprint_func_name(2)
1679 if caller_func_name.endswith("print_error_report"):
1680 stack_frame_ix += 1
Michael Walsh7bfa9ab2018-11-16 15:24:26 -06001681 buffer += sprint_call_stack(indent, stack_frame_ix)
Michael Walsh7423c012016-10-04 10:27:21 -05001682 buffer += sprint_pgm_header(indent)
1683 buffer += sprint_dashes(width=120, char="=")
1684
1685 return buffer
1686
Michael Walsh7423c012016-10-04 10:27:21 -05001687
Michael Walsh18176322016-11-15 15:11:21 -06001688def sprint_issuing(cmd_buf,
1689 test_mode=0):
Michael Walshde791732016-09-06 14:25:24 -05001690 r"""
1691 Return a line indicating a command that the program is about to execute.
1692
1693 Sample output for a cmd_buf of "ls"
1694
1695 #(CDT) 2016/08/25 17:57:36 - Issuing: ls
Michael Walshbec416d2016-11-10 08:54:52 -06001696
Michael Walsh4dbb6002019-05-17 15:51:15 -05001697 Description of argument(s):
Michael Walshde791732016-09-06 14:25:24 -05001698 cmd_buf The command to be executed by caller.
Michael Walsh4dbb6002019-05-17 15:51:15 -05001699 test_mode With test_mode set, the output will look
Michael Walshbec416d2016-11-10 08:54:52 -06001700 like this:
1701
1702 #(CDT) 2016/08/25 17:57:36 - (test_mode) Issuing: ls
1703
Michael Walshde791732016-09-06 14:25:24 -05001704 """
1705
Michael Walshbec416d2016-11-10 08:54:52 -06001706 buffer = sprint_time()
1707 if test_mode:
1708 buffer += "(test_mode) "
Michael Walsh61c12982019-03-28 12:38:01 -05001709 if type(cmd_buf) is list:
1710 # Assume this is a robot command in the form of a list.
1711 cmd_buf = ' '.join([str(element) for element in cmd_buf])
Michael Walshbec416d2016-11-10 08:54:52 -06001712 buffer += "Issuing: " + cmd_buf + "\n"
Michael Walshde791732016-09-06 14:25:24 -05001713
1714 return buffer
1715
Michael Walshde791732016-09-06 14:25:24 -05001716
Michael Walshde791732016-09-06 14:25:24 -05001717def sprint_pgm_footer():
Michael Walshde791732016-09-06 14:25:24 -05001718 r"""
1719 Return a standardized footer that programs should print at the end of the
1720 program run. It includes useful information like total run time, etc.
1721 """
1722
1723 buffer = "\n" + sprint_time() + "Finished running " + pgm_name + ".\n\n"
1724
1725 total_time = time.time() - start_time
1726 total_time_string = "%0.6f" % total_time
1727
Michael Walsh7423c012016-10-04 10:27:21 -05001728 buffer += sprint_varx(pgm_name_var_name + "_runtime", total_time_string)
Michael Walshbec416d2016-11-10 08:54:52 -06001729 buffer += "\n"
Michael Walsh7423c012016-10-04 10:27:21 -05001730
1731 return buffer
1732
Michael Walsh7423c012016-10-04 10:27:21 -05001733
Michael Walsh7423c012016-10-04 10:27:21 -05001734def sprint(buffer=""):
Michael Walsh7423c012016-10-04 10:27:21 -05001735 r"""
1736 Simply return the user's buffer. This function is used by the qprint and
1737 dprint functions defined dynamically below, i.e. it would not normally be
1738 called for general use.
1739
Michael Walsh4dbb6002019-05-17 15:51:15 -05001740 Description of argument(s).
Michael Walsh7423c012016-10-04 10:27:21 -05001741 buffer This will be returned to the caller.
1742 """
Michael Walshde791732016-09-06 14:25:24 -05001743
Michael Walsh95e45102018-02-09 12:44:43 -06001744 try:
1745 return str(buffer)
1746 except UnicodeEncodeError:
1747 return buffer
Michael Walshbec416d2016-11-10 08:54:52 -06001748
Michael Walshbec416d2016-11-10 08:54:52 -06001749
Michael Walshbec416d2016-11-10 08:54:52 -06001750def sprintn(buffer=""):
Michael Walshbec416d2016-11-10 08:54:52 -06001751 r"""
1752 Simply return the user's buffer with a line feed. This function is used
1753 by the qprint and dprint functions defined dynamically below, i.e. it
1754 would not normally be called for general use.
1755
Michael Walsh4dbb6002019-05-17 15:51:15 -05001756 Description of argument(s).
Michael Walshbec416d2016-11-10 08:54:52 -06001757 buffer This will be returned to the caller.
1758 """
1759
Michael Walsh95e45102018-02-09 12:44:43 -06001760 try:
1761 buffer = str(buffer) + "\n"
1762 except UnicodeEncodeError:
1763 buffer = buffer + "\n"
Michael Walshbec416d2016-11-10 08:54:52 -06001764
Michael Walshde791732016-09-06 14:25:24 -05001765 return buffer
1766
Michael Walsh168eb0f2017-12-01 15:35:32 -06001767
Michael Walshfd2733c2017-11-13 11:36:20 -06001768def gp_print(buffer,
1769 stream='stdout'):
Michael Walshfd2733c2017-11-13 11:36:20 -06001770 r"""
1771 Print the buffer using either sys.stdout.write or BuiltIn().log_to_console
1772 depending on whether we are running in a robot environment.
1773
1774 This function is intended for use only by other functions in this module.
1775
Michael Walsh4dbb6002019-05-17 15:51:15 -05001776 Description of argument(s):
Michael Walshfd2733c2017-11-13 11:36:20 -06001777 buffer The string to be printed.
1778 stream Either "stdout" or "stderr".
1779 """
1780
1781 if robot_env:
1782 BuiltIn().log_to_console(buffer, stream=stream, no_newline=True)
1783 else:
1784 if stream == "stdout":
1785 sys.stdout.write(buffer)
1786 sys.stdout.flush()
1787 else:
1788 sys.stderr.write(buffer)
1789 sys.stderr.flush()
Michael Walshde791732016-09-06 14:25:24 -05001790
1791
Michael Walsh168eb0f2017-12-01 15:35:32 -06001792def gp_log(buffer):
Michael Walsh168eb0f2017-12-01 15:35:32 -06001793 r"""
1794 Log the buffer using either python logging or BuiltIn().log depending on
1795 whether we are running in a robot environment.
1796
1797 This function is intended for use only by other functions in this module.
1798
Michael Walsh4dbb6002019-05-17 15:51:15 -05001799 Description of argument(s):
Michael Walsh168eb0f2017-12-01 15:35:32 -06001800 buffer The string to be logged.
1801 """
1802
1803 if robot_env:
1804 BuiltIn().log(buffer)
1805 else:
1806 logging.warning(buffer)
1807
1808
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001809def gp_debug_print(buffer):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001810 r"""
Michael Walshfd2733c2017-11-13 11:36:20 -06001811 Print with gp_print only if gen_print_debug is set.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001812
1813 This function is intended for use only by other functions in this module.
1814
Michael Walsh4dbb6002019-05-17 15:51:15 -05001815 Description of argument(s):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001816 buffer The string to be printed.
1817 """
1818
1819 if not gen_print_debug:
1820 return
1821
Michael Walshfd2733c2017-11-13 11:36:20 -06001822 gp_print(buffer)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001823
1824
Michael Walshb1500152017-04-12 15:42:43 -05001825def get_var_value(var_value=None,
1826 default=1,
1827 var_name=None):
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001828 r"""
Michael Walshb1500152017-04-12 15:42:43 -05001829 Return either var_value, the corresponding global value or default.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001830
Michael Walshb1500152017-04-12 15:42:43 -05001831 If var_value is not None, it will simply be returned.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001832
Michael Walshb1500152017-04-12 15:42:43 -05001833 If var_value is None, this function will return the corresponding global
1834 value of the variable in question.
1835
1836 Note: For global values, if we are in a robot environment,
1837 get_variable_value will be used. Otherwise, the __builtin__ version of
1838 the variable is returned (which are set by gen_arg.py functions).
1839
1840 If there is no global value associated with the variable, default is
1841 returned.
1842
1843 This function is useful for other functions in setting default values for
1844 parameters.
1845
1846 Example use:
1847
1848 def my_func(quiet=None):
1849
1850 quiet = int(get_var_value(quiet, 0))
1851
1852 Example calls to my_func():
1853
1854 In the following example, the caller is explicitly asking to have quiet be
1855 set to 1.
1856
1857 my_func(quiet=1)
1858
1859 In the following example, quiet will be set to the global value of quiet,
1860 if defined, or to 0 (the default).
1861
1862 my_func()
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001863
Michael Walsh4dbb6002019-05-17 15:51:15 -05001864 Description of argument(s):
Michael Walshb1500152017-04-12 15:42:43 -05001865 var_value The value to be returned (if not equal to
1866 None).
1867 default The value that is returned if var_value is
1868 None and there is no corresponding global
1869 value defined.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001870 var_name The name of the variable whose value is to
Michael Walshb1500152017-04-12 15:42:43 -05001871 be returned. Under most circumstances,
1872 this value need not be provided. This
1873 function can figure out the name of the
1874 variable passed as var_value. One
1875 exception to this would be if this
1876 function is called directly from a .robot
1877 file.
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001878 """
1879
Michael Walshb1500152017-04-12 15:42:43 -05001880 if var_value is not None:
1881 return var_value
1882
1883 if var_name is None:
1884 var_name = get_arg_name(None, 1, 2)
1885
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001886 if robot_env:
Michael Walshc6537442017-06-06 15:33:52 -05001887 var_value = BuiltIn().get_variable_value("${" + var_name + "}",
1888 default)
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001889 else:
1890 var_value = getattr(__builtin__, var_name, default)
1891
1892 return var_value
1893
Michael Walsh2ee77cd2017-03-08 11:50:17 -06001894
Michael Walsh052ff812018-05-18 16:09:09 -05001895def get_stack_var(var_name,
1896 default="",
1897 init_stack_ix=2):
Michael Walsh052ff812018-05-18 16:09:09 -05001898 r"""
1899 Starting with the caller's stack level, search upward in the call stack,
1900 for a variable named var_name and return its value. If the variable
1901 cannot be found, return default.
1902
1903 Example code:
1904
1905 def func12():
1906 my_loc_var1 = get_stack_var('my_var1', "default value")
1907
1908 def func11():
1909 my_var1 = 11
1910 func12()
1911
1912 In this example, get_stack_var will find the value of my_var1 in func11's
1913 stack and will therefore return the value 11. Therefore, my_loc_var1
1914 would get set to 11.
1915
1916 Description of argument(s):
1917 var_name The name of the variable to be searched
1918 for.
1919 default The value to return if the the variable
1920 cannot be found.
1921 init_stack_ix The initial stack index from which to
1922 begin the search. 0 would be the index of
1923 this func1tion ("get_stack_var"), 1 would
1924 be the index of the function calling this
1925 function, etc.
1926 """
1927
Michael Walsh6f0362c2019-03-25 14:05:14 -05001928 work_around_inspect_stack_cwd_failure()
Michael Walsh052ff812018-05-18 16:09:09 -05001929 return next((frame[0].f_locals[var_name]
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001930 for frame in inspect.stack()[init_stack_ix:]
1931 if var_name in frame[0].f_locals), default)
Michael Walsh052ff812018-05-18 16:09:09 -05001932
1933
Michael Walsh82acf002017-05-04 14:33:05 -05001934# hidden_text is a list of passwords which are to be replaced with asterisks
1935# by print functions defined in this module.
1936hidden_text = []
1937# password_regex is created based on the contents of hidden_text.
1938password_regex = ""
1939
1940
Michael Walsh82acf002017-05-04 14:33:05 -05001941def register_passwords(*args):
Michael Walsh82acf002017-05-04 14:33:05 -05001942 r"""
1943 Register one or more passwords which are to be hidden in output produced
1944 by the print functions in this module.
1945
1946 Note: Blank password values are NOT registered. They are simply ignored.
1947
1948 Description of argument(s):
1949 args One or more password values. If a given
1950 password value is already registered, this
1951 function will simply do nothing.
1952 """
1953
1954 global hidden_text
1955 global password_regex
1956
1957 for password in args:
1958 if password == "":
1959 break
1960 if password in hidden_text:
1961 break
1962
1963 # Place the password into the hidden_text list.
1964 hidden_text.append(password)
1965 # Create a corresponding password regular expression. Escape regex
1966 # special characters too.
1967 password_regex = '(' +\
1968 '|'.join([re.escape(x) for x in hidden_text]) + ')'
1969
Michael Walsh82acf002017-05-04 14:33:05 -05001970
Michael Walsh82acf002017-05-04 14:33:05 -05001971def replace_passwords(buffer):
Michael Walsh82acf002017-05-04 14:33:05 -05001972 r"""
1973 Return the buffer but with all registered passwords replaced by a string
1974 of asterisks.
1975
1976
1977 Description of argument(s):
1978 buffer The string to be returned but with
1979 passwords replaced.
1980 """
1981
1982 global password_regex
1983
1984 if int(os.environ.get("DEBUG_SHOW_PASSWORDS", "0")):
1985 return buffer
1986
1987 if password_regex == "":
1988 # No passwords to replace.
1989 return buffer
1990
1991 return re.sub(password_regex, "********", buffer)
1992
Michael Walshfd2733c2017-11-13 11:36:20 -06001993
1994def create_print_wrapper_funcs(func_names,
1995 stderr_func_names,
Michael Walsh61c12982019-03-28 12:38:01 -05001996 replace_dict,
1997 func_prefix=""):
Michael Walshfd2733c2017-11-13 11:36:20 -06001998 r"""
1999 Generate code for print wrapper functions and return the generated code as
2000 a string.
2001
2002 To illustrate, suppose there is a "print_foo_bar" function in the
2003 func_names list.
2004 This function will...
2005 - Expect that there is an sprint_foo_bar function already in existence.
2006 - Create a print_foo_bar function which calls sprint_foo_bar and prints
Michael Walshfaafa9c2018-06-27 16:39:31 -05002007 the result.
Michael Walshfd2733c2017-11-13 11:36:20 -06002008 - Create a qprint_foo_bar function which calls upon sprint_foo_bar only if
Michael Walshfaafa9c2018-06-27 16:39:31 -05002009 global value quiet is 0.
Michael Walshfd2733c2017-11-13 11:36:20 -06002010 - Create a dprint_foo_bar function which calls upon sprint_foo_bar only if
Michael Walshfaafa9c2018-06-27 16:39:31 -05002011 global value debug is 1.
Michael Walshfd2733c2017-11-13 11:36:20 -06002012
2013 Also, code will be generated to define aliases for each function as well.
2014 Each alias will be created by replacing "print_" in the function name with
2015 "p" For example, the alias for print_foo_bar will be pfoo_bar.
2016
2017 Description of argument(s):
2018 func_names A list of functions for which print
2019 wrapper function code is to be generated.
2020 stderr_func_names A list of functions whose generated code
2021 should print to stderr rather than to
2022 stdout.
2023 replace_dict Please see the create_func_def_string
2024 function in wrap_utils.py for details on
2025 this parameter. This parameter will be
2026 passed directly to create_func_def_string.
Michael Walsh61c12982019-03-28 12:38:01 -05002027 func_prefix Prefix to be pre-pended to the generated
2028 function name.
Michael Walshfd2733c2017-11-13 11:36:20 -06002029 """
2030
2031 buffer = ""
2032
2033 for func_name in func_names:
2034 if func_name in stderr_func_names:
2035 replace_dict['output_stream'] = "stderr"
2036 else:
2037 replace_dict['output_stream'] = "stdout"
2038
2039 s_func_name = "s" + func_name
2040 q_func_name = "q" + func_name
2041 d_func_name = "d" + func_name
2042
2043 # We don't want to try to redefine the "print" function, thus the
2044 # following if statement.
2045 if func_name != "print":
Michael Walsh61c12982019-03-28 12:38:01 -05002046 func_def = create_func_def_string(s_func_name,
2047 func_prefix + func_name,
Michael Walshfd2733c2017-11-13 11:36:20 -06002048 print_func_template,
2049 replace_dict)
2050 buffer += func_def
2051
Michael Walsh61c12982019-03-28 12:38:01 -05002052 func_def = create_func_def_string(s_func_name,
2053 func_prefix + "q" + func_name,
Michael Walshfd2733c2017-11-13 11:36:20 -06002054 qprint_func_template, replace_dict)
2055 buffer += func_def
2056
Michael Walsh61c12982019-03-28 12:38:01 -05002057 func_def = create_func_def_string(s_func_name,
2058 func_prefix + "d" + func_name,
Michael Walshfd2733c2017-11-13 11:36:20 -06002059 dprint_func_template, replace_dict)
2060 buffer += func_def
2061
Michael Walsh61c12982019-03-28 12:38:01 -05002062 func_def = create_func_def_string(s_func_name,
2063 func_prefix + "l" + func_name,
Michael Walsh168eb0f2017-12-01 15:35:32 -06002064 lprint_func_template, replace_dict)
2065 buffer += func_def
2066
Michael Walshfd2733c2017-11-13 11:36:20 -06002067 # Create abbreviated aliases (e.g. spvar is an alias for sprint_var).
2068 alias = re.sub("print_", "p", func_name)
2069 alias = re.sub("print", "p", alias)
Michael Walsh61c12982019-03-28 12:38:01 -05002070 prefixes = [func_prefix + "", "s", func_prefix + "q",
2071 func_prefix + "d", func_prefix + "l"]
Michael Walshfd2733c2017-11-13 11:36:20 -06002072 for prefix in prefixes:
2073 if alias == "p":
2074 continue
2075 func_def = prefix + alias + " = " + prefix + func_name
2076 buffer += func_def + "\n"
2077
2078 return buffer
Michael Walsh82acf002017-05-04 14:33:05 -05002079
2080
Michael Walshde791732016-09-06 14:25:24 -05002081# In the following section of code, we will dynamically create print versions
2082# for each of the sprint functions defined above. So, for example, where we
2083# have an sprint_time() function defined above that returns the time to the
Michael Walsh7423c012016-10-04 10:27:21 -05002084# caller in a string, we will create a corresponding print_time() function
2085# that will print that string directly to stdout.
Michael Walshde791732016-09-06 14:25:24 -05002086
Michael Walsh61c12982019-03-28 12:38:01 -05002087# It can be complicated to follow what's being created below. Here is an
Michael Walshfd2733c2017-11-13 11:36:20 -06002088# example of the print_time() function that will be created:
Michael Walshde791732016-09-06 14:25:24 -05002089
Michael Walshfd2733c2017-11-13 11:36:20 -06002090# def print_time(buffer=''):
Michael Walsh61c12982019-03-28 12:38:01 -05002091# gp_print(replace_passwords(sprint_time(buffer=buffer)), stream='stdout')
2092
2093# For each print function defined below, there will also be a qprint, a
2094# dprint and an lprint version defined (e.g. qprint_time, dprint_time,
2095# lprint_time).
2096
2097# The q version of each print function will only print if the quiet variable
2098# is 0.
2099# The d version of each print function will only print if the debug variable
2100# is 1.
2101# The l version of each print function will print the contents as log data.
2102# For conventional programs, this means use of the logging module. For robot
2103# programs it means use of the BuiltIn().log() function.
Michael Walshde791732016-09-06 14:25:24 -05002104
Michael Walshfd2733c2017-11-13 11:36:20 -06002105# Templates for the various print wrapper functions.
2106print_func_template = \
2107 [
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05002108 " <mod_qualifier>gp_print(<mod_qualifier>replace_passwords("
2109 + "<call_line>), stream='<output_stream>')"
Michael Walshfd2733c2017-11-13 11:36:20 -06002110 ]
2111
2112qprint_func_template = \
2113 [
Michael Walsh589ae762019-03-19 13:22:39 -05002114 " quiet_default = <mod_qualifier>get_var_value(None, 0, \"quiet\")",
2115 " quiet = <mod_qualifier>get_stack_var(\"quiet\", quiet_default)",
2116 " if int(quiet): return"
Michael Walshfd2733c2017-11-13 11:36:20 -06002117 ] + print_func_template
2118
2119dprint_func_template = \
2120 [
Michael Walsh589ae762019-03-19 13:22:39 -05002121 " debug_default = <mod_qualifier>get_var_value(None, 0, \"debug\")",
2122 " debug = <mod_qualifier>get_stack_var(\"debug\", debug_default)",
2123 " if not int(debug): return"
Michael Walshfd2733c2017-11-13 11:36:20 -06002124 ] + print_func_template
2125
Michael Walsh168eb0f2017-12-01 15:35:32 -06002126lprint_func_template = \
2127 [
Michael Walsh61c12982019-03-28 12:38:01 -05002128 " <mod_qualifier>set_last_seconds_ix(<mod_qualifier>"
2129 + "lprint_last_seconds_ix())",
2130 " <mod_qualifier>gp_log(<mod_qualifier>replace_passwords"
2131 + "(<call_line>))",
2132 " <mod_qualifier>set_last_seconds_ix(<mod_qualifier>"
2133 + "standard_print_last_seconds_ix())"
Michael Walsh168eb0f2017-12-01 15:35:32 -06002134 ]
2135
Michael Walsh81c02342018-01-05 15:43:28 -06002136replace_dict = {'output_stream': 'stdout', 'mod_qualifier': ''}
Michael Walshfd2733c2017-11-13 11:36:20 -06002137
Michael Walsh61c12982019-03-28 12:38:01 -05002138gp_debug_print("robot_env: " + str(robot_env) + "\n")
Michael Walshde791732016-09-06 14:25:24 -05002139
2140# func_names contains a list of all print functions which should be created
2141# from their sprint counterparts.
2142func_names = ['print_time', 'print_timen', 'print_error', 'print_varx',
Michael Walsh18176322016-11-15 15:11:21 -06002143 'print_var', 'print_vars', 'print_dashes', 'indent',
2144 'print_call_stack', 'print_func_name', 'print_executing',
2145 'print_pgm_header', 'print_issuing', 'print_pgm_footer',
2146 'print_error_report', 'print', 'printn']
Michael Walshde791732016-09-06 14:25:24 -05002147
Michael Walsh2ee77cd2017-03-08 11:50:17 -06002148# stderr_func_names is a list of functions whose output should go to stderr
2149# rather than stdout.
2150stderr_func_names = ['print_error', 'print_error_report']
Michael Walshde791732016-09-06 14:25:24 -05002151
Michael Walshfd2733c2017-11-13 11:36:20 -06002152func_defs = create_print_wrapper_funcs(func_names, stderr_func_names,
2153 replace_dict)
2154gp_debug_print(func_defs)
2155exec(func_defs)