Fatal1ty / mashumaro

Fast and well tested serialization library
Apache License 2.0
758 stars 45 forks source link

"`dict[str, T]` as a field type is not supported" #177

Closed aldanor closed 10 months ago

aldanor commented 10 months ago

In a seemingly simple example with generics, close to the one presented in the docs, mashumaro seems to stumble on dict[str, T] annotation; wonder if this is intended, or a bug, or some misunderstanding on my side?

from __future__ import annotations

from dataclasses import dataclass
from typing import Generic, TypeVar

from mashumaro import DataClassDictMixin
from mashumaro.types import SerializableType

T = TypeVar('T')

class Foo(Generic[T], SerializableType, use_annotations=True):
    a: T

    def __init__(self, a: T) -> None:
        self.a = a

    @classmethod
    def _deserialize(cls, value: dict[str, T]) -> Foo[T]:
        return cls(**value)

    def _serialize(self) -> dict[str, T]:
        return {'a': self.a}

@dataclass
class Bar(DataClassDictMixin):
    x: Foo[int]

print(Bar(Foo(1)).to_dict())

yields

UnserializableDataError: dict[str, T] as a field type is not supported by mashumaro
aldanor commented 10 months ago

Edit: after some trial and error, it seems to work if and only if you quote 'Foo[T]' which isn't immediately obvious from the docs, since it's valid code (e.g. with from __future__ import annotations).

What makes it even worse is that some linters (e.g. ruff) force-strip quotes from type annotations by default (link).

Fatal1ty commented 10 months ago

@aldanor Thank you for bringing it in.

Postponed evaluation of annotations is pain in the ass. Function inspect.signature returns string instead of ForwardRef object in this case and we need to evaluate it. Fortunately, almost everything is ready for this since I've been working on ForwardRef support:

I need to tweak evaluate_forward_ref method and turn the string annotation into ForwardRef by hand. This fix won't take much time.