# HG changeset patch # User Tero Marttila # Date 1387715056 -7200 # Node ID 9384a10f7d5e2e9bf0fa5f7c2dc7c26f4185a183 # Parent 538c02bd95e02f0102454862cc22e3273e7ac134# Parent 9f1ecf190fe0a77a593fcb8a5ab7a5d189810cb5 merge diff -r 9f1ecf190fe0 -r 9384a10f7d5e pvl/args.py --- a/pvl/args.py Sat Dec 21 22:49:12 2013 +0200 +++ b/pvl/args.py Sun Dec 22 14:24:16 2013 +0200 @@ -30,14 +30,20 @@ general.add_option('--log-file', help="Log to file") general.add_option('--debug-module', action='append', metavar='MODULE', help="Enable logging for the given logger/module name") - + + parser.add_option('-c', '--config', metavar='PATH', + help="Read option defaults from config") + parser.add_option('--config-encoding', metavar='CHARSET', default='utf-8', + help="Unicode decoding for config file") + + if setuid : general.add_option('--uid', help="Change uid") general.add_option('--gid', help="Change gid") # defaults parser.set_defaults( - _setuid = setuid, + setuid = setuid, logname = parser.prog, loglevel = logging.WARN, debug_module = [], @@ -127,6 +133,106 @@ else: return [apply_file(None, *args, **opts)] +import configobj +import copy +import optparse + +class Options (object) : + """ + Custom optparse.Values implementation. + + Passed to OptionParser.parse_args(), called by Option.take_action(): + setattr(values, dest, ...) + values.ensure_value(dest, ...) + """ + + def __init__ (self, defaults={ }) : + self._defaults = defaults + self._options = { } + + def __setattr__ (self, name, value) : + if name.startswith('_') : + self.__dict__[name] = value + else : + self._options[name] = value + + def ensure_value (self, name, default) : + if name in self._options : + pass + elif name in self._defaults : + self._options[name] = copy.copy(self._defaults[name]) + else : + self._options[name] = default + + return self._options[name] + + def _merge (self, options) : + """ + Merge in options from given Options. + """ + + # TODO: lists? + self._options.update(options._options) + + def __getattr__ (self, name) : + if name.startswith('_') : + raise AttributeError(name) + + if name in self._options : + return self._options[name] + elif name in self._defaults : + return self._defaults[name] + else : + raise AttributeError(name) + +def apply_config (options, parser, config, encoding=None) : + """ + Load options from config. + """ + + import configobj + + config = configobj.ConfigObj(config, + encoding = options.config_encoding if encoding is None else encoding, + ) + config_options = Options(options._defaults) + + # load scalars + for scalar in config.scalars : + # option from config + option = parser._long_opt.get('--' + scalar) + + if not option : + raise optparse.BadOptionError(scalar) + + # value from config + if option.takes_value() : + value = config[scalar] + + else : + # ignore + value = None + + # apply + option.process(scalar, value, config_options, parser) + + # apply in actual options + config_options._merge(options) + + return config_options + +def parse (parser, argv) : + """ + Parse options, args from argv. + """ + + options, args = parser.parse_args(argv[1:], values=Options(parser.defaults)) + + if options.config : + options = apply_config(options, parser, options.config) + + return options, args + def apply (options, logname=None, rootok=True) : """ Apply the optparse options. @@ -148,7 +254,7 @@ # TODO: use --quiet for stdout output? options.quiet = options.loglevel > logging.WARN - if options._setuid : + if options.setuid : if options.uid or options.gid or not rootok : # set uid/gid apply_setid(options, rootok=rootok)