home · contact · privacy
Refactor IrcConnException hierarchy.
authorChristian Heller <c.heller@plomlompom.de>
Mon, 6 Oct 2025 19:29:47 +0000 (21:29 +0200)
committerChristian Heller <c.heller@plomlompom.de>
Mon, 6 Oct 2025 19:29:47 +0000 (21:29 +0200)
src/ircplom/client.py
src/ircplom/irc_conn.py
src/ircplom/testing.py

index fd05bf96b1f67e3f26862f3066b3ca377df83cc8..141b59e9c0319cd1f3440b5254c9d7a00f6491e1 100644 (file)
@@ -13,9 +13,8 @@ from uuid import UUID, uuid4
 from ircplom.events import (
     AffectiveEvent, CrashingException, ExceptionEvent, QueueMixin)
 from ircplom.irc_conn import (
-    BaseIrcConnection, IrcConnAbortException, IrcConnException,
-    IrcConnTimeoutException, IrcMessage, ILLEGAL_NICK_CHARS,
-    ILLEGAL_NICK_FIRSTCHARS, ISUPPORT_DEFAULTS, PORT_SSL)
+    BaseIrcConnection, IrcConnException, IrcMessage, ERR_STR_TIMEOUT,
+    ILLEGAL_NICK_CHARS, ILLEGAL_NICK_FIRSTCHARS, ISUPPORT_DEFAULTS, PORT_SSL)
 from ircplom.msg_parse_expectations import MSG_EXPECTATIONS
 
 
@@ -861,7 +860,7 @@ class Client(ABC, ClientQueueMixin):
 
     def _on_connecting_exception(self, e: IrcConnException) -> None:
         self.db.connection_state = f'failed to connect: {e}'
-        if isinstance(e, IrcConnTimeoutException):
+        if e.retry:
             if not self._retry_connect_in_s > 0:
                 self._retry_connect_in_s = 1
             self._delayed_retry_connect()
@@ -902,20 +901,20 @@ class Client(ABC, ClientQueueMixin):
             self._delayed_retry_connect()
 
     def on_handled_loop_exception(self, e: IrcConnException) -> None:
-        'On …AbortException, call .close(), on …Timeout… first (!) try PING.'
-        if isinstance(e, IrcConnAbortException):
-            self._retry_connect_in_s = 1
+        'Execute e.abort, .retry, or interpret str(e) to PING/check on PONG.'
+        if e.abort:
+            if e.retry:
+                self._retry_connect_in_s = 1
             self.db.connection_state = f'broken: {e}'
             self.close()
-        elif isinstance(e, IrcConnTimeoutException):
+        elif str(e) == ERR_STR_TIMEOUT:
             if self._expected_pong:
                 self.on_handled_loop_exception(
-                        IrcConnAbortException('no timely PONG from server'))
+                        IrcConnException('no timely PONG from server',
+                                         abort=True, retry=True))
             else:
                 self._expected_pong = "what's up?"
                 self.send('PING', self._expected_pong)
-        else:
-            raise e
 
     @abstractmethod
     def _on_update(self, *path) -> None:
index 2605e9474ee3a68d793131cd0110640fdf636e4a..9e74bd29311ecf2aed04891ae85e689f084d1e7c 100644 (file)
@@ -15,6 +15,8 @@ _TIMEOUT_PING = 120
 _TIMEOUT_CONNECT = 5
 _CONN_RECV_BUFSIZE = 1024
 
