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 = [] |
