Add config init flow from a file in a config directory
This commit is contained in:
parent
04db9a020f
commit
7a85604ece
|
@ -55,15 +55,34 @@ def get_parser(valid_commands):
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="Available commands:\n{}\n".format(command_descs),
|
description="Available commands:\n{}\n".format(command_descs),
|
||||||
formatter_class=argparse.RawDescriptionHelpFormatter)
|
formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||||
|
# The config directory.
|
||||||
|
parser.add_argument("--config-dir",
|
||||||
|
dest="config_dir",
|
||||||
|
default=os.environ.get(configs.ENV_CONFIG_DIR, "./config"),
|
||||||
|
help="The config directory for Amanuensis")
|
||||||
|
# Logging settings.
|
||||||
|
parser.add_argument("--verbose", "-v",
|
||||||
|
action="store_true",
|
||||||
|
dest="verbose",
|
||||||
|
help="Enable verbose console logging")
|
||||||
|
parser.add_argument("--log-file",
|
||||||
|
dest="log_file",
|
||||||
|
default=os.environ.get(configs.ENV_LOG_FILE),
|
||||||
|
help="Enable verbose file logging")
|
||||||
|
parser.add_argument("--log-file-size",
|
||||||
|
dest="log_file_size",
|
||||||
|
default=os.environ.get(configs.ENV_LOG_FILE_SIZE),
|
||||||
|
help="Maximum rolling log file size")
|
||||||
|
parser.add_argument("--log-file-num",
|
||||||
|
dest="log_file_num",
|
||||||
|
default=os.environ.get(configs.ENV_LOG_FILE_NUM),
|
||||||
|
help="Maximum rolling file count")
|
||||||
|
# Lexicon settings.
|
||||||
parser.add_argument("-n",
|
parser.add_argument("-n",
|
||||||
metavar="LEXICON",
|
metavar="LEXICON",
|
||||||
dest="lexicon",
|
dest="lexicon",
|
||||||
help="The name of the lexicon to operate on")
|
help="The name of the lexicon to operate on")
|
||||||
parser.add_argument("-v",
|
parser.set_defaults(func=lambda args: repl(args) if args.lexicon else parser.print_help())
|
||||||
action="store_true",
|
|
||||||
dest="verbose",
|
|
||||||
help="Enable debug logging")
|
|
||||||
parser.set_defaults(func=repl)
|
|
||||||
subp = parser.add_subparsers(
|
subp = parser.add_subparsers(
|
||||||
metavar="COMMAND",
|
metavar="COMMAND",
|
||||||
help="The command to execute")
|
help="The command to execute")
|
||||||
|
@ -90,9 +109,8 @@ def main(argv):
|
||||||
|
|
||||||
args = get_parser(commands).parse_args(argv)
|
args = get_parser(commands).parse_args(argv)
|
||||||
|
|
||||||
# Configure logging.
|
# With the arguments parsed, initialize the configs.
|
||||||
if args.verbose:
|
configs.init(args)
|
||||||
configs.log_verbose()
|
|
||||||
|
|
||||||
# Execute command.
|
# Execute command.
|
||||||
args.func(args)
|
args.func(args)
|
||||||
|
|
|
@ -2,8 +2,24 @@
|
||||||
from argparse import ArgumentParser as AP
|
from argparse import ArgumentParser as AP
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
|
#
|
||||||
|
# The cli module must not import other parts of the application at the module
|
||||||
|
# level. This is because most other modules depend on the config module. The
|
||||||
|
# config module may depend on __main__'s commandline parsing to locate config
|
||||||
|
# files, and __main__'s commandline parsing requires importing (but not
|
||||||
|
# executing) the functions in the cli module. Thus, cli functions must only
|
||||||
|
# import the config module inside the various command methods, which are only
|
||||||
|
# run after commandline parsing has already occurred.
|
||||||
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# These function wrappers are used to make the command_* methods accept an
|
||||||
|
# ArgumentParser as a parameter, which it then configures with the given
|
||||||
|
# argument and returns. This way, we can configure each command's subparser
|
||||||
|
# in this module without having to write a separate function to configure it.
|
||||||
|
#
|
||||||
def add_argument(*args, **kwargs):
|
def add_argument(*args, **kwargs):
|
||||||
|
"""Passes the given args and kwargs to subparser.add_argument"""
|
||||||
def argument_adder(command):
|
def argument_adder(command):
|
||||||
@wraps(command)
|
@wraps(command)
|
||||||
def augmented_command(cmd_args):
|
def augmented_command(cmd_args):
|
||||||
|
@ -15,6 +31,7 @@ def add_argument(*args, **kwargs):
|
||||||
return argument_adder
|
return argument_adder
|
||||||
|
|
||||||
def no_argument(command):
|
def no_argument(command):
|
||||||
|
"""Noops for subparsers"""
|
||||||
@wraps(command)
|
@wraps(command)
|
||||||
def augmented_command(cmd_args):
|
def augmented_command(cmd_args):
|
||||||
if type(cmd_args) is not AP:
|
if type(cmd_args) is not AP:
|
||||||
|
@ -22,7 +39,9 @@ def no_argument(command):
|
||||||
return augmented_command
|
return augmented_command
|
||||||
|
|
||||||
@add_argument("--foo", action="store_true")
|
@add_argument("--foo", action="store_true")
|
||||||
def command_a(args):
|
def command_dump(args):
|
||||||
"""a docstring"""
|
"""Dumps the global config or the config for the given lexicon"""
|
||||||
print(args.foo)
|
import json
|
||||||
|
import configs
|
||||||
|
print(json.dumps(configs.GLOBAL_CONFIG, indent=2))
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,93 @@
|
||||||
|
# Standard library imports
|
||||||
|
from collections import OrderedDict as odict
|
||||||
|
import copy
|
||||||
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
import logging.config
|
||||||
|
import os
|
||||||
|
|
||||||
logger = logging.getLogger("amanuensis")
|
# Module imports
|
||||||
handler = logging.StreamHandler()
|
from errors import MissingConfigError, MalformedConfigError
|
||||||
logger.addHandler(handler)
|
|
||||||
|
|
||||||
def log_normal():
|
|
||||||
logger.setLevel(logging.INFO)
|
|
||||||
handler.setLevel(logging.INFO)
|
|
||||||
formatter = logging.Formatter('[{levelname}] {message}', style="{")
|
|
||||||
handler.setFormatter(formatter)
|
|
||||||
|
|
||||||
def log_verbose():
|
# Environment variable name constants
|
||||||
logger.setLevel(logging.DEBUG)
|
ENV_CONFIG_DIR = "AMANUENSIS_CONFIG_DIR"
|
||||||
handler.setLevel(logging.DEBUG)
|
ENV_LOG_FILE = "AMANUENSIS_LOG_FILE"
|
||||||
formatter = logging.Formatter('[{asctime}] [{levelname}:{filename}:{lineno}] {message}', style="{")
|
ENV_LOG_FILE_SIZE = "AMANUENSIS_LOG_FILE_SIZE"
|
||||||
handler.setFormatter(formatter)
|
ENV_LOG_FILE_NUM = "AMANUENSIS_LOG_FILE_NUM"
|
||||||
|
|
||||||
|
# Functions to be used for moving configs on and off of disk.
|
||||||
|
def read(path):
|
||||||
|
with open(path, 'r') as config_file:
|
||||||
|
return json.load(config_file, object_pairs_hook=odict)
|
||||||
|
|
||||||
|
def write(config, path):
|
||||||
|
with open(path, 'w') as dest_file:
|
||||||
|
json.dump(config, dest_file, allow_nan=False, indent='\t')
|
||||||
|
|
||||||
|
#
|
||||||
|
# The config directory can be set by cli input, so the config infrastructure
|
||||||
|
# needs to wait for initialization before it can load any configs.
|
||||||
|
#
|
||||||
|
CONFIG_DIR = None
|
||||||
|
GLOBAL_CONFIG = None
|
||||||
|
|
||||||
|
def init(args):
|
||||||
|
"""
|
||||||
|
Initializes the config infrastructure to read configs from the
|
||||||
|
directory given by args.config_dir. Initializes logging.
|
||||||
|
"""
|
||||||
|
# Check that config dir exists
|
||||||
|
if not os.path.isdir(args.config_dir):
|
||||||
|
raise MissingConfigError("Config directory not found: {}".format(args.config_dir))
|
||||||
|
# Check that global config file exists
|
||||||
|
global_config_path = os.path.join(args.config_dir, "config.json")
|
||||||
|
if not os.path.isfile(global_config_path):
|
||||||
|
raise MissingConfigError("Config directory missing global config file: {}".format(args.config_dir))
|
||||||
|
# Check that global config file has logging settings
|
||||||
|
global_config_file = read(global_config_path)
|
||||||
|
if 'logging' not in global_config_file.keys():
|
||||||
|
raise MalformedConfigError("No 'logging' section in global config")
|
||||||
|
# Check that the global config file has a lexicon data directory
|
||||||
|
if 'lexicon_data' not in global_config_file.keys():
|
||||||
|
raise MalformedConfigError("No 'lexicon_data' setting in global config")
|
||||||
|
# Configs verified, use them for initialization
|
||||||
|
global CONFIG_DIR, GLOBAL_CONFIG
|
||||||
|
CONFIG_DIR = args.config_dir
|
||||||
|
GLOBAL_CONFIG = global_config_file
|
||||||
|
# Initialize logging
|
||||||
|
init_logging(args)
|
||||||
|
|
||||||
|
def init_logging(args):
|
||||||
|
"""
|
||||||
|
Initializes logging by using the logging section of the global config
|
||||||
|
file.
|
||||||
|
"""
|
||||||
|
# Get the logging config section
|
||||||
|
cfg = copy.deepcopy(GLOBAL_CONFIG['logging'])
|
||||||
|
# Apply any commandline settings to what was defined in the config file
|
||||||
|
handlers = cfg['loggers']['amanuensis']['handlers']
|
||||||
|
if args.verbose:
|
||||||
|
if 'cli-basic' in handlers:
|
||||||
|
handlers.remove('cli_basic')
|
||||||
|
handlers.append('cli_verbose')
|
||||||
|
if args.log_file:
|
||||||
|
cfg['handlers']['file']['filename'] = args.log_file
|
||||||
|
handlers.append("file")
|
||||||
|
# Load the config
|
||||||
|
try:
|
||||||
|
logging.config.dictConfig(cfg)
|
||||||
|
except:
|
||||||
|
raise MalformedConfigError("Failed to load logging config")
|
||||||
|
|
||||||
|
def logger():
|
||||||
|
"""Returns the main logger"""
|
||||||
|
return logging.getLogger("amanuensis")
|
||||||
|
|
||||||
|
# Global config values, which shouldn't be changing during runtime, are
|
||||||
|
# accessed through config.get()
|
||||||
|
|
||||||
|
def get(key):
|
||||||
|
"""Gets the given config value from the global config"""
|
||||||
|
return GLOBAL_CONFIG[key]
|
||||||
|
|
||||||
log_normal()
|
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
class AmanuensisError(Exception):
|
||||||
|
"""Base class for exceptions in amanuensis"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
class MissingConfigError(AmanuensisError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class MalformedConfigError(AmanuensisError):
|
||||||
|
pass
|
Loading…
Reference in New Issue