konradhalas / dacite

Simple creation of data classes from dictionaries.
MIT License
1.76k stars 106 forks source link

Type hooks are applied at the wrong time for Union cases. #84

Closed kwvanderlinde closed 4 years ago

kwvanderlinde commented 4 years ago

This relates to the fix for #82.

In most circumstances, a type hook is applied to input data before the corresponding type is instantiated. This lets us modify the input prior to parsing it. However, if the type is used in a Union, then the type hook is not applied to the input, but to the parsed result.

The expected behaviour would be for the type hook always and only be used to transform the input data.

Here is a minimal example demonstrating the difference:

#!/usr/bin/env python3
import dacite
from dataclasses import dataclass
from typing import Union

def hook(data):
    assert not isinstance(data, B)
    return data

@dataclass
class A:
    a: int

@dataclass
class B:
    b: str

@dataclass
class Top:
    field: Union[A, B]  # Removing `A` makes the assertion pass!

dacite.from_dict(
    data_class=Top,
    data={
        'field': {
            'b': 'Hello!'
        }
    },
    config=dacite.Config(
        strict=True,
        type_hooks={
            B: hook,
        }
    ))

This occurs on dacite 1.4.0.

konradhalas commented 4 years ago

@kwvanderlinde thank you for this issue.

Behaviour you described was intentional. I thought that it's not possible to solve this problem in a different way. Now I reviewed this once again and I think I found a solution. Union is a very tricky type.

I'll try to fix it soon.

konradhalas commented 4 years ago

@kwvanderlinde should be ok now, please check 1.5.0 version.