tools/configuration.py

141 lines
4.5 KiB
Python

# -*- coding: UTF-8 -*-
import os
import sys
import getopt
from yaml import load, YAMLError
try:
from yaml import CSafeLoader as SafeLoader
except ImportError:
from yaml import SafeLoader
from pathlib import Path
class Config():
def __init__(self, scriptname, progver, commands):
scriptname = os.path.realpath(scriptname)
self.basedir = os.path.dirname(scriptname)
self.progname = os.path.basename(scriptname)
self.progver = progver
self.commands = commands
# options
self.help = False
self.version = False
self.verbose = False
self.config = "config"
self.params = {}
# command
self.cmd = None
# config params
self.config_params = {}
def usage(self, exitcode):
usage = """Usage: """ + self.progname + """ [<options>] <command> [<options>]
Commands:
"""
for cmd, desc in self.commands:
usage += " " + cmd + (' ' if len(cmd) >= 31 else ' ' * (31-len(cmd))) + desc + "\n"
usage += """
Options:
-h, --help Print this help
-V, --version Print version number
-c, --config dir Specify config directory to read [""" + self.config + """]
-s, --set key=value Override config parameter key=value
-v, --verbose Print verbose messages
"""
sys.stderr.write(usage)
if exitcode is not None:
sys.exit(exitcode)
def parse(self, argv):
try:
(opt, args) = getopt.gnu_getopt(argv[1:], "hVvc:s:", [
"help", "version", "verbose", "config=", "set="
])
except getopt.GetoptError as e:
sys.stderr.write("Error: %s\n" % str(e))
self.usage(2)
for o, a in opt:
if o == '-h' or o == "--help":
self.help = True
elif o == '-V' or o == "--version":
self.version = True
elif o == '-v' or o == "--verbose":
self.verbose = True
elif o == '-c' or o == "--config":
if (a == ''):
sys.stderr.write("Error: option " + o + " requires a non-empty argument\n")
self.usage(2)
self.config = a
elif o == '-s' or o == "--set":
parts = a.partition('=')
if (parts[1] != '='):
sys.stderr.write("Error: option " + o + " requires argument in the format key=value\n")
self.usage(2)
if (parts[0] == ''):
sys.stderr.write("Error: option " + o + " requires argument in the format key=value; key may not be an empty string\n")
self.usage(2)
self.params[parts[0]] = parts[2]
if self.version:
sys.stdout.write("Version: " + self.progver + "\n")
if self.help:
self.usage(None)
if self.version or self.help:
sys.exit(0)
self.load_config()
for c in args:
if self.cmd != None:
sys.stderr.write("Multiple commands specified: " + self.cmd + " vs " + c + "\n")
self.usage(2)
for cmd, _ in self.commands:
if c == cmd:
self.cmd = c
break
if self.cmd == None:
sys.stderr.write("Unknown command specified: " + c + "\n")
self.usage(2)
if self.cmd == None:
sys.stderr.write("Missing command\n")
self.usage(2)
for k, v in self.params.items():
self.config_params[k] = v
if self.verbose:
sys.stdout.write("Config parameters:\n")
for k, v in self.config_params.items():
sys.stdout.write(" " + k + "=" + str(v) + "\n")
def merge_config(self, src, dest):
for key, value in src.items():
if isinstance(value, dict):
node = dest.setdefault(key, {})
self.merge_config(value, node)
else:
dest[key] = value
return dest
def parse_file(self, filepath):
try:
obj = load(open(filepath, 'r'), SafeLoader)
self.merge_config(obj, self.config_params)
except YAMLError as exc:
sys.stdout.write("Error: reading configuration file failed: %s" % (exc))
def load_config(self):
configdir = self.config if self.config.startswith('/') else self.basedir + '/' + self.config
if self.verbose:
sys.stdout.write("Reading config directory " + configdir + "\n")
if not Path(configdir).is_dir():
sys.stderr.write("Warning: config directory " + configdir + " does not exist\n")
return
with os.scandir(configdir) as it:
for entry in it:
if not entry.name.startswith('.') and (entry.name.endswith('.yml') or entry.name.endswith('.yaml')) and entry.is_file():
if self.verbose:
sys.stdout.write("Parsing config file " + entry.path + "\n")
self.parse_file(entry.path)