Skip to content

Instantly share code, notes, and snippets.

@cassava
Created February 22, 2022 07:45
Show Gist options
  • Save cassava/530aecd606f2bae0c3f128a6b5f01284 to your computer and use it in GitHub Desktop.
Save cassava/530aecd606f2bae0c3f128a6b5f01284 to your computer and use it in GitHub Desktop.
Fancy YAML proof of concept in Python
#!/usr/bin/env python3
from typing import Optional, Dict
import os
import yaml
import jinja2
class FancyLoader(yaml.SafeLoader):
def __init__(self, stream):
self.definitions = dict()
self.add_constructor("!env", self._yaml_tag_env)
self.add_constructor("!define", self._yaml_tag_define)
self.add_constructor("!ref", self._yaml_tag_ref)
self.add_constructor("!template", self._yaml_tag_template)
self.add_constructor("!literal", self._yaml_tag_literal)
yaml.SafeLoader.__init__(self, stream)
def _yaml_tag_env(self, loader, node) -> Optional[str]:
value: str = loader.construct_scalar(node)
return os.environ.get(value)
def _yaml_tag_define(self, loader, node):
mapping: Dict = loader.construct_mapping(node)
for k, v in mapping.items():
if k in self.definitions:
print(f"Warning: overwriting variable {k} = {self.definitions[k]}")
self.definitions[k] = v
return mapping
def _yaml_tag_ref(self, loader, node):
value: str = loader.construct_scalar(node)
return self.definitions[value]
def _yaml_tag_template(self, loader, node):
value: str = loader.construct_scalar(node)
tmpl = jinja2.Template(value)
return tmpl.render(**self.definitions)
def _yaml_tag_literal(self, loader, node):
value: str = loader.construct_scalar(node)
tmpl = jinja2.Template(value)
render = tmpl.render(**self.definitions)
if render in ["true", "True", "TRUE", "yes", "Yes", "YES"]:
return True
elif render in ["false", "False", "FALSE", "no", "No", "NO"]:
return False
elif "." in render:
return float(render)
else:
return int(render)
TEST = """
variables: !define
what: "World"
expr: "!"
who: !env "USER"
number: 35.45
answer: 42
output: !template "Hello {{who}} to the {{what}}{{expr}}"
answer: !ref answer
literal: !literal "{{number}}"
"""
ERROR_1 = """
variables:
number: 45
literal: !!int !template "{{number}}"
"""
if __name__ == "__main__":
data = yaml.load(TEST, Loader=FancyLoader)
print(yaml.dump(data))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment