mirror of
				https://github.com/craigerl/aprsd.git
				synced 2025-10-31 12:50:30 -04:00 
			
		
		
		
	Lots of fixes
This commit is contained in:
		
							parent
							
								
									4c0150dd97
								
							
						
					
					
						commit
						231c15b1af
					
				| @ -1,29 +1,48 @@ | ||||
| repos: | ||||
| - repo: https://github.com/pre-commit/pre-commit-hooks | ||||
|     rev: v3.2.0 | ||||
|   rev: v3.4.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 | ||||
|     - id: double-quote-string-fixer | ||||
| 
 | ||||
| - repo: https://github.com/asottile/setup-cfg-fmt | ||||
|   rev: v1.16.0 | ||||
|   hooks: | ||||
|     -   id: black | ||||
|     - id: setup-cfg-fmt | ||||
| 
 | ||||
| - 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.1 | ||||
|   rev: 3.8.4 | ||||
|   hooks: | ||||
|     - id: flake8 | ||||
|       additional_dependencies: [flake8-bugbear] | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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: | ||||
|  | ||||
| @ -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) | ||||
|  | ||||
| @ -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() | ||||
|  | ||||
| @ -1,4 +1,3 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| # | ||||
| # @author Sinu John | ||||
| #         sinuvian at gmail dot com | ||||
|  | ||||
| @ -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", | ||||
|  | ||||
| @ -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.""" | ||||
|  | ||||
| @ -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" | ||||
|  | ||||
| @ -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 | ||||
| 
 | ||||
|  | ||||
| @ -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>.""" | ||||
|     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") | ||||
|  | ||||
| @ -1,4 +1,3 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| import logging | ||||
| 
 | ||||
| from aprsd import plugin | ||||
|  | ||||
							
								
								
									
										36
									
								
								pyproject.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								pyproject.toml
									
									
									
									
									
										Normal file
									
								
							| @ -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 | ||||
							
								
								
									
										11
									
								
								setup.cfg
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								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 = | ||||
|  | ||||
							
								
								
									
										1
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								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"); | ||||
|  | ||||
| @ -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): | ||||
|  | ||||
| @ -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) | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user