E
Edd
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
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