Merge branch 'master' of git.zavage.net:Zavage/app_skellington

This commit is contained in:
Mathew Guest 2022-03-07 12:05:51 -07:00
commit 1f8013bd49
4 changed files with 45 additions and 15 deletions

@ -12,6 +12,8 @@ from . import _util
from . import cli from . import cli
from . import cfg from . import cfg
import logging
# These two variables affect the directory paths for # These two variables affect the directory paths for
# config files and logging. # config files and logging.
DEFAULT_APP_NAME = '' DEFAULT_APP_NAME = ''
@ -54,7 +56,13 @@ class ApplicationContainer:
config = cfg.Config(configspec_filepath, configini_filepath) config = cfg.Config(configspec_filepath, configini_filepath)
logger = log.LoggingLayer(self.appname, self.appauthor) logger = log.LoggingLayer(self.appname, self.appauthor)
logger.configure_logging() # Try and load logging configuration if provided
log_config = config.get('logging')
if log_config is not None:
logger.configure_logging(log_config)
else:
logger.configure_logging()
self.ctx = ApplicationContext(config, logger) self.ctx = ApplicationContext(config, logger)

@ -18,6 +18,11 @@ class Config:
""" """
Structure to store application runtime configuration. Also contains Structure to store application runtime configuration. Also contains
functionality to load configuration from local site file. functionality to load configuration from local site file.
Provide config.spec - specification file which defines allowed parameters and types.
Provide config.ini - configuration instance which contains values for any
configuration arguments.
""" """
DEFAULT_CAPABILITIES = { DEFAULT_CAPABILITIES = {
@ -146,6 +151,19 @@ class Config:
""" """
self._config_obj[key] = value self._config_obj[key] = value
def get(self, key, default=None):
"""
Attempt to retrieve configuration item, otherwise return default
provided value.
Similar to Dictionary.get()
"""
try:
v = self.__getitem__(key)
return v
except KeyError as ex:
return default
def load_config( def load_config(
self, configspec_filepath=None, configini_filepath=None self, configspec_filepath=None, configini_filepath=None
): ):

@ -80,9 +80,8 @@ class CommandTree:
Creates a root-level submenu with no entries. SubMenu node is Creates a root-level submenu with no entries. SubMenu node is
returned which can have submenus and commands attached to it. returned which can have submenus and commands attached to it.
""" """
# NOTE(MG) Fix below strategizes whether to pass in 'required' # NOTE(MG) Fix for Python>=3.7,
# paremter to ArgumentParser.add_subparsers() # argparse.ArgumentParser added 'required' argument.
# which was added in in Python3.7.
# Must also be written into SubMenu.create_submenu. # Must also be written into SubMenu.create_submenu.
func_args = { func_args = {
'dest': param_name, 'dest': param_name,
@ -91,11 +90,12 @@ class CommandTree:
} }
if ( if (
sys.version_info.major == 3 sys.version_info.major == 3
and sys.version_info.minor <= 6 and sys.version_info.minor < 7
): ):
if is_required: if is_required:
_bootstrap_logger.warn('Unable to enforce required submenu: Requires >= Python 3.7') _bootstrap_logger.warn('Unable to enforce required submenu: Requires >= Python 3.7')
del func_args['required'] del func_args['required']
# END fix for Python<3.7
# Creates an argument as a slot in the underlying argparse. # Creates an argument as a slot in the underlying argparse.
subparsers = self.root_parser.add_subparsers( subparsers = self.root_parser.add_subparsers(
@ -447,9 +447,8 @@ class SubMenu:
help='sub-submenu help', help='sub-submenu help',
description='sub-sub description') description='sub-sub description')
# NOTE(MG) Fix below strategizes whether to pass in 'required' # NOTE(MG) Fix for Python>=3.7,
# paremter to ArgumentParser.add_subparsers() # argparse.ArgumentParser added 'required' argument.
# which was added in in Python3.7.
# Must also be written into CommandTree.init_submenu # Must also be written into CommandTree.init_submenu
func_args = { func_args = {
'dest': var_name, 'dest': var_name,
@ -458,11 +457,14 @@ class SubMenu:
} }
if ( if (
sys.version_info.major == 3 sys.version_info.major == 3
and sys.version_info.minor <= 6 and sys.version_info.minor < 7
): ):
if is_required: if is_required:
_bootstrap_logger.warn('Unable to enforce required submenu: Requires >= Python 3.7') _bootstrap_logger.warn('Unable to enforce required submenu: Requires >= Python 3.7')
del func_args['required'] del func_args['required']
# END fix for Python<3.7
# Turn entry into a submenu of it's own: # Turn entry into a submenu of it's own:
# type = _SubParsersAction # type = _SubParsersAction
subp_node = entry_node.add_subparsers( subp_node = entry_node.add_subparsers(

@ -77,7 +77,6 @@ class LoggingLayer:
self.transform_config(config_dict) self.transform_config(config_dict)
try: try:
# TODO(MG) Pretty print
_bootstrap_logger.debug('log - Log configuration: %s', config_dict) _bootstrap_logger.debug('log - Log configuration: %s', config_dict)
logging.config.dictConfig(config_dict) logging.config.dictConfig(config_dict)
_bootstrap_logger.debug('log - Configured all logging') _bootstrap_logger.debug('log - Configured all logging')
@ -107,13 +106,16 @@ class LoggingLayer:
d = config_dict['loggers'][logger] d = config_dict['loggers'][logger]
self._convert_str_to_loglevel(d, 'level') self._convert_str_to_loglevel(d, 'level')
# Replace 'root' logger with '', logging module convention for root handler
# Note: '' is disallowed in ConfigObj (hence the reason for this replacement) # Implementation note:
# app_skellington expects root logger configuration to be under 'root'
# instead of '' (python spec) because '' is not a valid name in ConfigObj.
try: try:
config_dict['loggers'][''] = config_dict['loggers']['root'] if config_dict['loggers'].get('root') is not None:
del config_dict['loggers']['root'] config_dict['loggers'][''] = config_dict['loggers']['root']
del config_dict['loggers']['root']
except Exception as ex: except Exception as ex:
_bootstrap.logger.warn('internal failure patching root logger') _bootstrap_logger.warn('was not able to find and patch root logger configuration from arguments')
# Evaluate the full filepath of the file handler # Evaluate the full filepath of the file handler