Skip to content

Commit 4aa4535

Browse files
committed
Refactor SocketClient connection handling and error logging
Avoid nested partial exception capture and let exception propagate to where we can make an informed choices. We don't need to catch unexpected errors we can't do anything deep in nested functions. It's better for them to propagate out to where we can do something about them.
1 parent 22e21b3 commit 4aa4535

File tree

1 file changed

+56
-90
lines changed

1 file changed

+56
-90
lines changed

pychromecast/socket_client.py

Lines changed: 56 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -396,14 +396,7 @@ def mdns_backoff(
396396
except (OSError, NotConnected) as err:
397397
self.connecting = True
398398
if self.stop.is_set():
399-
self.logger.error(
400-
"[%s(%s):%s] Failed to connect: %s. aborting due to stop signal.",
401-
self.fn or "",
402-
self.host,
403-
self.port,
404-
err,
405-
)
406-
raise ChromecastConnectionError("Failed to connect") from err
399+
raise InterruptLoop() from err
407400

408401
self._report_connection_status(
409402
ConnectionStatus(
@@ -438,7 +431,6 @@ def mdns_backoff(
438431
if tries:
439432
tries -= 1
440433

441-
self.stop.set()
442434
self.logger.error(
443435
"[%s(%s):%s] Failed to connect. No retries.",
444436
self.fn or "",
@@ -531,43 +523,76 @@ def run(self) -> None:
531523
try:
532524
self.initialize_connection()
533525
except ChromecastConnectionError:
534-
self._report_connection_status(
535-
ConnectionStatus(
536-
CONNECTION_STATUS_DISCONNECTED,
537-
NetworkAddress(self.host, self.port),
538-
None,
539-
)
526+
self.logger.debug(
527+
"[%s(%s):%s] Connection error to host closing down",
528+
self.fn or "",
529+
self.host,
530+
self.port,
531+
exc_info=True,
540532
)
533+
self._cleanup()
534+
return
535+
536+
except InterruptLoop:
537+
self._cleanup()
541538
return
542539

543540
self.heartbeat_controller.reset()
544541
self.logger.debug("Thread started...")
545542
while not self.stop.is_set():
546543
try:
547-
if self._run_once() == 1:
548-
break
544+
self._run_once()
545+
546+
except ChromecastConnectionError:
547+
self.logger.debug(
548+
"[%s(%s):%s] Connection error to host closing down",
549+
self.fn or "",
550+
self.host,
551+
self.port,
552+
exc_info=True,
553+
)
554+
break
555+
556+
except InterruptLoop:
557+
break
558+
559+
except ChromecastConnectionClosed as exc:
560+
self._force_recon = True
561+
self.logger.debug(
562+
"[%s(%s):%s] %s",
563+
self.fn or "",
564+
self.host,
565+
self.port,
566+
exc,
567+
)
568+
549569
except Exception: # pylint: disable=broad-except
570+
if self.stop.is_set():
571+
self.logger.debug(
572+
"[%s(%s):%s] Unhandled exception in worker thread while stopping",
573+
self.fn or "",
574+
self.host,
575+
self.port,
576+
exc_info=True,
577+
)
578+
break
579+
550580
self._force_recon = True
551581
self.logger.exception(
552582
"[%s(%s):%s] Unhandled exception in worker thread, attempting reconnect",
553583
self.fn or "",
554584
self.host,
555585
self.port,
556586
)
587+
time.sleep(self.retry_wait)
557588

558589
self.logger.debug("Thread done...")
559590
# Clean up
560591
self._cleanup()
561592

562-
def _run_once(self) -> int:
593+
def _run_once(self) -> None:
563594
"""Receive from the socket and handle data."""
564-
# pylint: disable=too-many-branches, too-many-statements, too-many-return-statements
565-
566-
try:
567-
if not self._check_connection():
568-
return 0
569-
except ChromecastConnectionError:
570-
return 1
595+
self._check_connection()
571596

572597
# A connection has been established at this point by self._check_connection
573598
assert self.socket is not None
@@ -577,65 +602,13 @@ def _run_once(self) -> int:
577602
# the HeartbeatController from detecting a timeout
578603
# The socketpair allow us to be interrupted on shutdown without waiting
579604
# for the SELECT_TIMEOUT timout to expire
580-
try:
581-
ready = self.selector.select(SELECT_TIMEOUT)
582-
except (ValueError, OSError) as exc:
583-
self.logger.error(
584-
"[%s(%s):%s] Error in select call: %s",
585-
self.fn or "",
586-
self.host,
587-
self.port,
588-
exc,
589-
)
590-
self._force_recon = True
591-
return 0
605+
ready = self.selector.select(SELECT_TIMEOUT)
592606

593607
can_read = {key for key, _ in ready}
594608
# read message from chromecast
595609
message = None
596610
if self.remote_selector_key in can_read and not self._force_recon:
597-
try:
598-
message = self._read_message()
599-
except InterruptLoop as exc:
600-
if self.stop.is_set():
601-
self.logger.info(
602-
"[%s(%s):%s] Stopped while reading message, disconnecting.",
603-
self.fn or "",
604-
self.host,
605-
self.port,
606-
)
607-
else:
608-
self.logger.error(
609-
"[%s(%s):%s] Interruption caught without being stopped: %s",
610-
self.fn or "",
611-
self.host,
612-
self.port,
613-
exc,
614-
)
615-
return 1
616-
except ssl.SSLError as exc:
617-
if exc.errno == ssl.SSL_ERROR_EOF:
618-
if self.stop.is_set():
619-
return 1
620-
raise
621-
except ChromecastConnectionClosed as exc:
622-
self._force_recon = True
623-
self.logger.debug(
624-
"[%s(%s):%s] %s",
625-
self.fn or "",
626-
self.host,
627-
self.port,
628-
exc,
629-
)
630-
except socket.error as exc:
631-
self._force_recon = True
632-
self.logger.error(
633-
"[%s(%s):%s] Error reading from socket: %s",
634-
self.fn or "",
635-
self.host,
636-
self.port,
637-
exc,
638-
)
611+
message = self._read_message()
639612

640613
if self.wakeup_selector_key in can_read:
641614
# Clear the socket's buffer
@@ -644,10 +617,10 @@ def _run_once(self) -> int:
644617
# If we are stopped after receiving a message we skip the message
645618
# and tear down the connection
646619
if self.stop.is_set():
647-
return 1
620+
raise InterruptLoop()
648621

649622
if not message:
650-
return 0
623+
return
651624

652625
# See if any handlers will accept this message
653626
data = _dict_from_message_payload(message)
@@ -656,9 +629,7 @@ def _run_once(self) -> int:
656629
if REQUEST_ID in data and data[REQUEST_ID] in self._request_callbacks:
657630
self._request_callbacks.pop(data[REQUEST_ID])(True, data)
658631

659-
return 0
660-
661-
def _check_connection(self) -> bool:
632+
def _check_connection(self) -> None:
662633
"""
663634
Checks if the connection is active, and if not reconnect
664635
@@ -694,12 +665,7 @@ def _check_connection(self) -> bool:
694665
CONNECTION_STATUS_LOST, NetworkAddress(self.host, self.port), None
695666
)
696667
)
697-
try:
698-
self.initialize_connection()
699-
except ChromecastConnectionError:
700-
self.stop.set()
701-
return False
702-
return True
668+
self.initialize_connection()
703669

704670
def _route_message(self, message: CastMessage, data: dict) -> None:
705671
"""Route message to any handlers on the message namespace"""

0 commit comments

Comments
 (0)