~verterok/ubuntuone-client/volumemanager_udfs-2

« back to all changes in this revision

Viewing changes to ubuntuone/syncdaemon/config.py

  • Committer: guillermo.gonzalez at canonical
  • Date: 2010-01-04 18:54:17 UTC
  • mfrom: (294.2.6 trunk)
  • Revision ID: guillermo.gonzalez@canonical.com-20100104185417-is0onxq4znx57u0j
merge with trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
23
23
import functools
24
24
import logging
25
25
 
26
 
import ConfigParser
27
 
from configglue import TypedConfigParser
 
26
from ConfigParser import NoOptionError, NoSectionError
 
27
from optparse import OptionParser
 
28
from configglue import TypedConfigParser, glue
28
29
from xdg.BaseDirectory import (
29
30
    load_config_paths,
30
31
    save_config_path,
38
39
# sections
39
40
THROTTLING = 'bandwidth_throttling'
40
41
 
41
 
# get (and  possibly create if don't exists) the user config file
 
42
# global logger
 
43
logger = logging.getLogger('ubuntuone.SyncDaemon.config')
 
44
 
 
45
# get (and possibly create if don't exists) the user config file
42
46
_user_config_path = os.path.join(save_config_path('ubuntuone'), CONFIG_FILE)
43
47
 
44
48
# module private config instance.
114
118
    return config_files
115
119
 
116
120
 
117
 
def get_user_config(config_file=_user_config_path):
 
121
def get_user_config(config_file=_user_config_path, config_files=None):
118
122
    """return the shared _Config instance"""
119
123
    global _user_config
120
124
    if _user_config is None:
121
 
        _user_config = _Config(config_file)
 
125
        _user_config = _Config(config_file, config_files)
122
126
    return _user_config
123
127
 
124
128
 
136
140
    return wrapper
137
141
 
138
142
 
139
 
class _Config(TypedConfigParser):
 
143
class SyncDaemonConfigParser(TypedConfigParser):
 
144
    """Custom TypedConfigParser with upgrade support and syncdaemon parsers."""
 
145
 
 
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)
 
152
 
 
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' %
 
157
                             (section, option))
 
158
        self.upgrade_hooks[(section, option)] = func
 
159
 
 
160
    def parse_all(self):
 
161
        """Override default parse_all() and call upgrade_all() after it"""
 
162
        super(SyncDaemonConfigParser, self).parse_all()
 
163
        self.upgrade_all()
 
164
 
 
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)
 
170
 
 
171
 
 
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)
 
179
    else:
 
180
        current = cp.get('logging', 'level')
 
181
        parser = current.parser
 
182
        old = cp.get('__main__', 'log_level')
 
183
        if isinstance(old.value, basestring):
 
184
            # wasn't parsed
 
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')
 
195
 
 
196
 
 
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.
142
201
 
143
202
    Only supports bandwidth throttling options.
144
203
 
147
206
    advantage of all the nice tricks of configglue.
148
207
    """
149
208
 
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
153
212
        """
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()
160
221
 
161
222
    @staticmethod
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)
168
229
        cp.parse_all()
169
230
        return cp
170
231
 
171
232
    def save(self):
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:
174
235
            self.write(fp)
175
236
        if os.path.exists(self.config_file):
177
238
        os.rename(self.config_file+'.new', self.config_file)
178
239
 
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"""
181
242
        try:
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):
 
245
            try:
 
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
 
252
 
 
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
 
258
        to user config file.
 
259
        """
 
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()
188
265
 
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')
213
290
 
 
291
 
 
292
def configglue(fileobj, *filenames, **kwargs):
 
293
    """Populate an OptionParser with options and defaults taken from a
 
294
    series of files.
 
295
 
 
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:])
 
300
    """
 
301
    cp = SyncDaemonConfigParser()
 
302
    cp.readfp(fileobj)
 
303
    cp.read(filenames)
 
304
    cp.parse_all()
 
305
 
 
306
    args = kwargs.pop('args', None)
 
307
 
 
308
    op = OptionParser(**kwargs)
 
309
 
 
310
    for section in cp.sections():
 
311
        if section == '__main__':
 
312
            og = op
 
313
            tpl = '--%(option)s'
 
314
        else:
 
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
 
321
            if option.is_empty:
 
322
                default = None
 
323
            else:
 
324
                default = option.value
 
325
            og.add_option(tpl % {'section': section.lower(),
 
326
                                 'option': optname.lower()},
 
327
                          **dict(option.attrs, default=default))
 
328
 
 
329
    options, args = op.parse_args(args)
 
330
 
 
331
    overridden = []
 
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;
 
338
                # re-parse it.
 
339
                setattr(options, normoptname, optval.parser(value))
 
340
                overridden.append((section, optname, value))
 
341
 
 
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
 
346