blob: 529cd9732329aa0b7d57210d5d6e881e14cb4b0c [file] [log] [blame]
Michael Walsh7423c012016-10-04 10:27:21 -05001#!/usr/bin/env python
2
3r"""
Michael Walsh410b1782019-10-22 15:56:18 -05004This module provides valuable argument processing functions like gen_get_options and sprint_args.
Michael Walsh7423c012016-10-04 10:27:21 -05005"""
6
7import sys
George Keishing3b7115a2018-08-02 10:48:17 -05008try:
9 import __builtin__
10except ImportError:
11 import builtins as __builtin__
Michael Walsh7423c012016-10-04 10:27:21 -050012import atexit
13import signal
14import argparse
15
16import gen_print as gp
Michael Walsh69d58ae2018-06-01 15:18:57 -050017import gen_valid as gv
Michael Walsh7423c012016-10-04 10:27:21 -050018
19default_string = ' The default value is "%(default)s".'
20
21
Michael Walsh7423c012016-10-04 10:27:21 -050022def gen_get_options(parser,
23 stock_list=[]):
Michael Walsh7423c012016-10-04 10:27:21 -050024 r"""
Michael Walsh410b1782019-10-22 15:56:18 -050025 Parse the command line arguments using the parser object passed and return True/False (i.e. pass/fail).
26 However, if gv.exit_on_error is set, simply exit the program on failure. Also set the following built in
27 values:
Michael Walsh7423c012016-10-04 10:27:21 -050028
29 __builtin__.quiet This value is used by the qprint functions.
30 __builtin__.test_mode This value is used by command processing functions.
31 __builtin__.debug This value is used by the dprint functions.
32 __builtin__.arg_obj This value is used by print_program_header, etc.
33 __builtin__.parser This value is used by print_program_header, etc.
34
35 Description of arguments:
Michael Walsh410b1782019-10-22 15:56:18 -050036 parser A parser object. See argparse module documentation for details.
37 stock_list The caller can use this parameter to request certain stock parameters
38 offered by this function. For example, this function will define a
39 "quiet" option upon request. This includes stop help text and parm
40 checking. The stock_list is a list of tuples each of which consists of
41 an arg_name and a default value. Example: stock_list = [("test_mode",
42 0), ("quiet", 1), ("debug", 0)]
Michael Walsh7423c012016-10-04 10:27:21 -050043 """
44
45 # This is a list of stock parms that we support.
46 master_stock_list = ["quiet", "test_mode", "debug", "loglevel"]
47
48 # Process stock_list.
49 for ix in range(0, len(stock_list)):
50 if len(stock_list[ix]) < 1:
Michael Walsh69d58ae2018-06-01 15:18:57 -050051 error_message = "Programmer error - stock_list[" + str(ix) +\
52 "] is supposed to be a tuple containing at" +\
53 " least one element which is the name of" +\
54 " the desired stock parameter:\n" +\
55 gp.sprint_var(stock_list)
56 return gv.process_error_message(error_message)
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -050057 if isinstance(stock_list[ix], tuple):
Michael Walsh7423c012016-10-04 10:27:21 -050058 arg_name = stock_list[ix][0]
59 default = stock_list[ix][1]
60 else:
61 arg_name = stock_list[ix]
62 default = None
63
64 if arg_name not in master_stock_list:
Michael Walsh69d58ae2018-06-01 15:18:57 -050065 error_message = "Programmer error - arg_name \"" + arg_name +\
66 "\" not found found in stock list:\n" +\
67 gp.sprint_var(master_stock_list)
68 return gv.process_error_message(error_message)
Michael Walsh7423c012016-10-04 10:27:21 -050069
70 if arg_name == "quiet":
71 if default is None:
72 default = 0
73 parser.add_argument(
74 '--quiet',
75 default=default,
76 type=int,
77 choices=[1, 0],
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -050078 help='If this parameter is set to "1", %(prog)s'
79 + ' will print only essential information, i.e. it will'
80 + ' not echo parameters, echo commands, print the total'
81 + ' run time, etc.' + default_string)
Michael Walsh7423c012016-10-04 10:27:21 -050082 elif arg_name == "test_mode":
83 if default is None:
84 default = 0
85 parser.add_argument(
86 '--test_mode',
87 default=default,
88 type=int,
89 choices=[1, 0],
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -050090 help='This means that %(prog)s should go through all the'
91 + ' motions but not actually do anything substantial.'
92 + ' This is mainly to be used by the developer of'
93 + ' %(prog)s.' + default_string)
Michael Walsh7423c012016-10-04 10:27:21 -050094 elif arg_name == "debug":
95 if default is None:
96 default = 0
97 parser.add_argument(
98 '--debug',
99 default=default,
100 type=int,
101 choices=[1, 0],
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500102 help='If this parameter is set to "1", %(prog)s will print'
103 + ' additional debug information. This is mainly to be'
104 + ' used by the developer of %(prog)s.' + default_string)
Michael Walsh7423c012016-10-04 10:27:21 -0500105 elif arg_name == "loglevel":
106 if default is None:
107 default = "info"
108 parser.add_argument(
109 '--loglevel',
110 default=default,
111 type=str,
112 choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL',
113 'debug', 'info', 'warning', 'error', 'critical'],
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500114 help='If this parameter is set to "1", %(prog)s will print'
115 + ' additional debug information. This is mainly to be'
116 + ' used by the developer of %(prog)s.' + default_string)
Michael Walsh7423c012016-10-04 10:27:21 -0500117
118 arg_obj = parser.parse_args()
119
120 __builtin__.quiet = 0
121 __builtin__.test_mode = 0
122 __builtin__.debug = 0
123 __builtin__.loglevel = 'WARNING'
124 for ix in range(0, len(stock_list)):
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500125 if isinstance(stock_list[ix], tuple):
Michael Walsh7423c012016-10-04 10:27:21 -0500126 arg_name = stock_list[ix][0]
127 default = stock_list[ix][1]
128 else:
129 arg_name = stock_list[ix]
130 default = None
131 if arg_name == "quiet":
132 __builtin__.quiet = arg_obj.quiet
133 elif arg_name == "test_mode":
134 __builtin__.test_mode = arg_obj.test_mode
135 elif arg_name == "debug":
136 __builtin__.debug = arg_obj.debug
137 elif arg_name == "loglevel":
138 __builtin__.loglevel = arg_obj.loglevel
139
140 __builtin__.arg_obj = arg_obj
141 __builtin__.parser = parser
142
Michael Walsh410b1782019-10-22 15:56:18 -0500143 # For each command line parameter, create a corresponding global variable and assign it the appropriate
144 # value. For example, if the command line contained "--last_name='Smith', we'll create a global variable
145 # named "last_name" with the value "Smith".
Michael Walsh7423c012016-10-04 10:27:21 -0500146 module = sys.modules['__main__']
147 for key in arg_obj.__dict__:
148 setattr(module, key, getattr(__builtin__.arg_obj, key))
149
150 return True
151
Michael Walsh7423c012016-10-04 10:27:21 -0500152
Michael Walshc33ef372017-01-10 11:46:29 -0600153def set_pgm_arg(var_value,
154 var_name=None):
Michael Walshc33ef372017-01-10 11:46:29 -0600155 r"""
Michael Walsh410b1782019-10-22 15:56:18 -0500156 Set the value of the arg_obj.__dict__ entry named in var_name with the var_value provided. Also, set
157 corresponding global variable.
Michael Walshc33ef372017-01-10 11:46:29 -0600158
159 Description of arguments:
160 var_value The value to set in the variable.
Michael Walsh410b1782019-10-22 15:56:18 -0500161 var_name The name of the variable to set. This defaults to the name of the
162 variable used for var_value when calling this function.
Michael Walshc33ef372017-01-10 11:46:29 -0600163 """
164
165 if var_name is None:
166 var_name = gp.get_arg_name(None, 1, 2)
167
168 arg_obj.__dict__[var_name] = var_value
169 module = sys.modules['__main__']
170 setattr(module, var_name, var_value)
171 if var_name == "quiet":
172 __builtin__.quiet = var_value
173 elif var_name == "debug":
174 __builtin__.debug = var_value
175 elif var_name == "test_mode":
176 __builtin__.test_mode = var_value
177
Michael Walshc33ef372017-01-10 11:46:29 -0600178
Michael Walsh7423c012016-10-04 10:27:21 -0500179def sprint_args(arg_obj,
180 indent=0):
Michael Walsh7423c012016-10-04 10:27:21 -0500181 r"""
Michael Walsh410b1782019-10-22 15:56:18 -0500182 sprint_var all of the arguments found in arg_obj and return the result as a string.
Michael Walsh7423c012016-10-04 10:27:21 -0500183
184 Description of arguments:
Michael Walsh410b1782019-10-22 15:56:18 -0500185 arg_obj An argument object such as is returned by the argparse parse_args()
186 method.
187 indent The number of spaces to indent each line of output.
Michael Walsh7423c012016-10-04 10:27:21 -0500188 """
189
Michael Walsh0d5f96a2019-05-20 10:09:57 -0500190 col1_width = gp.dft_col1_width + indent
Michael Walshbec416d2016-11-10 08:54:52 -0600191
Michael Walsh7423c012016-10-04 10:27:21 -0500192 buffer = ""
Michael Walsh7423c012016-10-04 10:27:21 -0500193 for key in arg_obj.__dict__:
Michael Walshbec416d2016-11-10 08:54:52 -0600194 buffer += gp.sprint_varx(key, getattr(arg_obj, key), 0, indent,
Michael Walsh0d5f96a2019-05-20 10:09:57 -0500195 col1_width)
Michael Walsh7423c012016-10-04 10:27:21 -0500196 return buffer
197
Michael Walsh7423c012016-10-04 10:27:21 -0500198
Michael Walsh5328f382019-09-13 14:18:55 -0500199module = sys.modules["__main__"]
200
201
202def gen_exit_function(signal_number=0,
203 frame=None):
204 r"""
Michael Walsh410b1782019-10-22 15:56:18 -0500205 Execute whenever the program ends normally or with the signals that we catch (i.e. TERM, INT).
Michael Walsh5328f382019-09-13 14:18:55 -0500206 """
207
208 gp.dprint_executing()
209 gp.dprint_var(signal_number)
210
211 # Call the main module's exit_function if it is defined.
212 exit_function = getattr(module, "exit_function", None)
213 if exit_function:
214 exit_function(signal_number, frame)
215
216 gp.qprint_pgm_footer()
217
218
219def gen_signal_handler(signal_number,
220 frame):
221 r"""
Michael Walsh410b1782019-10-22 15:56:18 -0500222 Handle signals. Without a function to catch a SIGTERM or SIGINT, the program would terminate immediately
223 with return code 143 and without calling the exit_function.
Michael Walsh5328f382019-09-13 14:18:55 -0500224 """
225
Michael Walsh410b1782019-10-22 15:56:18 -0500226 # The convention is to set up exit_function with atexit.register() so there is no need to explicitly
227 # call exit_function from here.
Michael Walsh5328f382019-09-13 14:18:55 -0500228
229 gp.dprint_executing()
230
Michael Walsh410b1782019-10-22 15:56:18 -0500231 # Calling exit prevents control from returning to the code that was running when the signal was received.
Michael Walsh5328f382019-09-13 14:18:55 -0500232 exit(0)
233
234
Michael Walsh7423c012016-10-04 10:27:21 -0500235def gen_post_validation(exit_function=None,
236 signal_handler=None):
Michael Walsh7423c012016-10-04 10:27:21 -0500237 r"""
Michael Walsh410b1782019-10-22 15:56:18 -0500238 Do generic post-validation processing. By "post", we mean that this is to be called from a validation
239 function after the caller has done any validation desired. If the calling program passes exit_function
240 and signal_handler parms, this function will register them. In other words, it will make the
241 signal_handler functions get called for SIGINT and SIGTERM and will make the exit_function function run
242 prior to the termination of the program.
Michael Walsh7423c012016-10-04 10:27:21 -0500243
244 Description of arguments:
Michael Walsh410b1782019-10-22 15:56:18 -0500245 exit_function A function object pointing to the caller's exit function. This defaults
246 to this module's gen_exit_function.
247 signal_handler A function object pointing to the caller's signal_handler function. This
248 defaults to this module's gen_signal_handler.
Michael Walsh7423c012016-10-04 10:27:21 -0500249 """
250
Michael Walsh5328f382019-09-13 14:18:55 -0500251 # Get defaults.
252 exit_function = exit_function or gen_exit_function
253 signal_handler = signal_handler or gen_signal_handler
254
255 atexit.register(exit_function)
256 signal.signal(signal.SIGINT, signal_handler)
257 signal.signal(signal.SIGTERM, signal_handler)
258
259
260def gen_setup():
261 r"""
262 Do general setup for a program.
263 """
264
265 # Set exit_on_error for gen_valid functions.
266 gv.set_exit_on_error(True)
267
268 # Get main module variable values.
269 parser = getattr(module, "parser")
270 stock_list = getattr(module, "stock_list")
271 validate_parms = getattr(module, "validate_parms", None)
272
273 gen_get_options(parser, stock_list)
274
275 if validate_parms:
276 validate_parms()
277 gen_post_validation()
278
279 gp.qprint_pgm_header()