Reputation: 91790
I'm attempting to build a simple data-class example for YAML parsing which consists of recursive types.
The YAML in question looks like this:
---
folders:
- name: a
children:
- name: b
- name: c
children: []
The way I am defining my types is like so:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import annotations # I'm on Python 3.9
from dataclass_wizard import YAMLWizard, LoadMeta
from dataclasses import dataclass
@dataclass
class DemoManifest(YAMLWizard):
folders: List[DemoFolder]
@dataclass
class DemoFolder(YAMLWizard):
name: str
children: List[DemoFolder]
def main():
LoadMeta(recursive=True).bind_to(DemoFolder)
manifest = DemoManifest.from_yaml_file("recurse.yml")
print(manifest)
It's a pretty simple example. I have one outer type which defines a list of DemoFolder
objects, which potentially have a list of children of DemoFolder
types as well.
When I run this, I get a RecursionError
, maximum depth exceeded
. Clearly somehow recursion is breaking the parsing. I thought that I solved the issue using the meta above, but it is definitely not working.
Is it possible to do self-referential deserialization in YAML for dataclass-wizard
?
EDIT: This is not an issue of Python compiling. The code compiles and runs, it seems to be an issue with dataclass-wizard
, which is where the recursion occurs.
Upvotes: 3
Views: 512
Reputation: 11670
It's Q4 2024 and recursive dataclass support has recently been added to dataclass-wizard
as of v0.27.0, and I thought I'd update to add an answer here.
If one were to theoretically run the code provided in the question as is, e.g. with:
def main():
from textwrap import dedent
yaml_string = dedent("""
---
folders:
- name: a
children:
- name: b
- name: c
children: []
""")
manifest = DemoManifest.from_yaml(yaml_string)
print(manifest)
if __name__ == '__main__':
main()
They would see the following error printed to the console:
...
raise RecursiveClassError(cls) from None
dataclass_wizard.errors.RecursiveClassError: Failure parsing class `DemoFolder`. Consider updating the Meta config to enable the `recursive_classes` flag.
Example with `dataclass_wizard.LoadMeta`:
>>> LoadMeta(recursive_classes=True).bind_to(DemoFolder)
For more info, please see:
https://github.com/rnag/dataclass-wizard/issues/62
This is very helpful and points to the root cause as well as the potential solution that can be applied.
By the way, using the recursive
flag instead:
LoadMeta(recursive=True).bind_to(DemoFolder)
That only controls whether Meta
config for the outer class is applied recursively to inner dataclasses, so that is a red herring and not what we need to apply in this case.
Adding:
LoadMeta(recursive_classes=True).bind_to(DemoManifest)
Now seems to resolve the issue:
DemoManifest(folders=[DemoFolder(name='a', children=[DemoFolder(name='b', children=[])]), DemoFolder(name='c', children=[])])
Full code:
from dataclass_wizard import YAMLWizard, LoadMeta
from dataclasses import dataclass
@dataclass
class DemoManifest(YAMLWizard):
folders: list['DemoFolder']
@dataclass
class DemoFolder(YAMLWizard):
name: str
children: list['DemoFolder']
def main():
from textwrap import dedent
yaml_string = dedent("""
---
folders:
- name: a
children:
- name: b
children: []
- name: c
children: []
""")
LoadMeta(recursive_classes=True).bind_to(DemoManifest)
manifest = DemoManifest.from_yaml(yaml_string)
print(manifest)
if __name__ == '__main__':
main()
Upvotes: 1