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(
|
||||
description="Available commands:\n{}\n".format(command_descs),
|
||||
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",
|
||||
metavar="LEXICON",
|
||||
dest="lexicon",
|
||||
help="The name of the lexicon to operate on")
|
||||
parser.add_argument("-v",
|
||||
action="store_true",
|
||||
dest="verbose",
|
||||
help="Enable debug logging")
|
||||
parser.set_defaults(func=repl)
|
||||
parser.set_defaults(func=lambda args: repl(args) if args.lexicon else parser.print_help())
|
||||
subp = parser.add_subparsers(
|
||||
metavar="COMMAND",
|
||||
help="The command to execute")
|
||||
@ -90,9 +109,8 @@ def main(argv):
|
||||
|
||||
args = get_parser(commands).parse_args(argv)
|
||||
|
||||
# Configure logging.
|
||||
if args.verbose:
|
||||
configs.log_verbose()
|
||||
# With the arguments parsed, initialize the configs.
|
||||
configs.init(args)
|
||||
|
||||
# Execute command.
|
||||
args.func(args)
|
||||
|
@ -2,8 +2,24 @@
|
||||
from argparse import ArgumentParser as AP
|
||||
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):
|
||||
"""Passes the given args and kwargs to subparser.add_argument"""
|
||||
def argument_adder(command):
|
||||
@wraps(command)
|
||||
def augmented_command(cmd_args):
|
||||
@ -15,6 +31,7 @@ def add_argument(*args, **kwargs):
|
||||
return argument_adder
|
||||
|
||||
def no_argument(command):
|
||||
"""Noops for subparsers"""
|
||||
@wraps(command)
|
||||
def augmented_command(cmd_args):
|
||||
if type(cmd_args) is not AP:
|
||||
@ -22,7 +39,9 @@ def no_argument(command):
|
||||
return augmented_command
|
||||
|
||||
@add_argument("--foo", action="store_true")
|
||||
def command_a(args):
|
||||
"""a docstring"""
|
||||
print(args.foo)
|
||||
def command_dump(args):
|
||||
"""Dumps the global config or the config for the given lexicon"""
|
||||
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.config
|
||||
import os
|
||||
|
||||
logger = logging.getLogger("amanuensis")
|
||||
handler = logging.StreamHandler()
|
||||
logger.addHandler(handler)
|
||||
# Module imports
|
||||
from errors import MissingConfigError, MalformedConfigError
|
||||
|
||||
def log_normal():
|
||||
logger.setLevel(logging.INFO)
|
||||
handler.setLevel(logging.INFO)
|
||||
formatter = logging.Formatter('[{levelname}] {message}', style="{")
|
||||
handler.setFormatter(formatter)
|
||||
|
||||
def log_verbose():
|
||||
logger.setLevel(logging.DEBUG)
|
||||
handler.setLevel(logging.DEBUG)
|
||||
formatter = logging.Formatter('[{asctime}] [{levelname}:{filename}:{lineno}] {message}', style="{")
|
||||
handler.setFormatter(formatter)
|
||||
# Environment variable name constants
|
||||
ENV_CONFIG_DIR = "AMANUENSIS_CONFIG_DIR"
|
||||
ENV_LOG_FILE = "AMANUENSIS_LOG_FILE"
|
||||
ENV_LOG_FILE_SIZE = "AMANUENSIS_LOG_FILE_SIZE"
|
||||
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()
|
||||
|
9
amanuensis/errors.py
Normal file
9
amanuensis/errors.py
Normal file
@ -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
Block a user