Improve adherence to python coding standards and clear up IDE static analysis warnings

This commit is contained in:
Ian Renton
2026-02-27 19:17:04 +00:00
parent 6b18ec6f88
commit 6982354364
53 changed files with 633 additions and 626 deletions

View File

@@ -8,7 +8,7 @@ import tornado
from core.config import ALLOW_SPOTTING, MAX_SPOT_AGE
from core.constants import UNKNOWN_BAND
from core.lookup_helper import lookup_helper
from core.lookup_helper import infer_band_from_freq
from core.prometheus_metrics_handler import api_requests_counter
from core.sig_utils import get_ref_regex_for_sig
from core.utils import serialize_everything
@@ -20,15 +20,15 @@ class APISpotHandler(tornado.web.RequestHandler):
"""API request handler for /api/v1/spot (POST)"""
def initialize(self, spots, web_server_metrics):
self.spots = spots
self.web_server_metrics = web_server_metrics
self._spots = spots
self._web_server_metrics = web_server_metrics
def post(self):
try:
# Metrics
self.web_server_metrics["last_api_access_time"] = datetime.now(pytz.UTC)
self.web_server_metrics["api_access_counter"] += 1
self.web_server_metrics["status"] = "OK"
self._web_server_metrics["last_api_access_time"] = datetime.now(pytz.UTC)
self._web_server_metrics["api_access_counter"] += 1
self._web_server_metrics["status"] = "OK"
api_requests_counter.inc()
# Reject if not allowed
@@ -97,7 +97,7 @@ class APISpotHandler(tornado.web.RequestHandler):
return
# Reject if frequency not in a known band
if lookup_helper.infer_band_from_freq(spot.freq) == UNKNOWN_BAND:
if infer_band_from_freq(spot.freq) == UNKNOWN_BAND:
self.set_status(422)
self.write(json.dumps("Error - Frequency of " + str(spot.freq / 1000.0) + "kHz is not in a known band.",
default=serialize_everything))
@@ -130,7 +130,7 @@ class APISpotHandler(tornado.web.RequestHandler):
# infer missing data, and add it to our database.
spot.source = "API"
spot.infer_missing()
self.spots.add(spot.id, spot, expire=MAX_SPOT_AGE)
self._spots.add(spot.id, spot, expire=MAX_SPOT_AGE)
self.write(json.dumps("OK", default=serialize_everything))
self.set_status(201)

View File

