import copy class Dict(dict): def __init__(__self, *args, **kwargs): object.__setattr__(__self, '__parent', kwargs.pop('__parent', None)) object.__setattr__(__self, '__key', kwargs.pop('__key', None)) object.__setattr__(__self, '__frozen', False) for arg in args: if not arg: continue elif isinstance(arg, dict): for key, val in arg.items(): __self[key] = __self._hook(val) elif isinstance(arg, tuple) and (not isinstance(arg[0], tuple)): __self[arg[0]] = __self._hook(arg[1]) else: for key, val in iter(arg): __self[key] = __self._hook(val) for key, val in kwargs.items(): __self[key] = __self._hook(val) def __setattr__(self, name, value): if hasattr(self.__class__, name): raise AttributeError("'Dict' object attribute " "'{0}' is read-only".format(name)) else: self[name] = value def __setitem__(self, name, value): isFrozen = (hasattr(self, '__frozen') and object.__getattribute__(self, '__frozen')) if isFrozen and name not in super(Dict, self).keys(): raise KeyError(name) super(Dict, self).__setitem__(name, value) try: p = object.__getattribute__(self, '__parent') key = object.__getattribute__(self, '__key') except AttributeError: p = None key = None if p is not None: p[key] = self object.__delattr__(self, '__parent') object.__delattr__(self, '__key') def __add__(self, other): if not self.keys(): return other else: self_type = type(self).__name__ other_type = type(other).__name__ msg = "unsupported operand type(s) for +: '{}' and '{}'" raise TypeError(msg.format(self_type, other_type)) @classmethod def _hook(cls, item): if isinstance(item, dict): return cls(item) elif isinstance(item, (list, tuple)): return type(item)(cls._hook(elem) for elem in item) return item def __getattr__(self, item): return self.__getitem__(item) def __missing__(self, name): if object.__getattribute__(self, '__frozen'): raise KeyError(name) return self.__class__(__parent=self, __key=name) def __delattr__(self, name): del self[name] def to_dict(self): base = {} for key, value in self.items(): if isinstance(value, type(self)): base[key] = value.to_dict() elif isinstance(value, (list, tuple)): base[key] = type(value)( item.to_dict() if isinstance(item, type(self)) else item for item in value) else: base[key] = value return base def copy(self): return copy.copy(self) def deepcopy(self): return copy.deepcopy(self) def __deepcopy__(self, memo): other = self.__class__() memo[id(self)] = other for key, value in self.items(): other[copy.deepcopy(key, memo)] = copy.deepcopy(value, memo) return other def update(self, *args, **kwargs): other = {} if args: if len(args) > 1: raise TypeError() other.update(args[0]) other.update(kwargs) for k, v in other.items(): if ((k not in self) or (not isinstance(self[k], dict)) or (not isinstance(v, dict))): self[k] = v else: self[k].update(v) def __getnewargs__(self): return tuple(self.items()) def __getstate__(self): return self def __setstate__(self, state): self.update(state) def __or__(self, other): if not isinstance(other, (Dict, dict)): return NotImplemented new = Dict(self) new.update(other) return new def __ror__(self, other): if not isinstance(other, (Dict, dict)): return NotImplemented new = Dict(other) new.update(self) return new def __ior__(self, other): self.update(other) return self def setdefault(self, key, default=None): if key in self: return self[key] else: self[key] = default return default def freeze(self, shouldFreeze=True): object.__setattr__(self, '__frozen', shouldFreeze) for key, val in self.items(): if isinstance(val, Dict): val.freeze(shouldFreeze) def unfreeze(self): self.freeze(False)