Quick Start#
Addtional Utility Methods#
keys()
values()
items()
to_dict()
to_OrderedDict()
[44]:
from datetime import date, datetime
from attrs_mate import attrs, AttrsClass
from rich import print as rprint
@attrs.define
class User(AttrsClass):
id: str = attrs.field()
name: str = attrs.field()
@attrs.define
class DBUser(User):
role: str = attrs.field()
[45]:
admin = DBUser(id=1, name="Alice", role="admin")
[46]:
admin.keys()
[46]:
['id', 'name', 'role']
[47]:
admin.values()
[47]:
[1, 'Alice', 'admin']
[48]:
admin.items()
[48]:
[('id', 1), ('name', 'Alice'), ('role', 'admin')]
[49]:
admin.to_dict()
[49]:
{'id': 1, 'name': 'Alice', 'role': 'admin'}
[50]:
admin.to_OrderedDict()
[50]:
OrderedDict([('id', 1), ('name', 'Alice'), ('role', 'admin')])
Automatical Serialization and Deserialization#
The to_dict()
and Class.from_dict()
method automatically serialize and deserialize your data properly.
[51]:
@attrs.define
class Profile(AttrsClass):
firstname = AttrsClass.ib_str()
lastname = AttrsClass.ib_str()
ssn = AttrsClass.ib_str()
@attrs.define
class Degree(AttrsClass):
name = AttrsClass.ib_str()
year = AttrsClass.ib_int()
@attrs.define
class People(AttrsClass):
"""
- ``profile`` is nested field.
- ``degrees`` is collection type field.
"""
id = AttrsClass.ib_int()
profile = Profile.ib_nested()
degrees = Degree.ib_list_of_nested()
[52]:
# constructor data is generic dict
people = People(
id=1,
profile=dict(
firstname="David",
lastname="John",
ssn="123-45-6789",
),
degrees=[
dict(name="Bachelor", year=2004),
dict(name="Master", year=2006),
],
)
rprint(people)
People( id=1, profile=Profile(firstname='David', lastname='John', ssn='123-45-6789'), degrees=[Degree(name='Bachelor', year=2004), Degree(name='Master', year=2006)] )
[53]:
people_data = people.to_dict()
rprint(people_data)
{ 'id': 1, 'profile': {'firstname': 'David', 'lastname': 'John', 'ssn': '123-45-6789'}, 'degrees': [{'name': 'Bachelor', 'year': 2004}, {'name': 'Master', 'year': 2006}] }
[54]:
people1 = People.from_dict(people_data)
rprint(people1)
People( id=1, profile=Profile(firstname='David', lastname='John', ssn='123-45-6789'), degrees=[Degree(name='Bachelor', year=2004), Degree(name='Master', year=2006)] )
[55]:
# constructor data is already object
people = People(
id=1,
profile=Profile(
firstname="David",
lastname="John",
ssn="123-45-6789",
),
degrees=[
Degree(name="Bachelor", year=2004),
Degree(name="Master", year=2006),
],
)
rprint(people)
People( id=1, profile=Profile(firstname='David', lastname='John', ssn='123-45-6789'), degrees=[Degree(name='Bachelor', year=2004), Degree(name='Master', year=2006)] )
[56]:
people_data = people.to_dict()
rprint(people_data)
{ 'id': 1, 'profile': {'firstname': 'David', 'lastname': 'John', 'ssn': '123-45-6789'}, 'degrees': [{'name': 'Bachelor', 'year': 2004}, {'name': 'Master', 'year': 2006}] }
[57]:
people1 = People.from_dict(people_data)
rprint(people1)
People( id=1, profile=Profile(firstname='David', lastname='John', ssn='123-45-6789'), degrees=[Degree(name='Bachelor', year=2004), Degree(name='Master', year=2006)] )
Attributes with Type Validator#
[58]:
class User:
pass
@attrs.define
class Invalid:
pass
@attrs.define
class Data(AttrsClass):
ib_str = AttrsClass.ib_str()
ib_int = AttrsClass.ib_int()
ib_float = AttrsClass.ib_float()
ib_bool = AttrsClass.ib_bool()
ib_date = AttrsClass.ib_date()
ib_datetime = AttrsClass.ib_datetime()
ib_user = AttrsClass.ib_generic(User)
[59]:
data = Data(
ib_str="s",
ib_int=1,
ib_float=3.14,
ib_bool=True,
ib_date=date.today(),
ib_datetime=datetime.now(),
ib_user=User(),
)
[60]:
invalid = Invalid()
[61]:
attrs.evolve(data, ib_str=invalid)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[61], line 1
----> 1 attrs.evolve(data, ib_str=invalid)
File ~/Documents/GitHub/attrs_mate-project/.venv/lib/python3.8/site-packages/attr/_funcs.py:413, in evolve(*args, **changes)
410 if init_name not in changes:
411 changes[init_name] = getattr(inst, attr_name)
--> 413 return cls(**changes)
File <attrs generated init __main__.Data>:11, in __init__(self, ib_str, ib_int, ib_float, ib_bool, ib_date, ib_datetime, ib_user)
9 _setattr('ib_user', ib_user)
10 if _config._run_validators is True:
---> 11 __attr_validator_ib_str(self, __attr_ib_str, self.ib_str)
12 __attr_validator_ib_int(self, __attr_ib_int, self.ib_int)
13 __attr_validator_ib_float(self, __attr_ib_float, self.ib_float)
File ~/Documents/GitHub/attrs_mate-project/.venv/lib/python3.8/site-packages/attr/validators.py:269, in _OptionalValidator.__call__(self, inst, attr, value)
266 if value is None:
267 return
--> 269 self.validator(inst, attr, value)
File ~/Documents/GitHub/attrs_mate-project/.venv/lib/python3.8/site-packages/attr/validators.py:100, in _InstanceOfValidator.__call__(self, inst, attr, value)
96 """
97 We use a callable class to be able to change the ``__repr__``.
98 """
99 if not isinstance(value, self.type):
--> 100 raise TypeError(
101 "'{name}' must be {type!r} (got {value!r} that is a "
102 "{actual!r}).".format(
103 name=attr.name,
104 type=self.type,
105 actual=value.__class__,
106 value=value,
107 ),
108 attr,
109 self.type,
110 value,
111 )
TypeError: ("'ib_str' must be <class 'str'> (got Invalid() that is a <class '__main__.Invalid'>).", Attribute(name='ib_str', default=NOTHING, validator=<optional validator for <instance_of validator for type <class 'str'>> or None>, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None, kw_only=False, inherited=False, on_setattr=None, alias='ib_str'), <class 'str'>, Invalid())
[62]:
# These are similar
# attrs.evolve(data, ib_int=invalid)
# attrs.evolve(data, ib_float=invalid)
# attrs.evolve(data, ib_bool=invalid)
# attrs.evolve(data, ib_date=invalid)
# attrs.evolve(data, ib_datetime=invalid)
# attrs.evolve(data, ib_user=invalid)
[63]:
# By default, None is OK,
# if you don't want none, you can use AttrsClass.ib_str(nullable=False)
data = Data(
ib_str=None,
ib_int=None,
ib_float=None,
ib_bool=None,
ib_date=None,
ib_datetime=None,
ib_user=None,
)
[64]:
@attrs.define
class Data(AttrsClass):
ib_list = AttrsClass.ib_list()
ib_tuple = AttrsClass.ib_tuple()
ib_set = AttrsClass.ib_set()
ib_dict = AttrsClass.ib_dict()
ib_user_list = AttrsClass.ib_list_of_generic(User)
ib_user_mapper = AttrsClass.ib_dict_of_generic(key_type=str, value_type=User)
[65]:
data = Data(
ib_list=[1, 2],
ib_tuple=(1, 2),
ib_set={1, 2},
ib_dict={"a": 1, "b": 2},
ib_user_list=[User(), User()],
ib_user_mapper={"a": User(), "b": User()},
)
[66]:
# ib_list is not a list
attrs.evolve(data, ib_list=invalid)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[66], line 2
1 # ib_list is not a list
----> 2 attrs.evolve(data, ib_list=invalid)
File ~/Documents/GitHub/attrs_mate-project/.venv/lib/python3.8/site-packages/attr/_funcs.py:413, in evolve(*args, **changes)
410 if init_name not in changes:
411 changes[init_name] = getattr(inst, attr_name)
--> 413 return cls(**changes)
File <attrs generated init __main__.Data-3>:10, in __init__(self, ib_list, ib_tuple, ib_set, ib_dict, ib_user_list, ib_user_mapper)
8 _setattr('ib_user_mapper', ib_user_mapper)
9 if _config._run_validators is True:
---> 10 __attr_validator_ib_list(self, __attr_ib_list, self.ib_list)
11 __attr_validator_ib_tuple(self, __attr_ib_tuple, self.ib_tuple)
12 __attr_validator_ib_set(self, __attr_ib_set, self.ib_set)
File ~/Documents/GitHub/attrs_mate-project/.venv/lib/python3.8/site-packages/attr/validators.py:269, in _OptionalValidator.__call__(self, inst, attr, value)
266 if value is None:
267 return
--> 269 self.validator(inst, attr, value)
File ~/Documents/GitHub/attrs_mate-project/.venv/lib/python3.8/site-packages/attr/validators.py:100, in _InstanceOfValidator.__call__(self, inst, attr, value)
96 """
97 We use a callable class to be able to change the ``__repr__``.
98 """
99 if not isinstance(value, self.type):
--> 100 raise TypeError(
101 "'{name}' must be {type!r} (got {value!r} that is a "
102 "{actual!r}).".format(
103 name=attr.name,
104 type=self.type,
105 actual=value.__class__,
106 value=value,
107 ),
108 attr,
109 self.type,
110 value,
111 )
TypeError: ("'ib_list' must be <class 'list'> (got Invalid() that is a <class '__main__.Invalid'>).", Attribute(name='ib_list', default=NOTHING, validator=<optional validator for <instance_of validator for type <class 'list'>> or None>, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None, kw_only=False, inherited=False, on_setattr=None, alias='ib_list'), <class 'list'>, Invalid())
[67]:
# ib_user_list is not a list of User
attrs.evolve(data, ib_user_list=[User(), invalid])
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[67], line 2
1 # ib_user_list is not a list of User
----> 2 attrs.evolve(data, ib_user_list=[User(), invalid])
File ~/Documents/GitHub/attrs_mate-project/.venv/lib/python3.8/site-packages/attr/_funcs.py:413, in evolve(*args, **changes)
410 if init_name not in changes:
411 changes[init_name] = getattr(inst, attr_name)
--> 413 return cls(**changes)
File <attrs generated init __main__.Data-3>:14, in __init__(self, ib_list, ib_tuple, ib_set, ib_dict, ib_user_list, ib_user_mapper)
12 __attr_validator_ib_set(self, __attr_ib_set, self.ib_set)
13 __attr_validator_ib_dict(self, __attr_ib_dict, self.ib_dict)
---> 14 __attr_validator_ib_user_list(self, __attr_ib_user_list, self.ib_user_list)
15 __attr_validator_ib_user_mapper(self, __attr_ib_user_mapper, self.ib_user_mapper)
File ~/Documents/GitHub/attrs_mate-project/.venv/lib/python3.8/site-packages/attr/validators.py:269, in _OptionalValidator.__call__(self, inst, attr, value)
266 if value is None:
267 return
--> 269 self.validator(inst, attr, value)
File ~/Documents/GitHub/attrs_mate-project/.venv/lib/python3.8/site-packages/attr/validators.py:396, in _DeepIterable.__call__(self, inst, attr, value)
393 self.iterable_validator(inst, attr, value)
395 for member in value:
--> 396 self.member_validator(inst, attr, member)
File ~/Documents/GitHub/attrs_mate-project/.venv/lib/python3.8/site-packages/attr/validators.py:100, in _InstanceOfValidator.__call__(self, inst, attr, value)
96 """
97 We use a callable class to be able to change the ``__repr__``.
98 """
99 if not isinstance(value, self.type):
--> 100 raise TypeError(
101 "'{name}' must be {type!r} (got {value!r} that is a "
102 "{actual!r}).".format(
103 name=attr.name,
104 type=self.type,
105 actual=value.__class__,
106 value=value,
107 ),
108 attr,
109 self.type,
110 value,
111 )
TypeError: ("'ib_user_list' must be <class '__main__.User'> (got Invalid() that is a <class '__main__.Invalid'>).", Attribute(name='ib_user_list', default=NOTHING, validator=<optional validator for <deep_iterable validator for <instance_of validator for type <class 'list'>> iterables of <instance_of validator for type <class '__main__.User'>>> or None>, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None, kw_only=False, inherited=False, on_setattr=None, alias='ib_user_list'), <class '__main__.User'>, Invalid())
[68]:
# ib_user_mapper is not a str -> User mapper
attrs.evolve(data, ib_user_mapper={"a": 1})
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[68], line 2
1 # ib_user_mapper is not a str -> User mapper
----> 2 attrs.evolve(data, ib_user_mapper={"a": 1})
File ~/Documents/GitHub/attrs_mate-project/.venv/lib/python3.8/site-packages/attr/_funcs.py:413, in evolve(*args, **changes)
410 if init_name not in changes:
411 changes[init_name] = getattr(inst, attr_name)
--> 413 return cls(**changes)
File <attrs generated init __main__.Data-3>:15, in __init__(self, ib_list, ib_tuple, ib_set, ib_dict, ib_user_list, ib_user_mapper)
13 __attr_validator_ib_dict(self, __attr_ib_dict, self.ib_dict)
14 __attr_validator_ib_user_list(self, __attr_ib_user_list, self.ib_user_list)
---> 15 __attr_validator_ib_user_mapper(self, __attr_ib_user_mapper, self.ib_user_mapper)
File ~/Documents/GitHub/attrs_mate-project/.venv/lib/python3.8/site-packages/attr/validators.py:269, in _OptionalValidator.__call__(self, inst, attr, value)
266 if value is None:
267 return
--> 269 self.validator(inst, attr, value)
File ~/Documents/GitHub/attrs_mate-project/.venv/lib/python3.8/site-packages/attr/validators.py:445, in _DeepMapping.__call__(self, inst, attr, value)
443 for key in value:
444 self.key_validator(inst, attr, key)
--> 445 self.value_validator(inst, attr, value[key])
File ~/Documents/GitHub/attrs_mate-project/.venv/lib/python3.8/site-packages/attr/validators.py:269, in _OptionalValidator.__call__(self, inst, attr, value)
266 if value is None:
267 return
--> 269 self.validator(inst, attr, value)
File ~/Documents/GitHub/attrs_mate-project/.venv/lib/python3.8/site-packages/attr/validators.py:100, in _InstanceOfValidator.__call__(self, inst, attr, value)
96 """
97 We use a callable class to be able to change the ``__repr__``.
98 """
99 if not isinstance(value, self.type):
--> 100 raise TypeError(
101 "'{name}' must be {type!r} (got {value!r} that is a "
102 "{actual!r}).".format(
103 name=attr.name,
104 type=self.type,
105 actual=value.__class__,
106 value=value,
107 ),
108 attr,
109 self.type,
110 value,
111 )
TypeError: ("'ib_user_mapper' must be <class '__main__.User'> (got 1 that is a <class 'int'>).", Attribute(name='ib_user_mapper', default=NOTHING, validator=<optional validator for <deep_mapping validator for objects mapping <optional validator for <instance_of validator for type <class 'str'>> or None> to <optional validator for <instance_of validator for type <class '__main__.User'>> or None>> or None>, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None, kw_only=False, inherited=False, on_setattr=None, alias='ib_user_mapper'), <class '__main__.User'>, 1)
[ ]: