konradhalas / dacite

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

Compatibility for frozen dataclasses with non-init fields. #181

Closed nickjmiller closed 1 year ago

nickjmiller commented 2 years ago

I have a usecase where I am loading and dumping dataclasses to json. Some dataclasses have a field that should not change, but I'd still like this field to show up in asdict, since the classes are indistinguishable otherwise as JSON. Using field(init=False, default="value"), I can get the behavior I want, but dacite is not able to parse this into its original dataclass.

Minimal example:

from dataclasses import asdict, dataclass, field
from dacite import from_dict

@dataclass(frozen=True)
class X:
    y: str = field(init=False, default="value")

print(asdict(X())) # {'y': 'value'}

from_dict(data_class=X, data=asdict(X())) # throws exception dataclasses.FrozenInstanceError: cannot assign to field 'y'

Without init=False:

@dataclass(frozen=True)
class X:
    y: str = field(default="value")

print(asdict(X())) # {'y': 'value'}

from_dict(data_class=X, data=asdict(X())) # works

X(y="unwanted_value") # works, BAD for my case

Can from_dict be modified to respect default values when init=False?

konradhalas commented 1 year ago

@nickjmiller thank you for reporting this issue.

Your dataclass is frozen=True and your field is init=True. There is no way to setup this field via dacite so you shouldn't pass {'y': 'value'} in your data.

On the other hand I fixed #195 - it's very similar issue but with empty data. I hope it works in your case too.