pyyaml icon indicating copy to clipboard operation
pyyaml copied to clipboard

Custom constructors with nested YAML

Open kwon-young opened this issue 3 years ago • 0 comments

Hello,

I have the following yaml file:

A: &a
  p1:
    pp1: coucou
    pp2: 3
  p2: 0

B:
  pb1:
    <<: *a
    p2: 1

Here is the python code I use to load this:

import yaml
from yaml import FullLoader


class A:

    def __init__(self, p1_trans, p2):
        self.p1_trans = p1_trans
        self.p2 = p2

    @classmethod
    def from_yaml(cls, loader: FullLoader, node):
        data = loader.construct_mapping(node, deep=True)
        data['p1_trans'] = data['p1']['pp1'] + str(data['p1']['pp2'])
        del data['p1']
        return cls(**data)


yaml.add_constructor('!A', A.from_yaml)
yaml.add_path_resolver('!A', ['B', 'pb1'], dict)
with open('params.yaml') as f:
    params = yaml.load(f.read(), Loader=FullLoader)
__import__('pprint').pprint(params)

This fails with the following output:

$ python testpyyaml.py
Traceback (most recent call last):
  File "/srv/longdd/kchoi/isolating-gan/notebooks/testpyyaml.py", line 23, in <module>
    params = yaml.load(f.read(), Loader=FullLoader)
  File "/srv/longdd/kchoi/mambaforge/envs/isolating-gan/lib/python3.9/site-packages/yaml/__init__.py", line 114, in load
    return loader.get_single_data()
  File "/srv/longdd/kchoi/mambaforge/envs/isolating-gan/lib/python3.9/site-packages/yaml/constructor.py", line 51, in get_single_data
    return self.construct_document(node)
  File "/srv/longdd/kchoi/mambaforge/envs/isolating-gan/lib/python3.9/site-packages/yaml/constructor.py", line 60, in construct_document
    for dummy in generator:
  File "/srv/longdd/kchoi/mambaforge/envs/isolating-gan/lib/python3.9/site-packages/yaml/constructor.py", line 413, in construct_yaml_map
    value = self.construct_mapping(node)
  File "/srv/longdd/kchoi/mambaforge/envs/isolating-gan/lib/python3.9/site-packages/yaml/constructor.py", line 218, in construct_mapping
    return super().construct_mapping(node, deep=deep)
  File "/srv/longdd/kchoi/mambaforge/envs/isolating-gan/lib/python3.9/site-packages/yaml/constructor.py", line 143, in construct_mapping
    value = self.construct_object(value_node, deep=deep)
  File "/srv/longdd/kchoi/mambaforge/envs/isolating-gan/lib/python3.9/site-packages/yaml/constructor.py", line 100, in construct_object
    data = constructor(self, node)
  File "/srv/longdd/kchoi/isolating-gan/notebooks/testpyyaml.py", line 14, in from_yaml
    data['p1_trans'] = data['p1']['pp1'] + str(data['p1']['pp2'])
KeyError: 'pp1'

From what I could gather, it seems that data = loader.construct_mapping(node, deep=True) fails to construct the p1 key of pb1. This behavior seems to be a bug of pyyaml because if I do

yaml.add_constructor('!A', A.from_yaml)
yaml.add_path_resolver('!A', ['A'], dict)
yaml.add_path_resolver('!A', ['B', 'pb1'], dict)

The loading works as expected.

kwon-young avatar Jul 02 '21 11:07 kwon-young