You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
141 lines
4.4 KiB
141 lines
4.4 KiB
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
Name: DVM Host Configuration Annotator
|
|
Creator: K4YT3X
|
|
Date Created: April 1, 2023
|
|
Last Modified: April 1, 2023
|
|
|
|
This script updates DVM Host's example config with the content of a modified one
|
|
to generate a configuration with the settings of the modified config but with the
|
|
config from the original example config.
|
|
|
|
Example usages:
|
|
|
|
1. Annotate a config file in-place using DVM Host's default config file:
|
|
|
|
`python config_annotator.py -em modified.yaml`
|
|
|
|
2. Annotate a config file using a custom template file and export it to a separate file:
|
|
|
|
`python config_annotator.py -t template.yml -m modified.yml -o annotated.yml`
|
|
"""
|
|
import argparse
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import Dict
|
|
|
|
# since this stand-alone script does not have a pyproject.yaml or requirements.txt
|
|
# prompt the user to install the required libraries if they're not found
|
|
try:
|
|
import requests
|
|
import ruamel.yaml
|
|
except ImportError:
|
|
print(
|
|
"Error: unable to import ruamel.yaml or requests\n"
|
|
"Please install it with `pip install ruamel.yaml requests`",
|
|
file=sys.stderr,
|
|
)
|
|
sys.exit(1)
|
|
|
|
# URL to DVM Host's official example URL
|
|
EXAMPLE_CONFIG_URL = (
|
|
"https://github.com/DVMProject/dvmhost/raw/master/configs/config.example.yml"
|
|
)
|
|
|
|
|
|
def parse_arguments() -> argparse.Namespace:
|
|
"""
|
|
parse command line arguments
|
|
|
|
:return: parsed arguments in an argparse namespace object
|
|
"""
|
|
parser = argparse.ArgumentParser(description="Update a YAML file with new values.")
|
|
template_group = parser.add_mutually_exclusive_group(required=True)
|
|
template_group.add_argument(
|
|
"-e",
|
|
"--example",
|
|
help="use DVM Host's default example config as template",
|
|
action="store_true",
|
|
)
|
|
template_group.add_argument(
|
|
"-t",
|
|
"--template",
|
|
type=Path,
|
|
help="path to the template config YAML file with comments",
|
|
)
|
|
parser.add_argument(
|
|
"-m", "--modified", type=Path, help="path to the modified YAML file"
|
|
)
|
|
parser.add_argument(
|
|
"-o", "--output", type=Path, help="path to the save the annotated YAML file"
|
|
)
|
|
return parser.parse_args()
|
|
|
|
|
|
def update_dict(template_dict: Dict, modified_dict: Dict) -> Dict:
|
|
"""
|
|
Recursively updates the values of template_dict with the values of modified_dict,
|
|
without changing the order of the keys in template_dict.
|
|
|
|
:param template_dict: the dictionary to update.
|
|
:type template_dict: dict
|
|
:param modified_dict: the dictionary with the updated values
|
|
:type modified_dict: dict
|
|
:return: the updated dictionary
|
|
:rtype: dict
|
|
"""
|
|
for key, value in modified_dict.items():
|
|
if isinstance(value, dict) and key in template_dict:
|
|
update_dict(template_dict[key], value)
|
|
elif key in template_dict:
|
|
template_dict[key] = value
|
|
return template_dict
|
|
|
|
|
|
def main(
|
|
example: bool, template_path: Path, modified_path: Path, output_path: Path
|
|
) -> None:
|
|
# if the example file is to be used
|
|
# download it from the GitHub repo
|
|
if example is True:
|
|
response = requests.get(EXAMPLE_CONFIG_URL)
|
|
response.raise_for_status()
|
|
template_content = response.text
|
|
|
|
# if a custom template file is to be used
|
|
else:
|
|
with template_path.open(mode="r") as template_file:
|
|
template_content = template_file.read()
|
|
|
|
# load the YAML file
|
|
template = ruamel.yaml.YAML().load(template_content)
|
|
|
|
# load the modified YAML file
|
|
with modified_path.open(mode="r") as modified_file:
|
|
modified = ruamel.yaml.YAML().load(modified_file)
|
|
|
|
# update the template with the modified YAML data
|
|
update_dict(template, modified)
|
|
|
|
# if no output file path is specified, write it back to the modified file
|
|
if args.output is None:
|
|
write_path = modified_path
|
|
|
|
# if an output file path has been specified, write the output to that file
|
|
else:
|
|
write_path = output_path
|
|
|
|
# write the updated YAML file back to disk
|
|
with write_path.open(mode="w") as output_file:
|
|
yaml = ruamel.yaml.YAML()
|
|
|
|
# set the output YAML file's indentation to 4 spaces per project standard
|
|
yaml.indent(mapping=4, sequence=6, offset=4)
|
|
yaml.dump(template, output_file)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
args = parse_arguments()
|
|
main(args.example, args.template, args.modified, args.output)
|