1#!/usr/bin/env python
2
3r"""
4This module provides valuable argument processing functions like
5gen_get_options and sprint_args.
6"""
7
8import sys
9try:
10    import __builtin__
11except ImportError:
12    import builtins as __builtin__
13import atexit
14import signal
15import argparse
16
17import gen_print as gp
18import gen_valid as gv
19
20default_string = '  The default value is "%(default)s".'
21
22
23def gen_get_options(parser,
24                    stock_list=[]):
25    r"""
26    Parse the command line arguments using the parser object passed and return
27    True/False (i.e. pass/fail).  However, if gv.exit_on_error is set, simply
28    exit the program on failure.  Also set the following built in values:
29
30    __builtin__.quiet      This value is used by the qprint functions.
31    __builtin__.test_mode  This value is used by command processing functions.
32    __builtin__.debug      This value is used by the dprint functions.
33    __builtin__.arg_obj    This value is used by print_program_header, etc.
34    __builtin__.parser     This value is used by print_program_header, etc.
35
36    Description of arguments:
37    parser                          A parser object.  See argparse module
38                                    documentation for details.
39    stock_list                      The caller can use this parameter to
40                                    request certain stock parameters offered
41                                    by this function.  For example, this
42                                    function will define a "quiet" option upon
43                                    request.  This includes stop help text and
44                                    parm checking.  The stock_list is a list
45                                    of tuples each of which consists of an
46                                    arg_name and a default value.  Example:
47                                    stock_list = [("test_mode", 0), ("quiet",
48                                    1), ("debug", 0)]
49    """
50
51    # This is a list of stock parms that we support.
52    master_stock_list = ["quiet", "test_mode", "debug", "loglevel"]
53
54    # Process stock_list.
55    for ix in range(0, len(stock_list)):
56        if len(stock_list[ix]) < 1:
57            error_message = "Programmer error - stock_list[" + str(ix) +\
58                            "] is supposed to be a tuple containing at" +\
59                            " least one element which is the name of" +\
60                            " the desired stock parameter:\n" +\
61                            gp.sprint_var(stock_list)
62            return gv.process_error_message(error_message)
63        if isinstance(stock_list[ix], tuple):
64            arg_name = stock_list[ix][0]
65            default = stock_list[ix][1]
66        else:
67            arg_name = stock_list[ix]
68            default = None
69
70        if arg_name not in master_stock_list:
71            error_message = "Programmer error - arg_name \"" + arg_name +\
72                            "\" not found found in stock list:\n" +\
73                            gp.sprint_var(master_stock_list)
74            return gv.process_error_message(error_message)
75
76        if arg_name == "quiet":
77            if default is None:
78                default = 0
79            parser.add_argument(
80                '--quiet',
81                default=default,
82                type=int,
83                choices=[1, 0],
84                help='If this parameter is set to "1", %(prog)s'
85                     + ' will print only essential information, i.e. it will'
86                     + ' not echo parameters, echo commands, print the total'
87                     + ' run time, etc.' + default_string)
88        elif arg_name == "test_mode":
89            if default is None:
90                default = 0
91            parser.add_argument(
92                '--test_mode',
93                default=default,
94                type=int,
95                choices=[1, 0],
96                help='This means that %(prog)s should go through all the'
97                     + ' motions but not actually do anything substantial.'
98                     + '  This is mainly to be used by the developer of'
99                     + ' %(prog)s.' + default_string)
100        elif arg_name == "debug":
101            if default is None:
102                default = 0
103            parser.add_argument(
104                '--debug',
105                default=default,
106                type=int,
107                choices=[1, 0],
108                help='If this parameter is set to "1", %(prog)s will print'
109                     + ' additional debug information.  This is mainly to be'
110                     + ' used by the developer of %(prog)s.' + default_string)
111        elif arg_name == "loglevel":
112            if default is None:
113                default = "info"
114            parser.add_argument(
115                '--loglevel',
116                default=default,
117                type=str,
118                choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL',
119                         'debug', 'info', 'warning', 'error', 'critical'],
120                help='If this parameter is set to "1", %(prog)s will print'
121                     + ' additional debug information.  This is mainly to be'
122                     + ' used by the developer of %(prog)s.' + default_string)
123
124    arg_obj = parser.parse_args()
125
126    __builtin__.quiet = 0
127    __builtin__.test_mode = 0
128    __builtin__.debug = 0
129    __builtin__.loglevel = 'WARNING'
130    for ix in range(0, len(stock_list)):
131        if isinstance(stock_list[ix], tuple):
132            arg_name = stock_list[ix][0]
133            default = stock_list[ix][1]
134        else:
135            arg_name = stock_list[ix]
136            default = None
137        if arg_name == "quiet":
138            __builtin__.quiet = arg_obj.quiet
139        elif arg_name == "test_mode":
140            __builtin__.test_mode = arg_obj.test_mode
141        elif arg_name == "debug":
142            __builtin__.debug = arg_obj.debug
143        elif arg_name == "loglevel":
144            __builtin__.loglevel = arg_obj.loglevel
145
146    __builtin__.arg_obj = arg_obj
147    __builtin__.parser = parser
148
149    # For each command line parameter, create a corresponding global variable
150    # and assign it the appropriate value.  For example, if the command line
151    # contained "--last_name='Smith', we'll create a global variable named
152    # "last_name" with the value "Smith".
153    module = sys.modules['__main__']
154    for key in arg_obj.__dict__:
155        setattr(module, key, getattr(__builtin__.arg_obj, key))
156
157    return True
158
159
160def set_pgm_arg(var_value,
161                var_name=None):
162    r"""
163    Set the value of the arg_obj.__dict__ entry named in var_name with the
164    var_value provided.  Also, set corresponding global variable.
165
166    Description of arguments:
167    var_value                       The value to set in the variable.
168    var_name                        The name of the variable to set.  This
169                                    defaults to the name of the variable used
170                                    for var_value when calling this function.
171    """
172
173    if var_name is None:
174        var_name = gp.get_arg_name(None, 1, 2)
175
176    arg_obj.__dict__[var_name] = var_value
177    module = sys.modules['__main__']
178    setattr(module, var_name, var_value)
179    if var_name == "quiet":
180        __builtin__.quiet = var_value
181    elif var_name == "debug":
182        __builtin__.debug = var_value
183    elif var_name == "test_mode":
184        __builtin__.test_mode = var_value
185
186
187def sprint_args(arg_obj,
188                indent=0):
189    r"""
190    sprint_var all of the arguments found in arg_obj and return the result as
191    a string.
192
193    Description of arguments:
194    arg_obj                         An argument object such as is returned by
195                                    the argparse parse_args() method.
196    indent                          The number of spaces to indent each line
197                                    of output.
198    """
199
200    col1_width = gp.dft_col1_width + indent
201
202    buffer = ""
203    for key in arg_obj.__dict__:
204        buffer += gp.sprint_varx(key, getattr(arg_obj, key), 0, indent,
205                                 col1_width)
206    return buffer
207
208
209module = sys.modules["__main__"]
210
211
212def gen_exit_function(signal_number=0,
213                      frame=None):
214    r"""
215    Execute whenever the program ends normally or with the signals that we
216    catch (i.e. TERM, INT).
217    """
218
219    gp.dprint_executing()
220    gp.dprint_var(signal_number)
221
222    # Call the main module's exit_function if it is defined.
223    exit_function = getattr(module, "exit_function", None)
224    if exit_function:
225        exit_function(signal_number, frame)
226
227    gp.qprint_pgm_footer()
228
229
230def gen_signal_handler(signal_number,
231                       frame):
232    r"""
233    Handle signals.  Without a function to catch a SIGTERM or SIGINT, the
234    program would terminate immediately with return code 143 and without
235    calling the exit_function.
236    """
237
238    # The convention is to set up exit_function with atexit.register() so
239    # there is no need to explicitly call exit_function from here.
240
241    gp.dprint_executing()
242
243    # Calling exit prevents control from returning to the code that was
244    # running when the signal was received.
245    exit(0)
246
247
248def gen_post_validation(exit_function=None,
249                        signal_handler=None):
250    r"""
251    Do generic post-validation processing.  By "post", we mean that this is to
252    be called from a validation function after the caller has done any
253    validation desired.  If the calling program passes exit_function and
254    signal_handler parms, this function will register them.  In other words,
255    it will make the signal_handler functions get called for SIGINT and
256    SIGTERM and will make the exit_function function run prior to the
257    termination of the program.
258
259    Description of arguments:
260    exit_function                   A function object pointing to the caller's
261                                    exit function.  This defaults to this
262                                    module's gen_exit_function.
263    signal_handler                  A function object pointing to the caller's
264                                    signal_handler function.  This defaults to
265                                    this module's gen_signal_handler.
266    """
267
268    # Get defaults.
269    exit_function = exit_function or gen_exit_function
270    signal_handler = signal_handler or gen_signal_handler
271
272    atexit.register(exit_function)
273    signal.signal(signal.SIGINT, signal_handler)
274    signal.signal(signal.SIGTERM, signal_handler)
275
276
277def gen_setup():
278    r"""
279    Do general setup for a program.
280    """
281
282    # Set exit_on_error for gen_valid functions.
283    gv.set_exit_on_error(True)
284
285    # Get main module variable values.
286    parser = getattr(module, "parser")
287    stock_list = getattr(module, "stock_list")
288    validate_parms = getattr(module, "validate_parms", None)
289
290    gen_get_options(parser, stock_list)
291
292    if validate_parms:
293        validate_parms()
294    gen_post_validation()
295
296    gp.qprint_pgm_header()
297