""" Code based on solution https://gist.github.com/joshbode/569627ced3076931b02f """ from typing import Any, IO import json import logging import os import yaml from scanapi.errors import EmptyConfigFileError, FileFormatNotSupportedError logger = logging.getLogger(__name__) class Loader(yaml.SafeLoader): """YAML/JSON Loader with `!include` constructor.""" def __init__(self, stream: IO) -> None: """Initialise Loader.""" try: self._root = os.path.split(stream.name)[0] except AttributeError: self._root = os.path.curdir super().__init__(stream) def construct_include(loader: Loader, node: yaml.Node) -> Any: """Include file referenced at node.""" relative_path = os.path.join(loader._root, loader.construct_scalar(node)) full_path = os.path.abspath(relative_path) extension = os.path.splitext(full_path)[1].lstrip(".") with open(full_path, "r") as f: if extension in ("yaml", "yml"): return yaml.load(f, Loader) elif extension in ("json",): return json.load(f) else: raise FileFormatNotSupportedError(f".{extension}", relative_path) def load_config_file(file_path): extension = os.path.splitext(file_path)[-1] if extension not in (".yaml", ".yml", ".json"): raise FileFormatNotSupportedError(extension, file_path) with open(file_path, "r") as stream: logger.info(f"Loading file {file_path}") data = yaml.load(stream, Loader) if not data: raise EmptyConfigFileError(file_path) return data yaml.add_constructor("!include", construct_include, Loader)