Source code for watson.common.datastructures

# -*- coding: utf-8 -*-
from copy import deepcopy, copy
from types import ModuleType


[docs]def dict_deep_update(d1, d2): """Recursively merge two dictionaries. Merges two dictionaries together rather than a shallow update(). Args: d1 (dict): The original dict. d2 (dict): The dict to merge with d1. Returns: dict: A new dict containing the merged dicts. """ if not isinstance(d2, dict): return d2 try: result = deepcopy(d1) except: result = copy(d1) for k, v in d2.items(): if k in result and isinstance(result[k], dict): result[k] = dict_deep_update(result[k], v) elif k in result and isinstance(result[k], (list, tuple)) and isinstance(v, (list, tuple)): result[k] = result[k] + v else: try: result[k] = deepcopy(v) except: # pragma: no cover result[k] = copy(v) # pragma: no cover return result
[docs]def merge_dicts(*dicts): """Merges multiple dictionaries and returns a single new dict. Unlike dict.update this will create a new dict. Args: dicts (list): The dicts that are being merged Returns: dict: A new dict containing the merged dicts """ merged = {} for d in dicts: merged = dict_deep_update(merged, d) return merged
[docs]def module_to_dict(module, ignore_starts_with=''): """Load the contents of a module into a dict. Args: ignore_starts_with (string): Ignore all module keys that begin with this value. Returns: dict: The contents of the module as a dict Example: .. code-block:: python # my_module.py contents: # variable = 'value' import my_module a_dict = module_to_dict(my_module) a_dict['variable'] """ new_dict = {} for k in dir(module): item = getattr(module, k) if not isinstance(item, ModuleType) and not k.startswith(ignore_starts_with): new_dict[k] = item return new_dict
[docs]class MultiDict(dict): """A dictionary type that can contain multiple items for a single key. Dictionary type that will create a list of values if more than one item is set for that particular key. Example: .. code-block:: python multi_dict = MultiDict() multi_dict['one'] = 1 multi_dict['one'] = 'itchi' print(multi_dict) # {'one': [1, 'itchi']} """
[docs] def __init__(self, args=None): items = args.items() if isinstance(args, dict) else args or () for key, value in items: self.set(key, value)
[docs] def set(self, key, value, replace=False): """Add a new item to the dictionary. Set the key to value on the dictionary, converting the existing value to a list if it is a string, otherwise append the value. Args: key (string): The key used to the store the value. value (mixed): The value to store. replace (bool): Whether or not the value should be replaced. Example: .. code-block:: python multi_dict = MultiDict() multi_dict.set('item', 'value') # or multi_dict['item'] = 'value' """ self.__setitem__(key, value, replace)
def __setitem__(self, key, value, replace=False): # See MultiDict.set for more information. if replace and key in self: del self[key] if key in self: try: self[key].append(value) value = self[key] except: existing = [self[key]] existing.append(value) value = existing dict.__setitem__(self, key, value) def __getitem__(self, key, default=None): return self.get(key, default) def update(self, a_dict): items = a_dict.items() if isinstance(a_dict, dict) else a_dict for key, value in items: if isinstance(value, (tuple, list)): for value in value: self[key] = value else: self[key] = value def copy(self): return self.__class__(self) def deepcopy(self, memo=None): return self.__class__(deepcopy(self._flattened(), memo)) def _flattened(self): return dict(((key, value) for key, value in self.items())) def __copy__(self): return self.copy() def __deepcopy__(self, memo=None): return self.deepcopy(memo=memo)
class ImmutableMixin: _mutable = True def _is_immutable(self): if not self._mutable: raise TypeError('{0} is immutable'.format(self.__class__.__name__)) def make_immutable(self): self._mutable = False
[docs]class ImmutableDict(dict, ImmutableMixin): """Creates an immutable dict. While not truly immutable (_mutable can be changed), it works effectively in the same fashion. """
[docs] def __init__(self, *args): super(ImmutableDict, self).__init__(*args) self.make_immutable()
def __setitem__(self, key, value, replace=False): self._is_immutable() # pragma: no cover super( ImmutableDict, self).__setitem__(key, value, replace) # pragma: no cover def __delitem__(self, key): self._is_immutable() def __copy__(self): duplicate = {} for key, value in self.items(): duplicate[key] = value return duplicate def __deepcopy__(self, clone): duplicate = {} for key, value in self.items(): duplicate[deepcopy(key, clone)] = deepcopy(value, clone) return duplicate def appendlist(self, key, value): self._is_immutable() # pragma: no cover super(ImmutableDict, self).appendlist(key, value) # pragma: no cover def clear(self): self._is_immutable() super(ImmutableDict, self).clear() # pragma: no cover def copy(self): return self.__deepcopy__({}) def pop(self, key, *args): self._is_immutable() # pragma: no cover return super(ImmutableDict, self).pop(key, *args) # pragma: no cover def popitem(self): self._is_immutable() # pragma: no cover return super(ImmutableDict, self).popitem() # pragma: no cover def setdefault(self, key, default=None): self._is_immutable() # pragma: no cover return ( super( ImmutableDict, self).setdefault(key, default) # pragma: no cover ) def update(self, *args): self._is_immutable() # pragma: no cover super(ImmutableDict, self).update(*args) # pragma: no cover
[docs]class ImmutableMultiDict(MultiDict, ImmutableMixin): """Creates an immuatable MultiDict. """
[docs] def __init__(self, *args): super(ImmutableMultiDict, self).__init__(*args) self.make_immutable()
def __setitem__(self, key, value, replace=False): self._is_immutable() # pragma: no cover super( ImmutableMultiDict, self).__setitem__(key, value, replace) # pragma: no cover def __delitem__(self, key): self._is_immutable() # pragma: no cover def appendlist(self, key, value): self._is_immutable() # pragma: no cover super( ImmutableMultiDict, self).appendlist(key, value) # pragma: no cover def clear(self): self._is_immutable() # pragma: no cover super(MultiDict, self).clear() # pragma: no cover def pop(self, key, *args): self._is_immutable() # pragma: no cover return ( super(ImmutableMultiDict, self).pop(key, *args) # pragma: no cover ) def popitem(self): self._is_immutable() # pragma: no cover return super(ImmutableMultiDict, self).popitem() # pragma: no cover def setdefault(self, key, default=None): self._is_immutable() # pragma: no cover return ( super( ImmutableMultiDict, self).setdefault(key, default) # pragma: no cover ) def update(self, *args): self._is_immutable() # pragma: no cover super(ImmutableMultiDict, self).update(*args) # pragma: no cover