pyyaml icon indicating copy to clipboard operation
pyyaml copied to clipboard

Custom tags are evaluated after solving the union operators. But they must be calculated before them.

Open sergey-gru opened this issue 1 year ago • 2 comments

I'm trying to implement a file include operation using tag syntax. I am convinced that I wrote the classes correctly:

import yaml

class YamlLoaderInclude(yaml.SafeLoader):
    def __init__(self, stream):
        super().__init__(stream)


class YamlInclude(yaml.YAMLObject):
    yaml_tag = '!include'
    yaml_loader = YamlLoaderInclude

    @classmethod
    def from_yaml(cls, loader: YamlLoaderInclude, node) -> dict:
        arg = loader.construct_scalar(node)
        p = arg.strip().strip('"').strip("'")
        # data = include_yaml(p)
        data = { a: 1, b: 2 }

        return data

Here is the file to include.

#include.yaml

a: &a !include inc.yaml # Ok
<<: !include inc.yaml   # error
<<: *a  # it's literally the same as <<: !include inc.yaml

This is the output after running my autotests with this file. line and column in the logs point to the beginning of the tag !include

Traceback (most recent call last):
  File "D:\Projects\project\py_test\test_environ.py", line 90, in test_1_load_include
    data = loader.data
           ^^^^^^^^^^^
  File "D:\Projects\project\py\environ.py", line 126, in data
    self._data = self._yaml_parse()
                 ^^^^^^^^^^^^^^^^^^
  File "D:\Projects\project\py\environ.py", line 112, in _yaml_parse
    return loader.get_data()
           ^^^^^^^^^^^^^^^^^
  File "D:\Projects\project\.venv\Lib\site-packages\yaml\constructor.py", line 45, in get_data
    return self.construct_document(self.get_node())
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\Projects\project\.venv\Lib\site-packages\yaml\constructor.py", line 60, in construct_document
    for dummy in generator:
  File "D:\Projects\project\.venv\Lib\site-packages\yaml\constructor.py", line 414, in construct_yaml_map
    value = self.construct_mapping(node)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\Projects\project\.venv\Lib\site-packages\yaml\constructor.py", line 217, in construct_mapping
    self.flatten_mapping(node)
  File "D:\Projects\project\.venv\Lib\site-packages\yaml\constructor.py", line 204, in flatten_mapping
    raise ConstructorError("while constructing a mapping", node.start_mark,
yaml.constructor.ConstructorError: while constructing a mapping
  in "D:\Projects\project\py_test\environ\include.yaml", line 2, column 1
expected a mapping or list of mappings for merging, but found scalar
  in "D:\Projects\project\py_test\environ\include.yaml", line 11, column 4

After reading the code, it turned out that this is due to the fact that the library does not calculate tags before merging. On the contrary, it calculates them after the merger.

Regarding anchors, the library simply copies their contents, and the custom tags are expected to be evaluated before

sergey-gru avatar Jul 06 '24 04:07 sergey-gru

/cc https://github.com/yaml/pyyaml/issues/632 which is closely related if not identical.

ross avatar Oct 16 '25 02:10 ross

/cc https://github.com/yaml/pyyaml/pull/894

ross avatar Oct 16 '25 04:10 ross