parameter.py 12.1 KB
Newer Older
Jerome Mariette's avatar
Jerome Mariette committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#
# Copyright (C) 2012 INRA
# 
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

import types
Jerome Mariette's avatar
Jerome Mariette committed
19
import datetime
Jerome Mariette's avatar
Jerome Mariette committed
20
import argparse
21
22
from argparse import _ensure_value
import copy as _copy
Jerome Mariette's avatar
Jerome Mariette committed
23
24
# import custom types
from workflows.types import *
Jerome Mariette's avatar
Jerome Mariette committed
25
26


Jerome Mariette's avatar
Jerome Mariette committed
27
class MultipleParameters(object):
28
    def __init__(self, types, required, choices, excludes, default, actions):
Jerome Mariette's avatar
Jerome Mariette committed
29
        self.types = types
30
        self.choices = choices
Jerome Mariette's avatar
Jerome Mariette committed
31
        self.excludes = excludes
32
        self.default = default
33
        self.actions = actions
Jerome Mariette's avatar
Jerome Mariette committed
34
        self.index = None
35
        self.required = required
Jerome Mariette's avatar
Jerome Mariette committed
36
        self.__name__ = "MultipleParameters"
37
        
38
    def __call__(self, arg):
Jerome Mariette's avatar
Jerome Mariette committed
39
40
41
        parts = arg.split("=")
        if not self.types.has_key(parts[0]):
            raise argparse.ArgumentTypeError(parts[0] + " is an invalid flag! Available ones are: "+", ".join(self.types.keys()))
42
43
44
        if self.choices[parts[0]] != None:
            if parts[1] not in self.choices[parts[0]]:
                raise argparse.ArgumentTypeError("argument " + parts[0] + ": invalid choice: '" + parts[1] + "' (choose from " + ", ".join(self.choices[parts[0]]) + "))")
45
46
47
48
49
50
51
52
53
54
        
        if self.types[parts[0]] == types.BooleanType:
            return (parts[0], not self.default[parts[0]], self.required, self.excludes)
        else:
            try:
                value = self.types[parts[0]](parts[1])
            except:
                raise argparse.ArgumentTypeError("invalid " + self.types[parts[0]].__name__ + " value: '" + parts[1] + "' for sub parameter '" + parts[0] + "'")
        
            self.index = parts[0]
55
            return (parts[0], value, self.required, self.excludes, self.actions)
Jerome Mariette's avatar
Jerome Mariette committed
56

57
58
class MiltipleAction(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):
59
60
61
62
63
64
65
        # what is commun within required and excludes
        exclusif_required = {}
        for exclude_group in values[0][3]:
            exclusif_required[exclude_group] = False
            for param in values[0][3][exclude_group]:
                if param in values[0][2]:
                    exclusif_required[exclude_group] = True
Jerome Mariette's avatar
Jerome Mariette committed
66
67
        given_params = []
        # first check for required parameters
68
69
        try:
            required = _copy.copy(values[0][2])
70
71
72
73
74
75
            # delete required that are exclusive
            for group in exclusif_required:
                if exclusif_required[group]:
                    for val in values[0][3][group]:
                        if val in required:
                            del required[required.index(val)]
76
            for val in values:
Jerome Mariette's avatar
Jerome Mariette committed
77
                given_params.append(val[0])
78
79
80
81
82
                if val[0] in required:
                    del required[required.index(val[0])]
        except: pass
        if len(required) == 1: parser.error(", ".join(required) + " is a required parameter!")
        elif len(required) > 1: parser.error(", ".join(required) + " are required parameters!")
Jerome Mariette's avatar
Jerome Mariette committed
83
84
85
86
87
88
89
90
        # then for exclude ones    
        for exclude_group in values[0][3]:
            found = None
            for param in values[0][3][exclude_group]:
                if param in given_params and found != None:
                    parser.error("argument '" + found + "': not allowed with argument '" + param + "'")
                    break
                elif param in given_params: found = param
91
92
93
94
95
96
97
98
99
        
        # check for required exclusive if one of them is in
        if len(exclusif_required.keys()) > 0:
            for group in exclusif_required:
                if exclusif_required[group]:
                    rfound = False
                    for param in values[0][3][group]:
                        if param in given_params: rfound = True
            if not rfound: parser.error("one of the arguments: " + ", ".join(values[0][3][group]) + " is required")
100
        
Jerome Mariette's avatar
Jerome Mariette committed
101
        # if ok add the value
102
103
104
105
106
107
108
109
110
111
112
        final_hash, final_values = {}, []
        for value in values:
            if values[0][4][value[0]] == "append" and final_hash.has_key(value[0]):
                final_hash[value[0]].append(value[1])
            elif values[0][4][value[0]] == "append":
                final_hash[value[0]]= [value[1]]
            else:
                final_hash[value[0]]= value[1]
        for param in final_hash:
            final_values.append((param, final_hash[param]))
        setattr(namespace, self.dest, final_values)
113
114
115

class MiltipleAppendAction(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):
116
117
118
119
120
121
122
        # what is commun within required and excludes
        exclusif_required = {}
        for exclude_group in values[0][3]:
            exclusif_required[exclude_group] = False
            for param in values[0][3][exclude_group]:
                if param in values[0][2]:
                    exclusif_required[exclude_group] = True
Jerome Mariette's avatar
Jerome Mariette committed
123
124
        given_params = []
        # first check for required parameters
125
126
        try:
            required = _copy.copy(values[0][2])
127
128
129
130
131
132
            # delete required that are exclusive
            for group in exclusif_required:
                if exclusif_required[group]:
                    for val in values[0][3][group]:
                        if val in required:
                            del required[required.index(val)]
133
            for val in values:
Jerome Mariette's avatar
Jerome Mariette committed
134
                given_params.append(val[0])
135
136
137
138
139
                if val[0] in required:
                    del required[required.index(val[0])]
        except: pass
        if len(required) == 1: parser.error(", ".join(required) + " is a required parameter!")
        elif len(required) > 1: parser.error(", ".join(required) + " are required parameters!")
Jerome Mariette's avatar
Jerome Mariette committed
140
141
142
143
144
145
146
147
        # then for exclude ones    
        for exclude_group in values[0][3]:
            found = None
            for param in values[0][3][exclude_group]:
                if param in given_params and found != None:
                    parser.error("argument '" + found + "': not allowed with argument '" + param + "'")
                    break
                elif param in given_params: found = param
148
149
150
151
152
153
154
155
156
        
        # check for required exclusive if one of them is in
        if len(exclusif_required.keys()) > 0:
            for group in exclusif_required:
                if exclusif_required[group]:
                    rfound = False
                    for param in values[0][3][group]:
                        if param in given_params: rfound = True
            if not rfound: parser.error("one of the arguments: " + ", ".join(values[0][3][group]) + " is required")
Jerome Mariette's avatar
Jerome Mariette committed
157
        # if ok add the value
158
        items = _copy.copy(_ensure_value(namespace, self.dest, []))
159
160
161
162
163
164
165
166
167
168
169
        final_hash, final_values = {}, []
        for value in values:
            if values[0][4][value[0]] == "append" and final_hash.has_key(value[0]):
                final_hash[value[0]].append(value[1])
            elif values[0][4][value[0]] == "append":
                final_hash[value[0]]= [value[1]]
            else:
                final_hash[value[0]]= value[1]
        for param in final_hash:
            final_values.append((param, final_hash[param]))
        items.append(final_values)
170
171
        setattr(namespace, self.dest, items)        
        
Jerome Mariette's avatar
Jerome Mariette committed
172
173
174
175
class Parameter(object):
    """
    """
    
Jerome Mariette's avatar
Jerome Mariette committed
176
    def __init__(self, name, help, flag, default=None, type=types.StringType, choices=None, 
Jerome Mariette's avatar
Jerome Mariette committed
177
                 required=False, action="store", sub_parameters=[], group="default", display_name=None):
Jerome Mariette's avatar
Jerome Mariette committed
178

Jerome Mariette's avatar
Jerome Mariette committed
179
180
181
        self.name = name
        self.flag = flag
        self.help = help
182
        self.action = action
Jerome Mariette's avatar
Jerome Mariette committed
183
        self.nargs = None
Jerome Mariette's avatar
Jerome Mariette committed
184
        self.sub_parameters = sub_parameters
Jerome Mariette's avatar
Jerome Mariette committed
185
        self.group = group
Jerome Mariette's avatar
Jerome Mariette committed
186
187
188
        if display_name == None:
            self.display_name = name
        else: self.display_name = display_name
Jerome Mariette's avatar
Jerome Mariette committed
189
        
190
191
192
193
194
        self.required = str(required).lower() in ("yes", "y", "true",  "t", "1")
        self.choices = choices
        if self.choices:
            self.choices = choices.split("|")
        
Jerome Mariette's avatar
Jerome Mariette committed
195
        if len(self.sub_parameters) > 0:
Jerome Mariette's avatar
Jerome Mariette committed
196
            self.nargs = "+"
Jerome Mariette's avatar
Jerome Mariette committed
197
198
        if type == "date":
            self.type = date
Jerome Mariette's avatar
Jerome Mariette committed
199
        elif type == "multiple":
