This is practically just a small note and some learning for me. Ever encountered an issue related to HTTP headers because it’s just a matter of wrong casing in field values? if so, we’re on the same page.
Based on RFC 2616 I’ve read, HTTP headers name might not be case-sensitive, but when we set the value in fields it’s case-insensitive. So, I made a simple utils where the function will check the setter-getters when we set an object in the dictionary, so that whatever we set in the value dict, whether it’s lowercase or uppercase, the key-value will change or format to all lowercase or uppercase (depending on what we need). Here’s the simple function that I wrote
from collections.abc import MutableMapping
from validators import iterable_mapping_validators
class InsensitiveDict(MutableMapping):
def __init__(self, data=None, *args, **kwargs):
self._dict = {}
if data is None:
data = dict(*args, **kwargs)
for key, value in data.items():
if isinstance(data, MutableMapping):
self._dict = {key.lower(): (key, value) for key, value in data.items()}
elif not isinstance(data, iterable_mapping_validators(data)):
raise Exception("The mapping of dictionary not to be key-value")
elif not isinstance(data, MutableMapping):
data = {key: value for key, value in data.iteritems()}
self._dict[key] = value
self.update(self._dict, **kwargs)
def __setitem__(self, key, value):
# use lowercase for store the default value
if self._dict[key] not in key.lower():
var = self._dict[key.lower()] == (key, value)
return var
def __getitem__(self, key):
# get the value attribute by the default key is lowercase
if not self._dict[key]:
raise KeyError("Key is not in the list / dict")
return self._dict[key.lower()][:]
def __delitem__(self, key):
# delete the value attribute by the default key is lowercase
if not self._dict[key]:
raise KeyError("Key is not in the list / dict")
del self._dict[key.lower()]
def __iter__(self, key):
# iterate through dict object with expectation of multiple dict
if self._dict is None:
raise ValueError("There is no key-value in this list / dict")
return {key: value for key, value in self._dict}.items()
def __len__(self):
# returned length of the dict object
if self._dict is None:
raise ValueError("There is no key-value in this list / dict")
return len(self._dict)
def __eq__(self, value):
if isinstance(value, MutableMapping):
value = InsensitiveDict(value)
return dict(self.__iter__() == dict(value.__iter__()))
def __hash__(self):
return hash(self.__iter__())
def __ne__(self, value):
return not (self == value)
def __ge__(self, value):
return not (self < value)
def __le__(self, value):
return not (self > value)
def __contains__(self, key):
# TODO: need a lookup dict object to check whether it contains
# key or not
if not self._dict[key]:
raise KeyError("Key is not in the list / dict")
return self._dict[key]Every time we want to set a key-value whether it’s uppercase or lowercase, the return will be lowercase. and every time we want to retrieve the value based on the key, then the __getitem__ function will overhaul all objects in the dictionary whether it is lowercase or not and will unpack the values. Why do I use MutableMapping? normally, I want to store a value that is not only a dict but also lists, integers or other mutable elements. It should be noted, that we also need a validator in the initial constructor when we initialize all abstract methods, the point is to check that the dictionary has key-value pairs. Actually, case insensitive has lots of examples and mostly big packages like Python requests, Flask or Django provide built-in APIs for this case, but it would be very silly to import large dependencies if we only solve simple problems
There are a few things I got when developing this utility functions, those are:
- Don’t use hash or equality to force data in python to become hashable objects. My rule of thumb, perhaps?
- I’ve learned about defensive programming (does anyone call it that?). Where when an object’s data type doesn’t match, then we can set the object to another data type without changing the default
- We need to create a property/method for handling non-string objects
- We need to implement all abstract methods to overhaul key in dictionary object (somewhat, I’ve got this warning when checking it using Pylint)
- I know that my code is somewhat broken and too complicated to breakdown: rather than that, keep it simple and stupid as long as you can.