71 lines
2.0 KiB
Python
71 lines
2.0 KiB
Python
from collections.abc import Mapping
|
|
from dataclasses import dataclass, field
|
|
|
|
|
|
@dataclass
|
|
class AttributeMap(Mapping):
|
|
"""
|
|
AttributeMap is a data class that is also a mapping, converting a dict
|
|
into an object with attributes. Example:
|
|
|
|
>>> amap = AttributeMap(attributes={'foo': True, 'bar': False})
|
|
>>> amap.foo
|
|
True
|
|
>>> amap.bar
|
|
False
|
|
|
|
Instantiating an AttributeMap using the from_dict() class method will
|
|
recursively transform dictionary members sinto AttributeMaps:
|
|
|
|
>>> nested_dict = {'foo': {'bar': {'baz': True}, 'boz': False}}
|
|
>>> amap = AttributeMap.from_dict(nested_dict)
|
|
>>> amap.foo.bar.baz
|
|
True
|
|
>>> amap.foo.boz
|
|
False
|
|
|
|
The dictionary can be accessed directly via 'attributes':
|
|
|
|
>>> amap = AttributeMap(attributes={'foo': True, 'bar': False})
|
|
>>> list(amap.attributes.keys()):
|
|
>>>['foo', 'bar']
|
|
|
|
Because AttributeMap is a mapping, you can use it anywhere you would use
|
|
a regular mapping, like a dict:
|
|
|
|
>>> amap = AttributeMap(attributes={'foo': True, 'bar': False})
|
|
>>> 'foo' in amap
|
|
True
|
|
>>> "{foo}, {bar}".format(**amap)
|
|
True, False
|
|
|
|
|
|
"""
|
|
attributes: field(default_factory=dict)
|
|
|
|
def __getattr__(self, attr):
|
|
if attr in self.attributes:
|
|
return self.attributes[attr]
|
|
return self.__getattribute__(attr)
|
|
|
|
def __len__(self):
|
|
return len(self.attributes)
|
|
|
|
def __getitem__(self, key):
|
|
return self.attributes[key]
|
|
|
|
def __iter__(self):
|
|
return iter(self.attributes)
|
|
|
|
@classmethod
|
|
def from_dict(cls, kwargs: dict):
|
|
"""
|
|
Create a new AttributeMap object using keyword arguments. Dicts are
|
|
recursively converted to AttributeMap objects; everything else is
|
|
passed as-is.
|
|
"""
|
|
attrs = {}
|
|
for k, v in sorted(kwargs.items()):
|
|
attrs[k] = AttributeMap.from_dict(v) if type(v) is dict else v
|
|
return cls(attributes=attrs)
|