@@ -18,15 +18,15 @@ class APIAlertsHandler(tornado.web.RequestHandler):
"""API request handler for /api/v1/alerts"""
def initialize(self, alerts, web_server_metrics):
self.alerts = alerts
self.web_server_metrics = web_server_metrics
self._alerts = alerts
self._web_server_metrics = web_server_metrics
def get(self):
try:
# Metrics
self.web_server_metrics["last_api_access_time"] = datetime.now(pytz.UTC)
self.web_server_metrics["api_access_counter"] += 1
self.web_server_metrics["status"] = "OK"
self._web_server_metrics["last_api_access_time"] = datetime.now(pytz.UTC)
self._web_server_metrics["api_access_counter"] += 1
self._web_server_metrics["status"] = "OK"
api_requests_counter.inc()
# request.arguments contains lists for each param key because technically the client can supply multiple,
@@ -34,7 +34,7 @@ class APIAlertsHandler(tornado.web.RequestHandler):
query_params = {k: v[0].decode("utf-8") for k, v in self.request.arguments.items()}
# Fetch all alerts matching the query
data = get_alert_list_with_filters(self.alerts, query_params)
data = get_alert_list_with_filters(self._alerts, query_params)
self.write(json.dumps(data, default=serialize_everything))
self.set_status(200)
except ValueError as e:
@@ -53,8 +53,8 @@ class APIAlertsStreamHandler(tornado_eventsource.handler.EventSourceHandler):
"""API request handler for /api/v1/alerts/stream"""
def initialize(self, sse_alert_queues, web_server_metrics):
self.sse_alert_queues = sse_alert_queues
self.web_server_metrics = web_server_metrics
self._sse_alert_queues = sse_alert_queues
self._web_server_metrics = web_server_metrics
def custom_headers(self):
"""Custom headers to avoid e.g. nginx reverse proxy from buffering SSE data"""
@@ -65,58 +65,58 @@ class APIAlertsStreamHandler(tornado_eventsource.handler.EventSourceHandler):
def open(self):
try:
# Metrics
self.web_server_metrics["last_api_access_time"] = datetime.now(pytz.UTC)
self.web_server_metrics["api_access_counter"] += 1
self.web_server_metrics["status"] = "OK"
self._web_server_metrics["last_api_access_time"] = datetime.now(pytz.UTC)
self._web_server_metrics["api_access_counter"] += 1
self._web_server_metrics["status"] = "OK"
api_requests_counter.inc()
# request.arguments contains lists for each param key because technically the client can supply multiple,
# reduce that to just the first entry, and convert bytes to string
self.query_params = {k: v[0].decode("utf-8") for k, v in self.request.arguments.items()}
self._query_params = {k: v[0].decode("utf-8") for k, v in self.request.arguments.items()}
# Create a alert queue and add it to the web server's list. The web server will fill this when alerts arrive
self.alert_queue = Queue(maxsize=SSE_HANDLER_MAX_QUEUE_SIZE)
self.sse_alert_queues.append(self.alert_queue)
self._alert_queue = Queue(maxsize=SSE_HANDLER_MAX_QUEUE_SIZE)
self._sse_alert_queues.append(self._alert_queue)
# Set up a timed callback to check if anything is in the queue
self.heartbeat = tornado.ioloop.PeriodicCallback(self._callback, SSE_HANDLER_QUEUE_CHECK_INTERVAL)
self.heartbeat.start()
self._heartbeat = tornado.ioloop.PeriodicCallback(self._callback, SSE_HANDLER_QUEUE_CHECK_INTERVAL)
self._heartbeat.start()
except Exception as e:
logging.warn("Exception when serving SSE socket", e)
logging.warning("Exception when serving SSE socket", e)
def close(self):
"""When the user closes the socket, empty our queue and remove it from the list so the server no longer fills it"""
try:
if self.alert_queue in self.sse_alert_queues:
self.sse_alert_queues.remove(self.alert_queue)
empty_queue(self.alert_queue)
if self._alert_queue in self._sse_alert_queues:
self._sse_alert_queues.remove(self._alert_queue)
empty_queue(self._alert_queue)
except:
pass
try:
self.heartbeat.stop()
self._heartbeat.stop()
except:
pass
self.alert_queue = None
self._alert_queue = None
super().close()
def _callback(self):
"""Callback to check if anything has arrived in the queue, and if so send it to the client"""
try:
if self.alert_queue:
while not self.alert_queue.empty():
alert = self.alert_queue.get()
if self._alert_queue:
while not self._alert_queue.empty():
alert = self._alert_queue.get()
# If the new alert matches our param filters, send it to the client. If not, ignore it.
if alert_allowed_by_query(alert, self.query_params):
if alert_allowed_by_query(alert, self._query_params):
self.write_message(msg=json.dumps(alert, default=serialize_everything))
if self.alert_queue not in self.sse_alert_queues:
if self._alert_queue not in self._sse_alert_queues:
logging.error("Web server cleared up a queue of an active connection!")
self.close()
except:
logging.warn("Exception in SSE callback, connection will be closed.")
logging.warning("Exception in SSE callback, connection will be closed.")
self.close()

View File

