tabletop-frog/ttfrog/attribute_map.py
2024-02-04 11:40:30 -08:00

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)