1
0
mirror of https://github.com/craigerl/aprsd.git synced 2024-11-15 12:51:57 -05:00

Reworked all the common arguments

This patch reworks all the common arguments for the commands
and subcommands

--loglevel
--config_file
--quiet

These are all now processed in 1 place.
This commit is contained in:
Hemna 2021-11-08 11:52:41 -05:00
parent 617973f561
commit 89727e2b8e
10 changed files with 157 additions and 139 deletions

View File

@ -22,8 +22,6 @@
# python included libs
import datetime
import logging
from logging import NullHandler
from logging.handlers import RotatingFileHandler
import os
import signal
import sys
@ -36,7 +34,7 @@ import click_completion
import aprsd
from aprsd import cli_helper
from aprsd import config as aprsd_config
from aprsd import messaging, packets, stats, threads, utils
from aprsd import log, messaging, packets, stats, threads, utils
# setup the global logger
@ -56,52 +54,17 @@ def custom_startswith(string, incomplete):
click_completion.core.startswith = custom_startswith
click_completion.init()
cli_initialized = 0
@click.group(cls=cli_helper.GroupWithCommandOptions, context_settings=CONTEXT_SETTINGS)
@click.option(
"--loglevel",
default="INFO",
show_default=True,
type=click.Choice(
["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"],
case_sensitive=False,
),
show_choices=True,
help="The log level to use for aprsd.log",
)
@click.option(
"-c",
"--config",
"config_file",
show_default=True,
default=aprsd_config.DEFAULT_CONFIG_FILE,
help="The aprsd config file to use for options.",
)
@click.option(
"--quiet",
is_flag=True,
default=False,
help="Don't log to stdout",
)
@click.group(context_settings=CONTEXT_SETTINGS)
@click.version_option()
@click.pass_context
def cli(ctx, loglevel, config_file, quiet):
global cli_initialized
# Have to do the global crap because the cli_helper GroupWithCommandOptions
# ends up calling this twice.
ctx.ensure_object(dict)
ctx.obj["loglevel"] = loglevel
ctx.obj["config_file"] = config_file
ctx.obj["quiet"] = quiet
ctx.obj["config"] = aprsd_config.parse_config(config_file)
if not cli_initialized:
setup_logging(ctx.obj["config"], loglevel, quiet)
cli_initialized = 1
def cli(ctx):
pass
def main():
# First import all the possible commands for the CLI
from .cmds import ( # noqa
completion, dev, healthcheck, listen, send_message, server,
)
@ -130,57 +93,17 @@ def signal_handler(sig, frame):
signal.signal(signal.SIGTERM, sys.exit(0))
# Setup the logging faciility
# to disable logging to stdout, but still log to file
# use the --quiet option on the cmdln
def setup_logging(config, loglevel, quiet):
log_level = aprsd_config.LOG_LEVELS[loglevel]
LOG.setLevel(log_level)
log_format = config["aprsd"].get("logformat", aprsd_config.DEFAULT_LOG_FORMAT)
date_format = config["aprsd"].get("dateformat", aprsd_config.DEFAULT_DATE_FORMAT)
log_formatter = logging.Formatter(fmt=log_format, datefmt=date_format)
log_file = config["aprsd"].get("logfile", None)
if log_file:
fh = RotatingFileHandler(log_file, maxBytes=(10248576 * 5), backupCount=4)
else:
fh = NullHandler()
fh.setFormatter(log_formatter)
LOG.addHandler(fh)
imap_logger = None
if config.get("aprsd.email.enabled", default=False) and config.get("aprsd.email.imap.debug", default=False):
imap_logger = logging.getLogger("imapclient.imaplib")
imap_logger.setLevel(log_level)
imap_logger.addHandler(fh)
if config.get("aprsd.web.enabled", default=False):
qh = logging.handlers.QueueHandler(threads.logging_queue)
q_log_formatter = logging.Formatter(
fmt=aprsd_config.QUEUE_LOG_FORMAT,
datefmt=aprsd_config.QUEUE_DATE_FORMAT,
)
qh.setFormatter(q_log_formatter)
LOG.addHandler(qh)
if not quiet:
sh = logging.StreamHandler(sys.stdout)
sh.setFormatter(log_formatter)
LOG.addHandler(sh)
if imap_logger:
imap_logger.addHandler(sh)
@cli.command()
@cli_helper.add_options(cli_helper.common_options)
@click.pass_context
@cli_helper.process_standard_options
def check_version(ctx):
"""Check this version against the latest in pypi.org."""
config_file = ctx.obj["config_file"]
loglevel = ctx.obj["loglevel"]
config = aprsd_config.parse_config(config_file)
setup_logging(config, loglevel, False)
log.setup_logging(config, loglevel, False)
level, msg = utils._check_version()
if level:
LOG.warning(msg)

View File

