141 lines
4.5 KiB
Python
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)
|