Fatal1ty / mashumaro

Fast and well tested serialization library
Apache License 2.0
751 stars 44 forks source link

TypedDicts not working with `from __future__ import annotations` #187

Closed kpodp0ra closed 6 months ago

kpodp0ra commented 7 months ago

What I Did

from __future__ import annotations
from mashumaro.jsonschema import build_json_schema
from typing import TypedDict

class TypedDictRequiredKeys(TypedDict):
    int: int
    float: float

build_json_schema(TypedDictRequiredKeys)
Traceback (most recent call last):
  File "main.py", line 11, in <module>
    build_json_schema(TypedDictRequiredKeys)
  File "/usr/lib/python3.10/site-packages/mashumaro/jsonschema/builder.py", line 45, in build_json_schema
    schema = get_schema(instance, context, with_dialect_uri=with_dialect_uri)
  File "/usr/lib/python3.10/site-packages/mashumaro/jsonschema/schema.py", line 258, in get_schema
    schema = schema_creator(instance, ctx)
  File "/usr/lib/python3.10/site-packages/mashumaro/jsonschema/schema.py", line 802, in on_collection
    return on_typed_dict(instance, ctx)
  File "/usr/lib/python3.10/site-packages/mashumaro/jsonschema/schema.py", line 666, in on_typed_dict
    properties={
  File "/usr/lib/python3.10/site-packages/mashumaro/jsonschema/schema.py", line 667, in <dictcomp>
    key: get_schema(instance.derive(type=annotations[key]), ctx)
  File "/usr/lib/python3.10/site-packages/mashumaro/jsonschema/schema.py", line 258, in get_schema
    schema = schema_creator(instance, ctx)
  File "/usr/lib/python3.10/site-packages/mashumaro/jsonschema/schema.py", line 720, in on_collection
    if not issubclass(instance.origin_type, typing.Collection):

instance.origin_type is ForwardRef('int') https://github.com/Fatal1ty/mashumaro/blob/ab8242448631ab7a1ad2e437561377cd698a99e9/mashumaro/jsonschema/schema.py#L718C23-L718C43

  File "/usr/lib/python3.10/typing.py", line 1158, in __subclasscheck__
    return issubclass(cls, self.__origin__)
  File "/usr/lib/python3.10/abc.py", line 123, in __subclasscheck__
    return _abc_subclasscheck(cls, subclass)
TypeError: issubclass() arg 1 must be a class

Description

TypedDicts are not working with from __future__ import annotations.

Also, it's worth noting that adding this line on top of tests/test_jsonschema/test_jsonschema_generation.py breaks tests:

Fatal1ty commented 6 months ago

@kpodp0ra

Hi, thank you for opening this issue.

TypedDicts are not working with from future import annotations.

I managed to add support for ForwardRef in JSON Schema generation here, so your example will work as expected now.

Also, it's worth noting that adding this line on top of tests/test_jsonschema/test_jsonschema_generation.py breaks tests:

  • test_jsonschema_for_named_tuple_with_overridden_serialization_method
  • test_jsonschema_for_typeddict
  • test_overridden_serialization_method_with_return_annotation
  • test_dataclass_overridden_serialization_method
  • test_third_party_overridden_serialization_method

Thank you for pointing out. This is due to restrictions in PEP 563 implementation:

Annotations can only use names present in the module scope as postponed evaluation using local names is not reliable (with the sole exception of class-level names resolved by typing.get_type_hints()).

So, as long as you don't define the data types inside the function, everything will be fine. We could try to hack it by juggling f_locals of frame objects to support forward refs to local types, but I just don't see much need for it right now. In some cases it will work without hacks, but in others it won't. Do you need JSON Schema for local types?

kpodp0ra commented 6 months ago

Confirm that #191 fixes all my codebase.