@ -1,5 +1,73 @@
from functools import update_wrapper
import typing as t
import click
from aprsd import config as aprsd_config
from aprsd import log
F = t.TypeVar("F", bound=t.Callable[..., t.Any])
common_options = [
click.option(
"--loglevel",
default="INFO",
show_default=True,
type=click.Choice(
["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"],
case_sensitive=False,
),
show_choices=True,
help="The log level to use for aprsd.log",
),
click.option(
"-c",
"--config",
"config_file",
show_default=True,
default=aprsd_config.DEFAULT_CONFIG_FILE,
help="The aprsd config file to use for options.",
),
click.option(
"--quiet",
is_flag=True,
default=False,
help="Don't log to stdout",
),
]
def add_options(options):
def _add_options(func):
for option in reversed(options):
func = option(func)
return func
return _add_options
def process_standard_options(f: F) -> F:
def new_func(*args, **kwargs):
print(f"ARGS {args}")
print(f"KWARGS {kwargs}")
ctx = args[0]
ctx.ensure_object(dict)
ctx.obj["loglevel"] = kwargs["loglevel"]
ctx.obj["config_file"] = kwargs["config_file"]
ctx.obj["quiet"] = kwargs["quiet"]
ctx.obj["config"] = aprsd_config.parse_config(kwargs["config_file"])
log.setup_logging(
ctx.obj["config"], ctx.obj["loglevel"],
ctx.obj["quiet"],
)
del kwargs["loglevel"]
del kwargs["config_file"]
del kwargs["quiet"]
return f(*args, **kwargs)
return update_wrapper(t.cast(F, new_func), f)
class AliasedGroup(click.Group):
def command(self, *args, **kwargs):
@ -33,42 +101,3 @@ class AliasedGroup(click.Group):
self.add_command(cmd, name=alias)
return cmd
return decorator
class GroupWithCommandOptions(click.Group):
""" Allow application of options to group with multi command """
def add_command(self, cmd, name=None):
click.Group.add_command(self, cmd, name=name)
# add the group parameters to the command
for param in self.params:
cmd.params.append(param)
# hook the commands invoke with our own
cmd.invoke = self.build_command_invoke(cmd.invoke)
self.invoke_without_command = True
def build_command_invoke(self, original_invoke):
def command_invoke(ctx):
""" insert invocation of group function """
# separate the group parameters
ctx.obj = dict(_params={})
for param in self.params:
name = param.name
if name in ctx.params:
ctx.obj["_params"][name] = ctx.params[name]
del ctx.params[name]
# call the group function with its parameters
params = ctx.params
ctx.params = ctx.obj["_params"]
self.invoke(ctx)
ctx.params = params
# now call the original invoke(the command)
original_invoke(ctx)
return command_invoke

View File

@ -14,7 +14,7 @@ def completion(ctx):
# show dumps out the completion code for a particular shell
@completion.command(help="Show completion code for shell")
@completion.command(help="Show completion code for shell", name="show")
@click.option("-i", "--case-insensitive/--no-case-insensitive", help="Case insensitive completion")
@click.argument("shell", required=False, type=click_completion.DocumentedChoice(click_completion.core.shells))
def show(shell, case_insensitive):
@ -24,7 +24,7 @@ def show(shell, case_insensitive):
# install will install the completion code for a particular shell
@completion.command(help="Install completion code for a shell")
@completion.command(help="Install completion code for a shell", name="install")
@click.option("--append/--overwrite", help="Append the completion code to the file", default=None)
@click.option("-i", "--case-insensitive/--no-case-insensitive", help="Case insensitive completion")
@click.argument("shell", required=False, type=click_completion.DocumentedChoice(click_completion.core.shells))

View File

