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.
dvmhost/tools/config_annotator.py

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)

Powered by TurnKey Linux.