konradhalas / dacite

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

Possible bug: Cannot load dataclasses with members of defined generic type #131

Open samuelsd1 opened 3 years ago

samuelsd1 commented 3 years ago

I had a use case where some dataclasses had members with common fields. In order to reduce code duplication, I tried to create a common class using generics. When I tried to load my data - it failed with WrongTypeError, while the type received looks fine.

I wrote simple code example to replicate this behavior:

from dataclasses import dataclass, asdict
from typing import Generic, TypeVar, List
from dacite import from_dict

T = TypeVar('T')
@dataclass
class Common(Generic[T]):
    foo: T
    bar: T

@dataclass
class A:
    elements: List[Common[int]]

a = A([Common[int](1, 2), Common[int](3, 4)])

from_dict(A, asdict(a))

Running the code throws the following exception: dacite.exceptions.WrongTypeError: wrong value type for field "elements" - should be "typing.List[__main__.Common[int]]" instead of value "[{'foo': 1, 'bar': 2}, {'foo': 3, 'bar': 4}]" of type "list"

I believe this behavior to be a bug, since [{'foo': 1, 'bar': 2}, {'foo': 3, 'bar': 4}] is of type List[Common[int]]

Note: this example is simplified, in my case I had another class that had a member of type Common[str]

lukoerfer commented 3 years ago

I encountered the same problem. Did you find any workaround yet?

konradhalas commented 2 years ago

Hi @samuelsd1 - you have right, dacite doesn't work with custom-made generic types, but it looks like a nice feature to implement. I'm adding it on my list.

matthewaburton commented 1 year ago

Hi @konradhalas. Thanks for the great library! Any update on this issue, or a possible work-around? I have a generic type KeyValuePair(Generic[K, V_co]) (a dataclass), and dacite gets angry when I have a frozenset of them on a class.

@dataclass(frozen=True)
class SomeThing:
    id: UUID
    ref: Reference
    topics: frozenset[str] = field(default_factory=frozenset)
    metadata: frozenset[KeyValuePair[str, str]] = field(default_factory=frozenset)

dacite.from_dict is OK with the topics field because its generic type is a "base" (non-generic) type, but it fails when type checking the metadata field because the generic type is itself a user-defined generic of KeyValuePair[str, str].

ArthurVernon-TPG commented 1 year ago

I hit a similar problem with a delaration like this (running python 3.9) and concerned about running the app in python 3.8.

T = TypeVar('T')
@dataclass
class PagedDTO(Generic[T]):
  ...
       values: List[T]

Only option ATM appears to be to do away with the generics and create N subclasses. <class 'typing._GenericAlias'>

Sad that typing.py does not cope with generics of all sorts... (python 3.9 only appears to support <class 'function'>, <class 'builtin_function_or_method'>, <class 'method'>, <class 'module'>, <class 'wrapper_descriptor'>, <class 'method-wrapper'>, <class 'method_descriptor'>)

Not sure how well progressed type hinting is and whether my problem is that there is no current support for PEP-585.