home · contact · privacy
Refactor _CompletableStringsList usage.
authorChristian Heller <c.heller@plomlompom.de>
Mon, 25 Aug 2025 20:01:31 +0000 (22:01 +0200)
committerChristian Heller <c.heller@plomlompom.de>
Mon, 25 Aug 2025 20:01:31 +0000 (22:01 +0200)
ircplom/client.py
ircplom/client_tui.py

index 4784507d837fe3c80e248eb0980949845b862342..babc87f8a9dcffcb2e541eff3ece21d6eb4af0cf 100644 (file)
@@ -478,15 +478,11 @@ class _CapsManager:
         'Parse CAP message to negot. steps, DB inputs; return if successful.'
         for item in items:
             if verb == 'NEW':
-                self._ls.append(item, stay_complete=True)
-                self._update_cap(item)
+                name, data = _ServerCapability.split_name_data(item)
+                self._dict[name] = _ServerCapability(False, data)
             elif verb == 'DEL':
-                self._ls.remove(item, stay_complete=True)
-                for name in [
-                        name for name in self._list.visible
-                        if _ServerCapability.split_name_data(name)[0] == item]:
-                    self._list.remove(name, stay_complete=True)
-                self._del_cap(item)
+                name, _ = _ServerCapability.split_name_data(item)
+                del self._dict[name]
             elif verb in {'ACK', 'NACK'}:
                 self._list_expectations[verb].add(item)
         if verb in {'LS', 'LIST'}:
@@ -494,22 +490,22 @@ class _CapsManager:
             for item in items:
                 target.append(item)
             if not incomplete:
-                target.is_complete = True
+                target.complete()
                 if target == self._list:
                     acks = self._list_expectations['ACK']
                     naks = self._list_expectations['NAK']
-                    list_set = set(self._list.visible)
+                    list_set = set(target.completed)
                     assert acks == list_set & acks
                     assert set() == list_set & naks
-                if self._list.is_complete:
+                if self._list.completed:
                     for name, data in [_ServerCapability.split_name_data(item)
-                                       for item in self._ls.visible]:
+                                       for item in self._ls.completed]:
                         self._dict[name] = _ServerCapability(
-                            name in self._list.visible, data)
+                            name in self._list.completed, data)
                     return True
-                if self._ls.is_complete:
+                if self._ls.completed:
                     availables = [_ServerCapability.split_name_data(item)[0]
-                                  for item in self._ls.visible]
+                                  for item in self._ls.completed]
                     for cap_name in [n for n in _NAMES_DESIRED_SERVER_CAPS
                                      if n in availables]:
                         self._challenge('REQ', cap_name)
@@ -526,15 +522,6 @@ class _CapsManager:
             self._send(*params)
             self._sent_challenges.append(' '.join(params))
 
-    def _update_cap(self, full_ls_item: str) -> None:
-        name, data = _ServerCapability.split_name_data(full_ls_item)
-        is_enabled = name in self._list.visible
-        self._dict[name] = _ServerCapability(is_enabled, data)
-
-    def _del_cap(self, full_ls_item) -> None:
-        name, _ = _ServerCapability.split_name_data(full_ls_item)
-        del self._dict[name]
-
 
 @dataclass
 class IrcConnSetup:
@@ -572,33 +559,22 @@ class Db:
 
 
 class _CompletableStringsList:
+    _on_update: Callable
 
     def __init__(self) -> None:
-        self._items: list[str] = []
-        self.is_complete = False
+        self._incomplete: list[str] = []
+        self.completed: tuple[str, ...] = tuple()
 
-    @property
-    def visible(self) -> tuple[str, ...]:
-        'Tuple of collected items if .is_complete, otherwise empty.'
-        return tuple(self._items) if self.is_complete else tuple()
-
-    def _stay_complete(self, assert_complete: bool) -> None:
-        if assert_complete:
-            assert self.is_complete
-        else:
-            if self.is_complete:
-                self._items.clear()
-            self.is_complete = False
+    def append(self, value: str) -> None:
+        'Append value to list.'
+        self._incomplete.append(value)
 
-    def append(self, item: str, stay_complete=False) -> None:
-        'Append to list; if .is_complete yet not stay_complete, tabula rasa.'
-        self._stay_complete(stay_complete)
-        self._items.append(item)
-
-    def remove(self, item: str, stay_complete=False) -> None:
-        'Remove from list; if .is_complete yet not stay_complete, tabula rasa.'
-        self._stay_complete(stay_complete)
-        self._items.remove(item)
+    def complete(self) -> None:
+        'Declare list done, call updater if set.'
+        self.completed = tuple(self._incomplete)
+        self._incomplete.clear()
+        if hasattr(self, '_on_update'):
+            self._on_update()
 
 
 class _Db(Db):
@@ -652,7 +628,6 @@ class _ChannelDb(_Db, SharedChannelDbFields):
 class SharedClientDbFields(IrcConnSetup, Generic[_ChannelDbFields]):
     'API for fields shared directly in name and type with TUI.'
     connection_state: str
-    motd: tuple[str, ...]
     sasl_account: str
     sasl_auth_state: str
     user_modes: str
@@ -706,21 +681,12 @@ class _ClientDb(_Db, SharedClientDbFields):
     caps: _UpdatingDict
     isupports: _UpdatingDict
     users: _UpdatingDict
-    _completable_motd: list[str]
+    motd: _CompletableStringsList
     _channels: dict[str, _ChannelDb]
 
-    def _get_completable(self, key) -> list[str]:
-        return getattr(self, f'_completable_{key}')
-
-    def append_completable(self, key: str, value: str) -> None:
-        'To completable list of key append value.'
-        self._get_completable(key).append(value)
-
-    def declare_complete(self, key: str) -> None:
-        'Declare completable at key complete.'
-        completable = self._get_completable(key)
-        setattr(self, key, tuple(completable))
-        completable.clear()
+    def __init__(self, **kwargs) -> None:
+        super().__init__(**kwargs)
+        self.motd._on_update = lambda: self._on_update('motd')
 
     def user_id(self, query: str | _NickUserHost) -> str:
         'Return user_id for nickname of entire NickUserHost, create if none.'
@@ -754,7 +720,8 @@ class _ClientDb(_Db, SharedClientDbFields):
 
     def needs_arg(self, key: str) -> bool:
         'Reply if attribute of key may reasonably be addressed without an arg.'
-        return not isinstance(getattr(self, key), (bool, int, str, tuple))
+        return not isinstance(getattr(self, key), (bool, int, str, tuple,
+                                                   _CompletableStringsList))
 
     @property
     def chan_names(self) -> tuple[str, ...]:
@@ -962,9 +929,9 @@ class Client(ABC, ClientQueueMixin):
                     for name in ret['names']]:
                 ret['channel']['db'].add_user(user_id)
         elif ret['verb'] == '372':  # RPL_MOTD
-            self._db.append_completable('motd', ret['line'])
+            self._db.motd.append(ret['line'])
         elif ret['verb'] == '376':  # RPL_ENDOFMOTD
-            self._db.declare_complete('motd')
+            self._db.motd.complete()
         elif ret['verb'] == '401':  # ERR_NOSUCHNICK
             self._log(f'{ret["target"]} not online', scope=LogScope.CHAT,
                       target=ret['target'], alert=True)
index fae8c6acc77e11d3f73a1b880de1e357ea00d5df..a4e9e939d57440258b64dfd7945912472d293c37 100644 (file)
@@ -178,6 +178,7 @@ class _ChannelDb(_Db, SharedChannelDbFields):
 class _TuiClientDb(_Db, SharedClientDbFields):
     caps: dict[str, str]
     isupports: dict[str, str]
+    motd: tuple[str, ...]
     users: dict[str, NickUserHost]
     _channels: dict[str, _ChannelDb]
 
@@ -405,5 +406,7 @@ class _ClientKnowingTui(Client):
             value = getattr(self._db, path)
         if isinstance(value, NickUserHost):
             value = value.copy()
+        elif value and hasattr(value, 'completed'):
+            value = value.completed
         self._client_tui_trigger('update_db', update=_Update(
             path, arg, value, display))