Mercurial > hg > configuration
comparison configuration/configuration.py @ 133:f01d351ccf2b
STUB: configuration/configuration.py
author | Jeff Hammel <k0scist@gmail.com> |
---|---|
date | Sun, 30 Mar 2014 19:53:20 -0700 |
parents | dff886188b55 |
children |
comparison
equal
deleted
inserted
replaced
132:af5e83a4763b | 133:f01d351ccf2b |
---|---|
168 if value.get('default'): | 168 if value.get('default'): |
169 kw['action'] = 'store_false' | 169 kw['action'] = 'store_false' |
170 if not flags: | 170 if not flags: |
171 args = ['--no-%s' % name] | 171 args = ['--no-%s' % name] |
172 if not help: | 172 if not help: |
173 kw['help'] = 'disable %s' % name | 173 kw['help'] = 'disable {}'.format(name) |
174 else: | 174 else: |
175 kw['action'] = 'store_true' | 175 kw['action'] = 'store_true' |
176 if not help: | 176 if not help: |
177 kw['help'] = 'enable %s' % name | 177 kw['help'] = 'enable {}'.format(name) |
178 return args, kw | 178 return args, kw |
179 | |
179 | 180 |
180 class ListCLI(BaseCLI): | 181 class ListCLI(BaseCLI): |
181 | 182 |
182 def __call__(self, name, value): | 183 def __call__(self, name, value): |
183 args, kw = BaseCLI.__call__(self, name, value) | 184 args, kw = BaseCLI.__call__(self, name, value) |
184 | |
185 # TODO: could use 'extend' | |
186 # - http://hg.mozilla.org/build/mozharness/file/5f44ba08f4be/mozharness/base/config.py#l41 | |
187 | |
188 kw['action'] = 'append' | 185 kw['action'] = 'append' |
189 return args, kw | 186 return args, kw |
190 | 187 |
191 class IntCLI(BaseCLI): | 188 class IntCLI(BaseCLI): |
192 | 189 |
219 def take_action(self, value): | 216 def take_action(self, value): |
220 if self.delimeter not in value: | 217 if self.delimeter not in value: |
221 raise AssertionError("Each value must be delimited by '%s': %s" % (self.delimeter, value)) | 218 raise AssertionError("Each value must be delimited by '%s': %s" % (self.delimeter, value)) |
222 return value.split(self.delimeter, 1) | 219 return value.split(self.delimeter, 1) |
223 | 220 |
221 # types of CLI arguments | |
224 types = {bool: BoolCLI(), | 222 types = {bool: BoolCLI(), |
225 int: IntCLI(), | 223 int: IntCLI(), |
226 float: FloatCLI(), | 224 float: FloatCLI(), |
227 list: ListCLI(), | 225 list: ListCLI(), |
228 dict: DictCLI(), | 226 dict: DictCLI(), |
229 str: BaseCLI(), | 227 str: BaseCLI(), |
230 None: BaseCLI()} # default | 228 None: BaseCLI()} # default |
231 | 229 |
232 __all__ += [i.__class__.__name__ for i in types.values()] | 230 __all__ += [i.__class__.__name__ for i in types.values()] |
233 | 231 |
232 | |
234 class Configuration(optparse.OptionParser): | 233 class Configuration(optparse.OptionParser): |
235 """declarative configuration object""" | 234 """declarative configuration object""" |
236 | 235 |
237 options = {} # configuration basis definition | 236 options = {} # configuration basis definition |
238 extend = set() # which dicts/lists should be extended | 237 extend = set() # which dicts/lists should be extended |
249 def __init__(self, configuration_providers=configuration_providers, types=types, load=None, dump='--dump', **parser_args): | 248 def __init__(self, configuration_providers=configuration_providers, types=types, load=None, dump='--dump', **parser_args): |
250 | 249 |
251 # sanity check | 250 # sanity check |
252 if isinstance(self.options, dict): | 251 if isinstance(self.options, dict): |
253 self.option_dict = self.options | 252 self.option_dict = self.options |
254 elif isinstance(self.options, list): | 253 elif isinstance(self.options, (list, tuple)): |
255 # XXX could also be tuple, etc | |
256 self.option_dict = dict(self.options) | 254 self.option_dict = dict(self.options) |
257 else: | 255 else: |
258 raise NotImplementedError | 256 raise NotImplementedError("Configuration: `options` should be castable to a dict") |
259 | 257 |
260 # setup configuration | 258 # setup configuration |
261 self.config = {} | 259 self.config = {} |
262 self.configuration_providers = configuration_providers | 260 self.configuration_providers = configuration_providers |
263 self.types = types | 261 self.types = types |
294 dump = list(dump) | 292 dump = list(dump) |
295 self.add_option(*dump, **dict(dest='dump', | 293 self.add_option(*dump, **dict(dest='dump', |
296 help="Output configuration file; Formats: %s" % formats)) | 294 help="Output configuration file; Formats: %s" % formats)) |
297 | 295 |
298 | 296 |
299 ### methods for iteration | 297 ### iteration |
300 ### TODO: make the class a real iterator | |
301 | 298 |
302 def items(self): | 299 def items(self): |
300 """items in options""" | |
301 # TODO: make the class a real iterator | |
302 | |
303 # allow options to be a list of 2-tuples | 303 # allow options to be a list of 2-tuples |
304 if isinstance(self.options, dict): | 304 if isinstance(self.options, dict): |
305 return self.options.items() | 305 return self.options.items() |
306 return self.options | 306 return self.options |
307 | 307 |
313 """ | 313 """ |
314 | 314 |
315 # ensure options in configuration are in self.options | 315 # ensure options in configuration are in self.options |
316 unknown_options = [i for i in config if i not in self.option_dict] | 316 unknown_options = [i for i in config if i not in self.option_dict] |
317 if unknown_options: | 317 if unknown_options: |
318 raise UnknownOptionException("Unknown options: %s" % ', '.join(unknown_options)) | 318 raise UnknownOptionException("Unknown options: {}".format(', '.join(unknown_options))) |
319 | 319 |
320 # ensure options are of the right type (if specified) | 320 # ensure options are of the right type (if specified) |
321 for key, value in config.items(): | 321 for key, value in config.items(): |
322 _type = self.option_dict[key].get('type') | 322 _type = self.option_dict[key].get('type') |
323 if _type is None and 'default' in self.option_dict[key]: | 323 if _type is None and 'default' in self.option_dict[key]: |
332 pass | 332 pass |
333 if tocast: | 333 if tocast: |
334 try: | 334 try: |
335 config[key] = _type(value) | 335 config[key] = _type(value) |
336 except BaseException, e: | 336 except BaseException, e: |
337 raise TypeCastException("Could not coerce %s, %s, to type %s: %s" % (key, value, _type.__name__, e)) | 337 raise TypeCastException("Could not coerce '%s'=%s, to type %s: %s" % (key, value, _type.__name__, e)) |
338 | 338 |
339 return config | 339 return config |
340 | 340 |
341 def validate(self): | 341 def validate(self): |
342 """validate resultant configuration""" | 342 """validate resultant configuration""" |
346 required = value.get('required') | 346 required = value.get('required') |
347 if required: | 347 if required: |
348 if isinstance(required, basestring): | 348 if isinstance(required, basestring): |
349 required_message = required | 349 required_message = required |
350 else: | 350 else: |
351 required_message = "Parameter %s is required but not present" % key | 351 required_message = "Parameter {} is required but not present".format(key) |
352 # TODO: this should probably raise all missing values vs | 352 # TODO: this should probably raise all missing values vs |
353 # one by one | 353 # one by one |
354 raise MissingValueException(required_message) | 354 raise MissingValueException(required_message) |
355 # TODO: configuration should be locked after this is called | 355 |
356 | 356 |
357 ### methods for adding configuration | 357 ### methods for adding configuration |
358 | 358 |
359 def default_config(self): | 359 def default_config(self): |
360 """configuration defaults""" | 360 """configuration defaults""" |
370 def __getitem__(self, key): | 370 def __getitem__(self, key): |
371 return self.config[key] | 371 return self.config[key] |
372 | 372 |
373 def __call__(self, *args): | 373 def __call__(self, *args): |
374 """add items to configuration and check it""" | 374 """add items to configuration and check it""" |
375 # TODO: configuration should be locked after this is called | |
376 | 375 |
377 # start with defaults | 376 # start with defaults |
378 self.config = self.default_config() | 377 self.config = self.default_config() |
379 | 378 |
380 # add the configuration | 379 # add the configuration |
426 if 'type' in value: | 425 if 'type' in value: |
427 return value['type'] | 426 return value['type'] |
428 if 'default' in value: | 427 if 'default' in value: |
429 default = value['default'] | 428 default = value['default'] |
430 if default is None: | 429 if default is None: |
431 return None | 430 return None # not <type 'NoneType'> |
432 return type(value['default']) | 431 return type(value['default']) |
433 | 432 |
434 def optparse_options(self, parser): | 433 def optparse_options(self, parser): |
435 """add optparse options to a OptionParser instance""" | 434 """add optparse options to a OptionParser instance""" |
436 for key, value in self.items(): | 435 for key, value in self.items(): |
518 dump = getattr(options, 'dump') | 517 dump = getattr(options, 'dump') |
519 if dump: | 518 if dump: |
520 # TODO: have a way of specifying format other than filename | 519 # TODO: have a way of specifying format other than filename |
521 self.serialize(dump) | 520 self.serialize(dump) |
522 | 521 |
522 | |
523 ### serialization/deserialization | 523 ### serialization/deserialization |
524 | 524 |
525 def formats(self): | 525 def formats(self): |
526 """formats for deserialization""" | 526 """formats for deserialization""" |
527 retval = [] | 527 retval = [] |