Source code for spyql.qdict

from spyql.nulltype import NULL
from spyql import log


[docs]class qdict(dict): """ A dictionary that supports :data:`~spyql.nulltype.NULL` where items can be accessed like attributes:: mydict = qdict({ "a": 1, "b": { "c": 2 }, "d": None }) mydict.a # returns 1, same as mydict["a"] mydict.z # returns NULL whenever a key is not found mydict.b.c # returns 2, neested dicts also support attribute access mydict.b.x # returns NULL, neested dicts are null-safe too mydict.d # returns NULL, Nones are converted to NULLs) """ @staticmethod def __none2null(value): if type(value) is list: # TODO consider conversion of tuples/sets/etc return [NULL if x is None else x for x in value] return NULL if value is None else value @staticmethod def __none2null_dict(adic): # TODO: this should work with a list of pairs and not only with a dict return {k: qdict.__none2null(v) for k, v in adic.items()} def __init__(self, adic, dirty=True, **kwargs): # dirty option keeps None values in dict instead of converting to NULL self.update(adic if dirty else qdict.__none2null_dict(adic), **kwargs) self.__dict__["_dirty"] = dirty def __getitem__(self, key): try: item = dict.__getitem__(self, key) if type(item) is dict: # lazy convertion of dicts # TODO consider convertion of lists of dicts return qdict(item, self._dirty) if self._dirty: # lazy convertion return qdict.__none2null(item) return item except KeyError: return self.__missing__(key) def __getattr__(self, key): if ( not key.startswith("__") or key in self ): # because of special methods like __getstate__ return self[key] else: raise AttributeError def __setattr__(self, key, value): self[key] = value
[docs] def values(self): """ Returns a tuple of the dict values. Attention: does not return a view like dict! """ return tuple([qdict.__none2null(x) for x in super().values()])
[docs] def items(self): """ Returns a zip of keys and values. Attention: does not return a view like dict! """ return zip(self.keys(), self.values())
# returns NULL when key is not found def __missing__(self, key): log.user_warning4func("key not found", KeyError(key), key) return NULL def __hash__(self): # TODO make dict immutable import json # TODO check if this is sufficienly efficient... # This only needs to guarantee that two equivalent dicts have the same hash return hash(json.dumps(self, default=str, sort_keys=True))
[docs] def updatef(self, another_dict): # same as update but returns the dict self.update(another_dict) return self
[docs]class str_qdict(qdict): def __getitem__(self, key): if key is NULL: return NULL return super().__getitem__(str(key)) def __contains__(self, key): if key is NULL: return False return super().__contains__(str(key))
# implement if needed: # def __delitem__(self, key): # def __setitem__(self, key, val):