Source code for dataclass_wizard.utils.dict_helper
"""
Dict helper module
"""
[docs]
class DictWithLowerStore(dict):
"""
A ``dict``-like object with a lower-cased key store.
All keys are expected to be strings. The structure remembers the
case of the lower-cased key to be set, and methods like ``get()``
and ``get_key()`` will use the lower-cased store. However, querying
and contains testing is case sensitive::
dls = DictWithLowerStore()
dls['Accept'] = 'application/json'
dls['aCCEPT'] == 'application/json' # False (raises KeyError)
dls['Accept'] == 'application/json' # True
dls.get('aCCEPT') == 'application/json' # True
dls.get_key('aCCEPT') == 'Accept' # True
list(dls) == ['Accept'] # True
.. NOTE::
I don't want to use the `CaseInsensitiveDict` from
`request.structures`, because it turns out the lookup via that dict
implementation is rather slow. So this version is somewhat of a
trade-off, where I retain the same speed on lookups as a plain `dict`,
but I also have a lower-cased key store, in case I ever need to use it.
"""
__slots__ = ('_lower_store', )
def __init__(self, data=None, **kwargs):
super().__init__()
self._lower_store = {}
if data is None:
data = {}
self.update(data, **kwargs)
def __setitem__(self, key, value):
super().__setitem__(key, value)
# Store the lower-cased key for lookups via `get`. Also store the
# actual key alongside the value.
self._lower_store[key.lower()] = (key, value)
[docs]
def get_key(self, key) -> str:
"""Return the original cased key"""
return self._lower_store[key.lower()][0]
[docs]
def get(self, key):
"""
Do a case-insensitive lookup. This lower-cases `key` and looks up
from the lower-cased key store.
"""
try:
return self.__getitem__(key)
except KeyError:
return self._lower_store[key.lower()][1]
def __delitem__(self, key):
lower_key = key.lower()
actual_key, _ = self._lower_store[lower_key]
del self[actual_key]
del self._lower_store[lower_key]
[docs]
def lower_items(self):
"""Like iteritems(), but with all lowercase keys."""
return (
(lowerkey, keyval[1])
for (lowerkey, keyval)
in self._lower_store.items()
)
def __eq__(self, other):
if isinstance(other, dict):
other = DictWithLowerStore(other)
else:
return NotImplemented
# Compare insensitively
return dict(self.lower_items()) == dict(other.lower_items())
[docs]
def update(self, *args, **kwargs):
if len(args) > 1:
raise TypeError("update expected at most 1 arguments, got %d" % len(args))
other = dict(*args, **kwargs)
for key in other:
self[key] = other[key]
[docs]
def copy(self):
return DictWithLowerStore(self._lower_store.values())
def __repr__(self):
return str(dict(self.items()))