Typing fixes for snapcast (#2624)
authorOzGav <gavnosp@hotmail.com>
Tue, 18 Nov 2025 11:08:34 +0000 (21:08 +1000)
committerGitHub <noreply@github.com>
Tue, 18 Nov 2025 11:08:34 +0000 (12:08 +0100)
* Typing fix for SiriusXM

* Typing fixes for snapcast

* fix return types

* Remove erroneous commit

* Revert erroneous commit

* Fix minor errors

music_assistant/providers/snapcast/control.py
music_assistant/providers/snapcast/player.py
music_assistant/providers/snapcast/provider.py
pyproject.toml

index 9dec33ae3fafcddf97731723919fab7f63d3dc82..2a7c6da6826932138a119fdc3c89745d51c7e860 100755 (executable)
@@ -29,7 +29,7 @@ LOOP_STATUS_MAP_REVERSE = {v: k for k, v in LOOP_STATUS_MAP.items()}
 MessageCallback = Callable[[dict[str, Any]], None]
 
 
-def send(json_msg: dict[str, Any]):
+def send(json_msg: dict[str, Any]) -> None:
     """Send a message to stdout."""
     sys.stdout.write(json.dumps(json_msg))
     sys.stdout.write("\n")
@@ -47,8 +47,8 @@ class MusicAssistantControl:
         self.api_port = api_port
         self.streamserver_ip = streamserver_ip
         self.streamserver_port = streamserver_port
-        self._metadata = {}
-        self._properties = {}
+        self._metadata: dict[str, Any] = {}
+        self._properties: dict[str, Any] = {}
         self._request_callbacks: dict[str, MessageCallback] = {}
         self._seek_offset = 0.0
         self.websocket = websocket.WebSocketApp(
@@ -63,7 +63,7 @@ class MusicAssistantControl:
         self.websocket_thread.name = "massControl"
         self.websocket_thread.start()
 
-    def stop(self):
+    def stop(self) -> None:
         """Stop the websocket thread."""
         self._stopped = True
         self.websocket.close()
@@ -134,7 +134,7 @@ class MusicAssistantControl:
             #     self.send_request("core.mixer.set_mute", {"mute": properties["mute"]})
         elif cmd == "GetProperties":
 
-            def handle_result(result: dict[str, Any]):
+            def handle_result(result: dict[str, Any]) -> None:
                 send(
                     {
                         "jsonrpc": "2.0",
@@ -173,7 +173,7 @@ class MusicAssistantControl:
         """Send stream ready notification to Snapcast."""
         send({"jsonrpc": "2.0", "method": "Plugin.Stream.Ready"})
 
-    def _websocket_loop(self):
+    def _websocket_loop(self) -> None:
         logger.info("Started websocket loop")
         while not self._stopped:
             try:
@@ -235,7 +235,7 @@ class MusicAssistantControl:
 
         return properties
 
-    def _on_ws_message(self, ws, message: str):
+    def _on_ws_message(self, ws: websocket.WebSocket, message: str) -> None:
         # TODO: error handling
         logger.debug("websocket message received: %s", message)
         data = json.loads(message)
@@ -257,15 +257,17 @@ class MusicAssistantControl:
                 self.send_snapcast_properties_notification(properties)
                 return
 
-    def _on_ws_error(self, ws, error):
+    def _on_ws_error(self, ws: websocket.WebSocket, error: Exception | str) -> None:
         logger.error("Websocket error")
         logger.error(error)
 
-    def _on_ws_open(self, ws):
+    def _on_ws_open(self, ws: websocket.WebSocket) -> None:
         logger.info("Snapcast RPC websocket opened")
         self.send_snapcast_stream_ready_notification()
 
-    def _on_ws_close(self, ws, close_status_code, close_msg):
+    def _on_ws_close(
+        self, ws: websocket.WebSocket, close_status_code: int | None, close_msg: str | None
+    ) -> None:
         logger.info("Snapcast RPC websocket closed")
 
     def send_request(
index 8dff0dd6bffb2ad284cf7e76749340fe757a3d10..f9dc408a19896a216b4f104499038ab6c988a53a 100644 (file)
@@ -47,11 +47,11 @@ class SnapCastPlayer(Player):
         snap_client_id: str,
     ) -> None:
         """Init."""
-        self.provider: SnapCastProvider
+        self.provider: SnapCastProvider  # type: ignore[misc]
         self.snap_client = snap_client
         self.snap_client_id = snap_client_id
         super().__init__(provider, player_id)
-        self._stream_task: asyncio.Task | None = None
+        self._stream_task: asyncio.Task[None] | None = None
 
     @property
     def synced_to(self) -> str | None:
@@ -359,7 +359,6 @@ class SnapCastPlayer(Player):
                 f"--streamserver-ip={self.mass.streams.publish_ip}%20"
                 f"--streamserver-port={self.mass.streams.publish_port}"
             )
-            extra_args = ""
         else:
             extra_args = ""
 
@@ -425,10 +424,10 @@ class SnapCastPlayer(Player):
         if self.synced_to is not None:
             return
         self._attr_group_members.append(self.player_id)
-        {
-            self._attr_group_members.append(self.provider._get_ma_id(snap_client_id))
-            for snap_client_id in snap_group.clients
-            if self.provider._get_ma_id(snap_client_id) != self.player_id
-            and self.provider._snapserver.client(snap_client_id).connected
-        }
+        for snap_client_id in snap_group.clients:
+            if (
+                self.provider._get_ma_id(snap_client_id) != self.player_id
+                and self.provider._snapserver.client(snap_client_id).connected
+            ):
+                self._attr_group_members.append(self.provider._get_ma_id(snap_client_id))
         self.update_state()
index 39189cebb7206ed112157e4363b1b5f67a752f15..7d67587f3436d440cb2902f5985f97df5e6e52a6 100644 (file)
@@ -39,7 +39,7 @@ class SnapCastProvider(PlayerProvider):
     """SnapCastProvider."""
 
     _snapserver: Snapserver
-    _snapserver_runner: asyncio.Task | None
+    _snapserver_runner: asyncio.Task[None] | None
     _snapserver_started: asyncio.Event | None
     _snapcast_server_host: str
     _snapcast_server_control_port: int
@@ -191,9 +191,9 @@ class SnapCastProvider(PlayerProvider):
         ]
         async with AsyncProcess(args, stdout=True, name="snapserver") as snapserver_proc:
             # keep reading from stdout until exit
-            async for data in snapserver_proc.iter_any():
-                data = data.decode().strip()  # noqa: PLW2901
-                for line in data.split("\n"):
+            async for raw_data in snapserver_proc.iter_any():
+                text = raw_data.decode().strip()
+                for line in text.split("\n"):
                     logger.debug(line)
                     if "(Snapserver) Version 0." in line:
                         # delay init a small bit to prevent race conditions
@@ -212,7 +212,7 @@ class SnapCastProvider(PlayerProvider):
         assert snap_id is not None  # for type checking
         return snap_id
 
-    def _generate_and_register_id(self, snap_client_id) -> str:
+    def _generate_and_register_id(self, snap_client_id: str) -> str:
         search_dict = self._ids_map.inverse
         if snap_client_id not in search_dict:
             new_id = "ma_" + str(re.sub(r"\W+", "", snap_client_id))
@@ -254,8 +254,8 @@ class SnapCastProvider(PlayerProvider):
             if ma_player := self._handle_player_init(snap_client):
                 snap_client.set_callback(ma_player._handle_player_update)
         for snap_client in self._snapserver.clients:
-            if ma_player := self.mass.players.get(self._get_ma_id(snap_client.identifier)):
-                assert isinstance(ma_player, SnapCastPlayer)  # for type checking
+            if player := self.mass.players.get(self._get_ma_id(snap_client.identifier)):
+                ma_player = cast("SnapCastPlayer", player)
                 snap_client.set_callback(ma_player._handle_player_update)
         for snap_group in self._snapserver.groups:
             snap_group.set_callback(self._handle_group_update)
index a65ad10891af368ad634c7e25a4fbe88dd264c64..58983d689509605a3746dc515288c0f198a3793d 100644 (file)
@@ -143,7 +143,6 @@ exclude = [
   '^music_assistant/providers/bluesound/.*$',
   '^music_assistant/providers/chromecast/.*$',
   '^music_assistant/providers/sonos/.*$',
-  '^music_assistant/providers/snapcast/.*$',
   '^music_assistant/providers/ytmusic/.*$',
 ]
 extra_checks = false