refactor: extract constants, deduplicate ack pattern
keyboardstaff committed
Mar 28, 2026 at 02:51 UTC
4e222e2cf75d8dc7d427f0c7731be0f13d63e677
2 files changed
+69
-57
helpers/state_monitor.py
+2
-1
@@ -15,6 +15,7 @@ from helpers.state_snapshot import (
15
build_snapshot_from_request,
16
)
17
from helpers.ws import ConnectionIdentity, ConnectionNotFoundError, _ws_debug_enabled, ws_debug
18
+from helpers.ws_manager import STATE_PUSH_EVENT
19
20
if TYPE_CHECKING: # pragma: no cover - hints only
21
from helpers.ws_manager import WsManager
@@ -292,7 +293,7 @@ class StateMonitor:
293
await manager.emit_to(
294
namespace,
295
sid,
295
- "state_push",
296
+ STATE_PUSH_EVENT,
297
payload,
298
handler_id=handler_id,
299
)
helpers/ws_manager.py
+67
-56
@@ -233,6 +233,16 @@ class _HandlerExecution:
233
DIAGNOSTIC_EVENT = "ws_dev_console_event"
234
LIFECYCLE_CONNECT_EVENT = "ws_lifecycle_connect"
235
LIFECYCLE_DISCONNECT_EVENT = "ws_lifecycle_disconnect"
236
+STATE_PUSH_EVENT = "state_push"
237
+SERVER_RESTART_EVENT = "server_restart"
238
+
239
+# Error codes returned by _build_error_result
240
+ERR_NO_HANDLERS = "NO_HANDLERS"
241
+ERR_HANDLER_ERROR = "HANDLER_ERROR"
242
+ERR_INVALID_FILTER = "INVALID_FILTER"
243
+ERR_INVALID_EVENT = "INVALID_EVENT"
244
+ERR_CONNECTION_NOT_FOUND = "CONNECTION_NOT_FOUND"
245
+ERR_TIMEOUT = "TIMEOUT"
246
247
248
class WsManager:
@@ -559,13 +569,12 @@ class WsManager:
569
handler_payload["correlationId"] = correlation_id
570
571
if not handlers:
562
- error = self._build_error_result(
572
+ return self._ack_error(
573
handler_id=self._identifier,
564
- code="NO_HANDLERS",
574
+ code=ERR_NO_HANDLERS,
575
message="No handlers available after security filtering",
576
correlation_id=correlation_id,
577
)
568
- return {"correlationId": correlation_id, "results": [error]}
578
579
with self.lock:
580
info = self.connections.get((namespace, sid))
@@ -638,7 +647,7 @@ class WsManager:
647
results.append(
648
self._build_error_result(
649
handler_id=handler.identifier,
641
- code="HANDLER_ERROR",
650
+ code=ERR_HANDLER_ERROR,
651
message="Internal server error",
652
details=str(value),
653
correlation_id=correlation_id,
@@ -722,7 +731,7 @@ class WsManager:
731
await self.emit_to(
732
namespace,
733
sid,
725
- "server_restart",
734
+ SERVER_RESTART_EVENT,
735
{
736
"emittedAt": self._timestamp(),
737
"runtimeId": runtime.get_runtime_id(),
@@ -829,66 +838,55 @@ class WsManager:
838
include_meta_raw, "includeHandlers"
839
)
840
except ValueError as exc:
832
- error = self._build_error_result(
841
+ return self._ack_error(
842
handler_id=handler_id or self._identifier,
834
- code="INVALID_FILTER",
843
+ code=ERR_INVALID_FILTER,
844
message=str(exc),
845
correlation_id=correlation_id,
846
+ ack=ack,
847
)
838
- if ack:
839
- ack({"correlationId": correlation_id, "results": [error]})
840
- return {"correlationId": correlation_id, "results": [error]}
848
849
try:
850
exclude_meta = self._normalize_handler_filter(
851
exclude_meta_raw, "excludeHandlers"
852
)
853
except ValueError as exc:
847
- error = self._build_error_result(
854
+ return self._ack_error(
855
handler_id=handler_id or self._identifier,
849
- code="INVALID_FILTER",
856
+ code=ERR_INVALID_FILTER,
857
message=str(exc),
858
correlation_id=correlation_id,
859
+ ack=ack,
860
)
853
- payload_error = {"correlationId": correlation_id, "results": [error]}
854
- if ack:
855
- ack(payload_error)
856
- return payload_error
861
862
if exclude_meta_raw is not None and not allow_exclude:
859
- error = self._build_error_result(
863
+ return self._ack_error(
864
handler_id=handler_id or self._identifier,
861
- code="INVALID_FILTER",
865
+ code=ERR_INVALID_FILTER,
866
message="excludeHandlers is not supported for this operation",
867
correlation_id=correlation_id,
868
+ ack=ack,
869
)
865
- if ack:
866
- ack({"correlationId": correlation_id, "results": [error]})
867
- return {"correlationId": correlation_id, "results": [error]}
870
871
if include_handlers is not None and include_meta is not None:
872
if include_handlers != include_meta:
871
- error = self._build_error_result(
873
+ return self._ack_error(
874
handler_id=handler_id or self._identifier,
873
- code="INVALID_FILTER",
875
+ code=ERR_INVALID_FILTER,
876
message="Conflicting includeHandlers filters supplied",
877
correlation_id=correlation_id,
878
+ ack=ack,
879
)
877
- if ack:
878
- ack({"correlationId": correlation_id, "results": [error]})
879
- return {"correlationId": correlation_id, "results": [error]}
880
881
if allow_exclude and exclude_handlers is not None and exclude_meta is not None:
882
if exclude_handlers != exclude_meta:
883
- error = self._build_error_result(
883
+ return self._ack_error(
884
handler_id=handler_id or self._identifier,
885
- code="INVALID_FILTER",
885
+ code=ERR_INVALID_FILTER,
886
message="Conflicting excludeHandlers filters supplied",
887
correlation_id=correlation_id,
888
+ ack=ack,
889
)
889
- if ack:
890
- ack({"correlationId": correlation_id, "results": [error]})
891
- return {"correlationId": correlation_id, "results": [error]}
890
891
include = include_handlers or include_meta
892
exclude = exclude_handlers or (exclude_meta if allow_exclude else None)
@@ -896,54 +894,46 @@ class WsManager:
894
try:
895
validate_event_type(event_type)
896
except (TypeError, ValueError) as exc:
899
- error = self._build_error_result(
897
+ return self._ack_error(
898
handler_id=handler_id or self._identifier,
901
- code="INVALID_EVENT",
899
+ code=ERR_INVALID_EVENT,
900
message=str(exc),
901
correlation_id=correlation_id,
902
+ ack=ack,
903
)
905
- if ack:
906
- ack({"correlationId": correlation_id, "results": [error]})
907
- return {"correlationId": correlation_id, "results": [error]}
904
905
registered = self.handlers.get(namespace, [])
906
if not registered:
907
PrintStyle.warning(f"No handlers registered for namespace '{namespace}'")
912
- error = self._build_error_result(
908
+ return self._ack_error(
909
handler_id=handler_id or self._identifier,
914
- code="NO_HANDLERS",
910
+ code=ERR_NO_HANDLERS,
911
message=f"No handler for namespace '{namespace}'",
912
correlation_id=correlation_id,
913
+ ack=ack,
914
)
918
- if ack:
919
- ack({"correlationId": correlation_id, "results": [error]})
920
- return {"correlationId": correlation_id, "results": [error]}
915
916
try:
917
selected_handlers, _ = self._select_handlers(
918
namespace, include=include, exclude=exclude
919
)
920
except ValueError as exc:
927
- error = self._build_error_result(
921
+ return self._ack_error(
922
handler_id=handler_id or self._identifier,
929
- code="INVALID_FILTER",
923
+ code=ERR_INVALID_FILTER,
924
message=str(exc),
925
correlation_id=correlation_id,
926
+ ack=ack,
927
)
933
- if ack:
934
- ack({"correlationId": correlation_id, "results": [error]})
935
- return {"correlationId": correlation_id, "results": [error]}
928
929
if not selected_handlers:
938
- error = self._build_error_result(
930
+ return self._ack_error(
931
handler_id=handler_id or self._identifier,
940
- code="NO_HANDLERS",
932
+ code=ERR_NO_HANDLERS,
933
message=f"No handler for '{event_type}' after applying filters",
934
correlation_id=correlation_id,
935
+ ack=ack,
936
)
944
- if ack:
945
- ack({"correlationId": correlation_id, "results": [error]})
946
- return {"correlationId": correlation_id, "results": [error]}
937
938
with self.lock:
939
info = self.connections.get((namespace, sid))
@@ -1011,7 +1001,7 @@ class WsManager:
1001
"results": [
1002
self._build_error_result(
1003
handler_id=handler_id or self._identifier,
1014
- code="CONNECTION_NOT_FOUND",
1004
+ code=ERR_CONNECTION_NOT_FOUND,
1005
message=f"Connection '{sid}' not found in namespace '{namespace}'",
1006
correlation_id=correlation_id,
1007
)
@@ -1040,7 +1030,7 @@ class WsManager:
1030
"results": [
1031
self._build_error_result(
1032
handler_id=handler_id or self._identifier,
1043
- code="TIMEOUT",
1033
+ code=ERR_TIMEOUT,
1034
message="Request timeout",
1035
correlation_id=correlation_id,
1036
)
@@ -1073,7 +1063,7 @@ class WsManager:
1063
except ValueError as exc:
1064
error = self._build_error_result(
1065
handler_id=handler_id or self._identifier,
1076
- code="INVALID_FILTER",
1066
+ code=ERR_INVALID_FILTER,
1067
message=str(exc),
1068
correlation_id=correlation_id,
1069
)
@@ -1090,7 +1080,7 @@ class WsManager:
1080
elif exclude_meta is not None and exclude_combined != exclude_meta:
1081
error = self._build_error_result(
1082
handler_id=handler_id or self._identifier,
1093
- code="INVALID_FILTER",
1083
+ code=ERR_INVALID_FILTER,
1084
message="Conflicting excludeHandlers filters supplied",
1085
correlation_id=correlation_id,
1086
)
@@ -1155,7 +1145,7 @@ class WsManager:
1145
"results": [
1146
self._build_error_result(
1147
handler_id=handler_id or self._identifier,
1158
- code="TIMEOUT",
1148
+ code=ERR_TIMEOUT,
1149
message="Request timeout",
1150
correlation_id=correlation_id,
1151
)
@@ -1459,6 +1449,27 @@ class WsManager:
1449
result["durationMs"] = round(duration_ms, 4)
1450
return result
1451
1452
+ def _ack_error(
1453
+ self,
1454
+ *,
1455
+ handler_id: str | None = None,
1456
+ code: str,
1457
+ message: str,
1458
+ correlation_id: str | None = None,
1459
+ ack: Callable | None = None,
1460
+ ) -> dict[str, Any]:
1461
+ """Build an error response, optionally invoke the ack callback, and return."""
1462
+ error = self._build_error_result(
1463
+ handler_id=handler_id,
1464
+ code=code,
1465
+ message=message,
1466
+ correlation_id=correlation_id,
1467
+ )
1468
+ response = {"correlationId": correlation_id, "results": [error]}
1469
+ if ack:
1470
+ ack(response)
1471
+ return response
1472
+
1473
# Session tracking helpers (single-user defaults)
1474
def get_sids_for_user(self, user: str | None = None) -> list[ConnectionIdentity]:
1475
"""Return connection identities for a user; single-user default returns all."""