import collections import os import os.path import pickle import re import sys from configparser import RawConfigParser __all__ = [ 'ConfigCoreDump', 'ConfigCoreHierarchy', 'ConfigParser', ] class SchemaItemBoolean(object): def __call__(self, i): i = i.strip().lower() if i in ("true", "1"): return True if i in ("false", "0"): return False raise ValueError class SchemaItemInteger(object): def __call__(self, i): return int(i.strip(), 0) class SchemaItemList(object): def __init__(self, type=r"\s+"): self.type = type def __call__(self, i): i = i.strip() if not i: return [] return [j.strip() for j in re.split(self.type, i)] # Using OrderedDict instead of dict makes the pickled config reproducible class ConfigCore(collections.OrderedDict): def get_merge(self, section, arch, featureset, flavour, key, default=None): temp = [] if arch and featureset and flavour: temp.append(self.get((section, arch, featureset, flavour), {}) .get(key)) temp.append(self.get((section, arch, None, flavour), {}).get(key)) if arch and featureset: temp.append(self.get((section, arch, featureset), {}).get(key)) if arch: temp.append(self.get((section, arch), {}).get(key)) if featureset: temp.append(self.get((section, None, featureset), {}).get(key)) temp.append(self.get((section,), {}).get(key)) ret = [] for i in temp: if i is None: continue elif isinstance(i, (list, tuple)): ret.extend(i) elif ret: # TODO return ret else: return i return ret or default def merge(self, section, arch=None, featureset=None, flavour=None): ret = {} ret.update(self.get((section,), {})) if featureset: ret.update(self.get((section, None, featureset), {})) if arch: ret.update(self.get((section, arch), {})) if arch and featureset: ret.update(self.get((section, arch, featureset), {})) if arch and featureset and flavour: ret.update(self.get((section, arch, None, flavour), {})) ret.update(self.get((section, arch, featureset, flavour), {})) return ret def dump(self, fp): pickle.dump(self, fp, 0) class ConfigCoreDump(object): def __new__(self, fp): return pickle.load(fp) class ConfigCoreHierarchy(object): schema_base = { 'base': { 'arches': SchemaItemList(), 'enabled': SchemaItemBoolean(), 'featuresets': SchemaItemList(), 'flavours': SchemaItemList(), }, } def __new__(cls, schema, dirs=[]): schema_complete = cls.schema_base.copy() for key, value in schema.items(): schema_complete.setdefault(key, {}).update(value) return cls.Reader(dirs, schema_complete)() class Reader(object): config_name = "defines" def __init__(self, dirs, schema): self.dirs, self.schema = dirs, schema def __call__(self): ret = ConfigCore() self.read(ret) return ret def get_files(self, *dirs): dirs = list(dirs) dirs.append(self.config_name) return (os.path.join(i, *dirs) for i in self.dirs if i) def read_arch(self, ret, arch): config = ConfigParser(self.schema) config.read(self.get_files(arch)) featuresets = config['base', ].get('featuresets', []) flavours = config['base', ].get('flavours', []) for section in iter(config): if section[0] in featuresets: real = (section[-1], arch, section[0]) elif len(section) > 1: real = (section[-1], arch, None) + section[:-1] else: real = (section[-1], arch) + section[:-1] s = ret.get(real, {}) s.update(config[section]) ret[tuple(real)] = s for featureset in featuresets: self.read_arch_featureset(ret, arch, featureset) if flavours: base = ret['base', arch] featuresets.insert(0, 'none') base['featuresets'] = featuresets del base['flavours'] ret['base', arch] = base ret['base', arch, 'none'] = {'flavours': flavours, 'implicit-flavour': True} def read_arch_featureset(self, ret, arch, featureset): config = ConfigParser(self.schema) config.read(self.get_files(arch, featureset)) for section in iter(config): real = (section[-1], arch, featureset) + section[:-1] s = ret.get(real, {}) s.update(config[section]) ret[tuple(real)] = s def read(self, ret): config = ConfigParser(self.schema) config.read(self.get_files()) arches = config['base', ]['arches'] featuresets = config['base', ].get('featuresets', []) for section in iter(config): if section[0].startswith('featureset-'): real = (section[-1], None, section[0][11:]) else: real = (section[-1],) + section[1:] ret[real] = config[section] for arch in arches: self.read_arch(ret, arch) for featureset in featuresets: self.read_featureset(ret, featureset) def read_featureset(self, ret, featureset): config = ConfigParser(self.schema) config.read(self.get_files('featureset-%s' % featureset)) for section in iter(config): real = (section[-1], None, featureset) s = ret.get(real, {}) s.update(config[section]) ret[real] = s class ConfigParser(object): __slots__ = '_config', 'schemas' def __init__(self, schemas): self.schemas = schemas self._config = RawConfigParser() def __getitem__(self, key): return self._convert()[key] def __iter__(self): return iter(self._convert()) def __str__(self): return '<%s(%s)>' % (self.__class__.__name__, self._convert()) def _convert(self): ret = {} for section in self._config.sections(): data = {} for key, value in self._config.items(section): data[key] = value section_list = section.split('_') section_base = section_list[-1] if section_base in self.schemas: section_ret = tuple(section_list) data = self._convert_one(self.schemas[section_base], data) else: section_ret = (section, ) ret[section_ret] = data return ret def _convert_one(self, schema, data): ret = {} for key, value in data.items(): value = value.replace('\n', ' ') if key in schema: value = schema[key](value) ret[key] = value return ret def keys(self): return self._convert().keys() def read(self, data): return self._config.read(data) if __name__ == '__main__': sys.path.append('debian/lib/python') config = ConfigCoreDump(open('debian/config.defines.dump', 'rb')) for section, items in sorted(config.items(), key=(lambda a: tuple(i or '' for i in a[0]))): print(u"[%s]" % (section,)) for item, value in sorted(items.items()): print(u"%s: %s" % (item, value)) print()