Type hint Dict[int | Literal["123"], str] incorectly processed
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 250
- Forks
- 36
- PR merge metrics
- No merged PRs in 30d
Description
Following this example:
from dataclass_wizard import DataclassWizard
from typing import Dict, Literal
class Test(DataclassWizard):
error: Dict[int | Literal["123"], str]
successful: int | Literal["123"]
Test.from_json('{"error": {"1": "1", "2": "2", "3": "3", "123": "4"}, "successful": "1"}')
we've got this exception:
dataclass_wizard.errors.ParseError: Failed to load field `a` in class `A`. Expected a type (<class 'int'>, typing.Literal['123']), got str.
phase: load
value: '1'
error: Object was not in any of Union types
tag_key: __tag__
json_object: {"a": {"1": "1", "2": "2", "3": "3", "123": "4"}, "b": "test"}
but we shouldn't as every key in the Dict is valid
going deeper we can see that the output function is:
def __create__load_Test_union_7f0ed97afcc1_fn__(cls, fields, tag_key, as_int):
def _load_Test_union_7f0ed97afcc1(k2):
tp = type(k2)
if tp is int:
return k2
try:
return _load_literal_093b94741fad(v2)
except Exception:
pass
try:
return v2 if (t := v2.__class__) is int else int(f if '.' in v2 and (f := float(v2)).is_integer() else v2) if t is str else as_int(v2, t, int)
except Exception:
pass
raise ParseError(TypeError('Object was not in any of Union types'),k2,fields,'load',tag_key=tag_key)
return _load_Test_union_7f0ed97afcc1
v2 is used instead of k2 in this function only when a Dict contains a Union
possible fix at:
https://github.com/rnag/dataclass-wizard/blob/287fa66740042750ac0e4d07dcd57f31de4f0d48/dataclass_wizard/v1/loaders.py#L570
the line should be tp_new = TypeInfo(possible_tp, i=i, prefix=tp.prefix)
so that the prefix is correctly transmit to the next step
with this fix the output is
def __create__load_Test_union_7f0ed97afcc1_fn__(cls, fields, tag_key, as_int):
def _load_Test_union_7f0ed97afcc1(k2):
tp = type(k2)
if tp is int:
return k2
try:
return _load_literal_093b94741fad(k2)
except Exception:
pass
try:
return k2 if (t := k2.__class__) is int else int(f if '.' in k2 and (f := float(k2)).is_integer() else k2) if t is str else as_int(k2, t, int)
except Exception:
pass
raise ParseError(TypeError('Object was not in any of Union types'),k2,fields,'load',tag_key=tag_key)
return _load_Test_union_7f0ed97afcc1
full final test:
from dataclass_wizard import DataclassWizard
from typing import Dict, Literal
class Test(DataclassWizard):
error: Dict[int | Literal["AAA"], str]
successful: int | Literal["AAA"]
Test.from_json('{"error": {"1": "1", "2": "2", "3": "3", "AAA": "4"}, "successful": "1"}')
Test.from_json('{"error": {"1": "1", "2": "2", "3": "3", "AAA": "4"}, "successful": "AAA"}')
Test.from_json('{"error": {"1": "1", "2": "2", "3": "3", "BBB": "4"}, "successful": "1"}')
Test.from_json('{"error": {"1": "1", "2": "2", "3": "3", "AAA": "4"}, "successful": "BBB"}')
result:
>>> Test.from_json('{"error": {"1": "1", "2": "2", "3": "3", "AAA": "4"}, "successful": "1"}')
"2": "2", "3": "3", "AAA": "4"}, "successful": "AAA"}')Test(error={1: '1', 2: '2', 3: '3', 'AAA': '4'}, successful=1)
>>> Test.from_json('{"error": {"1": "1", "2": "2", "3": "3", "AAA": "4"}, "successful": "AAA"}')
Test(error={1: '1', 2: '2', 3: '3', 'AAA': '4'}, successful='AAA')
>>> Test.from_json('{"error": {"1": "1", "2": "2", "3": "3", "BBB": "4"}, "successful": "1"}')
dataclass_wizard.errors.ParseError: Failed to load field `error` in class `Test`. Expected a type (<class 'int'>, typing.Literal['AAA']), got str.
phase: load
value: 'BBB'
error: Object was not in any of Union types
tag_key: __tag__
json_object: {"error": {"1": "1", "2": "2", "3": "3", "BBB": "4"}, "successful": "1"}
>>> Test.from_json('{"error": {"1": "1", "2": "2", "3": "3", "AAA": "4"}, "successful": "BBB"}')
dataclass_wizard.errors.ParseError: Failed to load field `successful` in class `Test`. Expected a type (<class 'int'>, typing.Literal['AAA']), got str.
phase: load
value: 'BBB'
error: Object was not in any of Union types
tag_key: __tag__
json_object: {"error": {"1": "1", "2": "2", "3": "3", "AAA": "4"}, "successful": "BBB"}
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in dataclass_wizard/v1/loaders.py around line 570 and inspect how TypeInfo prefixes are passed when loading a dictionary key union. Reproduce the provided Dict[int | Literal["AAA"], str] examples, then add a regression test covering accepted integer and literal keys plus rejected values such as "BBB". Done means valid inputs deserialize and invalid keys still raise ParseError.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- tooling
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 82/100