Hi folks, I'd like to use Python itself as the configuration language for my Python application. I'd like the user to be able to write something like this in their config file(s):
cfg.laser.on = True cfg.laser.colour = 'blue' cfg.discombobulated.vegetables = ['carrots', 'broccoli'] # ... To this end, I've created a class that appears to allow instance variables to be created on the fly. In other words, I can to the following to read a config file: cfg = Config() execfile(filename, {'cfg', cfg}, {}) However, I think my implementation of the Config class is a little crappy. I'd really appreciate the critical eye of a pro. Here's the sauce: class Config(object): def __init__(self, sealed=False): def seal(): for v in self._attribs.values(): if isinstance(v, self.__class__): v.seal() del self.__dict__['seal'] d = {'_attribs': {}, '_a2p': None} if not sealed: d['seal'] = seal self.__dict__.update(d) def __getattr__(self, key): if not key in self._attribs: d = Config(sealed='seal' not in self.__dict__) def add2parent(): self._attribs[key] = d if self._a2p: self._a2p() self._a2p = None # if anything is assigned to an attribute of d, # make sure that d is recorded as an attribute of this Config d._a2p = add2parent return d else: return self._attribs[key] def __setattr__(self, key, value): if key in self.__dict__: self.__dict__[key] = value else: if not 'seal' in self.__dict__: clsname = self.__class__.__name__ raise AttributeError("'%s' object attribute '%s' is read-only (object is sealed)" % (clsname, key)) self.__dict__['_attribs'][key] = value if self._a2p: self._a2p() self._a2p = None def __delattr__(self, key): if key in self.__dict__: clsname = self.__class__.__name__ raise AttributeError("can't delete '%s' object attribute '%s' as it is used for book-keeping!" % (clsname, key)) else: if key in self._attribs: del self._attribs[key] def __bool__(self): return bool(self._attribs) def __nonzero__(self): return bool(self._attribs) if __name__ == '__main__': cfg = Config() cfg.a = 1 cfg.b.c = 2 cfg.d.e.f.g.h = [1, 2, 3] print cfg.a print cfg.b.c print cfg.d.e.f.g.h del cfg.b.c print cfg.b.c try: del cfg.d.e._attribs except AttributeError, ex: print ex cfg.seal() try: cfg.k.l.z = [] except AttributeError, ex: print ex Once the config is loaded, it will be passed down to other user- written scripts and it's important that these scripts don't accidentally change the config. So the idea is that I'll call cfg.seal () to prevent any further changes before passing it on to these other scripts. Beyond the general fiddliness of the code, I think the way seal() currently works is particularly pants. I considered using a simpler approach: def mkdd(): return defaultdict(mkdd) cfg = mkdd() execfile(filename, {'cfg': cfg}, {}) But I quite like the way the '.' separators quite naturally (IMO) indicate a hierarchy of settings. Comments and suggestions welcome! Kind regards, Edd -- http://mail.python.org/mailman/listinfo/python-list