Source code for py2store.utils.attr_dict

"""
a data object layer for object attributes
"""
from collections import abc
from keyword import iskeyword
from warnings import warn


[docs]class AttrMap: """A read-only façade for navigating a JSON-like object using attribute notation. Based on Luciano Ramalho's "Fluent Python" book. >>> t = AttrMap({'a': {'b': 2, 'foo': 'bar'}, 'b': [1,2,3]}) >>> t AttrMap({'a': {'b': 2, 'foo': 'bar'}, 'b': [1, 2, 3]}) >>> t.a AttrMap({'b': 2, 'foo': 'bar'}) >>> t.a.foo 'bar' >>> t.b [1, 2, 3] """ def __new__(cls, arg): # <1> if isinstance(arg, abc.Mapping): return super().__new__(cls) # <2> elif isinstance(arg, abc.MutableSequence): # <3> return [cls(item) for item in arg] else: return arg def __init__(self, mapping): self.__data = {} identifiers = [] for key, value in mapping.items(): if not isinstance(key, str) or not str.isidentifier(key): identifiers.append(key) elif iskeyword(key): key += '_' self.__data[key] = value if identifiers: warn( f'{len(identifiers)} keys were not identifiers. Namely:\n{identifiers}' ) def __getattr__(self, name): if hasattr(self.__data, name): return getattr(self.__data, name) else: return AttrMap(self.__data[name]) # <4> def __iter__(self): yield from self.__data def __dir__(self): s = set(dir(type(self))) s.update(self.__dict__) s.update(self) return s def __repr__(self): return f'{self.__class__.__name__}({self.__data})'
def special_dir(self): s = set(dir(type(self))) s.update(self.__dict__) s.update( filter( lambda k: isinstance(k, str) and str.isidentifier(k) and not iskeyword(k), self, ) ) return s
[docs]def attr_wrap(cls, name=None): """Returns a Mapping class that routes attribute access to keys of mapping. >>> A = attr_wrap(dict) >>> t = A({'a_special_attr': 'foo', 'another_attr': 2, # valid identifiers ... 42: [1, 2], '$invalid': 'identifier', 'class': 'is a reserved keyword'}) # not valid identifiers >>> # verify that we have the attr we want >>> assert 'a_special_attr' in dir(t) >>> assert 'another_attr' in dir(t) >>> # verify that we DO NOT have the attr we DO NOT want >>> assert 42 not in dir(t) >>> assert '$invalid' not in dir(t) >>> assert 'class' not in dir(t) """ return type( name or f'Attr{cls.__name__}', (cls,), {'__getattr__': cls.__getitem__, '__dir__': special_dir}, )