Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

self-referencing/forward-references dataclasses are not supported #28

Closed
baod-rate opened this issue Dec 22, 2020 · 4 comments
Closed

Comments

@baod-rate
Copy link

Mashumaro does not currently support self-referencing classes, the code-generation fails when it attempts to reflect on the field's type (which is a forward reference)

For example, consider the following case:

import dataclasses
from typing import Optional

@dataclasses.dataclass
class Node:
    value: str
    next: Optional['Node'] = None

@dataclasses.dataclass
class LinkedList:
    head: Optional[Node] = None

a = Node("A")
b = Node("B")
c = Node("C")

a.next = b
b.next = c

linked_list = LinkedList(head=a)

print("list", dataclasses.asdict(linked_list))
print("A", dataclasses.asdict(a))
print("B", dataclasses.asdict(b))
print("C", dataclasses.asdict(c))

This gives us the expected:

list {'head': {'value': 'A', 'next': {'value': 'B', 'next': {'value': 'C', 'next': None}}}}
A {'value': 'A', 'next': {'value': 'B', 'next': {'value': 'C', 'next': None}}}
B {'value': 'B', 'next': {'value': 'C', 'next': None}}
C {'value': 'C', 'next': None}

The equivalent classes using mashumaro:

import dataclasses
from typing import Optional

from mashumaro import DataClassJSONMixin

@dataclasses.dataclass
class Node(DataClassJSONMixin):
    value: str
    next: Optional['Node'] = None

@dataclasses.dataclass
class LinkedList(DataClassJSONMixin):
    head: Optional[Node] = None

a = Node("A")
b = Node("B")
c = Node("C")

a.next = b
b.next = c

linked_list = LinkedList(head=a)

print("list", linked_list.to_dict())
print("A", a.to_dict())
print("B", b.to_dict())
print("C", c.to_dict())

throws an error during Mashumaro's code generation:

Traceback (most recent call last):
  File "mashumaro_test.py", line 7, in <module>
    class Node(DataClassJSONMixin):
  File "mashumaro/serializer/base/dict.py", line 19, in __init_subclass__
    raise exc
  File "mashumaro/serializer/base/dict.py", line 15, in __init_subclass__
    builder.add_to_dict()
  File "mashumaro/serializer/base/metaprogramming.py", line 201, in add_to_dict
    for fname, ftype in self.fields.items():
  File "mashumaro/serializer/base/metaprogramming.py", line 78, in fields
    return self.__get_fields()
  File "mashumaro/serializer/base/metaprogramming.py", line 69, in __get_fields
    for fname, ftype in typing.get_type_hints(self.cls).items():
  File "/usr/lib/python3.9/typing.py", line 1410, in get_type_hints
    value = _eval_type(value, base_globals, localns)
  File "/usr/lib/python3.9/typing.py", line 279, in _eval_type
    ev_args = tuple(_eval_type(a, globalns, localns, recursive_guard) for a in t.__args__)
  File "/usr/lib/python3.9/typing.py", line 279, in <genexpr>
    ev_args = tuple(_eval_type(a, globalns, localns, recursive_guard) for a in t.__args__)
  File "/usr/lib/python3.9/typing.py", line 277, in _eval_type
    return t._evaluate(globalns, localns, recursive_guard)
  File "/usr/lib/python3.9/typing.py", line 533, in _evaluate
    eval(self.__forward_code__, globalns, localns),
  File "<string>", line 1, in <module>
NameError: name 'Node' is not defined
@baod-rate
Copy link
Author

FYI, the current workaround we are using is to declare the class once first, and then redefine it. This allows mashumaro to have a stub for the class being defined during code generation:

@dataclasses.dataclass
class Node(DataClassJSONMixin):
    pass

@dataclasses.dataclass
class Node(DataClassJSONMixin):
    value: str
    next: Optional['Node'] = None

This hasn't introduced any problems in our use-cases, other than the minor issue of confusing some code analysis tools.

@Fatal1ty
Copy link
Owner

Seems like another popular library has the same issue pydantic/pydantic#2678 👀

@Fatal1ty
Copy link
Owner

@qubidt Hi. I've fixed forward references in this branch, which you can play around with for now. There are a few things I want to experiment with before I release a new version.

@Fatal1ty
Copy link
Owner

Fixed in 2.3.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants