home · contact · privacy
Turn ._db.isupports into proper dict.
authorChristian Heller <c.heller@plomlompom.de>
Sun, 17 Aug 2025 02:47:27 +0000 (04:47 +0200)
committerChristian Heller <c.heller@plomlompom.de>
Sun, 17 Aug 2025 02:47:27 +0000 (04:47 +0200)
ircplom/client.py
ircplom/client_tui.py

index 036430fc7b9c0ea34630c8be408585949ee8f7a8..393f8e65ae65141561ab8aa5db071afcbe35c3a9 100644 (file)
@@ -274,6 +274,8 @@ class _Db(Db):
         self._still_on_init = True
         self._on_update = on_update
         super().__init__(**kwargs)
+        for key in [k for k in self._types if self._types[k] is _UpdatingDict]:
+            getattr(self, key).set_on_update(key, self._on_update)
         self._still_on_init = False
 
     def __setattr__(self, key: str, value) -> None:
@@ -312,13 +314,36 @@ class _ChannelDb(_Db):
     # channel_modes: str
 
 
+class _UpdatingDict:
+    _on_update: Callable
+
+    def __init__(self) -> None:
+        self._dict: dict[str, str] = {}
+
+    def set_on_update(self, name: str, on_update: Callable) -> None:
+        'Set on_update caller for "d {name} {key}."'
+        self._on_update = lambda k: on_update(f'd {name} {k}')
+
+    def clear(self) -> None:
+        'Zero dict and send CLEAR_WORD update.'
+        self._dict.clear()
+        self._on_update(CLEAR_WORD)
+
+    def __getitem__(self, key: str) -> str:
+        return self._dict[key]
+
+    def __setitem__(self, key: str, val: str) -> None:
+        self._dict[key] = val
+        self._on_update(key)
+
+
 class _ClientDb(_Db, IrcConnSetup):
     connection_state: str
     client_host: str
+    isupports: _UpdatingDict
     nickname_confirmed: bool
     user_modes: str
     _completable_motd: _CompletableStringsList
-    _completable_isupports: _CompletableStringsList
     _channels: dict[str, _ChannelDb]
 
     def del_chan(self, name: str) -> None:
@@ -330,7 +355,7 @@ class _ClientDb(_Db, IrcConnSetup):
         'Produce DB for channel of name – pre-existing, or newly created.'
         if name not in self._channels:
             self._channels[name] = _ChannelDb(
-                on_update=lambda k: self._on_update(f'{name} {k}'))
+                on_update=lambda k: self._on_update(f'{name} {k}'))
         return self._channels[name]
 
 
@@ -342,8 +367,8 @@ class Client(ABC, ClientQueueMixin):
     def __init__(self, conn_setup: IrcConnSetup, **kwargs) -> None:
         self.client_id = conn_setup.hostname
         super().__init__(client_id=self.client_id, **kwargs)
-        self._caps = _CapsManager(self.send, self._on_update)
         self._db = _ClientDb(on_update=self._on_update)
+        self._caps = _CapsManager(self.send, self._on_update)
         for k in conn_setup.__annotations__:
             setattr(self._db, k, getattr(conn_setup, k))
         if self._db.port <= 0:
@@ -411,6 +436,7 @@ class Client(ABC, ClientQueueMixin):
         if self.conn:
             self.conn.close()
         self.conn = None
+        self._db.isupports.clear()
         self._db.nickname_confirmed = False
 
     def on_handled_loop_exception(self, e: IrcConnAbortException) -> None:
@@ -421,8 +447,6 @@ class Client(ABC, ClientQueueMixin):
     def handle_msg(self, msg: IrcMessage) -> None:
         'Log msg.raw, then process incoming msg into appropriate client steps.'
         self._log(msg.raw, scope=LogScope.RAW, out=False)
-        if msg.verb != self._prev_verb and self._prev_verb == '005':
-            self._db.declare_complete('isupports')
         self._prev_verb = msg.verb
         if _NumericsToConfirmNickname.contain(msg.verb):
             self._db.nickname = msg.params[0]
@@ -436,7 +460,9 @@ class Client(ABC, ClientQueueMixin):
         match msg.verb:
             case '005' if len(msg.params) > 2:
                 for param in msg.params[1:-1]:
-                    self._db.append_completable('isupports', param)
+                    toks = param.split('=', maxsplit=1)
+                    self._db.isupports[toks[0]] = (toks[1] if len(toks) > 1
+                                                   else '')
             case '353' if len(msg.params) == 4:
                 for user in msg.params[3].split():
                     self._db.chan(msg.params[2]
index ce5961f7483e62b0ebab60bf5990e81a4f4c8ace..a0277c7eeb88ec4f26452c12b2dd659be1cb983f 100644 (file)
@@ -145,12 +145,24 @@ class _TuiClientDb(_Db, IrcConnSetup):
     caps: tuple[str]
     client_host: str
     connection_state: str
-    isupports: tuple[str]
+    isupports: dict[str, str]
     motd: tuple[str]
     nickname_confirmed: bool
     user_modes: str
     _channels: dict[str, _ChannelDb]
 
+    def set_and_check_for_change(self, key: str, value: _DbType) -> bool:
+        if ' ' in key:
+            _, dict_name, arg = key.split(' ')
+            d = getattr(self, dict_name)
+            if arg == CLEAR_WORD:
+                d.clear()
+                return True
+            old_value = d.get(arg, None)
+            d[arg] = value
+            return value != old_value
+        return super().set_and_check_for_change(key, value)
+
     def chan(self, name: str) -> _ChannelDb:
         'Produce DB for channel of name – pre-existing, or newly created.'
         if name not in self._channels:
@@ -210,17 +222,24 @@ class _ClientWindowsManager:
         db: _TuiClientDb | _ChannelDb = self._db
         scope = LogScope.SERVER
         log_kwargs: dict[str, str] = {}
+        clearing = False
+        what = key
         if ' ' in key:
-            chan_name, key = key.split()
-            db = self._db.chan(chan_name)
-            scope = LogScope.CHAT
-            log_kwargs |= {'channel': chan_name}
+            type_char, parent_name, subkey = key.split()
+            clearing = subkey == CLEAR_WORD
+            if type_char == 'c':
+                db = self._db.chan(parent_name)
+                scope = LogScope.CHAT
+                log_kwargs |= {'channel': parent_name}
+                key = what = subkey
+            else:
+                what = f'{parent_name}:{subkey}'
         elif key == 'connection_state':
             scope = LogScope.ALL
         if not db.set_and_check_for_change(key, value):
             return False
-        if key != CLEAR_WORD:
-            announcement = f'changed {key} to:'
+        if not clearing:
+            announcement = f'changed {what} to:'
             if isinstance(value, tuple):
                 self.log(announcement, scope=scope, **log_kwargs)
                 for item in value:
@@ -333,9 +352,13 @@ class _ClientKnowingTui(Client):
                 lines += [line]
             value = tuple(lines)
         elif ' ' in key:
-            chan_name, arg = key.split()
-            value = ('' if arg == CLEAR_WORD
-                     else getattr(self._db.chan(chan_name), arg))
+            type_char, parent_name, arg = key.split()
+            if arg == CLEAR_WORD:
+                value = ''
+            elif type_char == 'c':
+                value = getattr(self._db.chan(parent_name), arg)
+            else:
+                value = getattr(self._db, parent_name)[arg]
         else:
             value = getattr(self._db, key)
         self._client_tui_trigger('update_db', key=key, value=value)