diff --git a/aprsd/main.py b/aprsd/main.py index d04f5ea..e7e5272 100644 --- a/aprsd/main.py +++ b/aprsd/main.py @@ -1,4 +1,4 @@ -#!/usr/bin/python -u +# -*- coding: utf-8 -*- # # Listen on amateur radio aprs-is network for messages and respond to them. # You must have an amateur radio callsign to use this software. You must @@ -21,7 +21,6 @@ # # python included libs -import argparse import datetime import email import json @@ -40,6 +39,8 @@ import threading import time import urllib +import click +import click_completion from email.mime.text import MIMEText import imapclient import imaplib @@ -85,7 +86,7 @@ message_number = 0 #tn = None ### set default encoding for python, so body.decode doesn't blow up in email thread -#reload(sys) +#reload(sys) #sys.setdefaultencoding('utf8') #import locale @@ -94,17 +95,52 @@ message_number = 0 #locale.getpreferredencoding = getpreferredencoding ### default encoding failed attempts.... -# command line args -parser = argparse.ArgumentParser() -parser.add_argument("--loglevel", - default='DEBUG', - choices=['CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG'], - help="The log level to use for aprsd.log") -parser.add_argument("--quiet", - action='store_true', - help="Don't log to stdout") -args = parser.parse_args() +def custom_startswith(string, incomplete): + """A custom completion match that supports case insensitive matching.""" + if os.environ.get('_CLICK_COMPLETION_COMMAND_CASE_INSENSITIVE_COMPLETE'): + string = string.lower() + incomplete = incomplete.lower() + return string.startswith(incomplete) + + +click_completion.core.startswith = custom_startswith +click_completion.init() + + +cmd_help = """Shell completion for click-completion-command +Available shell types: +\b + %s +Default type: auto +""" % "\n ".join('{:<12} {}'.format(k, click_completion.core.shells[k]) for k in sorted( + click_completion.core.shells.keys())) + +@click.group(help=cmd_help) +@click.version_option() +def main(): + pass + + +@main.command() +@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): + """Show the click-completion-command completion code""" + extra_env = {'_CLICK_COMPLETION_COMMAND_CASE_INSENSITIVE_COMPLETE': 'ON'} if case_insensitive else {} + click.echo(click_completion.core.get_code(shell, extra_env=extra_env)) + + +@main.command() +@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)) +@click.argument('path', required=False) +def install(append, case_insensitive, shell, path): + """Install the click-completion-command completion""" + extra_env = {'_CLICK_COMPLETION_COMMAND_CASE_INSENSITIVE_COMPLETE': 'ON'} if case_insensitive else {} + shell, path = click_completion.core.install(shell=shell, path=path, append=append, extra_env=extra_env) + click.echo('%s completion installed in %s' % (shell, path)) def setup_connection(): @@ -370,7 +406,7 @@ def check_email_thread(): # slowly increase delay every iteration, max out at 300 seconds # any send/receive/resend activity will reset this to 60 seconds - if check_email_delay < 300: + if check_email_delay < 300: check_email_delay += 1 LOG.debug("check_email_delay is " + str(check_email_delay) + " seconds") @@ -512,7 +548,7 @@ def send_message(tocall, message): # 67 displays 64 on the ftm400. (+3 {01 suffix) # feature req: break long ones into two msgs message = message[:67] - # We all miss George Carlin + # We all miss George Carlin message = re.sub('fuck|shit|cunt|piss|cock|bitch', '****', message) thread = threading.Thread( target=send_message_thread, @@ -592,7 +628,7 @@ def send_email(to_addr, content): # Setup the logging faciility # to disable logging to stdout, but still log to file # use the --quiet option on the cmdln -def setup_logging(args): +def setup_logging(loglevel, quiet): global LOG levels = { 'CRITICAL': logging.CRITICAL, @@ -600,7 +636,7 @@ def setup_logging(args): 'WARNING': logging.WARNING, 'INFO': logging.INFO, 'DEBUG': logging.DEBUG} - log_level = levels[args.loglevel] + log_level = levels[loglevel] LOG.setLevel(log_level) log_format = ("%(asctime)s [%(threadName)-12s] [%(levelname)-5.5s]" @@ -614,19 +650,40 @@ def setup_logging(args): fh.setFormatter(log_formatter) LOG.addHandler(fh) - if not args.quiet: + if not quiet: sh = logging.StreamHandler(sys.stdout) sh.setFormatter(log_formatter) LOG.addHandler(sh) + + +@main.command() +def sample_config(): + """This dumps the config to stdout.""" + print(utils.example_config) + + # main() ### -def main(args=args): +@main.command() +@click.option("--loglevel", + default='DEBUG', + 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("--quiet", + is_flag=True, + default=False, + help="Don't log to stdout") +def server(loglevel, quiet): + """Start the aprsd server process.""" global CONFIG - CONFIG = utils.parse_config(args) + CONFIG = utils.parse_config() signal.signal(signal.SIGINT, signal_handler) - setup_logging(args) + setup_logging(loglevel, quiet) LOG.info("APRSD Started version: {}".format(aprsd.__version__)) time.sleep(2) @@ -671,7 +728,7 @@ def main(args=args): else: # LOG.debug("Noise: " + line) # detect closed socket, getting lots of empty lines - if len(line.strip()) == 0: + if len(line.strip()) == 0: LOG.debug("Zero line length received. Consecutive empty line count: " + str(empty_line_rx)) empty_line_rx += 1 if empty_line_rx >= 30: @@ -857,10 +914,9 @@ def main(args=args): time.sleep(5) continue # don't know what failed, so wait and then continue main loop again - LOG.debug("Main loop end") + LOG.debug("Main loop end") # end while True - if __name__ == "__main__": - main(args) + main() diff --git a/aprsd/utils.py b/aprsd/utils.py index b7c8c62..c1ade66 100644 --- a/aprsd/utils.py +++ b/aprsd/utils.py @@ -66,7 +66,7 @@ def get_config(): # and consume the settings. # If the required params don't exist, # it will look in the environment -def parse_config(args): +def parse_config(): # for now we still use globals....ugh global CONFIG, LOG diff --git a/requirements.txt b/requirements.txt index beb2a32..a784c14 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,6 @@ -pbr +click +click-completion imapclient +pbr pyyaml six diff --git a/test-requirements.txt b/test-requirements.txt index 3930480..28ecaca 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1 +1,2 @@ flake8 +pytest diff --git a/tox.ini b/tox.ini index 65d3683..488fea3 100644 --- a/tox.ini +++ b/tox.ini @@ -11,7 +11,7 @@ install_command = pip install {opts} {packages} deps = -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt commands = - pytest {posargs} + pytest aprsd/main.py {posargs} [testenv:cover] commands =