123 lines
3.9 KiB
Python
123 lines
3.9 KiB
Python
from argparse import ArgumentParser, Namespace
|
|
import logging
|
|
import logging.config
|
|
import os
|
|
from typing import Callable
|
|
|
|
import amanuensis.cli.admin
|
|
import amanuensis.cli.character
|
|
import amanuensis.cli.lexicon
|
|
import amanuensis.cli.user
|
|
from amanuensis.db import DbContext
|
|
|
|
|
|
LOGGING_CONFIG = {
|
|
"version": 1,
|
|
"formatters": {
|
|
"fmt_basic": {
|
|
"validate": True,
|
|
"format": "%(message)s",
|
|
},
|
|
"fmt_detailed": {
|
|
"validate": True,
|
|
"format": "%(asctime)s %(levelname)s %(message)s",
|
|
},
|
|
},
|
|
"handlers": {
|
|
"hnd_stderr": {
|
|
"class": "logging.StreamHandler",
|
|
"level": "INFO",
|
|
"formatter": "fmt_basic",
|
|
},
|
|
},
|
|
"loggers": {
|
|
__name__: {
|
|
"level": "DEBUG",
|
|
"handlers": ["hnd_stderr"],
|
|
},
|
|
},
|
|
}
|
|
|
|
|
|
def add_subcommand(subparsers, module) -> None:
|
|
"""Add a cli submodule's commands as a subparser."""
|
|
# Get the command information from the module
|
|
command_name: str = getattr(module, "COMMAND_NAME")
|
|
command_help: str = getattr(module, "COMMAND_HELP")
|
|
if not command_name and command_help:
|
|
return
|
|
|
|
# Add the subparser for the command and set a default action
|
|
command_parser: ArgumentParser = subparsers.add_parser(
|
|
command_name, help=command_help
|
|
)
|
|
command_parser.set_defaults(func=lambda args: command_parser.print_help())
|
|
|
|
# Add all subcommands in the command module
|
|
subcommands = command_parser.add_subparsers(metavar="SUBCOMMAND")
|
|
for name, obj in vars(module).items():
|
|
if name.startswith("command_"):
|
|
# Hyphenate subcommand names
|
|
sc_name: str = name[8:].replace("_", "-")
|
|
# Only the first line of the subcommand function docstring is used
|
|
sc_help = ((obj.__doc__ or "").strip() or "\n").splitlines()[0]
|
|
|
|
# Add the command and any arguments defined by its decorators
|
|
subcommand: ArgumentParser = subcommands.add_parser(
|
|
sc_name, help=sc_help, description=obj.__doc__
|
|
)
|
|
subcommand.set_defaults(func=obj)
|
|
for args, kwargs in reversed(obj.__dict__.get("add_argument", [])):
|
|
subcommand.add_argument(*args, **kwargs)
|
|
|
|
|
|
def init_logger(args):
|
|
"""Set up logging based on verbosity args"""
|
|
if args.verbose:
|
|
handler = LOGGING_CONFIG["handlers"]["hnd_stderr"]
|
|
handler["formatter"] = "fmt_detailed"
|
|
handler["level"] = "DEBUG"
|
|
logging.config.dictConfig(LOGGING_CONFIG)
|
|
|
|
|
|
def get_db_factory(args: Namespace) -> Callable[[], DbContext]:
|
|
"""Factory function for lazy-loading the database in subcommands."""
|
|
|
|
def get_db() -> DbContext:
|
|
"""Lazy loader for the database connection."""
|
|
if not os.path.exists(args.db_path):
|
|
args.parser.error(f"No database found at {args.db_path}")
|
|
return DbContext(path=args.db_path, echo=args.verbose)
|
|
|
|
return get_db
|
|
|
|
|
|
def main():
|
|
"""CLI entry point"""
|
|
# Set up the top-level parser
|
|
parser = ArgumentParser()
|
|
parser.set_defaults(
|
|
parser=parser,
|
|
func=lambda args: parser.print_help(),
|
|
get_db=None,
|
|
)
|
|
parser.add_argument("--verbose", "-v", action="store_true", help="Verbose output")
|
|
parser.add_argument(
|
|
"--db", dest="db_path", default="db.sqlite", help="Path to Amanuensis database"
|
|
)
|
|
|
|
# Add commands from cli submodules
|
|
subparsers = parser.add_subparsers(metavar="COMMAND")
|
|
add_subcommand(subparsers, amanuensis.cli.admin)
|
|
add_subcommand(subparsers, amanuensis.cli.character)
|
|
add_subcommand(subparsers, amanuensis.cli.lexicon)
|
|
add_subcommand(subparsers, amanuensis.cli.user)
|
|
|
|
# Parse args and perform top-level arg processing
|
|
args = parser.parse_args()
|
|
init_logger(args)
|
|
args.get_db = get_db_factory(args)
|
|
|
|
# Execute the desired action
|
|
args.func(args)
|