Type safety

The library will perform a lot of checks when creating a mapping. All these checks already happen during the definition of the mapper, they are not run every single time when converting an instance of one class to an instance of another class.

Missing fields check

>>> @dataclass
... class Person:
...     name: str
>>>
>>> @mapper(Person)
... @dataclass
... class Contact:
...     surname: str
...     first_name: str
Traceback (most recent call last):
  File "dataclass_mapper/mapper.py", line 55, in _make_mapper
    raise ValueError(
ValueError: 'name' of 'Person' has no mapping in 'Contact'

The field name in the target class Person doesn’t have an equivalent in the source class Contact. The library complains it.

This happens also when the field has a default value

>>> @dataclass
... class Foo:
...     x: int = 42
>>>
>>> @mapper(Foo)
... @dataclass
... class Bar:
...     pass
Traceback (most recent call last):
  File "dataclass_mapper/mapping_method.py", line 55, in add_mapping
    raise ValueError(
ValueError: 'x' of 'Foo' has no mapping in 'Bar'

However you can use the default value by using init_with_default. This needs to be done explicitly.

>>> @mapper(Foo, {"x": init_with_default()})
... @dataclass
... class Bar:
...     pass

Field name checks

>>> @mapper(Person, {"name": "surname", "first_name": "first_name"})
... @dataclass
... class Contact:
...     surname: str
...     first_name: str
Traceback (most recent call last):
  File "dataclass_mapper/mapper.py", line 60, in _make_mapper
    raise ValueError(
ValueError: 'first_name' of mapping in 'Contact' doesn't exist in 'Person'
>>>
>>> @mapper(Person, {"name": "surname", "name": "name"})
... @dataclass
... class Contact:
...     surname: str
...     first_name: str
Traceback (most recent call last):
  File "dataclass_mapper/mapper.py", line 60, in _make_mapper
    raise ValueError(
ValueError: 'name' of mapping in 'Contact' doesn't exist in 'Contact'

Here we tried to map the first_name parameter, however the target class Person doesn’t have a first_name parameter. And we tried to map the name parameter, however the source class Contact doesn’t have a name parameter.

Type checks

>>> @dataclass
... class Contract:
...     full_time: bool
...     salary: int
>>>
>>> @mapper(Contract)
... @dataclass
... class EmploymentAgreement:
...     full_time: str  # "y" or "n"
...     salary: Optional[int]
Traceback (most recent call last):
    ...
TypeError: 'full_time' of type 'str' of 'EmploymentAgreement' cannot be converted to 'full_time' of type 'bool' of 'Contract'

Here both classes use different types for the fields. The library cannot map the field full_time of type str to a bool.

>>> @mapper(Contract)
... @dataclass
... class EmploymentAgreement:
...     full_time: bool
...     salary: Optional[int]
Traceback (most recent call last):
    ...
TypeError: 'salary' of type 'Optional[int]' of 'EmploymentAgreement' cannot be converted to 'salary' of type 'int' of 'Contract'

Here the library complains about the mapping an optional field to an non-optional one. The other way around would be fine however.