Jerome Mariette's avatar
Jerome Mariette committed
200
            sub_param_hash, sub_param_types, sub_param_names, sub_param_required, sub_param_choices, sub_param_excludes = {}, [], [], [], {}, {}
201
            sub_param_default, sub_param_actions = {}, {}
Jerome Mariette's avatar
Jerome Mariette committed
202
            for sub_param in self.sub_parameters:
Jerome Mariette's avatar
Jerome Mariette committed
203
204
205
206
207
                try: sub_type = sub_param.type
                except: sub_type = types.StringType
                sub_param_hash[sub_param.flag] = sub_type
                sub_param_names.append(sub_param.flag)
                sub_param_types.append(sub_type)
208
                sub_param_choices[sub_param.flag] = sub_param.choices
209
                sub_param_default[sub_param.flag] = sub_param.default
210
                sub_param_actions[sub_param.flag] = sub_param.action
Jerome Mariette's avatar
Jerome Mariette committed
211
212
213
214
215
                if sub_param.group.startswith("exclude-"):
                    if sub_param.group in sub_param_excludes.keys():
                        sub_param_excludes[sub_param.group].append(sub_param.flag)
                    else:
                        sub_param_excludes[sub_param.group] = [sub_param.flag]
216
                if sub_param.required: sub_param_required.append(sub_param.flag)
Jerome Mariette's avatar
Jerome Mariette committed
217
            self.type = MultipleParameters(sub_param_hash, sub_param_required, sub_param_choices, sub_param_excludes, sub_param_default, sub_param_actions)
218
219
220
221
            if self.action == "append":
                self.action = MiltipleAppendAction
            else:
                self.action = MiltipleAction
Jerome Mariette's avatar
Jerome Mariette committed
222
            self.help += " | Format: " + " ".join([cname + "=<" + ctype.__name__.upper() + ">" for ctype, cname in zip(sub_param_types, sub_param_names)]) + ")"
Jerome Mariette's avatar
Jerome Mariette committed
223
224
        elif isinstance(type, types.FunctionType):
            self.type = type
Jerome Mariette's avatar
Jerome Mariette committed
225
226
227
        else:
            try: self.type = eval(type)
            except: self.type = types.StringType
Jerome Mariette's avatar
Jerome Mariette committed
228
                        
Jerome Mariette's avatar
Jerome Mariette committed
229
230
231
232
        self.default = default
        if type == "date" and not self.default:
            today = datetime.date.today()
            self.default = today.strftime('%d/%m/%Y')
233
        elif self.type == types.BooleanType :
Jerome Mariette's avatar
Jerome Mariette committed
234
235
            if self.default: self.default = str(self.default).lower() in ("true",  "t", "1")
            else: self.default = True
236
        elif self.action == "append" or self.action == MiltipleAppendAction:
Jerome Mariette's avatar
Jerome Mariette committed
237
            self.default = []
Jerome Mariette's avatar
Jerome Mariette committed
238
239
240
241
242
        elif type == "multiple":
            self.default = {}
            for param in self.sub_parameters:
                self.default[param.name] = param.default
                
243
    def export_to_argparse(self):
244
245
246
247
        if self.type == types.BooleanType and str(self.default).lower() in ("false",  "f", "0"):
            return {"help": self.help, "required": self.required, "dest": self.name, 
                    "default": False, "action": "store_true"}
        elif self.type == types.BooleanType:
248
            return {"help": self.help, "required": self.required, "dest": self.name, 
249
                    "default": True, "action": "store_false"}
Jerome Mariette's avatar
Jerome Mariette committed
250
251
252
        elif self.nargs > 1:
            return {"type": self.type, "help": self.help, "required": self.required,
                    "dest": self.name, "default": self.default,
Jerome Mariette's avatar
Jerome Mariette committed
253
                    "action": self.action, "choices": self.choices, "nargs": "+"}
254
255
        else:
            return {"type": self.type, "help": self.help, "required": self.required,
Jerome Mariette's avatar
Jerome Mariette committed
256
                    "dest": self.name, "default": self.default,
257
258
                    "action": self.action, "choices": self.choices}
    
Jerome Mariette's avatar
Jerome Mariette committed
259
260
261
    def get_type(self):
        return self.type.__name__
    
Jerome Mariette's avatar
Jerome Mariette committed
262
    def __str__(self):
Jerome Mariette's avatar
Jerome Mariette committed
263
        return self.name + ": " + str(self.flag) + " | " + self.help + " (default=" + \
Jerome Mariette's avatar
Jerome Mariette committed
264
               str(self.default) + ", required=" + str(self.required) + ", flag=" + self.flag + ")"