Source code for py2store.utils.mappify

"""
Utils to wrap any object into a mapping interface
"""
from py2store.base import KvReader
from py2store.utils.glom import glom, Path, Spec


# TODO: Handle names_of_literals concern better. Here affects all keys with that name (regardless of parent context)
#   See boltons remap: Has a path argument to carry the context
# TODO: Probably should make this a class factory instead.
# TODO: Might want the default to be caching iter (but need to be able to remove)
# TODO:
[docs]class Mappify(KvReader): """ >>> d = { ... 'a': 'simple', ... 'b': {'is': 'nested'}, ... 'c': {'is': 'nested', 'and': 'has', 'a': [1, 2, 3]} ... } >>> g = Mappify(d) >>> >>> assert list(g) == ['a', 'b.is', 'b', 'c.is', 'c.and', 'c.a', 'c'] >>> assert g['a'] == 'simple' >>> assert g['b.is'] == 'nested' >>> assert g['c.a'] == [1, 2, 3] >>> >>> for k, v in g.items(): ... print(f"{k}: {v}") ... a: simple b.is: nested b: {'is': 'nested'} c.is: nested c.and: has c.a: [1, 2, 3] c: {'is': 'nested', 'and': 'has', 'a': [1, 2, 3]} """ def __init__( self, target, node_types=(dict,), key_concat=lambda prefix, suffix: prefix + '.' + suffix, names_of_literals=(), **kwargs ): __doc__ = ( 'A Mapping interface for glom. Fixes the target, and keys are considered as keys\n\n' + str(glom.__doc__) ) self._target = target self._node_types = node_types self._key_concat = key_concat self._names_of_literals = set(names_of_literals) self._kwargs = ( kwargs # the stuff that is given to the **kwargs of glom ) # TODO: Not sure the following is kosher. Doesn't make me feel nice and fuzzy. self._mk_similar_mappify = lambda x: self.__class__( x, key_concat=key_concat, node_types=node_types, names_of_literals=self._names_of_literals, **kwargs ) def __getitem__(self, spec): return glom(self._target, spec, **self._kwargs) def __iter__(self): """Depth first traversal: All nodes yielded.""" for k in self._target: val = self[Path(k)] if isinstance(self[k], *self._node_types): yield from ( self._key_concat(k, nested_key) for nested_key in self._mk_similar_mappify(val) ) yield k
[docs]class LeafMappify(Mappify): """ A dict-like interface to glom. Here, only leaf keys are taken into account. >>> d = { ... 'a': 'simple', ... 'b': {'is': 'nested'}, ... 'c': {'is': 'nested', 'and': 'has', 'a': [1, 2, 3]} ... } >>> g = LeafMappify(d) >>> >>> assert list(g) == ['a', 'b.is', 'c.is', 'c.and', 'c.a'] >>> assert g['a'] == 'simple' >>> assert g['b.is'] == 'nested' >>> assert g['c.a'] == [1, 2, 3] >>> >>> for k, v in g.items(): ... print(f"{k}: {v}") ... a: simple b.is: nested c.is: nested c.and: has c.a: [1, 2, 3] """ def __iter__(self): """Depth first traversal: Only leaf nodes yielded.""" for k in self._target: val = self[Path(k)] if isinstance(val, *self._node_types): yield from ( self._key_concat(k, nested_key) for nested_key in self._mk_similar_mappify(val) ) else: yield k
dot_str_key_iterator = lambda p: p.split('.') bracket_getter = lambda obj, k: obj[k] def simple_glom( target, spec, node_types=(dict,), key_iterator=dot_str_key_iterator, item_getter=bracket_getter, ): for k in key_iterator(spec): print(k) target = item_getter(target, k) if not isinstance(target, node_types): break return target