139
class _Config(TypedConfigParser):
143
class SyncDaemonConfigParser(TypedConfigParser):
144
"""Custom TypedConfigParser with upgrade support and syncdaemon parsers."""
146
def __init__(self, *args, **kwargs):
147
super(SyncDaemonConfigParser, self).__init__(*args, **kwargs)
148
self.upgrade_hooks = {}
149
for name, parser in get_parsers():
150
self.add_parser(name, parser)
151
self.add_upgrade_hook('__main__', 'log_level', upgrade_log_level)
153
def add_upgrade_hook(self, section, option, func):
154
"""Add an upgrade hook for (section, option)"""
155
if (section, option) in self.upgrade_hooks:
156
raise ValueError('An upgrade hook for %s, %s already exists' %
158
self.upgrade_hooks[(section, option)] = func
161
"""Override default parse_all() and call upgrade_all() after it"""
162
super(SyncDaemonConfigParser, self).parse_all()
165
def upgrade_all(self):
166
"""Iterate over all upgrade_hooks and execute them."""
167
for section, option in self.upgrade_hooks:
168
if self.has_option(section, option):
169
self.upgrade_hooks[(section, option)](self)
172
def upgrade_log_level(cp):
173
"""upgrade log_level to logging-level option"""
174
if not cp.has_option('logging', 'level'):
175
# an old default config, someone changed it
176
# just replace the setting
177
old = cp.get('__main__', 'log_level')
178
cp.set('logging', 'level', old)
180
current = cp.get('logging', 'level')
181
parser = current.parser
182
old = cp.get('__main__', 'log_level')
183
if isinstance(old.value, basestring):
185
old.value = parser(old.value)
186
if parser(current.attrs['default']) == current.value:
187
# override the default in the new setting
188
current.value = old.value
189
cp.set('logging', 'level', current)
190
#else, we ignore the setting as we have a non-default
191
# value in logging-level (newer setting wins)
192
logger.warning("Found deprecated config option 'log_level'"
193
" in section: '__main__'")
194
cp.remove_option('__main__', 'log_level')
197
class _Config(SyncDaemonConfigParser):
140
198
"""Minimal config object to read/write config values
141
to the user config file.
199
from/to the user config file.
200
Most of the methods in this class aren't thread-safe.
143
202
Only supports bandwidth throttling options.
147
206
advantage of all the nice tricks of configglue.
150
def __init__(self, config_file=_user_config_path):
209
def __init__(self, config_file=_user_config_path, config_files=None):
151
210
"""Create the instance, add our custom parsers and
152
211
read the config file
154
213
super(_Config, self).__init__()
155
for name, parser in get_parsers():
156
self.add_parser(name, parser)
157
214
self.config_file = config_file
158
215
self.read(config_file)
159
self.default = self._load_defaults(get_config_files())
216
# create and fill the default typed config
217
self.default = self._load_defaults(config_files)
218
# create the overridden typed config
219
self.overridden = SyncDaemonConfigParser()
220
self.overridden.parse_all()
162
223
def _load_defaults(config_files):
163
224
"""load typed defaults from config_files"""
164
cp = TypedConfigParser()
165
for name, parser in get_parsers():
166
cp.add_parser(name, parser)
225
cp = SyncDaemonConfigParser()
226
if config_files is None:
227
config_files = get_config_files()
167
228
cp.read(config_files)
172
"""save the config object to disk"""
233
"""Save the config object to disk"""
173
234
with open(self.config_file+'.new', 'w') as fp:
175
236
if os.path.exists(self.config_file):
177
238
os.rename(self.config_file+'.new', self.config_file)
179
240
def get_parsed(self, section, option):
180
"""custom get that fallback to our custom defaults"""
241
"""get that fallbacks to our custom defaults"""
182
value = super(_Config, self).get(section, option)
183
# get the parser from the default config
184
default = self.default.get(section, option)
185
return default.parser(value)
186
except ConfigParser.NoOptionError, e:
187
return self.default.get(section, option).value
243
return self.overridden.get(section, option).value
244
except (NoOptionError, NoSectionError):
246
value = super(_Config, self).get(section, option)
247
# get the parser from the default config
248
default = self.default.get(section, option)
249
return default.parser(value)
250
except NoOptionError:
251
return self.default.get(section, option).value
253
def override_options(self, overridden_options):
254
"""Merge in the values provided by the options object, into
255
self.overridden TypedConfigParser.
256
This override the default and user configured values only if the values
257
are != to the default ones. These 'overriden' values are not saved
260
for section, optname, value in overridden_options:
261
if section not in self.overridden.sections():
262
self.overridden.add_section(section)
263
self.overridden.set(section, optname, value)
264
self.overridden.parse_all()
189
266
# throttling section get/set
190
267
@requires_section(THROTTLING)
211
288
def get_throttling_write_limit(self):
212
289
return self.get_parsed(THROTTLING, 'write_limit')
292
def configglue(fileobj, *filenames, **kwargs):
293
"""Populate an OptionParser with options and defaults taken from a
296
@param fileobj: An INI file, as a file-like object.
297
@param filenames: An optional series of filenames to merge.
298
@param kwargs: options passed on to the OptionParser constructor except for:
299
@param args: parse these args (defaults to sys.argv[1:])
301
cp = SyncDaemonConfigParser()
306
args = kwargs.pop('args', None)
308
op = OptionParser(**kwargs)
310
for section in cp.sections():
311
if section == '__main__':
315
og = op.add_option_group(section)
316
tpl = '--%(section)s-%(option)s'
317
for optname in cp.options(section):
318
option = cp.get(section, optname)
319
if 'help' in option.attrs:
320
option.attrs['help'] %= option.attrs
324
default = option.value
325
og.add_option(tpl % {'section': section.lower(),
326
'option': optname.lower()},
327
**dict(option.attrs, default=default))
329
options, args = op.parse_args(args)
332
for section in cp.sections():
333
for optname, optval in cp.items(section):
334
normoptname = glue.normoptname(cp, section, optname)
335
value = getattr(options, normoptname)
336
if optval.value != value:
337
# the value has been overridden by an argument;
339
setattr(options, normoptname, optval.parser(value))
340
overridden.append((section, optname, value))
342
config_files = [fileobj.name] + list(filenames)
343
config = get_user_config(config_files=config_files)
344
config.override_options(overridden)
345
return op, options, args