Created
February 26, 2022 11:08
-
-
Save yatharthb97/086f24c9a01bb5a508821fae80e190c9 to your computer and use it in GitHub Desktop.
Tool to import, verify, and generate config files {json and yaml} for python.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
#!/usr/bin/env python3 | |
#!/usr/bin/env python | |
""" | |
Configuration file manager for yaml and json. | |
Tool to import, verify, and generate config files {json and yaml} for python. | |
Author : *Yatharth Bhasin* (Github → yatharthb97) | |
License: *MIT open-source license* (https://opensource.org/licenses/mit-license.php) | |
This piece of software was released on GistHub : TODO. | |
Please give credit whenever possible. | |
""" | |
import os | |
import json | |
import yaml | |
class Configr: | |
""" | |
Tools to import, verify, and generate config files for python. | |
""" | |
def __init__(self, filename=None, ext=".json", ref=None): | |
# Configuration | |
self.config = None | |
# Declare defaults | |
self.default_ext = ext | |
self.default_config_name = f"autogenerated_config{self.default_ext}" #Default configuration option | |
self.ref_config = None # Reference config object. | |
self.ref_set = False # Reference config is set flag. | |
# Filename | |
self.file_path = None | |
if filename != None: | |
self.file_path = filename | |
# Reference Configuration | |
if ref != None: | |
self.set_ref(ref) | |
def find_config(self, dir_path=".", ext="", file_key="config", alphabatical=True): | |
""" | |
Checks for configuration files within the current working | |
directory, does a simple search with the given `file_key`. | |
dir_path : The directory path for search - converts to absolute file path. | |
ext : Extension of file to be searched. | |
file_key : [optional] key word that indicates a config file. | |
args: | |
--- | |
"alphabatical" : sorts and returns the first file in case multiple files are detected. | |
""" | |
# Extract all files within the folder | |
file_list = [file for file in os.listdir(os.path.abspath(dir_path))] | |
if ext != "": | |
file_list = [file for file in file_list if ext in file] | |
file_list = [file for file in file_list if file_key in file] | |
if "alphabatical": | |
file_list = sorted(file_list) | |
if not file_list: | |
return None | |
else: | |
return file_list[0] | |
if len(file_list) > 1: | |
print(f"[ERROR] Multiple config files options found - {file_list}.") | |
return None | |
elif len(file_list) == 0: | |
print(f"[ERROR] No config files found!") | |
return None | |
else: | |
return file_list[0] | |
def load(self, config_file): | |
""" | |
Loads the file into `self.config` field as dictionary. | |
Parses both json and yaml. | |
""" | |
if not os.path.exists(config_file): | |
raise Exception(f"File does not exist: {config_file}") | |
if config_file.endswith("yml") or config_file.endswith("yaml") or config_file.endswith("json"): | |
self.file_path = config_file | |
with open(config_file, 'r') as file: | |
self.config = yaml.safe_load(file) | |
else: | |
raise Exception(f"Can only load json and yaml files.") | |
return self.config | |
def set_ref(self, config_dict): | |
""" | |
Sets the default configuration. | |
""" | |
self.ref_config = config_dict | |
self.ref_set = True | |
def export(self, filename, config_dict): | |
""" | |
Exports a given | |
Returns path of exported file. | |
""" | |
# file_type selection | |
if filename.endswith(".yaml") or filename.endswith(".yml"): | |
file_type = ".yaml" | |
elif filename.endswith(".json"): | |
file_type = ".json" | |
else: | |
filename += self.default_ext | |
file_type = self.default_ext | |
serializer = None | |
if file_type == ".json": | |
import json as serializer | |
elif file_type == ".yaml" or file_type == ".yml": | |
import yaml as serializer | |
export_config = serializer.dumps(config_dict, indent=4) | |
with open(filename, 'w') as file: | |
file.write(export_config) | |
return filename | |
def export_def_config(self, filename=None): | |
""" | |
Generates a configuration file with default configuration options. | |
Returns path of exported file. | |
""" | |
# Filename selection | |
if filename == None: | |
filename = os.path.abspath(self.default_config_name) | |
self.export(filename, config_dict=self.ref_config) | |
print(f"[Configr] Configuration file generated → `{filename}`.") | |
return filename | |
def verify_syntax(self, filename): | |
""" | |
Verifies synatax and catches any exceptions during the parsing of files. | |
""" | |
with open(filename, 'r') as file_obj: | |
if filename.endswith(".json"): | |
try: | |
json.load(file_obj) | |
except ValueError as e: | |
return False | |
return True | |
if filename.endswith(".yml") or filename.endswith(".yaml"): | |
try: | |
yaml.safe_load(file_obj) | |
except yaml.YAMLError as e: | |
return False | |
return True | |
else: | |
return False | |
def verify_and_patch(self, patch_file=True): | |
""" | |
Fill missing fields and generate. | |
patch_file : True value applies the patch to the file as well. | |
Returns `True` when verification passed without any patching, `False` otherwise. | |
""" | |
# Check if the default configuration is set. | |
if not self.ref_set: | |
raise Exception("[Configr] Default configuration is not set. Verification not possible.") | |
update_state = False | |
# Unknown keys -> Only generates warning, does not remove the extra fields. | |
for key in self.config: | |
if not key in self.ref_config.keys(): | |
print(f"[Warning] Unrecognised configuration field -> {key} : {str(self.config[key])}") | |
# Missing keys | |
for key in self.ref_config: | |
if not key in self.config: | |
self.config[key] = self.ref_config[key] #Add | |
print(f"[Warning] Configuration : Missing field added -> `{key}: {str(self.config[key])}` .") | |
update_state = True | |
if update_state == True and patch_file == True: | |
self.export(self.file_path, self.config) | |
return (not update_state) | |
# Higher level APIs ↓ | |
def load_seq(self, directory): #TODO | |
""" | |
All configuration operations clubbed into one function. | |
* In case multiple config possibilities exist, choose alphabetically. | |
* Generate json file if config not found. | |
* Patches applied to file as well. | |
Returns the config dictionary at the end. | |
""" | |
conf_path = self.find_config(directory, ext=self.default_ext, alphabatical=True) | |
if conf_path == None: | |
print("[ERROR] No configuration file was found in the current directory.") | |
conf_path = self.export_def_config() #To file | |
# Load, verify and patch | |
conf_path = os.path.abspath(conf_path) | |
self.load(conf_path) | |
self.verify_and_patch(patch_file=True) | |
return self.config | |
#-- END of file |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment