You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
303 lines
9.1 KiB
303 lines
9.1 KiB
16 years ago
|
#!/usr/bin/env python
|
||
|
# ConfigOpt: An OptParse/ConfigParser object.
|
||
|
|
||
|
import os.path
|
||
|
import ConfigParser
|
||
|
|
||
|
from optparse import OptionParser, OptionGroup, Option
|
||
|
|
||
|
_true_values = ('True', 'true', 'Yes', 'yes')
|
||
|
_false_values = ('False', 'false', 'No', 'no')
|
||
|
|
||
|
|
||
|
class ReferenceOption(Option):
|
||
|
|
||
|
def __init__(self, *args, **kwargs):
|
||
|
if 'group' in kwargs:
|
||
|
self.group = kwargs['group']
|
||
|
del kwargs['group']
|
||
|
else:
|
||
|
self.group = None
|
||
|
|
||
|
if 'option' in kwargs:
|
||
|
self.option = kwargs['option']
|
||
|
del kwargs['option']
|
||
|
else:
|
||
|
self.option = None
|
||
|
|
||
|
if 'conflict_group' in kwargs:
|
||
|
self.conflict_group = kwargs['conflict_group']
|
||
|
del kwargs['conflict_group']
|
||
|
else:
|
||
|
self.conflict_group = None
|
||
|
|
||
|
Option.__init__(self, *args, **kwargs)
|
||
|
|
||
|
|
||
|
class ConfigOptOption(object):
|
||
|
"""An option."""
|
||
|
|
||
|
def __init__(self, *args, **kwargs):
|
||
|
if not 'name' in kwargs:
|
||
|
raise AttributeError('Missing option name')
|
||
|
|
||
|
self.name = kwargs['name']
|
||
|
del kwargs['name']
|
||
|
|
||
|
if 'is_cmd_option' in kwargs:
|
||
|
self._cmd_option = kwargs['is_cmd_option']
|
||
|
del kwargs['is_cmd_option']
|
||
|
else:
|
||
|
self._cmd_option = True # default value
|
||
|
|
||
|
if 'is_config_option' in kwargs:
|
||
|
self._config_option = kwargs['is_config_option']
|
||
|
del kwargs['is_config_option']
|
||
|
else:
|
||
|
self._config_option = True
|
||
|
|
||
|
if 'default' in kwargs:
|
||
|
self._default = kwargs['default']
|
||
|
del kwargs['default'] # Remove it so we don't pass it to
|
||
|
# OptionParser -- we have our own defaults
|
||
|
# and, passing it to OptionParser would
|
||
|
# make it always return as set in the
|
||
|
# command line, breaking the behaviour we
|
||
|
# want.
|
||
|
else:
|
||
|
self._default = None
|
||
|
|
||
|
self._params = kwargs
|
||
|
self._args = args
|
||
|
self.config_value = None
|
||
|
self.cmd_value = None
|
||
|
self._set_value = None
|
||
|
|
||
|
def _get_value(self):
|
||
|
"""Return the value of a variable."""
|
||
|
# this is the priority order: First, the value set by the application;
|
||
|
# if it's not set, use the command line value; if there is no option
|
||
|
# in the command line, use the config value; if this is also not set,
|
||
|
# return the default value for the variable.
|
||
|
if self._set_value is not None:
|
||
|
return self._set_value
|
||
|
|
||
|
if self.cmd_value is not None:
|
||
|
return self.cmd_value
|
||
|
|
||
|
if self.config_value is not None:
|
||
|
return self.config_value
|
||
|
|
||
|
return self._default
|
||
|
|
||
|
def _set_value(self, x):
|
||
|
"""Set the value of the variable. Used by the application to set a
|
||
|
value for it."""
|
||
|
self._set_value = x
|
||
|
|
||
|
value = property(_get_value, _set_value)
|
||
|
|
||
|
@property
|
||
|
def args(self):
|
||
|
return self._args
|
||
|
|
||
|
@property
|
||
|
def params(self):
|
||
|
return self._params
|
||
|
|
||
|
@property
|
||
|
def cmd_option(self):
|
||
|
return self._cmd_option
|
||
|
|
||
|
@property
|
||
|
def config_option(self):
|
||
|
return self._config_option
|
||
|
|
||
|
|
||
|
class ConfigOptGroup(object):
|
||
|
"""A group of options."""
|
||
|
|
||
|
def __init__(self, name, desc):
|
||
|
self.name = name
|
||
|
self.desc = desc
|
||
|
self.options = {}
|
||
|
return
|
||
|
|
||
|
def add_option(self, *args, **kwargs):
|
||
|
"""Add an option in the group."""
|
||
|
id = kwargs['name']
|
||
|
if id in self.options:
|
||
|
return
|
||
|
self.options[id]= ConfigOptOption(*args, **kwargs)
|
||
|
return
|
||
|
|
||
|
def cmd_parser(self, parser):
|
||
|
"""Build the command line parser for the option."""
|
||
|
group = OptionGroup(parser, self.desc)
|
||
|
options = 0 # to avoid adding an empty group
|
||
|
|
||
|
for option_id in self.options:
|
||
|
option = self.options[option_id]
|
||
|
if option.cmd_option:
|
||
|
internal_name = self.name + '_' + option.name # to avoid
|
||
|
# clashes
|
||
|
option_params = option.params
|
||
|
option_params['dest'] = internal_name
|
||
|
option_params['group'] = self.name
|
||
|
option_params['option'] = option.name
|
||
|
|
||
|
if not 'metavar' in option_params:
|
||
|
option_params['metavar'] = option.name.upper()
|
||
|
|
||
|
group.add_option(*option.args, **option_params)
|
||
|
|
||
|
options += 1
|
||
|
|
||
|
if options > 0:
|
||
|
parser.add_option_group(group)
|
||
|
|
||
|
return
|
||
|
|
||
|
def __getitem__(self, key):
|
||
|
return self.options[key].value
|
||
|
|
||
|
def __setitem__(self, key, value):
|
||
|
self.options[key].value = value
|
||
|
|
||
|
|
||
|
class ConfigOpt(object):
|
||
|
"""Command line and config file option merger."""
|
||
|
|
||
|
def __init__(self, app_name=None):
|
||
|
"""Class initialization. <app_name> is used to create the config file
|
||
|
in the user home directory."""
|
||
|
|
||
|
if app_name is None:
|
||
|
import sys
|
||
|
filename = os.path.basename(sys.argv[0])
|
||
|
(name, _) = os.path.splitext(filename)
|
||
|
app_name = name
|
||
|
|
||
|
|
||
|
self._config_name = os.path.expanduser(os.path.join('~', '.' +
|
||
|
app_name + '.ini'))
|
||
|
self._cmd_parser = OptionParser(option_class=ReferenceOption)
|
||
|
|
||
|
self._cmd_parser.add_option('-c', '--config',
|
||
|
dest='config_file',
|
||
|
help='Configuration file.',
|
||
|
default = self._config_name)
|
||
|
|
||
|
self._groups = {}
|
||
|
self._conflicts = None
|
||
|
|
||
|
@property
|
||
|
def conflicts(self):
|
||
|
"""Return the dictionary with the groups in the conflict groups."""
|
||
|
return self._conflicts
|
||
|
|
||
|
def add_group(self, id, desc=None):
|
||
|
"""Create a new group of options."""
|
||
|
if id in self._groups:
|
||
|
return
|
||
|
self._groups[id] = ConfigOptGroup(id, desc)
|
||
|
return
|
||
|
|
||
|
def add_option(self, *args, **kwargs):
|
||
|
"""Add an option in the list of options."""
|
||
|
assert 'group' in kwargs
|
||
|
assert 'option' in kwargs
|
||
|
|
||
|
group_id = kwargs['group']
|
||
|
del kwargs['group']
|
||
|
|
||
|
option_id = kwargs['option']
|
||
|
del kwargs['option']
|
||
|
|
||
|
group = self._groups[group_id]
|
||
|
group.add_option(name=option_id, *args, **kwargs)
|
||
|
|
||
|
def load(self):
|
||
|
"""Load the options for the config file."""
|
||
|
config = ConfigParser.SafeConfigParser()
|
||
|
config.read(self._config_name)
|
||
|
|
||
|
for section in config.sections():
|
||
|
if section not in self._groups:
|
||
|
continue # ignore this group
|
||
|
|
||
|
for option in config.options(section):
|
||
|
if option not in self._groups[section].options:
|
||
|
continue
|
||
|
|
||
|
value = config.get(section, option)
|
||
|
# convert int values
|
||
|
if value.isdigit():
|
||
|
value = int(value)
|
||
|
|
||
|
# Convert boolean values
|
||
|
if value in _true_values or value in _false_values:
|
||
|
value = (value in _true_values)
|
||
|
|
||
|
self._groups[section].options[option].config_value = value
|
||
|
return
|
||
|
|
||
|
def save(self):
|
||
|
"""Save the config file."""
|
||
|
config = ConfigParser.SafeConfigParser()
|
||
|
for group in self._groups:
|
||
|
group_added = False
|
||
|
|
||
|
for option_id in self._groups[group].options:
|
||
|
option = self._groups[group].options[option_id]
|
||
|
|
||
|
if not option.config_option:
|
||
|
continue
|
||
|
|
||
|
if not group_added:
|
||
|
# prevents empty groups
|
||
|
config.add_section(group)
|
||
|
group_added = True
|
||
|
|
||
|
config.set(group, option.name, str(option.value))
|
||
|
|
||
|
config_file = file(self._config_name, 'w')
|
||
|
config.write(config_file)
|
||
|
config_file.close()
|
||
|
return
|
||
|
|
||
|
def __call__(self):
|
||
|
"""Callable object, do the parsing of the command line and loads the
|
||
|
config file required."""
|
||
|
for group in self._groups:
|
||
|
self._groups[group].cmd_parser(self._cmd_parser)
|
||
|
|
||
|
(options, args) = self._cmd_parser.parse_args()
|
||
|
self._config_name = options.config_file
|
||
|
self._conflicts = {}
|
||
|
|
||
|
for group in self._cmd_parser.option_groups:
|
||
|
for option in group.option_list:
|
||
|
group_id = option.group
|
||
|
option_id = option.option
|
||
|
value = getattr(options, option.dest)
|
||
|
conflict_group = option.conflict_group
|
||
|
|
||
|
if value is None:
|
||
|
continue
|
||
|
|
||
|
if conflict_group and conflict_group in self._conflicts:
|
||
|
if group_id != self._conflicts[conflict_group]:
|
||
|
self._cmd_parser.error(
|
||
|
"You can't mix options from %s and %s." % (
|
||
|
self._conflicts[conflict_group], group_id))
|
||
|
return
|
||
|
|
||
|
self._conflicts[conflict_group] = group_id
|
||
|
self._groups[group_id].options[option_id].cmd_value = value
|
||
|
|
||
|
self.load()
|
||
|
|
||
|
def __getitem__(self, key):
|
||
|
return self._groups[key]
|