'pyyaml support for default list in dataclass?

Below is a minimal example that demonstrates the issue.

First, imports:

from dataclasses import dataclass, field
from typing import List
import yaml

Next, define a class that derives from yaml.YAMLObject:

@dataclass
class Yaz(yaml.YAMLObject):
    name: str
    items: List[str] = field(default_factory=lambda: ['a', 'b', 'c'])
    yaml_tag: str = u'!Yaz'

Demonstrate usage:

Yaz(name='Yazzzy')  # >>> Yaz(name='Yazzzy', items=['a', 'b', 'c'], yaml_tag='!Yaz')
Yaz(name='Yazzzy', items=[])  # >>> Yaz(name='Yazzzy', items=[], yaml_tag='!Yaz')

But sometimes I'd like to instantiate an instance from a YAML file. Mock a YAML file:

yaml_str = """---
!Yaz
  name: Yazzzy
"""

Load the YAML file into an instance:

yaml.load(yaml_str, Loader=yaml.Loader)  # ERROR!

The last line yields the following error:

AttributeError: 'Yaz' object has no attribute 'items'

Is there some sort of hook I'm missing?

Thanks in advance!

Note: I am using Python 3.8 with pyyaml 6.0.



Solution 1:[1]

I would humbly suggest using the dataclass-wizard for this task. It comes with a helper Mixin class called the YAMLWizard, which you can use along with pyyaml 6.0. To clarify it does use pyyaml for working with YAML data.

Here's a working example below. Note that I went ahead and removed the yaml.YAMLObject and the associated tags in the YAML object, since this is not needed anymore.

I haven't shown this in the example, but it's also possible to load a nested dataclass model from YAML using this same approach. You can check out an example of a nested model here.

from __future__ import annotations  # can be removed in Python 3.9+

from dataclasses import dataclass, field
from dataclass_wizard import YAMLWizard


@dataclass
class Yaz(YAMLWizard):
    name: str
    items: list[str] = field(default_factory=lambda: ['a', 'b', 'c'])


Yaz(name='Yazzzy')  # >>> Yaz(name='Yazzzy', items=['a', 'b', 'c'])
Yaz(name='Yazzzy', items=[])  # >>> Yaz(name='Yazzzy', items=[])


yaml_str = """---
  name: Yazzzy
"""

yaz_object = Yaz.from_yaml(yaml_str)
print(yaz_object)

Output:

Yaz(name='Yazzzy', items=['a', 'b', 'c'])

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1