@ -8,7 +8,7 @@ import logging
import click
# local imports here
from aprsd import client, plugin
from aprsd import cli_helper, client, plugin
from ..aprsd import cli
@ -17,7 +17,14 @@ LOG = logging.getLogger("APRSD")
CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"])
@cli.command()
@cli.group(help="Development type subcommands", context_settings=CONTEXT_SETTINGS)
@click.pass_context
def dev(ctx):
pass
@dev.command()
@cli_helper.add_options(cli_helper.common_options)
@click.option(
"--aprs-login",
envvar="APRS_LOGIN",
@ -51,6 +58,7 @@ CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"])
)
@click.argument("message", nargs=-1, required=True)
@click.pass_context
@cli_helper.process_standard_options
def test_plugin(
ctx,
aprs_login,
@ -59,12 +67,7 @@ def test_plugin(
number,
message,
):
"""Test an APRSD plugin
This allows you to develop a plugin and send a 'command' string
directly to the plugin during development/testing. Use this before
releasing as a plugin for aprsd.
"""
"""Test an individual APRSD plugin given a python path."""
config = ctx.obj["config"]
fromcall = aprs_login

View File

@ -13,7 +13,7 @@ import click
import requests
import aprsd
from aprsd import utils
from aprsd import cli_helper, utils
# local imports here
from ..aprsd import cli
@ -25,6 +25,7 @@ LOG = logging.getLogger("APRSD")
@cli.command()
@cli_helper.add_options(cli_helper.common_options)
@click.option(
"--url",
"health_url",
@ -39,6 +40,7 @@ LOG = logging.getLogger("APRSD")
help="How long to wait for healtcheck url to come back",
)
@click.pass_context
@cli_helper.process_standard_options
def healthcheck(ctx, health_url, timeout):
"""Check the health of the running aprsd server."""
ctx.obj["config"]

View File

@ -13,7 +13,9 @@ import click
# local imports here
import aprsd
from aprsd import client, messaging, packets, stats, threads, trace, utils
from aprsd import (
cli_helper, client, messaging, packets, stats, threads, trace, utils,
)
from ..aprsd import cli
@ -36,6 +38,7 @@ def signal_handler(sig, frame):
@cli.command()
@cli_helper.add_options(cli_helper.common_options)
@click.option(
"--aprs-login",
envvar="APRS_LOGIN",
@ -54,6 +57,7 @@ def signal_handler(sig, frame):
required=True,
)
@click.pass_context
@cli_helper.process_standard_options
def listen(
ctx,
aprs_login,

View File

@ -7,7 +7,7 @@ from aprslib.exceptions import LoginError
import click
import aprsd
from aprsd import client, messaging, packets
from aprsd import cli_helper, client, messaging, packets
from ..aprsd import cli
@ -16,6 +16,7 @@ LOG = logging.getLogger("APRSD")
@cli.command()
@cli_helper.add_options(cli_helper.common_options)
@click.option(
"--aprs-login",
envvar="APRS_LOGIN",
@ -48,6 +49,7 @@ LOG = logging.getLogger("APRSD")
@click.argument("tocallsign", required=True)
@click.argument("command", nargs=-1, required=True)
@click.pass_context
@cli_helper.process_standard_options
def send_message(
ctx,
aprs_login,

View File

@ -6,7 +6,8 @@ import click
import aprsd
from aprsd import (
client, flask, messaging, packets, plugin, stats, threads, trace, utils,
cli_helper, client, flask, messaging, packets, plugin, stats, threads,
trace, utils,
)
from aprsd import aprsd as aprsd_main
@ -18,6 +19,7 @@ LOG = logging.getLogger("APRSD")
# main() ###
@cli.command()
@cli_helper.add_options(cli_helper.common_options)
@click.option(
"-f",
"--flush",
@ -28,6 +30,7 @@ LOG = logging.getLogger("APRSD")
help="Flush out all old aged messages on disk.",
)
@click.pass_context
@cli_helper.process_standard_options
def server(ctx, flush):
"""Start the aprsd server gateway process."""
ctx.obj["config_file"]

53
aprsd/log.py Normal file
View File

@ -0,0 +1,53 @@
import logging
from logging import NullHandler
from logging.handlers import RotatingFileHandler
import queue
import sys
from aprsd import config as aprsd_config
LOG = logging.getLogger("APRSD")
logging_queue = queue.Queue()
# Setup the logging faciility
# to disable logging to stdout, but still log to file
# use the --quiet option on the cmdln
def setup_logging(config, loglevel, quiet):
log_level = aprsd_config.LOG_LEVELS[loglevel]
LOG.setLevel(log_level)
log_format = config["aprsd"].get("logformat", aprsd_config.DEFAULT_LOG_FORMAT)
date_format = config["aprsd"].get("dateformat", aprsd_config.DEFAULT_DATE_FORMAT)
log_formatter = logging.Formatter(fmt=log_format, datefmt=date_format)
log_file = config["aprsd"].get("logfile", None)
if log_file:
fh = RotatingFileHandler(log_file, maxBytes=(10248576 * 5), backupCount=4)
else:
fh = NullHandler()
fh.setFormatter(log_formatter)
LOG.addHandler(fh)
imap_logger = None
if config.get("aprsd.email.enabled", default=False) and config.get("aprsd.email.imap.debug", default=False):
imap_logger = logging.getLogger("imapclient.imaplib")
imap_logger.setLevel(log_level)
imap_logger.addHandler(fh)
if config.get("aprsd.web.enabled", default=False):
qh = logging.handlers.QueueHandler(logging_queue)
q_log_formatter = logging.Formatter(
fmt=aprsd_config.QUEUE_LOG_FORMAT,
datefmt=aprsd_config.QUEUE_DATE_FORMAT,
)
qh.setFormatter(q_log_formatter)
LOG.addHandler(qh)
if not quiet:
sh = logging.StreamHandler(sys.stdout)
sh.setFormatter(log_formatter)
LOG.addHandler(sh)
if imap_logger:
imap_logger.addHandler(sh)

View File

@ -17,7 +17,6 @@ RX_THREAD = "RX"
EMAIL_THREAD = "Email"
rx_msg_queue = queue.Queue(maxsize=20)
logging_queue = queue.Queue()
msg_queues = {
"rx": rx_msg_queue,
}