aprsd 4.2.0__py3-none-any.whl → 4.2.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aprsd/client/client.py +49 -21
- aprsd/client/drivers/aprsis.py +10 -6
- aprsd/client/drivers/fake.py +2 -1
- aprsd/client/drivers/registry.py +1 -0
- aprsd/client/drivers/tcpkiss.py +64 -39
- aprsd/cmds/dev.py +6 -1
- aprsd/cmds/fetch_stats.py +2 -0
- aprsd/cmds/list_plugins.py +6 -133
- aprsd/cmds/listen.py +2 -0
- aprsd/cmds/send_message.py +4 -1
- aprsd/cmds/server.py +3 -1
- aprsd/conf/common.py +7 -1
- aprsd/exception.py +7 -0
- aprsd/main.py +1 -0
- aprsd/stats/collector.py +6 -0
- aprsd/threads/rx.py +1 -1
- aprsd/utils/package.py +176 -0
- aprsd/utils/trace.py +21 -20
- {aprsd-4.2.0.dist-info → aprsd-4.2.3.dist-info}/METADATA +1 -1
- {aprsd-4.2.0.dist-info → aprsd-4.2.3.dist-info}/RECORD +25 -24
- {aprsd-4.2.0.dist-info → aprsd-4.2.3.dist-info}/WHEEL +0 -0
- {aprsd-4.2.0.dist-info → aprsd-4.2.3.dist-info}/entry_points.txt +0 -0
- {aprsd-4.2.0.dist-info → aprsd-4.2.3.dist-info}/licenses/AUTHORS +0 -0
- {aprsd-4.2.0.dist-info → aprsd-4.2.3.dist-info}/licenses/LICENSE +0 -0
- {aprsd-4.2.0.dist-info → aprsd-4.2.3.dist-info}/top_level.txt +0 -0
aprsd/client/client.py
CHANGED
@@ -10,14 +10,14 @@ from oslo_config import cfg
|
|
10
10
|
from aprsd.client import drivers # noqa - ensure drivers are registered
|
11
11
|
from aprsd.client.drivers.registry import DriverRegistry
|
12
12
|
from aprsd.packets import core
|
13
|
-
from aprsd.utils import keepalive_collector
|
13
|
+
from aprsd.utils import keepalive_collector, trace
|
14
14
|
|
15
15
|
CONF = cfg.CONF
|
16
16
|
LOG = logging.getLogger('APRSD')
|
17
17
|
LOGU = logger
|
18
18
|
|
19
19
|
|
20
|
-
class APRSDClient:
|
20
|
+
class APRSDClient(metaclass=trace.TraceWrapperMetaclass):
|
21
21
|
"""APRSD client class.
|
22
22
|
|
23
23
|
This is a singleton class that provides a single instance of the APRSD client.
|
@@ -38,15 +38,18 @@ class APRSDClient:
|
|
38
38
|
keepalive_collector.KeepAliveCollector().register(cls)
|
39
39
|
return cls._instance
|
40
40
|
|
41
|
-
def __init__(self):
|
41
|
+
def __init__(self, auto_connect: bool = True):
|
42
|
+
self.auto_connect = auto_connect
|
42
43
|
self.connected = False
|
44
|
+
self.running = False
|
43
45
|
self.login_status = {
|
44
46
|
'success': False,
|
45
47
|
'message': None,
|
46
48
|
}
|
47
49
|
if not self.driver:
|
48
50
|
self.driver = DriverRegistry().get_driver()
|
49
|
-
|
51
|
+
if self.auto_connect:
|
52
|
+
self.connect()
|
50
53
|
|
51
54
|
def stats(self, serializable=False) -> dict:
|
52
55
|
stats = {}
|
@@ -54,17 +57,20 @@ class APRSDClient:
|
|
54
57
|
stats = self.driver.stats(serializable=serializable)
|
55
58
|
return stats
|
56
59
|
|
57
|
-
@
|
58
|
-
def is_enabled(
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
60
|
+
@staticmethod
|
61
|
+
def is_enabled():
|
62
|
+
for driver in DriverRegistry().drivers:
|
63
|
+
if driver.is_enabled():
|
64
|
+
return True
|
65
|
+
return False
|
66
|
+
|
67
|
+
@staticmethod
|
68
|
+
def is_configured():
|
69
|
+
"""Check if ANY driver is configured."""
|
70
|
+
for driver in DriverRegistry().drivers:
|
71
|
+
if driver.is_configured():
|
72
|
+
return True
|
73
|
+
return False
|
68
74
|
|
69
75
|
# @property
|
70
76
|
# def is_connected(self):
|
@@ -96,9 +102,20 @@ class APRSDClient:
|
|
96
102
|
return self.driver.filter
|
97
103
|
|
98
104
|
def is_alive(self):
|
99
|
-
return self.driver.is_alive
|
105
|
+
return self.driver.is_alive
|
106
|
+
|
107
|
+
@wrapt.synchronized(lock)
|
108
|
+
def connect(self):
|
109
|
+
if not self.driver:
|
110
|
+
self.driver = DriverRegistry().get_driver()
|
111
|
+
if not self.connected:
|
112
|
+
self.driver.setup_connection()
|
113
|
+
self.connected = self.driver.is_alive
|
114
|
+
self.running = True
|
100
115
|
|
101
116
|
def close(self):
|
117
|
+
self.running = False
|
118
|
+
self.connected = False
|
102
119
|
if not self.driver:
|
103
120
|
return
|
104
121
|
self.driver.close()
|
@@ -106,17 +123,22 @@ class APRSDClient:
|
|
106
123
|
@wrapt.synchronized(lock)
|
107
124
|
def reset(self):
|
108
125
|
"""Call this to force a rebuild/reconnect."""
|
109
|
-
LOG.
|
126
|
+
LOG.warning('Resetting client connection.')
|
110
127
|
if self.driver:
|
111
128
|
self.driver.close()
|
112
|
-
self.
|
129
|
+
if self.auto_connect:
|
130
|
+
self.driver.setup_connection()
|
113
131
|
if self.filter:
|
114
132
|
self.driver.set_filter(self.filter)
|
115
133
|
else:
|
116
134
|
LOG.warning('Client not initialized, nothing to reset.')
|
117
135
|
|
118
136
|
def send(self, packet: core.Packet) -> bool:
|
119
|
-
|
137
|
+
if self.running:
|
138
|
+
return self.driver.send(packet)
|
139
|
+
else:
|
140
|
+
LOG.error('Client not running, not sending packet.')
|
141
|
+
return False
|
120
142
|
|
121
143
|
# For the keepalive collector
|
122
144
|
def keepalive_check(self):
|
@@ -135,7 +157,13 @@ class APRSDClient:
|
|
135
157
|
LOGU.opt(colors=True).info(f'<green>Client keepalive {keepalive}</green>')
|
136
158
|
|
137
159
|
def consumer(self, callback: Callable, raw: bool = False):
|
138
|
-
|
160
|
+
if self.running:
|
161
|
+
return self.driver.consumer(callback=callback, raw=raw)
|
139
162
|
|
140
163
|
def decode_packet(self, *args, **kwargs) -> core.Packet:
|
141
|
-
|
164
|
+
try:
|
165
|
+
packet = self.driver.decode_packet(*args, **kwargs)
|
166
|
+
except Exception as e:
|
167
|
+
LOG.error(f'Error decoding packet: {e}')
|
168
|
+
return None
|
169
|
+
return packet
|
aprsd/client/drivers/aprsis.py
CHANGED
@@ -10,6 +10,7 @@ from oslo_config import cfg
|
|
10
10
|
from aprsd import client, exception
|
11
11
|
from aprsd.client.drivers.lib.aprslib import APRSLibClient
|
12
12
|
from aprsd.packets import core
|
13
|
+
from aprsd.utils import singleton
|
13
14
|
|
14
15
|
CONF = cfg.CONF
|
15
16
|
LOG = logging.getLogger('APRSD')
|
@@ -17,6 +18,7 @@ LOGU = logger
|
|
17
18
|
|
18
19
|
|
19
20
|
# class APRSISDriver(metaclass=trace.TraceWrapperMetaclass):
|
21
|
+
@singleton
|
20
22
|
class APRSISDriver:
|
21
23
|
"""This is the APRS-IS driver for the APRSD client.
|
22
24
|
|
@@ -26,6 +28,7 @@ class APRSISDriver:
|
|
26
28
|
|
27
29
|
_client = None
|
28
30
|
_checks = False
|
31
|
+
connected = False
|
29
32
|
|
30
33
|
def __init__(self):
|
31
34
|
max_timeout = {'hours': 0.0, 'minutes': 2, 'seconds': 0}
|
@@ -77,16 +80,18 @@ class APRSISDriver:
|
|
77
80
|
if self._client:
|
78
81
|
self._client.stop()
|
79
82
|
self._client.close()
|
83
|
+
self.connected = False
|
80
84
|
|
81
85
|
def send(self, packet: core.Packet) -> bool:
|
82
86
|
return self._client.send(packet)
|
83
87
|
|
84
88
|
def setup_connection(self):
|
89
|
+
if self.connected:
|
90
|
+
return
|
85
91
|
user = CONF.aprs_network.login
|
86
92
|
password = CONF.aprs_network.password
|
87
93
|
host = CONF.aprs_network.host
|
88
94
|
port = CONF.aprs_network.port
|
89
|
-
self.connected = False
|
90
95
|
backoff = 1
|
91
96
|
retries = 3
|
92
97
|
retry_count = 0
|
@@ -96,7 +101,7 @@ class APRSISDriver:
|
|
96
101
|
break
|
97
102
|
try:
|
98
103
|
LOG.info(
|
99
|
-
f'Creating aprslib client({host}:{port}) and logging in {user}.'
|
104
|
+
f'Creating aprslib client({host}:{port}) and logging in {user}. try #{retry_count}'
|
100
105
|
)
|
101
106
|
self._client = APRSLibClient(
|
102
107
|
user, passwd=password, host=host, port=port
|
@@ -151,7 +156,7 @@ class APRSISDriver:
|
|
151
156
|
def _is_stale_connection(self):
|
152
157
|
delta = datetime.datetime.now() - self._client.aprsd_keepalive
|
153
158
|
if delta > self.max_delta:
|
154
|
-
LOG.
|
159
|
+
LOG.warning(f'Connection is stale, last heard {delta} ago.')
|
155
160
|
return True
|
156
161
|
return False
|
157
162
|
|
@@ -164,7 +169,7 @@ class APRSISDriver:
|
|
164
169
|
return core.factory(args[0])
|
165
170
|
|
166
171
|
def consumer(self, callback: Callable, raw: bool = False):
|
167
|
-
if self._client:
|
172
|
+
if self._client and self.connected:
|
168
173
|
try:
|
169
174
|
self._client.consumer(
|
170
175
|
callback,
|
@@ -177,10 +182,9 @@ class APRSISDriver:
|
|
177
182
|
LOG.info(e.__cause__)
|
178
183
|
raise e
|
179
184
|
else:
|
180
|
-
LOG.warning('client is None, might be resetting.')
|
181
185
|
self.connected = False
|
182
186
|
|
183
|
-
def stats(self, serializable=False) -> dict:
|
187
|
+
def stats(self, serializable: bool = False) -> dict:
|
184
188
|
stats = {}
|
185
189
|
if self.is_configured():
|
186
190
|
if self._client:
|
aprsd/client/drivers/fake.py
CHANGED
@@ -64,7 +64,7 @@ class APRSDFakeDriver(metaclass=trace.TraceWrapperMetaclass):
|
|
64
64
|
return None
|
65
65
|
|
66
66
|
@wrapt.synchronized(lock)
|
67
|
-
def send(self, packet: core.Packet):
|
67
|
+
def send(self, packet: core.Packet) -> bool:
|
68
68
|
"""Send an APRS Message object."""
|
69
69
|
LOG.info(f'Sending packet: {packet}')
|
70
70
|
payload = None
|
@@ -84,6 +84,7 @@ class APRSDFakeDriver(metaclass=trace.TraceWrapperMetaclass):
|
|
84
84
|
f"FAKE::Send '{payload}' TO '{packet.to_call}' From "
|
85
85
|
f'\'{packet.from_call}\' with PATH "{self.path}"',
|
86
86
|
)
|
87
|
+
return True
|
87
88
|
|
88
89
|
def consumer(self, callback: Callable, raw: bool = False):
|
89
90
|
LOG.debug('Start non blocking FAKE consumer')
|
aprsd/client/drivers/registry.py
CHANGED
aprsd/client/drivers/tcpkiss.py
CHANGED
@@ -9,7 +9,6 @@ import datetime
|
|
9
9
|
import logging
|
10
10
|
import select
|
11
11
|
import socket
|
12
|
-
import time
|
13
12
|
from typing import Any, Callable, Dict
|
14
13
|
|
15
14
|
import aprslib
|
@@ -25,6 +24,7 @@ from aprsd import ( # noqa
|
|
25
24
|
exception,
|
26
25
|
)
|
27
26
|
from aprsd.packets import core
|
27
|
+
from aprsd.utils import trace
|
28
28
|
|
29
29
|
CONF = cfg.CONF
|
30
30
|
LOG = logging.getLogger('APRSD')
|
@@ -46,10 +46,12 @@ def handle_fend(buffer: bytes, strip_df_start: bool = True) -> bytes:
|
|
46
46
|
return bytes(frame)
|
47
47
|
|
48
48
|
|
49
|
-
|
50
|
-
class TCPKISSDriver:
|
49
|
+
class TCPKISSDriver(metaclass=trace.TraceWrapperMetaclass):
|
50
|
+
# class TCPKISSDriver:
|
51
51
|
"""APRSD client driver for TCP KISS connections."""
|
52
52
|
|
53
|
+
_instance = None
|
54
|
+
|
53
55
|
# Class level attributes required by Client protocol
|
54
56
|
packets_received = 0
|
55
57
|
packets_sent = 0
|
@@ -62,6 +64,12 @@ class TCPKISSDriver:
|
|
62
64
|
select_timeout = 1
|
63
65
|
path = None
|
64
66
|
|
67
|
+
def __new__(cls, *args, **kwargs):
|
68
|
+
"""This magic turns this into a singleton."""
|
69
|
+
if cls._instance is None:
|
70
|
+
cls._instance = super().__new__(cls)
|
71
|
+
return cls._instance
|
72
|
+
|
65
73
|
def __init__(self):
|
66
74
|
"""Initialize the KISS client.
|
67
75
|
|
@@ -71,7 +79,6 @@ class TCPKISSDriver:
|
|
71
79
|
super().__init__()
|
72
80
|
self._connected = False
|
73
81
|
self.keepalive = datetime.datetime.now()
|
74
|
-
self._running = False
|
75
82
|
# This is initialized in setup_connection()
|
76
83
|
self.socket = None
|
77
84
|
|
@@ -79,8 +86,8 @@ class TCPKISSDriver:
|
|
79
86
|
def transport(self) -> str:
|
80
87
|
return client.TRANSPORT_TCPKISS
|
81
88
|
|
82
|
-
@
|
83
|
-
def is_enabled(
|
89
|
+
@staticmethod
|
90
|
+
def is_enabled() -> bool:
|
84
91
|
"""Check if KISS is enabled in configuration.
|
85
92
|
|
86
93
|
Returns:
|
@@ -111,7 +118,15 @@ class TCPKISSDriver:
|
|
111
118
|
|
112
119
|
def close(self):
|
113
120
|
"""Close the connection."""
|
114
|
-
self.
|
121
|
+
self._connected = False
|
122
|
+
if self.socket:
|
123
|
+
try:
|
124
|
+
self.socket.close()
|
125
|
+
except Exception as e:
|
126
|
+
LOG.error(f'close: error closing socket: {e}')
|
127
|
+
pass
|
128
|
+
else:
|
129
|
+
LOG.warning('close: socket not initialized. no reason to close.')
|
115
130
|
|
116
131
|
def send(self, packet: core.Packet):
|
117
132
|
"""Send an APRS packet.
|
@@ -163,6 +178,10 @@ class TCPKISSDriver:
|
|
163
178
|
LOG.error('KISS is not enabled in configuration')
|
164
179
|
return
|
165
180
|
|
181
|
+
if self._connected:
|
182
|
+
LOG.warning('KISS interface already connected')
|
183
|
+
return
|
184
|
+
|
166
185
|
try:
|
167
186
|
# Configure for TCP KISS
|
168
187
|
if self.is_enabled():
|
@@ -217,22 +236,18 @@ class TCPKISSDriver:
|
|
217
236
|
Raises:
|
218
237
|
Exception: If not connected to KISS TNC
|
219
238
|
"""
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
if not self._connected:
|
224
|
-
if not self.connect():
|
225
|
-
time.sleep(1)
|
226
|
-
continue
|
239
|
+
# Ensure connection
|
240
|
+
if not self._connected:
|
241
|
+
return
|
227
242
|
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
243
|
+
# Read frame
|
244
|
+
frame = self.read_frame()
|
245
|
+
if frame:
|
246
|
+
LOG.info(f'GOT FRAME: {frame} calling {callback}')
|
247
|
+
kwargs = {
|
248
|
+
'frame': frame,
|
249
|
+
}
|
250
|
+
callback(**kwargs)
|
236
251
|
|
237
252
|
def decode_packet(self, *args, **kwargs) -> core.Packet:
|
238
253
|
"""Decode a packet from an AX.25 frame.
|
@@ -245,24 +260,17 @@ class TCPKISSDriver:
|
|
245
260
|
LOG.warning('No frame received to decode?!?!')
|
246
261
|
return None
|
247
262
|
|
248
|
-
LOG.warning(f'FRAME: {str(frame)}')
|
249
263
|
try:
|
250
264
|
aprslib_frame = aprslib.parse(str(frame))
|
251
|
-
|
265
|
+
packet = core.factory(aprslib_frame)
|
266
|
+
if isinstance(packet, core.ThirdPartyPacket):
|
267
|
+
return packet.subpacket
|
268
|
+
else:
|
269
|
+
return packet
|
252
270
|
except Exception as e:
|
253
271
|
LOG.error(f'Error decoding packet: {e}')
|
254
272
|
return None
|
255
273
|
|
256
|
-
def stop(self):
|
257
|
-
"""Stop the KISS interface."""
|
258
|
-
self._running = False
|
259
|
-
self._connected = False
|
260
|
-
if self.socket:
|
261
|
-
try:
|
262
|
-
self.socket.close()
|
263
|
-
except Exception:
|
264
|
-
pass
|
265
|
-
|
266
274
|
def stats(self, serializable: bool = False) -> Dict[str, Any]:
|
267
275
|
"""Get client statistics.
|
268
276
|
|
@@ -271,8 +279,19 @@ class TCPKISSDriver:
|
|
271
279
|
"""
|
272
280
|
if serializable:
|
273
281
|
keepalive = self.keepalive.isoformat()
|
282
|
+
if self.last_packet_sent:
|
283
|
+
last_packet_sent = self.last_packet_sent.isoformat()
|
284
|
+
else:
|
285
|
+
last_packet_sent = 'None'
|
286
|
+
if self.last_packet_received:
|
287
|
+
last_packet_received = self.last_packet_received.isoformat()
|
288
|
+
else:
|
289
|
+
last_packet_received = 'None'
|
274
290
|
else:
|
275
291
|
keepalive = self.keepalive
|
292
|
+
last_packet_sent = self.last_packet_sent
|
293
|
+
last_packet_received = self.last_packet_received
|
294
|
+
|
276
295
|
stats = {
|
277
296
|
'client': self.__class__.__name__,
|
278
297
|
'transport': self.transport,
|
@@ -280,8 +299,8 @@ class TCPKISSDriver:
|
|
280
299
|
'path': self.path,
|
281
300
|
'packets_sent': self.packets_sent,
|
282
301
|
'packets_received': self.packets_received,
|
283
|
-
'last_packet_sent':
|
284
|
-
'last_packet_received':
|
302
|
+
'last_packet_sent': last_packet_sent,
|
303
|
+
'last_packet_received': last_packet_received,
|
285
304
|
'connection_keepalive': keepalive,
|
286
305
|
'host': CONF.kiss_tcp.host,
|
287
306
|
'port': CONF.kiss_tcp.port,
|
@@ -338,13 +357,19 @@ class TCPKISSDriver:
|
|
338
357
|
"""
|
339
358
|
Generator for complete lines, received from the server
|
340
359
|
"""
|
360
|
+
if not self.socket:
|
361
|
+
return None
|
362
|
+
|
363
|
+
if not self._connected:
|
364
|
+
return None
|
365
|
+
|
341
366
|
try:
|
342
367
|
self.socket.setblocking(0)
|
343
368
|
except OSError as e:
|
344
369
|
LOG.error(f'socket error when setblocking(0): {str(e)}')
|
345
370
|
raise aprslib.ConnectionDrop('connection dropped') from e
|
346
371
|
|
347
|
-
while self.
|
372
|
+
while self._connected:
|
348
373
|
short_buf = b''
|
349
374
|
|
350
375
|
try:
|
@@ -360,14 +385,14 @@ class TCPKISSDriver:
|
|
360
385
|
else:
|
361
386
|
continue
|
362
387
|
except Exception as e:
|
388
|
+
# No need to log if we are not running.
|
389
|
+
# this happens when the client is stopped/closed.
|
363
390
|
LOG.error(f'Error in read loop: {e}')
|
364
391
|
self._connected = False
|
365
392
|
break
|
366
393
|
|
367
394
|
try:
|
368
|
-
print('reading from socket')
|
369
395
|
short_buf = self.socket.recv(1024)
|
370
|
-
print(f'short_buf: {short_buf}')
|
371
396
|
# sock.recv returns empty if the connection drops
|
372
397
|
if not short_buf:
|
373
398
|
if not blocking:
|
aprsd/cmds/dev.py
CHANGED
@@ -4,11 +4,13 @@
|
|
4
4
|
#
|
5
5
|
# python included libs
|
6
6
|
import logging
|
7
|
+
import sys
|
7
8
|
|
8
9
|
import click
|
9
10
|
from oslo_config import cfg
|
10
11
|
|
11
|
-
|
12
|
+
import aprsd
|
13
|
+
from aprsd import cli_helper, conf, packets, plugin, utils
|
12
14
|
|
13
15
|
# local imports here
|
14
16
|
from aprsd.main import cli
|
@@ -71,6 +73,9 @@ def test_plugin(
|
|
71
73
|
):
|
72
74
|
"""Test an individual APRSD plugin given a python path."""
|
73
75
|
|
76
|
+
LOG.info(f'Python version: {sys.version}')
|
77
|
+
LOG.info(f'APRSD DEV Started version: {aprsd.__version__}')
|
78
|
+
utils.package.log_installed_extensions_and_plugins()
|
74
79
|
CONF.log_opt_values(LOG, logging.DEBUG)
|
75
80
|
|
76
81
|
if not aprs_login:
|
aprsd/cmds/fetch_stats.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# Fetch active stats from a remote running instance of aprsd admin web interface.
|
2
2
|
import logging
|
3
|
+
import sys
|
3
4
|
|
4
5
|
import click
|
5
6
|
import requests
|
@@ -38,6 +39,7 @@ CONF = cfg.CONF
|
|
38
39
|
def fetch_stats(ctx, host, port):
|
39
40
|
"""Fetch stats from a APRSD admin web interface."""
|
40
41
|
console = Console()
|
42
|
+
console.print(f'Python version: {sys.version}')
|
41
43
|
console.print(f'APRSD Fetch-Stats started version: {aprsd.__version__}')
|
42
44
|
|
43
45
|
CONF.log_opt_values(LOG, logging.DEBUG)
|
aprsd/cmds/list_plugins.py
CHANGED
@@ -1,119 +1,18 @@
|
|
1
|
-
import fnmatch
|
2
|
-
import importlib
|
3
1
|
import inspect
|
4
2
|
import logging
|
5
|
-
import os
|
6
|
-
import pkgutil
|
7
|
-
import sys
|
8
|
-
from traceback import print_tb
|
9
3
|
|
10
4
|
import click
|
11
|
-
import requests
|
12
5
|
from rich.console import Console
|
13
6
|
from rich.table import Table
|
14
7
|
from rich.text import Text
|
15
|
-
from thesmuggler import smuggle
|
16
8
|
|
17
9
|
from aprsd import cli_helper
|
18
10
|
from aprsd import plugin as aprsd_plugin
|
19
11
|
from aprsd.main import cli
|
20
12
|
from aprsd.plugins import fortune, notify, ping, time, version, weather
|
13
|
+
from aprsd.utils import package as aprsd_package
|
21
14
|
|
22
15
|
LOG = logging.getLogger('APRSD')
|
23
|
-
PYPI_URL = 'https://pypi.org/search/'
|
24
|
-
|
25
|
-
|
26
|
-
def onerror(name):
|
27
|
-
print(f'Error importing module {name}')
|
28
|
-
type, value, traceback = sys.exc_info()
|
29
|
-
print_tb(traceback)
|
30
|
-
|
31
|
-
|
32
|
-
def is_plugin(obj):
|
33
|
-
for c in inspect.getmro(obj):
|
34
|
-
if issubclass(c, aprsd_plugin.APRSDPluginBase):
|
35
|
-
return True
|
36
|
-
|
37
|
-
return False
|
38
|
-
|
39
|
-
|
40
|
-
def plugin_type(obj):
|
41
|
-
for c in inspect.getmro(obj):
|
42
|
-
if issubclass(c, aprsd_plugin.APRSDRegexCommandPluginBase):
|
43
|
-
return 'RegexCommand'
|
44
|
-
if issubclass(c, aprsd_plugin.APRSDWatchListPluginBase):
|
45
|
-
return 'WatchList'
|
46
|
-
if issubclass(c, aprsd_plugin.APRSDPluginBase):
|
47
|
-
return 'APRSDPluginBase'
|
48
|
-
|
49
|
-
return 'Unknown'
|
50
|
-
|
51
|
-
|
52
|
-
def walk_package(package):
|
53
|
-
return pkgutil.walk_packages(
|
54
|
-
package.__path__,
|
55
|
-
package.__name__ + '.',
|
56
|
-
onerror=onerror,
|
57
|
-
)
|
58
|
-
|
59
|
-
|
60
|
-
def get_module_info(package_name, module_name, module_path):
|
61
|
-
if not os.path.exists(module_path):
|
62
|
-
return None
|
63
|
-
|
64
|
-
dir_path = os.path.realpath(module_path)
|
65
|
-
pattern = '*.py'
|
66
|
-
|
67
|
-
obj_list = []
|
68
|
-
|
69
|
-
for path, _subdirs, files in os.walk(dir_path):
|
70
|
-
for name in files:
|
71
|
-
if fnmatch.fnmatch(name, pattern):
|
72
|
-
module = smuggle(f'{path}/{name}')
|
73
|
-
for mem_name, obj in inspect.getmembers(module):
|
74
|
-
if inspect.isclass(obj) and is_plugin(obj):
|
75
|
-
obj_list.append(
|
76
|
-
{
|
77
|
-
'package': package_name,
|
78
|
-
'name': mem_name,
|
79
|
-
'obj': obj,
|
80
|
-
'version': obj.version,
|
81
|
-
'path': f'{".".join([module_name, obj.__name__])}',
|
82
|
-
},
|
83
|
-
)
|
84
|
-
|
85
|
-
return obj_list
|
86
|
-
|
87
|
-
|
88
|
-
def _get_installed_aprsd_items():
|
89
|
-
# installed plugins
|
90
|
-
plugins = {}
|
91
|
-
extensions = {}
|
92
|
-
for _finder, name, ispkg in pkgutil.iter_modules():
|
93
|
-
if ispkg and name.startswith('aprsd_'):
|
94
|
-
module = importlib.import_module(name)
|
95
|
-
pkgs = walk_package(module)
|
96
|
-
for pkg in pkgs:
|
97
|
-
pkg_info = get_module_info(
|
98
|
-
module.__name__, pkg.name, module.__path__[0]
|
99
|
-
)
|
100
|
-
if 'plugin' in name:
|
101
|
-
plugins[name] = pkg_info
|
102
|
-
elif 'extension' in name:
|
103
|
-
extensions[name] = pkg_info
|
104
|
-
return plugins, extensions
|
105
|
-
|
106
|
-
|
107
|
-
def get_installed_plugins():
|
108
|
-
# installed plugins
|
109
|
-
plugins, extensions = _get_installed_aprsd_items()
|
110
|
-
return plugins
|
111
|
-
|
112
|
-
|
113
|
-
def get_installed_extensions():
|
114
|
-
# installed plugins
|
115
|
-
plugins, extensions = _get_installed_aprsd_items()
|
116
|
-
return extensions
|
117
16
|
|
118
17
|
|
119
18
|
def show_built_in_plugins(console):
|
@@ -157,34 +56,8 @@ def show_built_in_plugins(console):
|
|
157
56
|
console.print(table)
|
158
57
|
|
159
58
|
|
160
|
-
def _get_pypi_packages():
|
161
|
-
if simple_r := requests.get(
|
162
|
-
'https://pypi.org/simple',
|
163
|
-
headers={'Accept': 'application/vnd.pypi.simple.v1+json'},
|
164
|
-
):
|
165
|
-
simple_response = simple_r.json()
|
166
|
-
else:
|
167
|
-
simple_response = {}
|
168
|
-
|
169
|
-
key = 'aprsd'
|
170
|
-
matches = [
|
171
|
-
p['name'] for p in simple_response['projects'] if p['name'].startswith(key)
|
172
|
-
]
|
173
|
-
|
174
|
-
packages = []
|
175
|
-
for pkg in matches:
|
176
|
-
# Get info for first match
|
177
|
-
if r := requests.get(
|
178
|
-
f'https://pypi.org/pypi/{pkg}/json',
|
179
|
-
headers={'Accept': 'application/json'},
|
180
|
-
):
|
181
|
-
packages.append(r.json())
|
182
|
-
|
183
|
-
return packages
|
184
|
-
|
185
|
-
|
186
59
|
def show_pypi_plugins(installed_plugins, console):
|
187
|
-
packages =
|
60
|
+
packages = aprsd_package.get_pypi_packages()
|
188
61
|
|
189
62
|
title = Text.assemble(
|
190
63
|
('Pypi.org APRSD Installable Plugin Packages\n\n', 'bold magenta'),
|
@@ -225,7 +98,7 @@ def show_pypi_plugins(installed_plugins, console):
|
|
225
98
|
|
226
99
|
|
227
100
|
def show_pypi_extensions(installed_extensions, console):
|
228
|
-
packages =
|
101
|
+
packages = aprsd_package.get_pypi_packages()
|
229
102
|
|
230
103
|
title = Text.assemble(
|
231
104
|
('Pypi.org APRSD Installable Extension Packages\n\n', 'bold magenta'),
|
@@ -282,7 +155,7 @@ def show_installed_plugins(installed_plugins, console):
|
|
282
155
|
name.replace('_', '-'),
|
283
156
|
plugin['name'],
|
284
157
|
plugin['version'],
|
285
|
-
plugin_type(plugin['obj']),
|
158
|
+
aprsd_package.plugin_type(plugin['obj']),
|
286
159
|
plugin['path'],
|
287
160
|
)
|
288
161
|
|
@@ -302,7 +175,7 @@ def list_plugins(ctx):
|
|
302
175
|
show_built_in_plugins(console)
|
303
176
|
|
304
177
|
status.update('Fetching pypi.org plugins')
|
305
|
-
installed_plugins = get_installed_plugins()
|
178
|
+
installed_plugins = aprsd_package.get_installed_plugins()
|
306
179
|
show_pypi_plugins(installed_plugins, console)
|
307
180
|
|
308
181
|
status.update('Looking for installed APRSD plugins')
|
@@ -321,5 +194,5 @@ def list_extensions(ctx):
|
|
321
194
|
status.update('Fetching pypi.org APRSD Extensions')
|
322
195
|
|
323
196
|
status.update('Looking for installed APRSD Extensions')
|
324
|
-
installed_extensions = get_installed_extensions()
|
197
|
+
installed_extensions = aprsd_package.get_installed_extensions()
|
325
198
|
show_pypi_extensions(installed_extensions, console)
|
aprsd/cmds/listen.py
CHANGED
@@ -221,7 +221,9 @@ def listen(
|
|
221
221
|
# CONF.aprs_network.login = aprs_login
|
222
222
|
# config["aprs"]["password"] = aprs_password
|
223
223
|
|
224
|
+
LOG.info(f'Python version: {sys.version}')
|
224
225
|
LOG.info(f'APRSD Listen Started version: {aprsd.__version__}')
|
226
|
+
utils.package.log_installed_extensions_and_plugins()
|
225
227
|
|
226
228
|
CONF.log_opt_values(LOG, logging.DEBUG)
|
227
229
|
collector.Collector()
|
aprsd/cmds/send_message.py
CHANGED
@@ -13,6 +13,7 @@ from aprsd import (
|
|
13
13
|
cli_helper,
|
14
14
|
conf, # noqa : F401
|
15
15
|
packets,
|
16
|
+
utils,
|
16
17
|
)
|
17
18
|
from aprsd.client.client import APRSDClient
|
18
19
|
from aprsd.main import cli
|
@@ -89,7 +90,9 @@ def send_message(
|
|
89
90
|
else:
|
90
91
|
aprs_password = CONF.aprs_network.password
|
91
92
|
|
92
|
-
LOG.info(f'
|
93
|
+
LOG.info(f'Python version: {sys.version}')
|
94
|
+
LOG.info(f'APRSD SEND_MESSAGE Started version: {aprsd.__version__}')
|
95
|
+
utils.package.log_installed_extensions_and_plugins()
|
93
96
|
if type(command) is tuple:
|
94
97
|
command = ' '.join(command)
|
95
98
|
if not quiet:
|
aprsd/cmds/server.py
CHANGED
@@ -40,12 +40,14 @@ def server(ctx, flush):
|
|
40
40
|
|
41
41
|
service_threads = service.ServiceThreads()
|
42
42
|
|
43
|
+
LOG.info(f'Python version: {sys.version}')
|
44
|
+
LOG.info(f'APRSD Started version: {aprsd.__version__}')
|
43
45
|
level, msg = utils._check_version()
|
44
46
|
if level:
|
45
47
|
LOG.warning(msg)
|
46
48
|
else:
|
47
49
|
LOG.info(msg)
|
48
|
-
|
50
|
+
utils.package.log_installed_extensions_and_plugins()
|
49
51
|
|
50
52
|
# Make sure we have 1 client transport enabled
|
51
53
|
if not APRSDClient().is_enabled:
|
aprsd/conf/common.py
CHANGED
@@ -19,7 +19,7 @@ registry_group = cfg.OptGroup(
|
|
19
19
|
aprsd_opts = [
|
20
20
|
cfg.StrOpt(
|
21
21
|
'callsign',
|
22
|
-
|
22
|
+
default='NOCALL',
|
23
23
|
help='Callsign to use for messages sent by APRSD',
|
24
24
|
),
|
25
25
|
cfg.BoolOpt(
|
@@ -137,6 +137,12 @@ aprsd_opts = [
|
|
137
137
|
help='Set this to False, to disable sending of ack packets. This will entirely stop'
|
138
138
|
'APRSD from sending ack packets.',
|
139
139
|
),
|
140
|
+
cfg.BoolOpt(
|
141
|
+
'is_digipi',
|
142
|
+
default=False,
|
143
|
+
help='Set this to True, if APRSD is running on a Digipi.'
|
144
|
+
'This is useful for changing the behavior of APRSD to work with Digipi.',
|
145
|
+
),
|
140
146
|
]
|
141
147
|
|
142
148
|
watch_list_opts = [
|
aprsd/exception.py
CHANGED
@@ -13,3 +13,10 @@ class ConfigOptionBogusDefaultException(Exception):
|
|
13
13
|
f"Config file option '{config_option}' needs to be "
|
14
14
|
f"changed from provided default of '{default_fail}'"
|
15
15
|
)
|
16
|
+
|
17
|
+
|
18
|
+
class APRSClientNotConfiguredException(Exception):
|
19
|
+
"""APRS client is not configured."""
|
20
|
+
|
21
|
+
def __init__(self):
|
22
|
+
self.message = 'APRS client is not configured.'
|
aprsd/main.py
CHANGED
aprsd/stats/collector.py
CHANGED
@@ -44,3 +44,9 @@ class Collector:
|
|
44
44
|
if not isinstance(producer_name, StatsProducer):
|
45
45
|
raise TypeError(f'Producer {producer_name} is not a StatsProducer')
|
46
46
|
self.producers.remove(producer_name)
|
47
|
+
|
48
|
+
def stop_all(self):
|
49
|
+
"""Stop and unregister all registered stats producers."""
|
50
|
+
for producer in self.producers[:]:
|
51
|
+
LOG.info(f'Stopping Stats producer {producer}')
|
52
|
+
self.unregister_producer(producer)
|
aprsd/threads/rx.py
CHANGED
@@ -275,7 +275,7 @@ class APRSDProcessPacketThread(APRSDFilterThread):
|
|
275
275
|
def process_other_packet(self, packet, for_us=False):
|
276
276
|
"""Process an APRS Packet that isn't a message or ack"""
|
277
277
|
if not for_us:
|
278
|
-
LOG.info("Got a packet meant for someone else '{packet.to_call}'")
|
278
|
+
LOG.info(f"Got a packet meant for someone else '{packet.to_call}'")
|
279
279
|
else:
|
280
280
|
LOG.info('Got a non AckPacket/MessagePacket')
|
281
281
|
|
aprsd/utils/package.py
ADDED
@@ -0,0 +1,176 @@
|
|
1
|
+
import fnmatch
|
2
|
+
import importlib
|
3
|
+
import inspect
|
4
|
+
import logging
|
5
|
+
import os
|
6
|
+
import pkgutil
|
7
|
+
import sys
|
8
|
+
from traceback import print_tb
|
9
|
+
|
10
|
+
import requests
|
11
|
+
from thesmuggler import smuggle
|
12
|
+
|
13
|
+
from aprsd import plugin as aprsd_plugin
|
14
|
+
|
15
|
+
LOG = logging.getLogger()
|
16
|
+
|
17
|
+
|
18
|
+
def onerror(name):
|
19
|
+
type, value, traceback = sys.exc_info()
|
20
|
+
print_tb(traceback)
|
21
|
+
|
22
|
+
|
23
|
+
def plugin_type(obj):
|
24
|
+
for c in inspect.getmro(obj):
|
25
|
+
if issubclass(c, aprsd_plugin.APRSDRegexCommandPluginBase):
|
26
|
+
return 'RegexCommand'
|
27
|
+
if issubclass(c, aprsd_plugin.APRSDWatchListPluginBase):
|
28
|
+
return 'WatchList'
|
29
|
+
if issubclass(c, aprsd_plugin.APRSDPluginBase):
|
30
|
+
return 'APRSDPluginBase'
|
31
|
+
|
32
|
+
return 'Unknown'
|
33
|
+
|
34
|
+
|
35
|
+
def is_plugin(obj):
|
36
|
+
for c in inspect.getmro(obj):
|
37
|
+
if issubclass(c, aprsd_plugin.APRSDPluginBase):
|
38
|
+
return True
|
39
|
+
|
40
|
+
return False
|
41
|
+
|
42
|
+
|
43
|
+
def walk_package(package):
|
44
|
+
return pkgutil.walk_packages(
|
45
|
+
package.__path__,
|
46
|
+
package.__name__ + '.',
|
47
|
+
onerror=onerror,
|
48
|
+
)
|
49
|
+
|
50
|
+
|
51
|
+
def get_module_info(package_name, module_name, module_path):
|
52
|
+
if not os.path.exists(module_path):
|
53
|
+
return None
|
54
|
+
|
55
|
+
dir_path = os.path.realpath(module_path)
|
56
|
+
pattern = '*.py'
|
57
|
+
|
58
|
+
obj_list = []
|
59
|
+
|
60
|
+
for path, _subdirs, files in os.walk(dir_path):
|
61
|
+
for name in files:
|
62
|
+
if fnmatch.fnmatch(name, pattern):
|
63
|
+
module = smuggle(f'{path}/{name}')
|
64
|
+
for mem_name, obj in inspect.getmembers(module):
|
65
|
+
if inspect.isclass(obj) and is_plugin(obj):
|
66
|
+
obj_list.append(
|
67
|
+
{
|
68
|
+
'package': package_name,
|
69
|
+
'name': mem_name,
|
70
|
+
'obj': obj,
|
71
|
+
'version': obj.version,
|
72
|
+
'path': f'{".".join([module_name, obj.__name__])}',
|
73
|
+
},
|
74
|
+
)
|
75
|
+
|
76
|
+
return obj_list
|
77
|
+
|
78
|
+
|
79
|
+
def is_aprsd_package(name):
|
80
|
+
if name.startswith('aprsd_'):
|
81
|
+
return True
|
82
|
+
|
83
|
+
|
84
|
+
def is_aprsd_extension(name):
|
85
|
+
if name.startswith('aprsd_') and 'extension' in name:
|
86
|
+
# This is an installed package that is an extension of
|
87
|
+
# APRSD
|
88
|
+
return True
|
89
|
+
else:
|
90
|
+
# We might have an editable install of an extension
|
91
|
+
# of APRSD.
|
92
|
+
return '__editable__' in name and 'aprsd_' in name and 'extension' in name
|
93
|
+
|
94
|
+
|
95
|
+
def get_installed_aprsd_items():
|
96
|
+
# installed plugins
|
97
|
+
plugins = {}
|
98
|
+
extensions = {}
|
99
|
+
for _finder, name, ispkg in pkgutil.iter_modules():
|
100
|
+
if ispkg and is_aprsd_package(name):
|
101
|
+
module = importlib.import_module(name)
|
102
|
+
pkgs = walk_package(module)
|
103
|
+
for pkg in pkgs:
|
104
|
+
pkg_info = get_module_info(
|
105
|
+
module.__name__, pkg.name, module.__path__[0]
|
106
|
+
)
|
107
|
+
if 'plugin' in name:
|
108
|
+
plugins[name] = pkg_info
|
109
|
+
elif 'extension' in name:
|
110
|
+
mod = importlib.import_module(name)
|
111
|
+
extensions[name] = mod
|
112
|
+
elif is_aprsd_extension(name):
|
113
|
+
# This isn't a package, so it could be an editable install
|
114
|
+
module = importlib.import_module(name)
|
115
|
+
key_name = next(iter(module.MAPPING.keys()))
|
116
|
+
module = importlib.import_module(key_name)
|
117
|
+
pkg_info = get_module_info(module.__name__, key_name, module.__path__[0])
|
118
|
+
extensions[key_name] = module
|
119
|
+
return plugins, extensions
|
120
|
+
|
121
|
+
|
122
|
+
def get_installed_plugins():
|
123
|
+
# installed plugins
|
124
|
+
plugins, _ = get_installed_aprsd_items()
|
125
|
+
return plugins
|
126
|
+
|
127
|
+
|
128
|
+
def get_installed_extensions():
|
129
|
+
# installed plugins
|
130
|
+
_, extensions = get_installed_aprsd_items()
|
131
|
+
return extensions
|
132
|
+
|
133
|
+
|
134
|
+
def get_pypi_packages():
|
135
|
+
if simple_r := requests.get(
|
136
|
+
'https://pypi.org/simple',
|
137
|
+
headers={'Accept': 'application/vnd.pypi.simple.v1+json'},
|
138
|
+
):
|
139
|
+
simple_response = simple_r.json()
|
140
|
+
else:
|
141
|
+
simple_response = {}
|
142
|
+
|
143
|
+
key = 'aprsd'
|
144
|
+
matches = [
|
145
|
+
p['name'] for p in simple_response['projects'] if p['name'].startswith(key)
|
146
|
+
]
|
147
|
+
|
148
|
+
packages = []
|
149
|
+
for pkg in matches:
|
150
|
+
# Get info for first match
|
151
|
+
if r := requests.get(
|
152
|
+
f'https://pypi.org/pypi/{pkg}/json',
|
153
|
+
headers={'Accept': 'application/json'},
|
154
|
+
):
|
155
|
+
packages.append(r.json())
|
156
|
+
|
157
|
+
return packages
|
158
|
+
|
159
|
+
|
160
|
+
def log_installed_extensions_and_plugins():
|
161
|
+
plugins, extensions = get_installed_aprsd_items()
|
162
|
+
|
163
|
+
for name in extensions:
|
164
|
+
ext = extensions[name]
|
165
|
+
# print(f"Extension: {ext}")
|
166
|
+
# print(f"Extension: {ext.__dict__}")
|
167
|
+
if hasattr(ext, '__version__'):
|
168
|
+
version = ext.__version__
|
169
|
+
elif hasattr(ext, 'version'):
|
170
|
+
version = ext.version
|
171
|
+
else:
|
172
|
+
version = ext['version']
|
173
|
+
LOG.info(f'Extension: {name} version: {version}')
|
174
|
+
|
175
|
+
for plugin in plugins:
|
176
|
+
LOG.info(f'Plugin: {plugin} version: {plugins[plugin][0]["version"]}')
|
aprsd/utils/trace.py
CHANGED
@@ -5,11 +5,11 @@ import logging
|
|
5
5
|
import time
|
6
6
|
import types
|
7
7
|
|
8
|
-
VALID_TRACE_FLAGS = {
|
8
|
+
VALID_TRACE_FLAGS = {'method', 'api'}
|
9
9
|
TRACE_API = False
|
10
10
|
TRACE_METHOD = False
|
11
11
|
TRACE_ENABLED = False
|
12
|
-
LOG = logging.getLogger(
|
12
|
+
LOG = logging.getLogger('APRSD')
|
13
13
|
|
14
14
|
|
15
15
|
def trace(*dec_args, **dec_kwargs):
|
@@ -27,11 +27,12 @@ def trace(*dec_args, **dec_kwargs):
|
|
27
27
|
|
28
28
|
def _decorator(f):
|
29
29
|
func_name = f.__qualname__
|
30
|
-
func_file =
|
30
|
+
func_file = '/'.join(f.__code__.co_filename.split('/')[-4:])
|
31
|
+
func_line_number = f.__code__.co_firstlineno
|
31
32
|
|
32
33
|
@functools.wraps(f)
|
33
34
|
def trace_logging_wrapper(*args, **kwargs):
|
34
|
-
filter_function = dec_kwargs.get(
|
35
|
+
filter_function = dec_kwargs.get('filter_function')
|
35
36
|
logger = LOG
|
36
37
|
|
37
38
|
# NOTE(ameade): Don't bother going any further if DEBUG log level
|
@@ -40,16 +41,16 @@ def trace(*dec_args, **dec_kwargs):
|
|
40
41
|
return f(*args, **kwargs)
|
41
42
|
|
42
43
|
all_args = inspect.getcallargs(f, *args, **kwargs)
|
43
|
-
|
44
44
|
pass_filter = filter_function is None or filter_function(all_args)
|
45
45
|
|
46
46
|
if pass_filter:
|
47
47
|
logger.debug(
|
48
|
-
|
48
|
+
'==> %(func)s: call %(all_args)r file: %(file)s:%(line_number)d',
|
49
49
|
{
|
50
|
-
|
51
|
-
|
52
|
-
|
50
|
+
'func': func_name,
|
51
|
+
'all_args': str(all_args),
|
52
|
+
'file': func_file,
|
53
|
+
'line_number': func_line_number,
|
53
54
|
},
|
54
55
|
)
|
55
56
|
|
@@ -59,11 +60,11 @@ def trace(*dec_args, **dec_kwargs):
|
|
59
60
|
except Exception as exc:
|
60
61
|
total_time = int(round(time.time() * 1000)) - start_time
|
61
62
|
logger.debug(
|
62
|
-
|
63
|
+
'<== %(func)s: exception (%(time)dms) %(exc)r',
|
63
64
|
{
|
64
|
-
|
65
|
-
|
66
|
-
|
65
|
+
'func': func_name,
|
66
|
+
'time': total_time,
|
67
|
+
'exc': exc,
|
67
68
|
},
|
68
69
|
)
|
69
70
|
raise
|
@@ -78,11 +79,11 @@ def trace(*dec_args, **dec_kwargs):
|
|
78
79
|
|
79
80
|
if pass_filter:
|
80
81
|
logger.debug(
|
81
|
-
|
82
|
+
'<== %(func)s: return (%(time)dms) %(result)r',
|
82
83
|
{
|
83
|
-
|
84
|
-
|
85
|
-
|
84
|
+
'func': func_name,
|
85
|
+
'time': total_time,
|
86
|
+
'result': mask_result,
|
86
87
|
},
|
87
88
|
)
|
88
89
|
return result
|
@@ -174,7 +175,7 @@ def setup_tracing(trace_flags):
|
|
174
175
|
except TypeError: # Handle when trace_flags is None or a test mock
|
175
176
|
trace_flags = []
|
176
177
|
for invalid_flag in set(trace_flags) - VALID_TRACE_FLAGS:
|
177
|
-
LOG.warning(
|
178
|
-
TRACE_METHOD =
|
179
|
-
TRACE_API =
|
178
|
+
LOG.warning('Invalid trace flag: %s', invalid_flag)
|
179
|
+
TRACE_METHOD = 'method' in trace_flags
|
180
|
+
TRACE_API = 'api' in trace_flags
|
180
181
|
TRACE_ENABLED = True
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: aprsd
|
3
|
-
Version: 4.2.
|
3
|
+
Version: 4.2.3
|
4
4
|
Summary: APRSd is a APRS-IS server that can be used to connect to APRS-IS and send and receive APRS packets.
|
5
5
|
Author-email: Craig Lamparter <craig@craiger.org>, "Walter A. Boring IV" <waboring@hemna.com>, Emre Saglam <emresaglam@gmail.com>, Jason Martin <jhmartin@toger.us>, John <johng42@users.noreply.github.com>, Martiros Shakhzadyan <vrzh@vrzh.net>, Zoe Moore <zoenb@mailbox.org>, ranguli <hello@joshmurphy.ca>
|
6
6
|
Maintainer-email: Craig Lamparter <craig@craiger.org>, "Walter A. Boring IV" <waboring@hemna.com>
|
@@ -1,31 +1,31 @@
|
|
1
1
|
aprsd/__init__.py,sha256=ci_49KK2a4GXyxcM2lFZfNAOsBfXzh0yayIGQazw56I,687
|
2
2
|
aprsd/cli_helper.py,sha256=mKHww_cStwFBThjntFylrSnF6n9jOPHlR8r9SsrCxdY,4605
|
3
|
-
aprsd/exception.py,sha256=
|
4
|
-
aprsd/main.py,sha256=
|
3
|
+
aprsd/exception.py,sha256=bzln7sP9USQ67tz5nQh6e4KMaS8X2uhPJMNVmCkKV30,679
|
4
|
+
aprsd/main.py,sha256=JDI8VUGvi8KSG4hH125OlvdXC4xtwyakU0cB2YHpTXA,4756
|
5
5
|
aprsd/plugin.py,sha256=pdyvKhSugFo4vRy7VflnaZ-BKozGa3GTTAHErskW3WU,17848
|
6
6
|
aprsd/plugin_utils.py,sha256=beXQ1n_YI38rqDwswtE8NjFwFzjpaeve9W_JotX1n1A,2544
|
7
7
|
aprsd/client/__init__.py,sha256=lh7mKBoSo2Tt82QoqlAARGm4LY6ffsn7d8x6M4B7n9g,154
|
8
|
-
aprsd/client/client.py,sha256=
|
8
|
+
aprsd/client/client.py,sha256=sTZaxduDyGYtBpxpOvYAGG6D6gz6CPsivou9eKnW0tw,4898
|
9
9
|
aprsd/client/stats.py,sha256=dUqRZao04B2TTj9TGMSygdLHa3if3pEy86eK-fK4sj4,353
|
10
10
|
aprsd/client/drivers/__init__.py,sha256=IWSSrAEMrJQzulCIAx-N7Du0HK9sQOXIh98aJRCtfdg,421
|
11
|
-
aprsd/client/drivers/aprsis.py,sha256=
|
12
|
-
aprsd/client/drivers/fake.py,sha256=
|
13
|
-
aprsd/client/drivers/registry.py,sha256=
|
14
|
-
aprsd/client/drivers/tcpkiss.py,sha256=
|
11
|
+
aprsd/client/drivers/aprsis.py,sha256=DLtNgj1YAOiaHiALfHD6W-QF6JkzgQdmL7BmPXOa7KA,6763
|
12
|
+
aprsd/client/drivers/fake.py,sha256=iCe7zRJxL3giADSX6PMNMcasr9g4GpPNFSGhrKlgCeg,3277
|
13
|
+
aprsd/client/drivers/registry.py,sha256=vTKYTGAnVdBAyvCL51BadGFMNBsibzr9-xLPfXm_a9U,2227
|
14
|
+
aprsd/client/drivers/tcpkiss.py,sha256=begkOCkrd1g5JpuPGgY-EMiy3NcMxZaHaaWZqoCIQ5w,13817
|
15
15
|
aprsd/client/drivers/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
16
16
|
aprsd/client/drivers/lib/aprslib.py,sha256=9KTEKlivGT1f1js-u-w1WNLhOs1kQxkr3COa1CI95oE,9439
|
17
17
|
aprsd/cmds/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
18
18
|
aprsd/cmds/completion.py,sha256=BMM8dSYUihYmj2fSsefo6LzgJZKbxHnNfRIhwVZW_U8,816
|
19
|
-
aprsd/cmds/dev.py,sha256=
|
20
|
-
aprsd/cmds/fetch_stats.py,sha256=
|
19
|
+
aprsd/cmds/dev.py,sha256=9znqGv8qUd1hRV_-Iit8ktxFhYFIIB4rOnluhurrv28,4296
|
20
|
+
aprsd/cmds/fetch_stats.py,sha256=L8l-43GfZiWu9c9BgxpUV1PdrB8IG8z1FlsryAvPeAI,9684
|
21
21
|
aprsd/cmds/healthcheck.py,sha256=swXb4LE1qdr3Z-417fv4LhWvscE4WUrqF9So1sWXiCU,2635
|
22
|
-
aprsd/cmds/list_plugins.py,sha256=
|
23
|
-
aprsd/cmds/listen.py,sha256=
|
24
|
-
aprsd/cmds/send_message.py,sha256=
|
25
|
-
aprsd/cmds/server.py,sha256=
|
22
|
+
aprsd/cmds/list_plugins.py,sha256=PzvoyXfV26xAiTHtgSiJyWAmdPd2BEua5ta5fGuspVQ,7010
|
23
|
+
aprsd/cmds/listen.py,sha256=3M8eXIneEuM5wfskAFFYUZSfHTQveTAFSmnRZAfe9WY,9519
|
24
|
+
aprsd/cmds/send_message.py,sha256=EVJ-VayxcSNYAJ8dM8-LM9HLbsanC2mOWA6bfZwcLn4,4830
|
25
|
+
aprsd/cmds/server.py,sha256=hB3Tu0MbzEvg4XkoCFomvfMZqIitDKdIejQ6uwHDm0o,4138
|
26
26
|
aprsd/conf/__init__.py,sha256=2ikrZahcS2EULQEJ5xErnzMVR2vcsDdMvixEPuNE-xE,1678
|
27
27
|
aprsd/conf/client.py,sha256=9oheIUnotnbHmpKtxlw-00quW0o-3pZYtFmaim9bsjs,3013
|
28
|
-
aprsd/conf/common.py,sha256=
|
28
|
+
aprsd/conf/common.py,sha256=l5tUw6G5CNOiFMZQPoStkpsNa3d9xr4BunzZK0FNgV8,7605
|
29
29
|
aprsd/conf/log.py,sha256=qBoF8ptGHK7G6NPHNZCE2PBH7S-L587wFkT-9ikUtfE,1656
|
30
30
|
aprsd/conf/opts.py,sha256=kbZELePpBDoQrKbowbWHgmLksbREARU8T8UhX9FIhq8,2725
|
31
31
|
aprsd/conf/plugin_common.py,sha256=H7LxiJL0Sl1D_dpAmJHEHSbPxaOK938M1WCOcTpOYN8,1990
|
@@ -52,12 +52,12 @@ aprsd/plugins/version.py,sha256=NAPFKQ0lQqgjoDyTVy8tZcRGmdcJE_ZogRElDz27V2U,839
|
|
52
52
|
aprsd/plugins/weather.py,sha256=XOspEoAumUb35TW2RYqjRV0lp6hrR_kOMIT41iEUGQQ,13399
|
53
53
|
aprsd/stats/__init__.py,sha256=ltAtUiEvpokBEtOpq0sxpDGOLQT2evgeZSVBzDzjkSo,808
|
54
54
|
aprsd/stats/app.py,sha256=axqMA137zKqU03yO8XI5f1QE8ajmr9YK0J9O9m4iSzo,1378
|
55
|
-
aprsd/stats/collector.py,sha256=
|
55
|
+
aprsd/stats/collector.py,sha256=hIwJyw7P457NduqEzP0FPHEzf_HOuXepsBRmxiSCKKs,1743
|
56
56
|
aprsd/threads/__init__.py,sha256=KPqAOhS4q995NCEWDnPCHiuu3guyuMZlyDUGx_h50z8,259
|
57
57
|
aprsd/threads/aprsd.py,sha256=13AS7jAhcQBmTervBiADaC1Ins9C-6TrMDYjW1fQJkg,4794
|
58
58
|
aprsd/threads/keepalive.py,sha256=E48_erNnqik5AAllMGx5gexAwPIaznCp0HCG45NltFw,3939
|
59
59
|
aprsd/threads/registry.py,sha256=zWG4-SMBSx46NY93__Bt_F8vFrljxQvMHbi80WP5kY8,1690
|
60
|
-
aprsd/threads/rx.py,sha256=
|
60
|
+
aprsd/threads/rx.py,sha256=u0W3YgU83OaZcKyw60C3B-Chb6LXYXpDv9RsXKSKHcg,14937
|
61
61
|
aprsd/threads/service.py,sha256=fGjyr34i2-CJdBynxachNaBrBsULwmAnewLHdTujfmk,1375
|
62
62
|
aprsd/threads/stats.py,sha256=drKqHV2WxgXhyWXGrmRHQx7oKlrJ9bwEMtCInCzulPY,884
|
63
63
|
aprsd/threads/tx.py,sha256=spuWk4E5HvTWYNyhKB1kxqVbVvxNapzYvi9tNScTAHs,9264
|
@@ -67,12 +67,13 @@ aprsd/utils/fuzzyclock.py,sha256=qKV8SYZhQGOHG9biF8TeueLb6RMppspx1Zg4IOy1Z10,326
|
|
67
67
|
aprsd/utils/json.py,sha256=eHoBfXGcchO4Q1MXj6uKK9YU-H8HKPJ2cThLZ1dM_vo,2578
|
68
68
|
aprsd/utils/keepalive_collector.py,sha256=r0oKCpKPGsivOKTQkTfwcJRESqMfbNawCyqrcHoJo8M,1807
|
69
69
|
aprsd/utils/objectstore.py,sha256=0OivUeagncWGH7eWjTwZhauf-etweTabp8Oykt0hoF4,3541
|
70
|
+
aprsd/utils/package.py,sha256=BtyIT6F5tPTFIATy5zDtYnB2k-rSFey5K2t9RPGenck,5025
|
70
71
|
aprsd/utils/ring_buffer.py,sha256=lWWuw7lEbc2URhqAJfRLjpXBDLiK6UUWzk3j2VFnERQ,1111
|
71
|
-
aprsd/utils/trace.py,sha256=
|
72
|
-
aprsd-4.2.
|
73
|
-
aprsd-4.2.
|
74
|
-
aprsd-4.2.
|
75
|
-
aprsd-4.2.
|
76
|
-
aprsd-4.2.
|
77
|
-
aprsd-4.2.
|
78
|
-
aprsd-4.2.
|
72
|
+
aprsd/utils/trace.py,sha256=3W4V14qg63x39l77H7iaCEacXd_otvz3oJKwbKqH_jM,5812
|
73
|
+
aprsd-4.2.3.dist-info/licenses/AUTHORS,sha256=fGZhgXFMCfDPbp0hHllwmPyfiKPobLNA3sJA3BMIJVE,22
|
74
|
+
aprsd-4.2.3.dist-info/licenses/LICENSE,sha256=CeipvOyAZxBGUsFoaFqwkx54aPnIKEtm9a5u2uXxEws,10142
|
75
|
+
aprsd-4.2.3.dist-info/METADATA,sha256=PJmJ8nucsHVSdnJRPggmAhLIiKHqJWC0hLyEYiN6NHU,45583
|
76
|
+
aprsd-4.2.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
77
|
+
aprsd-4.2.3.dist-info/entry_points.txt,sha256=4fReoJUB-bFqOUK6eeXYYCvTdVLprL7KVH0hWQRP9eM,171
|
78
|
+
aprsd-4.2.3.dist-info/top_level.txt,sha256=v1O96niUcJOTMh9aQnFRknbScJ6mMOwqurdbxeaeSjs,6
|
79
|
+
aprsd-4.2.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|