1#!/usr/bin/env python3
2
3r"""
4See help text for details (--help or -h option)..
5
6Example properties file content:
7quiet=n
8test_mode=y
9pos=file1 file2 file3
10
11Example call:
12
13prop_call.py --prop_file_name=prop_file my_program
14
15The result is that the following command will be run:
16my_program --test_mode=y --quiet=n file1 file2 file3
17"""
18
19import os
20import sys
21
22save_path_0 = sys.path[0]
23del sys.path[0]
24
25from gen_arg import *  # NOQA
26from gen_cmd import *  # NOQA
27from gen_misc import *  # NOQA
28from gen_print import *  # NOQA
29from gen_valid import *  # NOQA
30
31# Restore sys.path[0].
32sys.path.insert(0, save_path_0)
33
34
35parser = argparse.ArgumentParser(
36    usage="%(prog)s [OPTIONS]",
37    description="%(prog)s will call a program using parameters retrieved"
38    + " from the given properties file.",
39    formatter_class=argparse.ArgumentDefaultsHelpFormatter,
40    prefix_chars="-+",
41)
42
43parser.add_argument(
44    "--prop_dir_path",
45    default=os.environ.get("PROP_DIR_PATH", os.getcwd()),
46    help="The path to the directory that contains the properties file."
47    + '  The default value is environment variable "PROP_DIR_PATH", if'
48    + " set.  Otherwise, it is the current working directory.",
49)
50
51parser.add_argument(
52    "--prop_file_name",
53    help="The path to a properties file that contains the parameters to"
54    + ' pass to the program.  If the properties file has a ".properties"'
55    + " extension, the caller need not specify the extension.  The format"
56    + " of each line in the properties file should be as follows:"
57    + " <parm_name=parm_value>.  Do not quote the parm value.  To specify"
58    + ' positional parms, use a parm name of "pos".  For example: pos=this'
59    " value",
60)
61
62parser.add_argument("program_name", help="The name of the program to be run.")
63
64# Populate stock_list with options we want.
65stock_list = [("test_mode", 0), ("quiet", 1), ("debug", 0)]
66
67
68def exit_function(signal_number=0, frame=None):
69    r"""
70    Execute whenever the program ends normally or with the signals that we catch (i.e. TERM, INT).
71    """
72
73    dprint_executing()
74    dprint_var(signal_number)
75
76    qprint_pgm_footer()
77
78
79def signal_handler(signal_number, frame):
80    r"""
81    Handle signals.  Without a function to catch a SIGTERM or SIGINT, our program would terminate immediately
82    with return code 143 and without calling our exit_function.
83    """
84
85    # Our convention is to set up exit_function with atexit.register() so there is no need to explicitly
86    # call exit_function from here.
87
88    dprint_executing()
89
90    # Calling exit prevents us from returning to the code that was running when we received the signal.
91    exit(0)
92
93
94def validate_parms():
95    r"""
96    Validate program parameters, etc.  Return True or False (i.e. pass/fail) accordingly.
97    """
98
99    global prop_dir_path
100    global prop_file_path
101
102    if not valid_dir_path(prop_dir_path):
103        return False
104    prop_dir_path = add_trailing_slash(prop_dir_path)
105
106    if not valid_value(prop_file_name):
107        return False
108
109    prop_file_path = prop_dir_path + prop_file_name
110
111    # If properties file is not found, try adding ".properties" extension.
112    if not os.path.isfile(prop_file_path):
113        alt_prop_file_path = prop_file_path + ".properties"
114        if os.path.isfile(alt_prop_file_path):
115            prop_file_path = alt_prop_file_path
116
117    if not valid_file_path(prop_file_path):
118        return False
119
120    if not valid_value(program_name):
121        return False
122
123    gen_post_validation(exit_function, signal_handler)
124
125    return True
126
127
128def main():
129    if not gen_get_options(parser, stock_list):
130        return False
131
132    if not validate_parms():
133        return False
134
135    qprint_pgm_header()
136
137    # Get the parameters from the properties file.
138    properties = my_parm_file(prop_file_path)
139    # The parms (including program name) need to go into a list.
140    parms = [program_name]
141    for key, value in properties.items():
142        if key == "pos":
143            # Process positional parm(s).
144            parms.extend(value.split())
145        else:
146            parms.append("--" + key + "=" + escape_bash_quotes(value))
147
148    # parm_string is only created for display in non-quiet mode.
149    parm_string = " ".join(parms[1:])
150    cmd_buf = program_name + " " + parm_string
151    qprint_issuing(cmd_buf)
152    if not test_mode:
153        os.execvp(program_name, parms)
154
155    return True
156
157
158# Main
159
160if not main():
161    exit(1)
162