Source code for yaloader.loader
from __future__ import annotations
from typing import Dict, Type, Any
import yaml
from yaml import MarkedYAMLError, Node
from yaloader import YAMLBaseConfig
from yaloader.utils import full_object_name
[docs]
class YAMLConfigLoader(yaml.SafeLoader):
"""YAML loader for the configs."""
yaml_config_classes: Dict[str, Type[YAMLBaseConfig]] = {}
anchors: dict[Any, Node] = {}
[docs]
@classmethod
def add_config_constructor(
cls,
config_class: Type[YAMLBaseConfig],
constructor,
overwrite_tag: bool = False,
) -> None:
"""Add a yaml config class with its constructor to the loader.
:param config_class: The config which should be added to the loader.
:param constructor: The constructor to load the class.
:param overwrite_tag: If true and an other config with the same tag is
already registered the previous config for
the same tag is overwritten.
Else a RuntimeError is raised if the tag of
the config is already registered.
:return:
"""
tag = config_class.get_yaml_tag()
if tag.startswith("!!"):
raise RuntimeError(
f"The tag {tag} has the prefix !! and can "
f"therefore not be used by {full_object_name(config_class)}. "
f"Choose another tag for the yaml config class."
)
if tag in cls.yaml_constructors:
# If tag is not in the registered yaml config classes,
# it is a tag of the SafeLoader and should not be overwritten.
if tag not in cls.yaml_config_classes:
raise RuntimeError(
f"The tag {tag} is already registered and can not be used "
f"by {full_object_name(config_class)}. "
f"Choose another tag for the yaml config class."
)
if not overwrite_tag:
raise RuntimeError(
f"The tag {tag} is already registered by {full_object_name(cls.yaml_config_classes[tag])} "
f"and can therefore not be used by {full_object_name(config_class)}. "
f"Set overwrite_tag to True if you want to overwrite the existing tag "
f"or choose another tag for one of the yaml config classes."
)
# Overwrite existing tag, while keeping is under a different tag.
old_constructor = cls.yaml_constructors[tag]
del cls.yaml_constructors[tag]
old_config_class = cls.yaml_config_classes[tag]
del cls.yaml_config_classes[tag]
old_config_class.set_yaml_tag(f"!{full_object_name(old_config_class)}")
cls.add_config_constructor(old_config_class, old_constructor)
cls.add_constructor(tag, constructor)
# TODO: Not quite sure if this makes sense. Copied from the original yaml loader.
if "yaml_config_classes" not in cls.__dict__:
cls.yaml_config_classes = cls.yaml_config_classes.copy()
cls.yaml_config_classes[tag] = config_class
def compose_document(self):
# Drop the DOCUMENT-START event.
self.get_event()
self.anchors.update(self.__class__.anchors)
# Compose the root node.
node = self.compose_node(None, None)
# Drop the DOCUMENT-END event.
self.get_event()
self.__class__.anchors.update(self.anchors)
self.anchors = {}
return node
def __init_subclass__(cls, **kwargs):
cls.yaml_config_classes = {}
cls.anchors = {}
super().__init_subclass__(**kwargs)
[docs]
class YAMLValueError(MarkedYAMLError):
"""Error of wrong values in the loaded yaml configs."""