@@ -5,7 +5,6 @@ from datetime import datetime
import pytz
import tornado
from pyhamtools.locator import locator_to_latlong
from core.constants import SIGS
from core.geo_utils import lat_lon_for_grid_sw_corner_plus_size, lat_lon_to_cq_zone, lat_lon_to_itu_zone
@@ -20,14 +19,14 @@ class APILookupCallHandler(tornado.web.RequestHandler):
"""API request handler for /api/v1/lookup/call"""
def initialize(self, web_server_metrics):
self.web_server_metrics = web_server_metrics
self._web_server_metrics = web_server_metrics
def get(self):
try:
# Metrics
self.web_server_metrics["last_api_access_time"] = datetime.now(pytz.UTC)
self.web_server_metrics["api_access_counter"] += 1
self.web_server_metrics["status"] = "OK"
self._web_server_metrics["last_api_access_time"] = datetime.now(pytz.UTC)
self._web_server_metrics["api_access_counter"] += 1
self._web_server_metrics["status"] = "OK"
api_requests_counter.inc()
# request.arguments contains lists for each param key because technically the client can supply multiple,
@@ -80,14 +79,14 @@ class APILookupSIGRefHandler(tornado.web.RequestHandler):
"""API request handler for /api/v1/lookup/sigref"""
def initialize(self, web_server_metrics):
self.web_server_metrics = web_server_metrics
self._web_server_metrics = web_server_metrics
def get(self):
try:
# Metrics
self.web_server_metrics["last_api_access_time"] = datetime.now(pytz.UTC)
self.web_server_metrics["api_access_counter"] += 1
self.web_server_metrics["status"] = "OK"
self._web_server_metrics["last_api_access_time"] = datetime.now(pytz.UTC)
self._web_server_metrics["api_access_counter"] += 1
self._web_server_metrics["status"] = "OK"
api_requests_counter.inc()
# request.arguments contains lists for each param key because technically the client can supply multiple,
@@ -98,15 +97,15 @@ class APILookupSIGRefHandler(tornado.web.RequestHandler):
# the provided id must match it.
if "sig" in query_params.keys() and "id" in query_params.keys():
sig = query_params.get("sig").upper()
id = query_params.get("id").upper()
ref_id = query_params.get("id").upper()
if sig in list(map(lambda p: p.name, SIGS)):
if not get_ref_regex_for_sig(sig) or re.match(get_ref_regex_for_sig(sig), id):
data = populate_sig_ref_info(SIGRef(id=id, sig=sig))
if not get_ref_regex_for_sig(sig) or re.match(get_ref_regex_for_sig(sig), ref_id):
data = populate_sig_ref_info(SIGRef(id=ref_id, sig=sig))
self.write(json.dumps(data, default=serialize_everything))
else:
self.write(
json.dumps("Error - '" + id + "' does not look like a valid reference ID for " + sig + ".",
json.dumps("Error - '" + ref_id + "' does not look like a valid reference ID for " + sig + ".",
default=serialize_everything))
self.set_status(422)
else:
@@ -129,14 +128,14 @@ class APILookupGridHandler(tornado.web.RequestHandler):
"""API request handler for /api/v1/lookup/grid"""
def initialize(self, web_server_metrics):
self.web_server_metrics = web_server_metrics
self._web_server_metrics = web_server_metrics
def get(self):
try:
# Metrics
self.web_server_metrics["last_api_access_time"] = datetime.now(pytz.UTC)
self.web_server_metrics["api_access_counter"] += 1
self.web_server_metrics["status"] = "OK"
self._web_server_metrics["last_api_access_time"] = datetime.now(pytz.UTC)
self._web_server_metrics["api_access_counter"] += 1
self._web_server_metrics["status"] = "OK"
api_requests_counter.inc()
# request.arguments contains lists for each param key because technically the client can supply multiple,

View File

@@ -4,7 +4,7 @@ from datetime import datetime
import pytz
import tornado
from core.config import MAX_SPOT_AGE, ALLOW_SPOTTING, WEB_UI_OPTIONS
from core.config import MAX_SPOT_AGE, ALLOW_SPOTTING
from core.constants import BANDS, ALL_MODES, MODE_TYPES, SIGS, CONTINENTS
from core.prometheus_metrics_handler import api_requests_counter
from core.utils import serialize_everything
@@ -14,14 +14,14 @@ class APIOptionsHandler(tornado.web.RequestHandler):
"""API request handler for /api/v1/options"""
def initialize(self, status_data, web_server_metrics):
self.status_data = status_data
self.web_server_metrics = web_server_metrics
self._status_data = status_data
self._web_server_metrics = web_server_metrics
def get(self):
# Metrics
self.web_server_metrics["last_api_access_time"] = datetime.now(pytz.UTC)
self.web_server_metrics["api_access_counter"] += 1
self.web_server_metrics["status"] = "OK"
self._web_server_metrics["last_api_access_time"] = datetime.now(pytz.UTC)
self._web_server_metrics["api_access_counter"] += 1
self._web_server_metrics["status"] = "OK"
api_requests_counter.inc()
options = {"bands": BANDS,
@@ -30,9 +30,9 @@ class APIOptionsHandler(tornado.web.RequestHandler):
"sigs": SIGS,
# Spot/alert sources are filtered for only ones that are enabled in config, no point letting the user toggle things that aren't even available.
"spot_sources": list(
map(lambda p: p["name"], filter(lambda p: p["enabled"], self.status_data["spot_providers"]))),
map(lambda p: p["name"], filter(lambda p: p["enabled"], self._status_data["spot_providers"]))),
"alert_sources": list(
map(lambda p: p["name"], filter(lambda p: p["enabled"], self.status_data["alert_providers"]))),
map(lambda p: p["name"], filter(lambda p: p["enabled"], self._status_data["alert_providers"]))),
"continents": CONTINENTS,
"max_spot_age": MAX_SPOT_AGE,
"spot_allowed": ALLOW_SPOTTING}

View File

@@ -18,15 +18,15 @@ class APISpotsHandler(tornado.web.RequestHandler):
"""API request handler for /api/v1/spots"""
def initialize(self, spots, web_server_metrics):
self.spots = spots
self.web_server_metrics = web_server_metrics
self._spots = spots
self._web_server_metrics = web_server_metrics
def get(self):
try:
# Metrics
self.web_server_metrics["last_api_access_time"] = datetime.now(pytz.UTC)
self.web_server_metrics["api_access_counter"] += 1
self.web_server_metrics["status"] = "OK"
self._web_server_metrics["last_api_access_time"] = datetime.now(pytz.UTC)
self._web_server_metrics["api_access_counter"] += 1
self._web_server_metrics["status"] = "OK"
api_requests_counter.inc()
# request.arguments contains lists for each param key because technically the client can supply multiple,
@@ -34,7 +34,7 @@ class APISpotsHandler(tornado.web.RequestHandler):
query_params = {k: v[0].decode("utf-8") for k, v in self.request.arguments.items()}
# Fetch all spots matching the query
data = get_spot_list_with_filters(self.spots, query_params)
data = get_spot_list_with_filters(self._spots, query_params)
self.write(json.dumps(data, default=serialize_everything))
self.set_status(200)
except ValueError as e:
@@ -53,8 +53,8 @@ class APISpotsStreamHandler(tornado_eventsource.handler.EventSourceHandler):
"""API request handler for /api/v1/spots/stream"""
def initialize(self, sse_spot_queues, web_server_metrics):
self.sse_spot_queues = sse_spot_queues
self.web_server_metrics = web_server_metrics
self._sse_spot_queues = sse_spot_queues
self._web_server_metrics = web_server_metrics
def custom_headers(self):
"""Custom headers to avoid e.g. nginx reverse proxy from buffering SSE data"""
@@ -67,58 +67,58 @@ class APISpotsStreamHandler(tornado_eventsource.handler.EventSourceHandler):
try:
# Metrics
self.web_server_metrics["last_api_access_time"] = datetime.now(pytz.UTC)
self.web_server_metrics["api_access_counter"] += 1
self.web_server_metrics["status"] = "OK"
self._web_server_metrics["last_api_access_time"] = datetime.now(pytz.UTC)
self._web_server_metrics["api_access_counter"] += 1
self._web_server_metrics["status"] = "OK"
api_requests_counter.inc()
# request.arguments contains lists for each param key because technically the client can supply multiple,
# reduce that to just the first entry, and convert bytes to string
self.query_params = {k: v[0].decode("utf-8") for k, v in self.request.arguments.items()}
self._query_params = {k: v[0].decode("utf-8") for k, v in self.request.arguments.items()}
# Create a spot queue and add it to the web server's list. The web server will fill this when spots arrive
self.spot_queue = Queue(maxsize=SSE_HANDLER_MAX_QUEUE_SIZE)
self.sse_spot_queues.append(self.spot_queue)
self._spot_queue = Queue(maxsize=SSE_HANDLER_MAX_QUEUE_SIZE)
self._sse_spot_queues.append(self._spot_queue)
# Set up a timed callback to check if anything is in the queue
self.heartbeat = tornado.ioloop.PeriodicCallback(self._callback, SSE_HANDLER_QUEUE_CHECK_INTERVAL)
self.heartbeat.start()
self._heartbeat = tornado.ioloop.PeriodicCallback(self._callback, SSE_HANDLER_QUEUE_CHECK_INTERVAL)
self._heartbeat.start()
except Exception as e:
logging.warn("Exception when serving SSE socket", e)
logging.warning("Exception when serving SSE socket", e)
def close(self):
"""When the user closes the socket, empty our queue and remove it from the list so the server no longer fills it"""
try:
if self.spot_queue in self.sse_spot_queues:
self.sse_spot_queues.remove(self.spot_queue)
empty_queue(self.spot_queue)
if self._spot_queue in self._sse_spot_queues:
self._sse_spot_queues.remove(self._spot_queue)
empty_queue(self._spot_queue)
except:
pass
try:
self.heartbeat.stop()
self._heartbeat.stop()
except:
pass
self.spot_queue = None
self._spot_queue = None
super().close()
def _callback(self):
"""Callback to check if anything has arrived in the queue, and if so send it to the client"""
try:
if self.spot_queue:
while not self.spot_queue.empty():
spot = self.spot_queue.get()
if self._spot_queue:
while not self._spot_queue.empty():
spot = self._spot_queue.get()
# If the new spot matches our param filters, send it to the client. If not, ignore it.
if spot_allowed_by_query(spot, self.query_params):
if spot_allowed_by_query(spot, self._query_params):
self.write_message(msg=json.dumps(spot, default=serialize_everything))
if self.spot_queue not in self.sse_spot_queues:
if self._spot_queue not in self._sse_spot_queues:
logging.error("Web server cleared up a queue of an active connection!")
self.close()
except:
logging.warn("Exception in SSE callback, connection will be closed.")
logging.warning("Exception in SSE callback, connection will be closed.")
self.close()

