"""Load and store configuration objects. Test this module using nosetests -v --with-doctest --doctest-tests IPython.config """ from __future__ import with_statement from contextlib import contextmanager import inspect import types from IPython.config import traitlets from traitlets import Traitlet from IPython.external.configobj import ConfigObj def debug(s): import sys sys.stderr.write(str(s) + '\n') @contextmanager def raw(config): """Context manager for accessing traitlets directly. """ config.__getattribute__('',raw_access=True) yield config config.__getattribute__('',raw_access=False) class Config(object): """ Implementation Notes ==================== All instances of the same Config class share properties. Therefore, >>> class Sample(Config): ... my_float = traitlets.Float(3) >>> s0 = Sample() >>> s1 = Sample() >>> s0.my_float = 5 >>> s0.my_float == s1.my_float True """ def __init__(self): # Instantiate subconfigs with raw(self): subconfigs = [(n,v) for n,v in inspect.getmembers(self, inspect.isclass) if not n.startswith('__')] for n,v in subconfigs: setattr(self, n, v()) def __getattribute__(self,attr,raw_access=None, _ns={'raw_access':False}): if raw_access is not None: _ns['raw_access'] = raw_access return obj = object.__getattribute__(self,attr) if isinstance(obj,Traitlet) and not _ns['raw_access']: return obj.__call__() else: return obj def __setattr__(self,attr,value): obj = object.__getattribute__(self,attr) if isinstance(obj,Traitlet): obj(value) else: self.__dict__[attr] = value def __str__(self,level=1,only_modified=True): ci = ConfigInspector(self) out = '' spacer = ' '*(level-1) # Add traitlet representations for p,v in ci.properties: if (v.modified and only_modified) or not only_modified: out += spacer + '%s = %s\n' % (p,v) # Add subconfig representations for (n,v) in ci.subconfigs: sub_str = v.__str__(level=level+1,only_modified=only_modified) if sub_str: out += '\n' + spacer + '[' * level + ('%s' % n) \ + ']'*level + '\n' out += sub_str return out def __iadd__(self,source): """Load configuration from filename, and update self. """ if not isinstance(source,dict): source = ConfigObj(source, unrepr=True) update_from_dict(self,source) return self class ConfigInspector(object): """Allow the inspection of Config objects. """ def __init__(self,config): self._config = config @property def properties(self): "Return all traitlet names." with raw(self._config): return inspect.getmembers(self._config, lambda obj: isinstance(obj, Traitlet)) @property def subconfigs(self): "Return all subconfig names and values." with raw(self._config): return [(n,v) for n,v in inspect.getmembers(self._config, lambda obj: isinstance(obj,Config)) if not n.startswith('__')] def reset(self): for (p,v) in self.properties: v.reset() for (s,v) in self.subconfigs: ConfigInspector(v).reset() def update_from_dict(config,d): """Propagate the values of the dictionary to the given configuration. Useful to load configobj instances. """ for k,v in d.items(): try: prop_or_subconfig = getattr(config, k) except AttributeError: print "Invalid section/property in config file: %s" % k else: if isinstance(v,dict): update_from_dict(prop_or_subconfig,v) else: setattr(config, k, v) def dict_from_config(config,only_modified=True): """Create a dictionary from a Config object.""" ci = ConfigInspector(config) out = {} for p,v in ci.properties: if (v.modified and only_modified) or not only_modified: out[p] = v for s,v in ci.subconfigs: d = dict_from_config(v,only_modified) if d != {}: out[s] = d return out def write(config, target): """Write a configuration to file. """ if isinstance(target, str): target = open(target, 'w+') target.flush() target.seek(0) confobj = ConfigObj(dict_from_config(config), unrepr=True) confobj.write(target)