style: black and isort

This commit is contained in:
Mathew Guest 2024-11-16 00:57:00 -07:00
parent 82543ce157
commit 25ded9e2b3
7 changed files with 130 additions and 47 deletions

@ -11,7 +11,7 @@ Application framework for Python, features include:
Principles:
- Lend to creating beautiful, easy to read and understand code in the application.
- Minimize coupling of applications to this framework.
- Compatable with Linux, Windows, and Mac. Try to be compatible as possible otherwise.
- Compatible with Linux, Windows, and Mac. Try to be compatible as possible otherwise.
- Try to be compatible with alternate Python runtimes such as PyPy and older python environments. \*WIP
# PyPi Hosted Link
@ -103,6 +103,13 @@ Install:
pip install .
```
Formatting and Linters:
```
black app_skellington
isort app_skellington
flake8 app_skellington
```
# Version
@ -123,7 +130,6 @@ MIT no attribution required - https://opensource.org/license/mit-0
* Project page: https://zavage-software.com/portfolio/app_skellington
* Please report bugs, improvements, or feedback!
* Contact: mathew@zavage.net
* Packing and distribution conforms to PEP 621 https://peps.python.org/pep-0621/
* Reference https://packaging.python.org/en/latest/guides/distributing-packages-using-setuptools/

@ -39,7 +39,9 @@ if os.environ.get("APPSKELLINGTON_DEBUG", None):
handler = logging.StreamHandler()
handler.setFormatter(fmt)
_bootstrap_logger.addHandler(handler)
_bootstrap_logger.debug("log - APPSKELLINGTON_DEBUG set in environment: Enabling verbose logging.")
_bootstrap_logger.debug(
"log - APPSKELLINGTON_DEBUG set in environment: Enabling verbose logging."
)
# Logging is by default off, excepting CRITICAL
else:

@ -7,12 +7,7 @@ import sys
import appdirs
from . import (
_util,
cfg,
cli,
log,
)
from . import _util, cfg, cli, log
# Application scaffolding:
from ._bootstrap import _bootstrap_logger
@ -47,7 +42,9 @@ class ApplicationContainer:
directories.
"""
def __init__(self, configspec_filepath=None, configini_filepath=None, *args, **kwargs):
def __init__(
self, configspec_filepath=None, configini_filepath=None, *args, **kwargs
):
self.appname = kwargs.get("appname") or DEFAULT_APP_NAME
self.appauthor = kwargs.get("appauthor") or DEFAULT_APP_AUTHOR
@ -100,7 +97,9 @@ class ApplicationContainer:
app['datalayer'] => returns the made-up "datalayer" service.
"""
try:
service_factory = self._dependencies[service_name] # Retrieve factory function
service_factory = self._dependencies[
service_name
] # Retrieve factory function
return service_factory() # Call factory() to return instance of service
except KeyError as ex:
msg = "failed to inject service: {}".format(service_name)
@ -160,7 +159,9 @@ class ApplicationContainer:
"""
sig = inspect.signature(constructor.__init__)
params = sig.parameters
params = [params[paramname].name for paramname in params] # Convert Param() type => str
params = [
params[paramname].name for paramname in params
] # Convert Param() type => str
cls_dependencies = params[1:] # Skip 'self' parameter on class methods.
return functools.partial(self._construct_model, constructor, *cls_dependencies)

@ -31,7 +31,9 @@ class Config:
"allow_options_beyond_spec": True,
}
def __init__(self, configspec_filepath=None, configini_filepath=None, capabilities=None):
def __init__(
self, configspec_filepath=None, configini_filepath=None, capabilities=None
):
self._config_obj = None # must be type configobj.ConfigObj()
self._configini_data = None
self._configini_filepath = None
@ -93,11 +95,15 @@ class Config:
self._configspec_filepath = filepath
self._configspec_data = data
self._has_changed_internally = True
_bootstrap_logger.debug("cfg - Set configspec and read contents: %s", filepath)
_bootstrap_logger.debug(
"cfg - Set configspec and read contents: %s", filepath
)
self.load_config()
return
except OSError as ex:
_bootstrap_logger.critical("cfg - Failed to find config.spec: file not found (%s)", filepath)
_bootstrap_logger.critical(
"cfg - Failed to find config.spec: file not found (%s)", filepath
)
raise OSError("Failed to read provided config.spec file")
self.load_config()
@ -187,12 +193,16 @@ class Config:
# raise_errors
)
_bootstrap_logger.debug(
"cfg - Parsed configuration. config.spec = %s, config.ini = %s", config_spec, config_ini
"cfg - Parsed configuration. config.spec = %s, config.ini = %s",
config_spec,
config_ini,
)
return True
except configobj.ParseError as ex:
msg = "cfg - Failed to load config: error in config.spec configuration: {}".format(config_filepath)
msg = "cfg - Failed to load config: error in config.spec configuration: {}".format(
config_filepath
)
_bootstrap_logger.error(msg)
return False
except OSError as ex:
@ -217,7 +227,9 @@ class Config:
self._config_obj, configobj.ConfigObj
), "expecting configobj.ConfigObj, received %s" % type(self._config_obj)
# NOTE(MG) copy arg below instructs configobj to use defaults from spec file
test_results = self._config_obj.validate(val, copy=True, preserve_errors=True)
test_results = self._config_obj.validate(
val, copy=True, preserve_errors=True
)
if test_results is True:
_bootstrap_logger.info(
"cfg- Successfully validated configuration against spec. input = %s, validation spec = %s",
@ -227,19 +239,27 @@ class Config:
return True
elif test_results is False:
_bootstrap_logger.debug("cfg - Potentially discovered invalid config.spec")
_bootstrap_logger.debug(
"cfg - Potentially discovered invalid config.spec"
)
else:
self._validate_parse_errors(test_results)
return False
except ValueError as ex:
_bootstrap_logger.error("cfg - Failed while validating config against spec. ")
_bootstrap_logger.error(
"cfg - Failed while validating config against spec. "
)
return False
def _validate_parse_errors(self, test_results):
_bootstrap_logger.critical("cfg - Config file failed validation.")
for section_list, key, rslt in configobj.flatten_errors(self._config_obj, test_results):
_bootstrap_logger.critical("cfg - Config error info: %s %s %s", section_list, key, rslt)
for section_list, key, rslt in configobj.flatten_errors(
self._config_obj, test_results
):
_bootstrap_logger.critical(
"cfg - Config error info: %s %s %s", section_list, key, rslt
)
if key is not None:
_bootstrap_logger.critical(
"cfg - Config failed validation: [%s].%s appears invalid. msg = %s",

@ -75,7 +75,9 @@ class CommandTree:
"""
Adds an argument to the root parser.
"""
_bootstrap_logger.info("adding argument to root parser: %s and %s", args, kwargs)
_bootstrap_logger.info(
"adding argument to root parser: %s and %s", args, kwargs
)
self.root_parser.add_argument(*args, **kwargs)
def init_submenu(self, param_name, is_required=False):
@ -89,7 +91,9 @@ class CommandTree:
func_args = {"dest": param_name, "metavar": param_name, "required": is_required}
if sys.version_info.major == 3 and sys.version_info.minor < 7:
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"]
# END fix for Python<3.7
@ -100,13 +104,17 @@ class CommandTree:
submenu.submenu_path = ""
submenu.var_name = param_name
_bootstrap_logger.info("Initialized root-level submenu: Parameter = '%s'", param_name)
_bootstrap_logger.info(
"Initialized root-level submenu: Parameter = '%s'", param_name
)
self.entries[param_name] = submenu
self.submenu_param = param_name
return submenu
def register_command(self, func, cmd_name=None, func_signature=None, docstring=None):
def register_command(
self, func, cmd_name=None, func_signature=None, docstring=None
):
"""
When no submenu functionality is desired, this links a single
command into underlying argparse options.
@ -154,7 +162,9 @@ class CommandTree:
helptext = "default provided"
else:
helptext = "default = '{}'".format(param.default)
self.root_parser.add_argument(key, help=helptext, nargs="?", default=param.default)
self.root_parser.add_argument(
key, help=helptext, nargs="?", default=param.default
)
else:
helptext = "required"
self.root_parser.add_argument(key, help=helptext)
@ -230,16 +240,22 @@ class CommandTree:
# the CommandTree with no SubMenu (submenu will be disabled
# in this case):
if self._cmd_tree_is_single_command:
assert self._cmd_tree_is_single_command is True, "corrupt data structure in CommandMenu"
assert (
self._cmd_tree_is_single_command is True
), "corrupt data structure in CommandMenu"
assert self._entries is None, "corrupt data structure in CommandMenu"
assert isinstance(self._single_command, CommandEntry), "corrupt data structure in CommandMenu"
assert isinstance(
self._single_command, CommandEntry
), "corrupt data structure in CommandMenu"
return self._single_command
# There is at least one submenu we need to go down:
else:
assert self._single_command is None, "corrupt data structure in CommandMenu"
assert self._cmd_tree_is_single_command == False, "corrupt data structure in CommandMenu"
assert (
self._cmd_tree_is_single_command == False
), "corrupt data structure in CommandMenu"
# Key or variable name used by argparse to store the submenu options
argparse_param = self.submenu_param # e.g.: submenu_root
@ -251,10 +267,14 @@ class CommandTree:
input("<broken>")
val = args.get(argparse_param)
_bootstrap_logger.debug("cli - argparse command is '{}' = {}".format(argparse_param, val))
_bootstrap_logger.debug(
"cli - argparse command is '{}' = {}".format(argparse_param, val)
)
lookup = submenu.entries.get(val)
_bootstrap_logger.debug("cli - lookup, entries[{}] = {}".format(val, lookup))
_bootstrap_logger.debug(
"cli - lookup, entries[{}] = {}".format(val, lookup)
)
# pop value
del args[argparse_param]
@ -297,7 +317,9 @@ class SubMenu:
self.entries = {}
def register_command(self, func, cmd_name=None, func_signature=None, docstring=None):
def register_command(
self, func, cmd_name=None, func_signature=None, docstring=None
):
"""
Registers a command as an entry in this submenu. Provided function is
converted into argparse arguments and made available to the user.
@ -371,7 +393,9 @@ class SubMenu:
helptext = "default provided"
else:
helptext = "default = '{}'".format(param.default)
child_node.add_argument(key, help=helptext, nargs="?", default=param.default)
child_node.add_argument(
key, help=helptext, nargs="?", default=param.default
)
else:
helptext = "required"
child_node.add_argument(key, help=helptext)
@ -422,7 +446,9 @@ class SubMenu:
func_args = {"dest": var_name, "metavar": var_name, "required": is_required}
if sys.version_info.major == 3 and sys.version_info.minor < 7:
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"]
# END fix for Python<3.7

@ -16,7 +16,13 @@ DEFAULT_LOG_SETTINGS = {
"format": "%(white)s%(name)7s%(reset)s|%(log_color)s%(message)s",
}
},
"handlers": {"stderr": {"class": "logging.StreamHandler", "level": "debug", "formatter": "colored"}},
"handlers": {
"stderr": {
"class": "logging.StreamHandler",
"level": "debug",
"formatter": "colored",
}
},
"loggers": {
"root": {
"handlers": [
@ -65,7 +71,9 @@ class LoggingLayer:
noise for typical operation.
"""
if config_dict is None:
_bootstrap_logger.debug("log - No application logging configuration provided. Using default")
_bootstrap_logger.debug(
"log - No application logging configuration provided. Using default"
)
config_dict = DEFAULT_LOG_SETTINGS
self.transform_config(config_dict)
@ -108,19 +116,26 @@ class LoggingLayer:
config_dict["loggers"][""] = config_dict["loggers"]["root"]
del config_dict["loggers"]["root"]
except Exception as ex:
_bootstrap_logger.warn("was not able to find and patch root logger configuration from arguments")
_bootstrap_logger.warn(
"was not able to find and patch root logger configuration from arguments"
)
# Evaluate the full filepath of the file handler
if "file" not in config_dict["handlers"]:
return
if os.path.abspath(config_dict["handlers"]["file"]["filename"]) == config_dict["handlers"]["file"]["filename"]:
if (
os.path.abspath(config_dict["handlers"]["file"]["filename"])
== config_dict["handlers"]["file"]["filename"]
):
# Path is already absolute
pass
else:
dirname = appdirs.user_log_dir(self.appname, self.appauthor)
_util.ensure_dir_exists(dirname)
log_filepath = os.path.join(dirname, config_dict["handlers"]["file"]["filename"])
log_filepath = os.path.join(
dirname, config_dict["handlers"]["file"]["filename"]
)
config_dict["handlers"]["file"]["filename"] = log_filepath
def _add_own_logconfig(self, config_dict):
@ -133,7 +148,10 @@ class LoggingLayer:
# See _bootstrap.py
if os.environ.get("APPSKELLINGTON_DEBUG", None):
if _logger_name not in config_dict["loggers"]:
config_dict["loggers"][_logger_name] = {"level": "debug", "propagate": "false"}
config_dict["loggers"][_logger_name] = {
"level": "debug",
"propagate": "false",
}
else:
config_dict["loggers"][_logger_name]["level"] = "debug"

@ -32,12 +32,18 @@ def sample_invalid_configspec_filepath():
class TestConfig_e2e:
def test_allows_reading_ini_and_no_spec(self, sample_configini_filepath):
cfg = Config(configini_filepath=sample_configini_filepath)
assert cfg["root_option"] == "root_option_val", "expecting default from config.spec (didnt get)"
assert cfg["app"]["sub_option"] == "sub_option_val", "expecting default for sub option"
assert (
cfg["root_option"] == "root_option_val"
), "expecting default from config.spec (didnt get)"
assert (
cfg["app"]["sub_option"] == "sub_option_val"
), "expecting default for sub option"
def test_allows_reading_spec_and_no_ini(self, sample_configspec_filepath):
cfg = Config(configspec_filepath=sample_configspec_filepath)
assert cfg["root_option"] == "def_string", "expecting default from config.spec (didnt get)"
assert (
cfg["root_option"] == "def_string"
), "expecting default from config.spec (didnt get)"
# NOTE(MG) Changed the functionality to not do it this way.
# def test_constructor_fails_with_invalid_spec(
@ -65,7 +71,9 @@ class TestConfig_e2e:
assert cfg["root_option"] == "newval"
cfg["app"]["sub_option"] = "another_new_val"
assert cfg["app"]["sub_option"] == "another_new_val", "expecting default for sub option"
assert (
cfg["app"]["sub_option"] == "another_new_val"
), "expecting default for sub option"
def test_can_set_option_without_config(self):
cfg = Config()
@ -78,5 +86,7 @@ class TestConfig_e2e:
def test_uses_spec_as_defaults(self, sample_configspec_filepath):
cfg = Config(configspec_filepath=sample_configspec_filepath)
assert cfg["root_option"] == "def_string", "expecting default from config.spec (didnt get)"
assert (
cfg["root_option"] == "def_string"
), "expecting default from config.spec (didnt get)"
assert cfg["app"]["sub_option"] == "def_sub", "expecting default for sub option"