|
|
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) |
|
|
|