msgspec icon indicating copy to clipboard operation
msgspec copied to clipboard

Allow passing custom globals to use when converting dataclasses

Open provinzkraut opened this issue 1 year ago • 6 comments

Description

Currently there's an issue where msgspec cannot convert a dataclass that contains forward references. This isn't unusual or msgspec specific, it is however a problem when trying to use msgspec to convert dataclasses that you don't have control over, i.e. cannot change the fact that they contain forward references.

# a.py
from __future__ import annotations
from dataclasses import dataclass
from typing import TYPE_CHECKING

if TYPE_CHECKING:
 from typing import Any

@dataclass
class Some:
 value: Any
# b.py
from a import Some
import msgspec

# this fails with  NameError: name 'Any' is not defined
msgspec.convert({"value": "something"}, type=Some)

A simple interface that would allow passing a custom set of globals to the internally used get_class_annotations would do the trick I think. Or is there another solution here that I'm not aware of? :)

provinzkraut avatar Jun 17 '24 10:06 provinzkraut

A possible workaround is injecting the types imported under the TYPE_CHECKING condition directly into the module. This prevents the error without modifying the a.py. Example b.py:

# b.py
from a import Some
import msgspec


# Insert the type `Any` into module containing class `Some`
import sys
from typing import Any
a_module = sys.modules.get(Some.__module__)
a_module.__dict__['Any'] = Any


# No longer fails
msgspec.convert({"value": "something"}, type=Some)

But it is quite ugly workaround.

bedapisl avatar Mar 25 '25 12:03 bedapisl

At some point __future__ == datetime.datetime.now() and all annotations will be like this.

CharString avatar Apr 15 '25 06:04 CharString

It's more about if TYPE_CHECKING: which removes those definitions at runtime, and AFAIK is unnecessary with that __future__ import

rafalkrupinski avatar Apr 15 '25 09:04 rafalkrupinski

No. The __future__ import makes it possible to do the if TYPE_CHECKING: at all; without that import, you'd get a NameError already in the a module instead of the convert call.

PS: I guess we're both right... Ditching the if TYPE_CHECKING would fix it :)

CharString avatar Apr 15 '25 09:04 CharString

Right, without from __future__ import you'd need to quote those names.

And yes, removing the conditional would have fixed it, except OP mentioned it's a 3rd party code that needs fixing. So should msgspec expect that code be fixed or support it? From what I see, msgspec leans more towards being optimized than rich in such features.

rafalkrupinski avatar Apr 15 '25 10:04 rafalkrupinski

The __future__ import also breaks the new Generic notation:

class MyResponse[T](Struct):
  results: list[T]

Because the TypeVar T is not part of the runtime then.

CharString avatar Apr 15 '25 11:04 CharString