A micro-blogging tool with multiple interfaces.
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.
 
 

302 lines
9.1 KiB

#!/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]