0001"""
0002**Deprecated** (13 Aug 2006)
0003
0004Layers multiple dictionaries.
0005
0006Nested dictionaries can be traversed, with each dictionary shadowing
0007the previous dictionaries.  So for example:
0008
0009    >>> d = NestedDict([{'foo': 'bar'}])
0010    >>> d['foo']
0011    'bar'
0012    >>> d2 = d.clone(add_dict={'foo': 'bar2'})
0013    >>> d2['foo']
0014    'bar2'
0015    >>> d2.getlist('foo')
0016    ['bar2', 'bar']
0017
0018This works for deeply nested dictionaries, not just at the top level;
0019each nested dictionary gets wrapped in a NestedDict as well.
0020"""
0021
0022# This was deprecated 13 Aug 2006
0023import warnings
0024warnings.warn('initools.nested is not supported or recommended for use.',
0025              DeprecationWarning)
0026
0027from UserDict import DictMixin, UserDict
0028
0029class NestedDict(DictMixin):
0030
0031    def __init__(self, configs=None, mutable=True, nest=True):
0032        if configs is None:
0033            configs = [{}]
0034        assert isinstance(configs, (list, tuple)), (
0035            "The configs must be a list or tuple, not: %r"
0036            % configs)
0037        self.configs = configs
0038        self.mutable = mutable
0039        self.nest = nest
0040
0041    def __getitem__(self, key):
0042        results = self.raw_get(key)
0043        if not results:
0044            raise KeyError, "Key not found: %r" % key
0045        return self._convert_single(key, results)
0046
0047    def add_config(self, config, position=0):
0048        assert not isinstance(config, (str, unicode)), (
0049            "Bad configuration (not a mapping): %r" % config)
0050        if position is None:
0051            self.configs.append(config)
0052        else:
0053            self.configs.insert(position, config)
0054
0055    def set_configs(self, new_configs):
0056        self.configs[:] = []
0057        for config in new_configs:
0058            self.add_config(config, None)
0059
0060    def raw_get(self, key, add_inherited=True):
0061        results = []
0062        if add_inherited:
0063            all_configs = self.configs
0064        else:
0065            all_configs = [self.configs[-1]]
0066        for config in all_configs:
0067            if key in config:
0068                if isinstance(config, (str, unicode)):
0069                    print [key, config, all_configs, self.configs]
0070                results.append(config[key])
0071        return results
0072
0073    def getlist(self, key, add_inherited=True):
0074        results = self.raw_get(key, add_inherited=add_inherited)
0075        converted = []
0076        for result_set in results:
0077            if not isinstance(result_set, (list, tuple)):
0078                result_set = [result_set]
0079            converted.extend(self._convert_many(key, result_set))
0080        return converted
0081
0082    def _convert_single(self, key, value_list):
0083        if (not self.nest
0084            or not isinstance(value_list[0],
0085                              (dict, DictMixin, UserDict))):
0086            # @@: doesn't handle all dict alternatives
0087            return value_list[0]
0088        elif len(value_list) == 1 and self.mutable:
0089            return value_list[0]
0090        else:
0091            return self.__class__(
0092                value_list, mutable=self.mutable, nest=True)
0093
0094    def _convert_many(self, key, value_list):
0095        return [self._convert_single(key, [v]) for v in value_list]
0096
0097    def __setitem__(self, key, value):
0098        if self.mutable:
0099            self.configs[0][key] = value
0100        else:
0101            raise KeyError, (
0102                "Dictionary is read-only")
0103
0104    def __delitem__(self, key):
0105        if not self.mutable:
0106            raise KeyError, (
0107                "Dictionary is read-only")
0108        if self.configs[0].has_key(key):
0109            del self.configs[0][key]
0110        elif self.has_key(key):
0111            raise KeyError, (
0112                "You cannot delete the key %r, as it belongs to the "
0113                "a master configuration %r"
0114                % (key, self.master))
0115        else:
0116            raise KeyError, (
0117                "Key does not exist: %r" % key)
0118
0119    def keys(self):
0120        return list(self)
0121
0122    def __contains__(self, key):
0123        for config in self.configs:
0124            if config.has_key(key):
0125                return True
0126        return False
0127
0128    def has_key(self, key):
0129        return key in self
0130
0131    def __iter__(self):
0132        used = {}
0133        for config in self.configs:
0134            for key in config:
0135                if key in used:
0136                    continue
0137                used[key] = None
0138                yield key
0139
0140    _clone_sentry = []
0141
0142    def clone(self, add_dict=None, mutable=_clone_sentry,
0143              nest=_clone_sentry):
0144        if mutable is self._clone_sentry:
0145            mutable = self.mutable
0146        if nest is self._clone_sentry:
0147            nest = self.nest
0148        new = self.configs[:]
0149        if add_dict is not None:
0150            new.insert(0, add_dict)
0151        else:
0152            new.insert(0, {})
0153        return self.__class__(new, mutable=mutable, nest=nest)
0154
0155    def copy(self):
0156        return dict(self.iteritems())
0157
0158    def __eq__(self, other):
0159        if other is None:
0160            return False
0161        if (not hasattr(other, 'keys')
0162            or not hasattr(other, '__getitem__')):
0163            return False
0164        for key in other:
0165            if not key in self:
0166                return False
0167        for name, value in self.iteritems():
0168            if other[name] != value:
0169                return False
0170        return True
0171
0172    def __cmp__(self, other):
0173        return not self.__eq__(other)
0174
0175    def __repr__(self):
0176        return '<%s %r>' % (self.__class__.__name__,
0177                            dict(self.iteritems()))