konradhalas / dacite

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

Return value of custom type hook is discarded when decoding generic type in union #230

Open Feuermurmel opened 1 year ago

Feuermurmel commented 1 year ago

Describe the bug

Return value of custom type hook is discarded when decoding generic type in union.

To Reproduce

In the following example, a generic dataclass Foo[T] is defined and used as a field in another dataclass Bar. In the example, only the type Foo[int] is actually used and for that type, a custom type hook is defined that decodes a value from a JSON int:

from typing import TypeVar, Generic, Union
from dataclasses import dataclass

import dacite

T = TypeVar('T')

@dataclass
class Foo(Generic[T]):
    value: T

@dataclass
class Bar:
    foo: Union[Foo[int], str]

def _read_int_foo(data):
    assert isinstance(data, int)
    result = Foo(data)
    print(f'_read_int_foo: {data} -> {result}')
    return result

_dacite_type_hooks = {
    Foo[int]: _read_int_foo}

dacite_config = dacite.Config(type_hooks=_dacite_type_hooks, check_types=False)

data = {
    'foo': 123
}

print(dacite.from_dict(Bar, data, dacite_config))

Running this prints the following:

_read_int_foo: 123 -> Foo(value=123)
Bar(foo=123)

I.e. the field foo has been set to the original int value from the data instead of the value returned by _read_int_foo(). This only happens when check_types is set to False (otherwise a UnionMatchError is raised).

Expected behavior

IMHO, the returned value should be used:

_read_int_foo: 123 -> Foo(value=123)
Bar(foo=Foo(value=123))

Environment

$ pip list
Package    Version
---------- -------
dacite     1.8.0
pip        23.0.1
setuptools 67.3.2
$ python -V
Python 3.8.16