+ERR_STR_TIMEOUT = 'timeout'
+
 ILLEGAL_NICK_CHARS = ' ,*?!@'
 ILLEGAL_NICK_FIRSTCHARS = ':$'
 ISUPPORT_DEFAULTS = {
@@ -130,16 +132,16 @@ class IrcMessage:
         return self._raw
 
 
-class IrcConnException(BaseException):
+class IrcConnException(Exception):
     'Thrown by BaseIrcConnection on expectable connection issues.'
 
+    def __init__(self, *args, abort=True, retry=False, **kwargs) -> None:
+        super().__init__(*args, **kwargs)
+        self.abort = abort
+        self.retry = retry
 
-class IrcConnAbortException(IrcConnException):
-    'Thrown by BaseIrcConnection on expectable connection failures.'
-
-
-class IrcConnTimeoutException(IrcConnException):
-    'Thrown by BaseIrcConnection if recv timeout triggered.'
+    def __str__(self) -> str:
+        return str(self.__cause__) if not self.args else ' '.join(self.args)
 
 
 class BaseIrcConnection(QueueMixin, ABC):
@@ -160,9 +162,9 @@ class BaseIrcConnection(QueueMixin, ABC):
         try:
             self._socket.connect((hostname, port))
         except TimeoutError as e:
-            raise IrcConnTimeoutException(e) from e
+            raise IrcConnException(retry=True) from e
         except socket_gaierror as e:
-            raise IrcConnAbortException(e) from e
+            raise IrcConnException() from e
         self._socket.settimeout(_TIMEOUT_RECV_LOOP)
 
     def close(self) -> None:
@@ -191,20 +193,20 @@ class BaseIrcConnection(QueueMixin, ABC):
             while True:
                 try:
                     bytes_new = self._socket.recv(_CONN_RECV_BUFSIZE)
-                except TimeoutError as e:
+                except TimeoutError:
                     if (datetime.now() - time_last_ping_check).seconds\
                             > _TIMEOUT_PING:
                         time_last_ping_check = datetime.now()
                         yield self._on_handled_loop_exception(
-                                IrcConnTimeoutException(e))
+                                IrcConnException(ERR_STR_TIMEOUT, abort=False))
                     else:
                         yield None
                     continue
                 except ConnectionResetError as e:
-                    raise IrcConnAbortException(e) from e
+                    raise IrcConnException(retry=True) from e
                 except OSError as e:
                     if e.errno == 9:
-                        raise IrcConnAbortException(e) from e
+                        raise IrcConnException(retry=True) from e
                     raise e
                 if not bytes_new:
                     break
index b3ecd3ed4a8359c7f99a0238b09fb00ab8bc23b9..ad63e37deff067ad40742012102b05829fbae442 100644 (file)
@@ -7,8 +7,7 @@ from typing import Callable, Generator, Iterator, Optional
 from ircplom.events import Event, Loop, QueueMixin
 from ircplom.client import IrcConnection, IrcConnSetup
 from ircplom.client_tui import ClientKnowingTui, ClientTui, LOG_PREFIX_IN
-from ircplom.irc_conn import (IrcConnAbortException, IrcConnTimeoutException,
-                              IrcMessage)
+from ircplom.irc_conn import ERR_STR_TIMEOUT, IrcConnException, IrcMessage
 from ircplom.tui_base import (TerminalInterface, TuiEvent,
                               LOG_FMT_SEP, LOG_FMT_ATTRS)
 
@@ -69,7 +68,7 @@ class _FakeIrcConnection(IrcConnection):
 
     def _set_up_socket(self, hostname: str, port: int) -> None:
         if port > _FAKE_TIMEOUT_PORTS_BEYOND:
-            raise IrcConnTimeoutException('FAKE TESTING TIMEOUT')
+            raise IrcConnException('FAKE TESTING TIMEOUT', retry=True)
 
     def close(self) -> None:
         self._recv_loop.stop()
@@ -87,12 +86,12 @@ class _FakeIrcConnection(IrcConnection):
                     continue
                 if msg == 'FAKE_IRC_CONN_TIMEOUT_EXCEPTION':
                     yield self._on_handled_loop_exception(
-                            IrcConnTimeoutException(msg))
+                            IrcConnException(ERR_STR_TIMEOUT, abort=False))
                     continue
                 if msg == 'FAKE_IRC_CONN_ABORT_EXCEPTION':
-                    raise IrcConnAbortException(msg)
+                    raise IrcConnException(msg, retry=True)
                 yield self._make_recv_event(IrcMessage.from_raw(msg))
-        except IrcConnAbortException as e:
+        except IrcConnException as e:
             yield self._on_handled_loop_exception(e)