[BUG] Incomplete Support for Union or | Type Deserialization in Common Python Types (e.g., `int`, `Enum`, `Literal`)
StripedMonkey opened this issue · 9 comments
- Dataclass Wizard version: 0.22.1
- Python version: 3.10.4
- Operating System: Ubuntu 22.04
Description
Using the dataclass-wizard to deserialize a YAML file fails when trying to use string literals as field options.
In my particular case, a value in the config can be set manually or derived automatically depending on user preference.
In this instance, the only valid string value for x
should be Auto
. Any other value should fail to deserialize. However, since it appears that the parser is unaware of the possible equality of str/num/bytes to a Literal this fails.
What I Did
>>> from dataclass_wizard import YAMLWizard
>>> from typing import Literal
>>> from dataclasses import dataclass
>>> @dataclass
... class Example(YAMLWizard):
... x: Literal["Auto"] | float
...
>>> config_file = """
... x: Auto
... """
>>> Example.from_yaml(config_file)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "~/.venv/lib/python3.10/site-packages/dataclass_wizard/wizard_mixins.py", line 136, in from_yaml
return fromdict(cls, o) if isinstance(o, dict) else fromlist(cls, o)
File "~/.venv/lib/python3.10/site-packages/dataclass_wizard/loaders.py", line 536, in fromdict
return load(d)
File "~/.venv/lib/python3.10/site-packages/dataclass_wizard/loaders.py", line 619, in cls_fromdict
cls_kwargs[field_name] = field_to_parser[field_name](
File "~/.venv/lib/python3.10/site-packages/dataclass_wizard/parsers.py", line 250, in __call__
raise ParseError(
dataclass_wizard.errors.ParseError: Failure parsing field `x` in class `Example`. Expected a type [typing.Literal['Auto'], <class 'float'>], got str.
value: 'Auto'
error: Object was not in any of Union types
tag_key: '__tag__'
json_object: '{"x": "Auto"}'
I don't think this has anything to do with Literals - anything that needs to be deserialized in a union is not being deserialized.
>>> from dataclasses import dataclass
>>> from dataclass_wizard import JSONWizard
>>> @dataclass
... class B:
... thing: str
...
>>>
>>> @dataclass
... class A(JSONWizard):
... one_or_more: B | list[B]
...
>>>
>>> result = A.from_dict({
... 'one_or_more': {
... 'thing': 'value'
... }
... })
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python3.10/site-packages/dataclass_wizard/loaders.py", line 536, in fromdict
return load(d)
File "/usr/local/lib/python3.10/site-packages/dataclass_wizard/loaders.py", line 619, in cls_fromdict
cls_kwargs[field_name] = field_to_parser[field_name](
File "/usr/local/lib/python3.10/site-packages/dataclass_wizard/parsers.py", line 250, in __call__
raise ParseError(
dataclass_wizard.errors.ParseError: Failure parsing field `one_or_more` in class `A`. Expected a type [<class '__main__.B'>, <class 'list'>], got dict.
value: {'thing': 'value'}
error: Object was not in any of Union types
tag_key: '__tag__'
json_object: '{"one_or_more": {"thing": "value"}}'
>>>
Got the same issue here. Mixing primitives with dataclasses in unions seems to be the problem:
from dataclass_wizard import fromdict
from dataclasses import dataclass
@dataclass
class MoreDetails:
arg: str
@dataclass
class Config:
# Unions with primitives and dataclasses
entry: str | MoreDetails | int
# Works
fromdict(Config, {
"entry": "a"
})
# Works too
fromdict(Config, {
"entry": 1
})
# Fails, fails to interpret the dict as a dataclass
fromdict(Config, {
"entry": {"arg": "str"}
})
Traceback (most recent call last):
File "/mnt/c/Users/fakui/Seafile/Programming/projects/cc_crapton/./ha_modbus_mqtt/dataclasswz_bug.py", line 29, in <module>
fromdict(Config2, {
File "/home/fakui/.local/lib/python3.10/site-packages/dataclass_wizard/loaders.py", line 536, in fromdict
return load(d)
File "/home/fakui/.local/lib/python3.10/site-packages/dataclass_wizard/loaders.py", line 619, in cls_fromdict
cls_kwargs[field_name] = field_to_parser[field_name](
File "/home/fakui/.local/lib/python3.10/site-packages/dataclass_wizard/parsers.py", line 250, in __call__
raise ParseError(
dataclass_wizard.errors.ParseError: Failure parsing field `entry` in class `Config2`. Expected a type [<class '__main__.MoreDetails'>, <class '__main__.Config'>], got dict.
value: {'arg': '1'}
error: Object was not in any of Union types
tag_key: '__tag__'
json_object: '{"entry": {"arg": "1"}}'
dataclass-wizard version: 0.22.2
It appears you might be unfamiliar with the GitHub workflow. In order to propose your changes you must fork this repository and submit those changes to your fork. Once you have done so there will appear a button on your fork that asks if you want to open a PR.
TL;Dr you need to fork, submit your changes to the fork, then create a PR. It's not normal for you to have the ability to commit to a repository you are not a maintainer of.
@StripedMonkey thanks for the quick reply! Indeed I was unfamiliar with this way of working. I forked the repo and created #111.