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 # python included libs
import datetime import datetime
import logging import logging
from logging import NullHandler
from logging.handlers import RotatingFileHandler
import os import os
import signal import signal
import sys import sys
@ -36,7 +34,7 @@ import click_completion
import aprsd import aprsd
from aprsd import cli_helper from aprsd import cli_helper
from aprsd import config as aprsd_config 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 # setup the global logger
@ -56,52 +54,17 @@ def custom_startswith(string, incomplete):
click_completion.core.startswith = custom_startswith click_completion.core.startswith = custom_startswith
click_completion.init() click_completion.init()
cli_initialized = 0
@click.group(cls=cli_helper.GroupWithCommandOptions, context_settings=CONTEXT_SETTINGS) @click.group(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.version_option() @click.version_option()
@click.pass_context @click.pass_context
def cli(ctx, loglevel, config_file, quiet): def cli(ctx):
global cli_initialized pass
# 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 main(): def main():
# First import all the possible commands for the CLI
from .cmds import ( # noqa from .cmds import ( # noqa
completion, dev, healthcheck, listen, send_message, server, completion, dev, healthcheck, listen, send_message, server,
) )
@ -130,57 +93,17 @@ def signal_handler(sig, frame):
signal.signal(signal.SIGTERM, sys.exit(0)) 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.command()
@cli_helper.add_options(cli_helper.common_options)
@click.pass_context @click.pass_context
@cli_helper.process_standard_options
def check_version(ctx): def check_version(ctx):
"""Check this version against the latest in pypi.org.""" """Check this version against the latest in pypi.org."""
config_file = ctx.obj["config_file"] config_file = ctx.obj["config_file"]
loglevel = ctx.obj["loglevel"] loglevel = ctx.obj["loglevel"]
config = aprsd_config.parse_config(config_file) config = aprsd_config.parse_config(config_file)
setup_logging(config, loglevel, False) log.setup_logging(config, loglevel, False)
level, msg = utils._check_version() level, msg = utils._check_version()
if level: if level:
LOG.warning(msg) LOG.warning(msg)

View File

@ -1,5 +1,73 @@
from functools import update_wrapper
import typing as t
import click 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): class AliasedGroup(click.Group):
def command(self, *args, **kwargs): def command(self, *args, **kwargs):
@ -33,42 +101,3 @@ class AliasedGroup(click.Group):
self.add_command(cmd, name=alias) self.add_command(cmd, name=alias)
return cmd return cmd
return decorator 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 # 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.option("-i", "--case-insensitive/--no-case-insensitive", help="Case insensitive completion")
@click.argument("shell", required=False, type=click_completion.DocumentedChoice(click_completion.core.shells)) @click.argument("shell", required=False, type=click_completion.DocumentedChoice(click_completion.core.shells))
def show(shell, case_insensitive): def show(shell, case_insensitive):
@ -24,7 +24,7 @@ def show(shell, case_insensitive):
# install will install the completion code for a particular shell # 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("--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.option("-i", "--case-insensitive/--no-case-insensitive", help="Case insensitive completion")
@click.argument("shell", required=False, type=click_completion.DocumentedChoice(click_completion.core.shells)) @click.argument("shell", required=False, type=click_completion.DocumentedChoice(click_completion.core.shells))

View File

@ -8,7 +8,7 @@ import logging
import click import click
# local imports here # local imports here
from aprsd import client, plugin from aprsd import cli_helper, client, plugin
from ..aprsd import cli from ..aprsd import cli
@ -17,7 +17,14 @@ LOG = logging.getLogger("APRSD")
CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"]) 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( @click.option(
"--aprs-login", "--aprs-login",
envvar="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.argument("message", nargs=-1, required=True)
@click.pass_context @click.pass_context
@cli_helper.process_standard_options
def test_plugin( def test_plugin(
ctx, ctx,
aprs_login, aprs_login,
@ -59,12 +67,7 @@ def test_plugin(
number, number,
message, message,
): ):
"""Test an APRSD plugin """Test an individual APRSD plugin given a python path."""
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.
"""
config = ctx.obj["config"] config = ctx.obj["config"]
fromcall = aprs_login fromcall = aprs_login

View File

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

View File

@ -13,7 +13,9 @@ import click
# local imports here # local imports here
import aprsd 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 from ..aprsd import cli
@ -36,6 +38,7 @@ def signal_handler(sig, frame):
@cli.command() @cli.command()
@cli_helper.add_options(cli_helper.common_options)
@click.option( @click.option(
"--aprs-login", "--aprs-login",
envvar="APRS_LOGIN", envvar="APRS_LOGIN",
@ -54,6 +57,7 @@ def signal_handler(sig, frame):
required=True, required=True,
) )
@click.pass_context @click.pass_context
@cli_helper.process_standard_options
def listen( def listen(
ctx, ctx,
aprs_login, aprs_login,

View File

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

View File

@ -6,7 +6,8 @@ import click
import aprsd import aprsd
from aprsd import ( 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 from aprsd import aprsd as aprsd_main
@ -18,6 +19,7 @@ LOG = logging.getLogger("APRSD")
# main() ### # main() ###
@cli.command() @cli.command()
@cli_helper.add_options(cli_helper.common_options)
@click.option( @click.option(
"-f", "-f",
"--flush", "--flush",
@ -28,6 +30,7 @@ LOG = logging.getLogger("APRSD")
help="Flush out all old aged messages on disk.", help="Flush out all old aged messages on disk.",
) )
@click.pass_context @click.pass_context
@cli_helper.process_standard_options
def server(ctx, flush): def server(ctx, flush):
"""Start the aprsd server gateway process.""" """Start the aprsd server gateway process."""
ctx.obj["config_file"] 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" EMAIL_THREAD = "Email"
rx_msg_queue = queue.Queue(maxsize=20) rx_msg_queue = queue.Queue(maxsize=20)
logging_queue = queue.Queue()
msg_queues = { msg_queues = {
"rx": rx_msg_queue, "rx": rx_msg_queue,
} }