konradhalas / dacite

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

When parsing with Union Optional values are swallowing the input #263

Open m-aciek opened 3 weeks ago

m-aciek commented 3 weeks ago

Hi, thanks for your work on the library.

Describe the bug

When parsing with union an Optional value in a dataclass doesn't consider the existence of keys in dictionaries when parsing the structure. Please see the reproducer.

To Reproduce Consider the code below:

from dataclasses import dataclass
from typing import Union, Optional

import dacite

@dataclass
class OptionalDataclass:
    optional: Optional[int]

@dataclass
class Required:
    required: int

@dataclass
class UnionDataclass:
    field: Union[OptionalDataclass, Required]

print(dacite.from_dict(UnionDataclass, {"field": {"required": 2}}))
# UnionDataclass(field=OptionalDataclass(optional=None))

Expected behavior

print(dacite.from_dict(UnionDataclass, {"field": {"required": 2}}))
# UnionDataclass(field=Required(required=2))

I would expect dacite to look into dictionary keys and prefer an object which has the key.

Environment

Additional context

Changing the order of classes in the Union annotation helps in this case.

from dataclasses import dataclass
from typing import Union, Optional

import dacite

@dataclass
class OptionalDataclass:
    optional: Optional[int]

@dataclass
class Required:
    required: int

@dataclass
class UnionDataclass:
    field: Union[Required, OptionalDataclass]

print(dacite.from_dict(UnionDataclass, {"field": {"required": 2}}))
# UnionDataclass(field=Required(required=2))