Source code for profile_photo.utils.dict_helper
"""
Dict helper module
"""
from __future__ import annotations
from typing import Dict, Generator, TypeVar
_KT = TypeVar('_KT', bound=str)
_VT = TypeVar('_VT')
[docs]class DictWithLowerStore(Dict[_KT, _VT]):
"""
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
dls.get('aCCEPT') == 'application/json' # 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: dict[str, tuple[_KT, _VT]] = {}
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) -> _VT:
"""
Do a case-insensitive lookup. This lower-cases `key` and looks up
from the lower-cased key store.
"""
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) -> Generator[tuple[str, _VT], None, None]:
"""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()))