View File

@@ -12,17 +12,17 @@ class APIStatusHandler(tornado.web.RequestHandler):
"""API request handler for /api/v1/status"""
def initialize(self, status_data, web_server_metrics):
self.status_data = status_data
self.web_server_metrics = web_server_metrics
self._status_data = status_data
self._web_server_metrics = web_server_metrics
def get(self):
# Metrics
self.web_server_metrics["last_api_access_time"] = datetime.now(pytz.UTC)
self.web_server_metrics["api_access_counter"] += 1
self.web_server_metrics["status"] = "OK"
self._web_server_metrics["last_api_access_time"] = datetime.now(pytz.UTC)
self._web_server_metrics["api_access_counter"] += 1
self._web_server_metrics["status"] = "OK"
api_requests_counter.inc()
self.write(json.dumps(self.status_data, default=serialize_everything))
self.write(json.dumps(self._status_data, default=serialize_everything))
self.set_status(200)
self.set_header("Cache-Control", "no-store")
self.set_header("Content-Type", "application/json")

View File

@@ -12,16 +12,16 @@ class PageTemplateHandler(tornado.web.RequestHandler):
"""Handler for all HTML pages generated from templates"""
def initialize(self, template_name, web_server_metrics):
self.template_name = template_name
self.web_server_metrics = web_server_metrics
self._template_name = template_name
self._web_server_metrics = web_server_metrics
def get(self):
# Metrics
self.web_server_metrics["last_page_access_time"] = datetime.now(pytz.UTC)
self.web_server_metrics["page_access_counter"] += 1
self.web_server_metrics["status"] = "OK"
self._web_server_metrics["last_page_access_time"] = datetime.now(pytz.UTC)
self._web_server_metrics["page_access_counter"] += 1
self._web_server_metrics["status"] = "OK"
page_requests_counter.inc()
# Load named template, and provide variables used in templates
self.render(self.template_name + ".html", software_version=SOFTWARE_VERSION, allow_spotting=ALLOW_SPOTTING,
self.render(self._template_name + ".html", software_version=SOFTWARE_VERSION, allow_spotting=ALLOW_SPOTTING,
web_ui_options=WEB_UI_OPTIONS)

View File

@@ -22,13 +22,13 @@ class WebServer:
def __init__(self, spots, alerts, status_data, port):
"""Constructor"""
self.spots = spots
self.alerts = alerts
self.sse_spot_queues = []
self.sse_alert_queues = []
self.status_data = status_data
self.port = port
self.shutdown_event = asyncio.Event()
self._spots = spots
self._alerts = alerts
self._sse_spot_queues = []
self._sse_alert_queues = []
self._status_data = status_data
self._port = port
self._shutdown_event = asyncio.Event()
self.web_server_metrics = {
"last_page_access_time": None,
"last_api_access_time": None,
@@ -40,33 +40,33 @@ class WebServer:
def start(self):
"""Start the web server"""
asyncio.run(self.start_inner())
asyncio.run(self._start_inner())
def stop(self):
"""Stop the web server"""
self.shutdown_event.set()
self._shutdown_event.set()
async def start_inner(self):
async def _start_inner(self):
"""Start method (async). Sets up the Tornado application."""
app = tornado.web.Application([
# Routes for API calls
(r"/api/v1/spots", APISpotsHandler, {"spots": self.spots, "web_server_metrics": self.web_server_metrics}),
(r"/api/v1/spots", APISpotsHandler, {"spots": self._spots, "web_server_metrics": self.web_server_metrics}),
(r"/api/v1/alerts", APIAlertsHandler,
{"alerts": self.alerts, "web_server_metrics": self.web_server_metrics}),
{"alerts": self._alerts, "web_server_metrics": self.web_server_metrics}),
(r"/api/v1/spots/stream", APISpotsStreamHandler,
{"sse_spot_queues": self.sse_spot_queues, "web_server_metrics": self.web_server_metrics}),
{"sse_spot_queues": self._sse_spot_queues, "web_server_metrics": self.web_server_metrics}),
(r"/api/v1/alerts/stream", APIAlertsStreamHandler,
{"sse_alert_queues": self.sse_alert_queues, "web_server_metrics": self.web_server_metrics}),
{"sse_alert_queues": self._sse_alert_queues, "web_server_metrics": self.web_server_metrics}),
(r"/api/v1/options", APIOptionsHandler,
{"status_data": self.status_data, "web_server_metrics": self.web_server_metrics}),
{"status_data": self._status_data, "web_server_metrics": self.web_server_metrics}),
(r"/api/v1/status", APIStatusHandler,
{"status_data": self.status_data, "web_server_metrics": self.web_server_metrics}),
{"status_data": self._status_data, "web_server_metrics": self.web_server_metrics}),
(r"/api/v1/lookup/call", APILookupCallHandler, {"web_server_metrics": self.web_server_metrics}),
(r"/api/v1/lookup/sigref", APILookupSIGRefHandler, {"web_server_metrics": self.web_server_metrics}),
(r"/api/v1/lookup/grid", APILookupGridHandler, {"web_server_metrics": self.web_server_metrics}),
(r"/api/v1/spot", APISpotHandler, {"spots": self.spots, "web_server_metrics": self.web_server_metrics}),
(r"/api/v1/spot", APISpotHandler, {"spots": self._spots, "web_server_metrics": self.web_server_metrics}),
# Routes for templated pages
(r"/", PageTemplateHandler, {"template_name": "spots", "web_server_metrics": self.web_server_metrics}),
(r"/map", PageTemplateHandler, {"template_name": "map", "web_server_metrics": self.web_server_metrics}),
@@ -87,14 +87,14 @@ class WebServer:
],
template_path=os.path.join(os.path.dirname(__file__), "../templates"),
debug=False)
app.listen(self.port)
await self.shutdown_event.wait()
app.listen(self._port)
await self._shutdown_event.wait()
def notify_new_spot(self, spot):
"""Internal method called when a new spot is added to the system. This is used to ping any SSE clients that are
awaiting a server-sent message with new spots."""
for queue in self.sse_spot_queues:
for queue in self._sse_spot_queues:
try:
queue.put(spot)
except:
@@ -106,7 +106,7 @@ class WebServer:
"""Internal method called when a new alert is added to the system. This is used to ping any SSE clients that are
awaiting a server-sent message with new spots."""
for queue in self.sse_alert_queues:
for queue in self._sse_alert_queues:
try:
queue.put(alert)
except:
@@ -118,22 +118,22 @@ class WebServer:
"""Clean up any SSE queues that are growing too large; probably their client disconnected and we didn't catch it
properly for some reason."""
for q in self.sse_spot_queues:
for q in self._sse_spot_queues:
try:
if q.full():
logging.warn(
logging.warning(
"A full SSE spot queue was found, presumably because the client disconnected strangely. It has been removed.")
self.sse_spot_queues.remove(q)
self._sse_spot_queues.remove(q)
empty_queue(q)
except:
# Probably got deleted already on another thread
pass
for q in self.sse_alert_queues:
for q in self._sse_alert_queues:
try:
if q.full():
logging.warn(
logging.warning(
"A full SSE alert queue was found, presumably because the client disconnected strangely. It has been removed.")
self.sse_alert_queues.remove(q)
self._sse_alert_queues.remove(q)
empty_queue(q)
except:
# Probably got deleted already on another thread