tanbro / pyyaml-include

yaml include other yaml
https://pypi.org/project/pyyaml-include/
GNU General Public License v3.0
78 stars 20 forks source link

Include and achors #20

Closed zaufi closed 3 years ago

zaufi commented 3 years ago

I've got the following files:

# main.yaml
included: &included
    !include include.yaml

rendering:
  - << : *included

and

# include.yaml
-
    output: ignore.txt
    template: ignore.tpl

The following code doesn't work:

In [1]: import yaml 
   ...: from yamlinclude import YamlIncludeConstructor                                                                                                                       

In [2]: YamlIncludeConstructor.add_to_loader_class(loader_class=yaml.FullLoader, base_dir='/tmp')                                                                            
Out[2]: <yamlinclude.constructor.YamlIncludeConstructor at 0x7f4c3adcfbb0>

In [3]: import pathlib                                                                                                                                                       

In [4]: data = yaml.load(pathlib.Path('/tmp/main.yaml').open(), Loader=yaml.FullLoader)                                                                                      
---------------------------------------------------------------------------
ConstructorError                          Traceback (most recent call last)
<ipython-input-4-f98f7c4c3bd9> in <module>
----> 1 data = yaml.load(pathlib.Path('/tmp/main.yaml').open(), Loader=yaml.FullLoader)

~/work/OnixS/opcg/master/.venv/lib/python3.9/site-packages/yaml/__init__.py in load(stream, Loader)
    112     loader = Loader(stream)
    113     try:
--> 114         return loader.get_single_data()
    115     finally:
    116         loader.dispose()

~/work/OnixS/opcg/master/.venv/lib/python3.9/site-packages/yaml/constructor.py in get_single_data(self)
     49         node = self.get_single_node()
     50         if node is not None:
---> 51             return self.construct_document(node)
     52         return None
     53 

~/work/OnixS/opcg/master/.venv/lib/python3.9/site-packages/yaml/constructor.py in construct_document(self, node)
     58             self.state_generators = []
     59             for generator in state_generators:
---> 60                 for dummy in generator:
     61                     pass
     62         self.constructed_objects = {}

~/work/OnixS/opcg/master/.venv/lib/python3.9/site-packages/yaml/constructor.py in construct_yaml_map(self, node)
    414         data = {}
    415         yield data
--> 416         value = self.construct_mapping(node)
    417         data.update(value)
    418 

~/work/OnixS/opcg/master/.venv/lib/python3.9/site-packages/yaml/constructor.py in construct_mapping(self, node, deep)
    218     def construct_mapping(self, node, deep=False):
    219         if isinstance(node, MappingNode):
--> 220             self.flatten_mapping(node)
    221         return super().construct_mapping(node, deep=deep)
    222 

~/work/OnixS/opcg/master/.venv/lib/python3.9/site-packages/yaml/constructor.py in flatten_mapping(self, node)
    205                         merge.extend(value)
    206                 else:
--> 207                     raise ConstructorError("while constructing a mapping", node.start_mark,
    208                             "expected a mapping or list of mappings for merging, but found %s"
    209                             % value_node.id, value_node.start_mark)

ConstructorError: while constructing a mapping
  in "/tmp/main.yaml", line 5, column 5
expected a mapping or list of mappings for merging, but found scalar
  in "/tmp/main.yaml", line 1, column 11
tanbro commented 3 years ago

see #6

zaufi commented 3 years ago

see #6

Oh... %(

tanbro commented 3 years ago

To include anchors, a literal including and parsing are needed. But the constructor would parse the including parts first.

For such a thing, a template engine like Jinja2 may be useful. We shall render all YAML data into a whole one, then parse it.

eg:

from textwrap import dedent
import jinja2
import yaml

env = jinja2.Environment(loader=jinja2.FileSystemLoader("search path"))

tpl = env.from_string(dedent('''
boo: &boo
  k1: "0.1"
  k2: "0.2"

bars:
  {% include bar1.yml %}
  {% include bar2.yml %}
''').strip())

s = tpl.render()
yaml.load(s)

However, above code can NOT work properly, because of indention problem.