From 231c15b1af4bb384644efcfded90d0bc6c0e8ec2 Mon Sep 17 00:00:00 2001 From: Hemna Date: Fri, 8 Jan 2021 15:47:30 -0500 Subject: [PATCH] Lots of fixes --- .pre-commit-config.yaml | 71 +++++++++++++++++++----------- aprsd/__init__.py | 2 - aprsd/client.py | 15 ++++--- aprsd/email.py | 62 ++++++++++++++------------ aprsd/fake_aprs.py | 5 +-- aprsd/fuzzyclock.py | 1 - aprsd/main.py | 53 ++++++++++++++-------- aprsd/messaging.py | 38 +++++++++------- aprsd/plugin.py | 54 +++++++++++++---------- aprsd/threads.py | 32 +++++++++----- aprsd/utils.py | 22 +++++---- examples/plugins/example_plugin.py | 1 - pyproject.toml | 36 +++++++++++++++ setup.cfg | 11 ++--- setup.py | 1 - tests/test_main.py | 3 +- tests/test_plugin.py | 6 ++- 17 files changed, 258 insertions(+), 155 deletions(-) create mode 100644 pyproject.toml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2c31010..f2074b5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,29 +1,48 @@ repos: -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.2.0 - hooks: - - id: trailing-whitespace - - id: end-of-file-fixer - - id: check-yaml - - id: check-added-large-files - - id: fix-encoding-pragma - - id: detect-private-key - - id: check-merge-conflict - - id: check-case-conflict - - id: check-docstring-first - - id: check-builtin-literals -- repo: https://github.com/psf/black - rev: 19.3b0 - hooks: - - id: black +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v3.4.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files + - id: detect-private-key + - id: check-merge-conflict + - id: check-case-conflict + - id: check-docstring-first + - id: check-builtin-literals + - id: double-quote-string-fixer -- repo: https://github.com/pre-commit/mirrors-isort - rev: v5.7.0 - hooks: - - id: isort +- repo: https://github.com/asottile/setup-cfg-fmt + rev: v1.16.0 + hooks: + - id: setup-cfg-fmt -- repo: https://gitlab.com/pycqa/flake8 - rev: 3.8.1 - hooks: - - id: flake8 - additional_dependencies: [flake8-bugbear] +- repo: https://github.com/asottile/add-trailing-comma + rev: v2.0.2 + hooks: + - id: add-trailing-comma + args: [--py36-plus] + +- repo: https://github.com/asottile/pyupgrade + rev: v2.7.4 + hooks: + - id: pyupgrade + args: + - --py3-plus + +- repo: https://github.com/pre-commit/mirrors-isort + rev: v5.7.0 + hooks: + - id: isort + +- repo: https://github.com/psf/black + rev: 20.8b1 + hooks: + - id: black + +- repo: https://gitlab.com/pycqa/flake8 + rev: 3.8.4 + hooks: + - id: flake8 + additional_dependencies: [flake8-bugbear] diff --git a/aprsd/__init__.py b/aprsd/__init__.py index 0863171..221b6c8 100644 --- a/aprsd/__init__.py +++ b/aprsd/__init__.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at diff --git a/aprsd/client.py b/aprsd/client.py index 281c830..0f3fa44 100644 --- a/aprsd/client.py +++ b/aprsd/client.py @@ -1,7 +1,5 @@ -# -*- coding: utf-8 -*- import logging import select -import socket import time import aprslib @@ -9,7 +7,7 @@ import aprslib LOG = logging.getLogger("APRSD") -class Client(object): +class Client: """Singleton client class that constructs the aprslib connection.""" _instance = None @@ -19,7 +17,7 @@ class Client(object): def __new__(cls, *args, **kwargs): """This magic turns this into a singleton.""" if cls._instance is None: - cls._instance = super(Client, cls).__new__(cls) + cls._instance = super().__new__(cls) # Put any initialization here. return cls._instance @@ -82,7 +80,7 @@ class Aprsdis(aprslib.IS): """ try: self.sock.setblocking(0) - except socket.error as e: + except OSError as e: self.logger.error("socket error when setblocking(0): %s" % str(e)) raise aprslib.ConnectionDrop("connection dropped") @@ -93,7 +91,10 @@ class Aprsdis(aprslib.IS): # set a select timeout, so we get a chance to exit # when user hits CTRL-C readable, writable, exceptional = select.select( - [self.sock], [], [], self.select_timeout + [self.sock], + [], + [], + self.select_timeout, ) if not readable: continue @@ -105,7 +106,7 @@ class Aprsdis(aprslib.IS): if not short_buf: self.logger.error("socket.recv(): returned empty") raise aprslib.ConnectionDrop("connection dropped") - except socket.error as e: + except OSError as e: # self.logger.error("socket error on recv(): %s" % str(e)) if "Resource temporarily unavailable" in str(e): if not blocking: diff --git a/aprsd/email.py b/aprsd/email.py index f7084bc..ed562e8 100644 --- a/aprsd/email.py +++ b/aprsd/email.py @@ -1,18 +1,15 @@ -# -*- coding: utf-8 -*- import datetime import email +from email.mime.text import MIMEText import imaplib import logging import re import smtplib import time -from email.mime.text import MIMEText - -import imapclient -import six -from validate_email import validate_email from aprsd import messaging, threads +import imapclient +from validate_email import validate_email LOG = logging.getLogger("APRSD") @@ -30,7 +27,10 @@ def _imap_connect(): try: server = imapclient.IMAPClient( - CONFIG["imap"]["host"], port=imap_port, use_uid=True, ssl=use_ssl + CONFIG["imap"]["host"], + port=imap_port, + use_uid=True, + ssl=use_ssl, ) except Exception: LOG.error("Failed to connect IMAP server") @@ -53,7 +53,7 @@ def _smtp_connect(): use_ssl = CONFIG["smtp"].get("use_ssl", False) msg = "{}{}:{}".format("SSL " if use_ssl else "", host, smtp_port) LOG.debug( - "Connect to SMTP host {} with user '{}'".format(msg, CONFIG["imap"]["login"]) + "Connect to SMTP host {} with user '{}'".format(msg, CONFIG["imap"]["login"]), ) try: @@ -84,7 +84,7 @@ def validate_shortcuts(config): LOG.info( "Validating {} Email shortcuts. This can take up to 10 seconds" - " per shortcut".format(len(shortcuts)) + " per shortcut".format(len(shortcuts)), ) delete_keys = [] for key in shortcuts: @@ -102,8 +102,8 @@ def validate_shortcuts(config): if not is_valid: LOG.error( "'{}' is an invalid email address. Removing shortcut".format( - shortcuts[key] - ) + shortcuts[key], + ), ) delete_keys.append(key) @@ -173,14 +173,18 @@ def parse_email(msgid, data, server): if part.get_content_type() == "text/plain": LOG.debug("Email got text/plain") - text = six.text_type( - part.get_payload(decode=True), str(charset), "ignore" + text = str( + part.get_payload(decode=True), + str(charset), + "ignore", ).encode("utf8", "replace") if part.get_content_type() == "text/html": LOG.debug("Email got text/html") - html = six.text_type( - part.get_payload(decode=True), str(charset), "ignore" + html = str( + part.get_payload(decode=True), + str(charset), + "ignore", ).encode("utf8", "replace") if text is not None: @@ -192,12 +196,15 @@ def parse_email(msgid, data, server): # email.uscc.net sends no charset, blows up unicode function below LOG.debug("Email is not multipart") if msg.get_content_charset() is None: - text = six.text_type( - msg.get_payload(decode=True), "US-ASCII", "ignore" - ).encode("utf8", "replace") + text = str(msg.get_payload(decode=True), "US-ASCII", "ignore").encode( + "utf8", + "replace", + ) else: - text = six.text_type( - msg.get_payload(decode=True), msg.get_content_charset(), "ignore" + text = str( + msg.get_payload(decode=True), + msg.get_content_charset(), + "ignore", ).encode("utf8", "replace") body = text.strip() @@ -266,11 +273,11 @@ def resend_email(count, fromcall): month = date.strftime("%B")[:3] # Nov, Mar, Apr day = date.day year = date.year - today = "%s-%s-%s" % (day, month, year) + today = "{}-{}-{}".format(day, month, year) shortcuts = CONFIG["shortcuts"] # swap key/value - shortcuts_inverted = dict([[v, k] for k, v in shortcuts.items()]) + shortcuts_inverted = {v: k for k, v in shortcuts.items()} try: server = _imap_connect() @@ -310,7 +317,7 @@ def resend_email(count, fromcall): # thinking this is a duplicate message. # The FT1XDR pretty much ignores the aprs message number in this # regard. The FTM400 gets it right. - reply = "No new msg %s:%s:%s" % ( + reply = "No new msg {}:{}:{}".format( str(h).zfill(2), str(m).zfill(2), str(s).zfill(2), @@ -328,7 +335,7 @@ def resend_email(count, fromcall): class APRSDEmailThread(threads.APRSDThread): def __init__(self, msg_queues, config): - super(APRSDEmailThread, self).__init__("EmailThread") + super().__init__("EmailThread") self.msg_queues = msg_queues self.config = config @@ -354,13 +361,13 @@ class APRSDEmailThread(threads.APRSDThread): shortcuts = CONFIG["shortcuts"] # swap key/value - shortcuts_inverted = dict([[v, k] for k, v in shortcuts.items()]) + shortcuts_inverted = {v: k for k, v in shortcuts.items()} date = datetime.datetime.now() month = date.strftime("%B")[:3] # Nov, Mar, Apr day = date.day year = date.year - today = "%s-%s-%s" % (day, month, year) + today = "{}-{}-{}".format(day, month, year) server = None try: @@ -378,7 +385,8 @@ class APRSDEmailThread(threads.APRSDThread): envelope = data[b"ENVELOPE"] # LOG.debug('ID:%d "%s" (%s)' % (msgid, envelope.subject.decode(), envelope.date)) f = re.search( - r"'([[A-a][0-9]_-]+@[[A-a][0-9]_-\.]+)", str(envelope.from_[0]) + r"'([[A-a][0-9]_-]+@[[A-a][0-9]_-\.]+)", + str(envelope.from_[0]), ) if f is not None: from_addr = f.group(1) diff --git a/aprsd/fake_aprs.py b/aprsd/fake_aprs.py index 086a7e4..f6ed353 100644 --- a/aprsd/fake_aprs.py +++ b/aprsd/fake_aprs.py @@ -1,10 +1,9 @@ -# -*- coding: utf-8 -*- import argparse import logging +from logging.handlers import RotatingFileHandler import socketserver import sys import time -from logging.handlers import RotatingFileHandler from aprsd import utils @@ -74,7 +73,7 @@ def main(): ip = CONFIG["aprs"]["host"] port = CONFIG["aprs"]["port"] - LOG.info("Start server listening on %s:%s" % (args.ip, args.port)) + LOG.info("Start server listening on {}:{}".format(args.ip, args.port)) with socketserver.TCPServer((ip, port), MyAPRSTCPHandler) as server: server.serve_forever() diff --git a/aprsd/fuzzyclock.py b/aprsd/fuzzyclock.py index ba92596..19f105b 100644 --- a/aprsd/fuzzyclock.py +++ b/aprsd/fuzzyclock.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # @author Sinu John # sinuvian at gmail dot com diff --git a/aprsd/main.py b/aprsd/main.py index 2e58e5a..51ab8b2 100644 --- a/aprsd/main.py +++ b/aprsd/main.py @@ -1,4 +1,3 @@ -# -*- 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 @@ -22,23 +21,22 @@ # python included libs import logging +from logging import NullHandler +from logging.handlers import RotatingFileHandler import os import queue import signal import sys import threading import time -from logging import NullHandler -from logging.handlers import RotatingFileHandler - -import aprslib -import click -import click_completion -import yaml # local imports here import aprsd from aprsd import client, email, messaging, plugin, threads, utils +import aprslib +import click +import click_completion +import yaml # setup the global logger # logging.basicConfig(level=logging.DEBUG) # level=10 @@ -99,7 +97,9 @@ def main(): @main.command() @click.option( - "-i", "--case-insensitive/--no-case-insensitive", help="Case insensitive completion" + "-i", + "--case-insensitive/--no-case-insensitive", + help="Case insensitive completion", ) @click.argument( "shell", @@ -118,10 +118,14 @@ def show(shell, case_insensitive): @main.command() @click.option( - "--append/--overwrite", help="Append the completion code to the file", default=None + "--append/--overwrite", + help="Append the completion code to the file", + default=None, ) @click.option( - "-i", "--case-insensitive/--no-case-insensitive", help="Case insensitive completion" + "-i", + "--case-insensitive/--no-case-insensitive", + help="Case insensitive completion", ) @click.argument( "shell", @@ -137,16 +141,19 @@ def install(append, case_insensitive, shell, path): else {} ) shell, path = click_completion.core.install( - shell=shell, path=path, append=append, extra_env=extra_env + shell=shell, + path=path, + append=append, + extra_env=extra_env, ) - click.echo("%s completion installed in %s" % (shell, path)) + click.echo("{} completion installed in {}".format(shell, path)) def signal_handler(signal, frame): global server_vent LOG.info( - "Ctrl+C, Sending all threads exit! Can take up to 10 seconds to exit all threads" + "Ctrl+C, Sending all threads exit! Can take up to 10 seconds to exit all threads", ) threads.APRSDThreadList().stop_all() server_event.set() @@ -191,7 +198,8 @@ def sample_config(): default="DEBUG", show_default=True, type=click.Choice( - ["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"], case_sensitive=False + ["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"], + case_sensitive=False, ), show_choices=True, help="The log level to use for aprsd.log", @@ -220,7 +228,13 @@ def sample_config(): @click.argument("tocallsign") @click.argument("command", nargs=-1) def send_message( - loglevel, quiet, config_file, aprs_login, aprs_password, tocallsign, command + loglevel, + quiet, + config_file, + aprs_login, + aprs_password, + tocallsign, + command, ): """Send a message to a callsign via APRS_IS.""" global got_ack, got_response @@ -273,7 +287,9 @@ def send_message( got_response = True # Send the ack back? ack = messaging.AckMessage( - config["aprs"]["login"], fromcall, msg_id=msg_number + config["aprs"]["login"], + fromcall, + msg_id=msg_number, ) ack.send_direct() @@ -312,7 +328,8 @@ def send_message( default="DEBUG", show_default=True, type=click.Choice( - ["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"], case_sensitive=False + ["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"], + case_sensitive=False, ), show_choices=True, help="The log level to use for aprsd.log", diff --git a/aprsd/messaging.py b/aprsd/messaging.py index fd6cd4d..3f1f36d 100644 --- a/aprsd/messaging.py +++ b/aprsd/messaging.py @@ -1,14 +1,13 @@ -# -*- coding: utf-8 -*- import abc import datetime import logging +from multiprocessing import RawValue import os import pathlib import pickle import re import threading import time -from multiprocessing import RawValue from aprsd import client, threads, utils @@ -19,7 +18,7 @@ LOG = logging.getLogger("APRSD") NULL_MESSAGE = -1 -class MsgTrack(object): +class MsgTrack: """Class to keep track of outstanding text messages. This is a thread safe class that keeps track of active @@ -47,7 +46,7 @@ class MsgTrack(object): def __new__(cls, *args, **kwargs): if cls._instance is None: - cls._instance = super(MsgTrack, cls).__new__(cls) + cls._instance = super().__new__(cls) cls._instance.track = {} cls._instance.lock = threading.Lock() return cls._instance @@ -129,7 +128,7 @@ class MsgTrack(object): self.track = {} -class MessageCounter(object): +class MessageCounter: """ Global message id counter class. @@ -147,7 +146,7 @@ class MessageCounter(object): def __new__(cls, *args, **kwargs): """Make this a singleton class.""" if cls._instance is None: - cls._instance = super(MessageCounter, cls).__new__(cls) + cls._instance = super().__new__(cls) cls._instance.val = RawValue("i", 1) cls._instance.lock = threading.Lock() return cls._instance @@ -173,7 +172,7 @@ class MessageCounter(object): return str(self.val.value) -class Message(object, metaclass=abc.ABCMeta): +class Message(metaclass=abc.ABCMeta): """Base Message Class.""" # The message id to send over the air @@ -204,7 +203,7 @@ class TextMessage(Message): message = None def __init__(self, fromcall, tocall, message, msg_id=None, allow_delay=True): - super(TextMessage, self).__init__(fromcall, tocall, msg_id) + super().__init__(fromcall, tocall, msg_id) self.message = message # do we try and save this message for later if we don't get # an ack? Some messages we don't want to do this ever. @@ -213,7 +212,10 @@ class TextMessage(Message): def __repr__(self): """Build raw string to send over the air.""" return "{}>APRS::{}:{}{{{}\n".format( - self.fromcall, self.tocall.ljust(9), self._filter_for_send(), str(self.id) + self.fromcall, + self.tocall.ljust(9), + self._filter_for_send(), + str(self.id), ) def __str__(self): @@ -222,7 +224,11 @@ class TextMessage(Message): now = datetime.datetime.now() delta = now - self.last_send_time return "{}>{} Msg({})({}): '{}'".format( - self.fromcall, self.tocall, self.id, delta, self.message + self.fromcall, + self.tocall, + self.id, + delta, + self.message, ) def _filter_for_send(self): @@ -259,9 +265,7 @@ class SendMessageThread(threads.APRSDThread): def __init__(self, message): self.msg = message name = self.msg.message[:5] - super(SendMessageThread, self).__init__( - "SendMessage-{}-{}".format(self.msg.id, name) - ) + super().__init__("SendMessage-{}-{}".format(self.msg.id, name)) def loop(self): """Loop until a message is acked or it gets delayed. @@ -326,11 +330,13 @@ class AckMessage(Message): """Class for building Acks and sending them.""" def __init__(self, fromcall, tocall, msg_id): - super(AckMessage, self).__init__(fromcall, tocall, msg_id=msg_id) + super().__init__(fromcall, tocall, msg_id=msg_id) def __repr__(self): return "{}>APRS::{}:ack{}\n".format( - self.fromcall, self.tocall.ljust(9), self.id + self.fromcall, + self.tocall.ljust(9), + self.id, ) def __str__(self): @@ -378,7 +384,7 @@ class AckMessage(Message): class SendAckThread(threads.APRSDThread): def __init__(self, ack): self.ack = ack - super(SendAckThread, self).__init__("SendAck-{}".format(self.ack.id)) + super().__init__("SendAck-{}".format(self.ack.id)) def loop(self): """Separate thread to send acks with retries.""" diff --git a/aprsd/plugin.py b/aprsd/plugin.py index aab9ac3..eae9569 100644 --- a/aprsd/plugin.py +++ b/aprsd/plugin.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # The base plugin class import abc import fnmatch @@ -12,14 +11,12 @@ import shutil import subprocess import time -import pluggy -import requests -import six -from thesmuggler import smuggle - import aprsd from aprsd import email, messaging from aprsd.fuzzyclock import fuzzy +import pluggy +import requests +from thesmuggler import smuggle # setup the global logger LOG = logging.getLogger("APRSD") @@ -39,7 +36,7 @@ CORE_PLUGINS = [ ] -class PluginManager(object): +class PluginManager: # The singleton instance object for this class _instance = None @@ -52,7 +49,7 @@ class PluginManager(object): def __new__(cls, *args, **kwargs): """This magic turns this into a singleton.""" if cls._instance is None: - cls._instance = super(PluginManager, cls).__new__(cls) + cls._instance = super().__new__(cls) # Put any initialization here. return cls._instance @@ -79,7 +76,7 @@ class PluginManager(object): for mem_name, obj in inspect.getmembers(module): if inspect.isclass(obj) and self.is_plugin(obj): self.obj_list.append( - {"name": mem_name, "obj": obj(self.config)} + {"name": mem_name, "obj": obj(self.config)}, ) return self.obj_list @@ -108,14 +105,16 @@ class PluginManager(object): return assert hasattr(module, class_name), "class {} is not in {}".format( - class_name, module_name + class_name, + module_name, ) # click.echo('reading class {} from module {}'.format( # class_name, module_name)) cls = getattr(module, class_name) if super_cls is not None: assert issubclass(cls, super_cls), "class {} should inherit from {}".format( - class_name, super_cls.__name__ + class_name, + super_cls.__name__, ) # click.echo('initialising {} with params {}'.format(class_name, kwargs)) obj = cls(**kwargs) @@ -131,13 +130,17 @@ class PluginManager(object): plugin_obj = None try: plugin_obj = self._create_class( - plugin_name, APRSDPluginBase, config=self.config + plugin_name, + APRSDPluginBase, + config=self.config, ) if plugin_obj: LOG.info( "Registering Command plugin '{}'({}) '{}'".format( - plugin_name, plugin_obj.version, plugin_obj.command_regex - ) + plugin_name, + plugin_obj.version, + plugin_obj.command_regex, + ), ) self._pluggy_pm.register(plugin_obj) except Exception as ex: @@ -173,8 +176,10 @@ class PluginManager(object): if plugin_obj: LOG.info( "Registering Command plugin '{}'({}) '{}'".format( - o["name"], o["obj"].version, o["obj"].command_regex - ) + o["name"], + o["obj"].version, + o["obj"].command_regex, + ), ) self._pluggy_pm.register(o["obj"]) @@ -203,8 +208,7 @@ class APRSDCommandSpec: pass -@six.add_metaclass(abc.ABCMeta) -class APRSDPluginBase(object): +class APRSDPluginBase(metaclass=abc.ABCMeta): def __init__(self, config): """The aprsd config object is stored.""" self.config = config @@ -257,7 +261,8 @@ class FortunePlugin(APRSDPluginBase): try: process = subprocess.Popen( - [fortune_path, "-s", "-n 60"], stdout=subprocess.PIPE + [fortune_path, "-s", "-n 60"], + stdout=subprocess.PIPE, ) reply = process.communicate()[0] reply = reply.decode(errors="ignore").rstrip() @@ -406,7 +411,10 @@ class TimePlugin(APRSDPluginBase): m = stm.tm_min cur_time = fuzzy(h, m, 1) reply = "{} ({}:{} PDT) ({})".format( - cur_time, str(h), str(m).rjust(2, "0"), message.rstrip() + cur_time, + str(h), + str(m).rjust(2, "0"), + message.rstrip(), ) return reply @@ -497,7 +505,7 @@ class EmailPlugin(APRSDPluginBase): # send recipient link to aprs.fi map if content == "mapme": content = "Click for my location: http://aprs.fi/{}".format( - self.config["ham"]["callsign"] + self.config["ham"]["callsign"], ) too_soon = 0 now = time.time() @@ -521,7 +529,7 @@ class EmailPlugin(APRSDPluginBase): LOG.debug( "DEBUG: email_sent_dict is big (" + str(len(self.email_sent_dict)) - + ") clearing out." + + ") clearing out.", ) self.email_sent_dict.clear() self.email_sent_dict[ack] = now @@ -529,7 +537,7 @@ class EmailPlugin(APRSDPluginBase): LOG.info( "Email for message number " + ack - + " recently sent, not sending again." + + " recently sent, not sending again.", ) else: reply = "Bad email address" diff --git a/aprsd/threads.py b/aprsd/threads.py index 31fca98..4c61779 100644 --- a/aprsd/threads.py +++ b/aprsd/threads.py @@ -1,13 +1,11 @@ -# -*- coding: utf-8 -*- import abc import logging import queue import threading import time -import aprslib - from aprsd import client, messaging, plugin +import aprslib LOG = logging.getLogger("APRSD") @@ -16,7 +14,7 @@ TX_THREAD = "TX" EMAIL_THREAD = "Email" -class APRSDThreadList(object): +class APRSDThreadList: """Singleton class that keeps track of application wide threads.""" _instance = None @@ -26,7 +24,7 @@ class APRSDThreadList(object): def __new__(cls, *args, **kwargs): if cls._instance is None: - cls._instance = super(APRSDThreadList, cls).__new__(cls) + cls._instance = super().__new__(cls) cls.lock = threading.Lock() cls.threads_list = [] return cls._instance @@ -48,7 +46,7 @@ class APRSDThreadList(object): class APRSDThread(threading.Thread, metaclass=abc.ABCMeta): def __init__(self, name): - super(APRSDThread, self).__init__(name=name) + super().__init__(name=name) self.thread_stop = False APRSDThreadList().add(self) @@ -67,7 +65,7 @@ class APRSDThread(threading.Thread, metaclass=abc.ABCMeta): class APRSDRXThread(APRSDThread): def __init__(self, msg_queues, config): - super(APRSDRXThread, self).__init__("RX_MSG") + super().__init__("RX_MSG") self.msg_queues = msg_queues self.config = config @@ -112,7 +110,11 @@ class APRSDRXThread(APRSDThread): ack_num = packet.get("msgNo") LOG.info("Got ack for message {}".format(ack_num)) messaging.log_message( - "ACK", packet["raw"], None, ack=ack_num, fromcall=packet["from"] + "ACK", + packet["raw"], + None, + ack=ack_num, + fromcall=packet["from"], ) tracker = messaging.MsgTrack() tracker.remove(ack_num) @@ -153,7 +155,9 @@ class APRSDRXThread(APRSDThread): LOG.debug("Sending '{}'".format(reply)) msg = messaging.TextMessage( - self.config["aprs"]["login"], fromcall, reply + self.config["aprs"]["login"], + fromcall, + reply, ) self.msg_queues["tx"].put(msg) else: @@ -166,7 +170,9 @@ class APRSDRXThread(APRSDThread): reply = "Usage: {}".format(", ".join(names)) msg = messaging.TextMessage( - self.config["aprs"]["login"], fromcall, reply + self.config["aprs"]["login"], + fromcall, + reply, ) self.msg_queues["tx"].put(msg) except Exception as ex: @@ -178,7 +184,9 @@ class APRSDRXThread(APRSDThread): # let any threads do their thing, then ack # send an ack last ack = messaging.AckMessage( - self.config["aprs"]["login"], fromcall, msg_id=msg_id + self.config["aprs"]["login"], + fromcall, + msg_id=msg_id, ) self.msg_queues["tx"].put(ack) LOG.debug("Packet processing complete") @@ -213,7 +221,7 @@ class APRSDRXThread(APRSDThread): class APRSDTXThread(APRSDThread): def __init__(self, msg_queues, config): - super(APRSDTXThread, self).__init__("TX_MSG") + super().__init__("TX_MSG") self.msg_queues = msg_queues self.config = config diff --git a/aprsd/utils.py b/aprsd/utils.py index 4257ac9..d8bf573 100644 --- a/aprsd/utils.py +++ b/aprsd/utils.py @@ -1,17 +1,15 @@ -# -*- coding: utf-8 -*- """Utilities and helper functions.""" import errno import functools import os +from pathlib import Path import sys import threading -from pathlib import Path - -import click -import yaml from aprsd import plugin +import click +import yaml # an example of what should be in the ~/.aprsd/config.yml DEFAULT_CONFIG_DICT = { @@ -103,13 +101,13 @@ def get_config(config_file): """This tries to read the yaml config from .""" config_file_expanded = os.path.expanduser(config_file) if os.path.exists(config_file_expanded): - with open(config_file_expanded, "r") as stream: + with open(config_file_expanded) as stream: config = yaml.load(stream, Loader=yaml.FullLoader) return config else: if config_file == DEFAULT_CONFIG_FILE: click.echo( - "{} is missing, creating config file".format(config_file_expanded) + "{} is missing, creating config file".format(config_file_expanded), ) create_default_config() msg = ( @@ -144,7 +142,10 @@ def parse_config(config_file): if name and name not in config[section]: if not default: fail( - "'%s' was not in '%s' section of config file" % (name, section) + "'{}' was not in '{}' section of config file".format( + name, + section, + ), ) else: config[section][name] = default @@ -166,7 +167,10 @@ def parse_config(config_file): # special check here to make sure user has edited the config file # and changed the ham callsign check_option( - config, "ham", "callsign", default_fail=DEFAULT_CONFIG_DICT["ham"]["callsign"] + config, + "ham", + "callsign", + default_fail=DEFAULT_CONFIG_DICT["ham"]["callsign"], ) check_option(config, "aprs", "login") check_option(config, "aprs", "password") diff --git a/examples/plugins/example_plugin.py b/examples/plugins/example_plugin.py index e01281e..c4eb1d0 100644 --- a/examples/plugins/example_plugin.py +++ b/examples/plugins/example_plugin.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import logging from aprsd import plugin diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..09592fb --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,36 @@ +[build-system] +requires = ["setuptools>=46.0", "wheel"] +build-backend = "setuptools.build_meta" + +[tool.black] +# Use the more relaxed max line length permitted in PEP8. +line-length = 88 +target-version = ["py36", "py37", "py38"] +# black will automatically exclude all files listed in .gitignore +include = '\.pyi?$' +exclude = ''' +/( + \.git + | \.hg + | \.mypy_cache + | \.tox + | \.venv + | _build + | buck-out + | build + | dist +)/ +''' + +[tool.isort] +profile = "black" +line_length = 88 +force_sort_within_sections = true +# Inform isort of paths to import names that should be considered part of the "First Party" group. +src_paths = ["src/openstack_loadtest"] +skip_gitignore = true +# If you need to skip/exclude folders, consider using skip_glob as that will allow the +# isort defaults for skip to remain without the need to duplicate them. + +[tool.coverage.run] +branch = true diff --git a/setup.cfg b/setup.cfg index ac24c17..125cd1f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,15 +1,16 @@ [metadata] name = aprsd -summary = Amateur radio APRS daemon which listens for messages and responds -description-file = - README.rst -long-description-content-type = text/x-rst; charset=UTF-8 +long_description = file: README.rst +long_description_content_type = text/x-rst author = Craig Lamparter -author-email = something@somewhere.com +author_email = something@somewhere.com classifier = Topic :: Communications :: Ham Radio Operating System :: POSIX :: Linux Programming Language :: Python +description_file = + README.rst +summary = Amateur radio APRS daemon which listens for messages and responds [global] setup-hooks = diff --git a/setup.py b/setup.py index fda5a1c..90623e2 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (c) 2013 Hewlett-Packard Development Company, L.P. # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/tests/test_main.py b/tests/test_main.py index 1aecc1c..7996fcd 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import sys import unittest @@ -7,7 +6,7 @@ from aprsd import email if sys.version_info >= (3, 2): from unittest import mock else: - import mock + from unittest import mock class TestMain(unittest.TestCase): diff --git a/tests/test_plugin.py b/tests/test_plugin.py index c2f860d..8512f34 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import unittest from unittest import mock @@ -57,7 +56,10 @@ class TestPlugin(unittest.TestCase): message = "time" expected = "{} ({}:{} PDT) ({})".format( - cur_time, str(h), str(m).rjust(2, "0"), message.rstrip() + cur_time, + str(h), + str(m).rjust(2, "0"), + message.rstrip(), ) actual = time_plugin.run(fromcall, message, ack) self.assertEqual(expected, actual)