Skip to content

Type hint Dict[int | Literal["123"], str] incorectly processed #245

Description

@ctisserand

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:

tp_new = TypeInfo(possible_tp, i=i)

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"}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions