1#!/usr/bin/env python
2
3r"""
4This module provides valuable argument processing functions like gen_get_options and sprint_args.
5"""
6
7import sys
8try:
9    import __builtin__
10except ImportError:
11    import builtins as __builtin__
12import atexit
13import signal
14import argparse
15
16import gen_print as gp
17import gen_valid as gv
18
19default_string = '  The default value is "%(default)s".'
20
21
22def gen_get_options(parser,
23                    stock_list=[]):
24    r"""
25    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:
28
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:
36    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)]
43    """
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:
51            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)
57        if isinstance(stock_list[ix], tuple):
58            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:
65            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)
69
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],
78                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)
82        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],
90                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)
94        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],
102                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)
105        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'],
114                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)
117
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)):
125        if isinstance(stock_list[ix], tuple):
126            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
143    # 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".
146    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
152
153def set_pgm_arg(var_value,
154                var_name=None):
155    r"""
156    Set the value of the arg_obj.__dict__ entry named in var_name with the var_value provided.  Also, set
157    corresponding global variable.
158
159    Description of arguments:
160    var_value                       The value to set in the variable.
161    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.
163    """
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
178
179def sprint_args(arg_obj,
180                indent=0):
181    r"""
182    sprint_var all of the arguments found in arg_obj and return the result as a string.
183
184    Description of arguments:
185    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.
188    """
189
190    col1_width = gp.dft_col1_width + indent
191
192    buffer = ""
193    for key in arg_obj.__dict__:
194        buffer += gp.sprint_varx(key, getattr(arg_obj, key), 0, indent,
195                                 col1_width)
196    return buffer
197
198
199module = sys.modules["__main__"]
200
201
202def gen_exit_function(signal_number=0,
203                      frame=None):
204    r"""
205    Execute whenever the program ends normally or with the signals that we catch (i.e. TERM, INT).
206    """
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"""
222    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.
224    """
225
226    # The convention is to set up exit_function with atexit.register() so there is no need to explicitly
227    # call exit_function from here.
228
229    gp.dprint_executing()
230
231    # Calling exit prevents control from returning to the code that was running when the signal was received.
232    exit(0)
233
234
235def gen_post_validation(exit_function=None,
236                        signal_handler=None):
237    r"""
238    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.
243
244    Description of arguments:
245    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.
249    """
250
251    # 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()
280