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)
[ ]: