home · contact · privacy
Allow multiple loggings per ClientTui update.
authorChristian Heller <c.heller@plomlompom.de>
Thu, 18 Sep 2025 05:19:48 +0000 (07:19 +0200)
committerChristian Heller <c.heller@plomlompom.de>
Thu, 18 Sep 2025 05:19:48 +0000 (07:19 +0200)
ircplom/client_tui.py
test.txt

index ba7a2f58d294c2fabbf9782f0550594875aa8f05..1e5366ad78c9cbfe019555cc28dea2d533da4203 100644 (file)
@@ -69,7 +69,7 @@ class _UpdatingNode(AutoAttrMixin):
         return scopes.get(key, scopes[''])
 
     def recursive_set_and_report_change(
-            self, update: _Update) -> Optional[tuple[LogScope, Any]]:
+            self, update: _Update) -> tuple[tuple[LogScope, Any], ...]:
         'Apply update, return if that actually made a difference.'
         update.force_log = update.force_log or (not self._is_set(update.key))
         node = self._get(update.key)
@@ -79,7 +79,7 @@ class _UpdatingNode(AutoAttrMixin):
 
     def _focused_set_and_report_change(
             self, old_value: '_UpdatingNode', update: _Update
-            ) -> Optional[tuple[LogScope, Any]]:
+            ) -> tuple[tuple[LogScope, Any], ...]:
         scope = self._scope(update.key)
         do_report = update.force_log
         if update.value is None:
@@ -89,7 +89,7 @@ class _UpdatingNode(AutoAttrMixin):
         elif old_value != update.value:
             self._set(update.key, update.value)
             do_report |= True
-        return (scope, update.value) if do_report else None
+        return ((scope, update.value),) if do_report else tuple()
 
     def _get(self, key: str) -> Any:
         return getattr(self, key)
@@ -228,13 +228,13 @@ class _UpdatingChannel(_UpdatingNode, Channel):
 
     def _focused_set_and_report_change(
             self, old_value: '_UpdatingNode', update: _Update
-            ) -> Optional[tuple[LogScope, Any]]:
+            ) -> tuple[tuple[LogScope, Any], ...]:
         scope = self._scope(update.key)
         if update.key == 'topic':
             if super()._focused_set_and_report_change(old_value, update):
-                return (scope,
-                        f'RAW:{self.topic.who} set topic: {self.topic.what}')
-            return None
+                return ((scope,
+                        f'RAW:{self.topic.who} set topic: {self.topic.what}'),)
+            return tuple()
         assert update.key == 'user_ids'
         assert isinstance(update.value, set)
         d = ({'NUHS:joining': tuple(sorted(id_ for id_ in update.value
@@ -242,33 +242,34 @@ class _UpdatingChannel(_UpdatingNode, Channel):
              if self.user_ids
              else {'NICKS:residents': tuple(sorted(update.value))})
         if super()._focused_set_and_report_change(old_value, update):
-            return scope, d
-        return None
+            return ((scope, d),)
+        return tuple()
 
 
 class _UpdatingUser(_UpdatingNode, User):
-    log_scopes = {'nick': LogScope.USER, 'exit_msg': LogScope.USER}
+    log_scopes = {'exit_msg': LogScope.USER}
     prev_nick = '?'
 
     def _focused_set_and_report_change(
             self, old_value: '_UpdatingNode', update: _Update
-            ) -> Optional[tuple[LogScope, Any]]:
+            ) -> tuple[tuple[LogScope, Any], ...]:
         assert isinstance(update.value, str)
         if (result := super()._focused_set_and_report_change(old_value,
                                                              update)):
             if update.key not in {'nick', 'exit_msg'}:
                 return result
-            scope = self._scope(update.key)
             msg = 'RAW:'
             if update.key == 'nick':
-                return scope, msg + f'{self.prev} renames {update.value}'
+                return result + (
+                        (LogScope.USER,
+                         msg + f'{self.prev} renames {update.value}'),)
             if update.key == 'exit_msg' and update.value:
                 msg += f'{self} '
                 msg += 'quits' if update.value[0] == 'Q' else 'parts'
                 if len(update.value) > 1:
                     msg += ': ' + update.value[1:]
-                return scope, msg
-        return None
+                return ((self._scope(update.key), msg),)
+        return tuple()
 
     @property
     def prev(self) -> str:
@@ -361,36 +362,37 @@ class _ClientWindowsManager:
     def update_db(self, update: _Update) -> bool:
         'Apply update to .db, and if changing anything, log and trigger.'
         result = self.db.recursive_set_and_report_change(update)
-        if result is None:
+        if not result:
             return False
-        scope, value = result
-        log_path = ':'.join(update.path)
-        log_kwargs: dict[str, Any] = {'scope': scope}
-        if scope in {LogScope.CHAT, LogScope.USER}:
-            log_kwargs |= {'target': update.path[1]}
-        if value is None:
-            self.log(f'{log_path} cleared', **log_kwargs)
-        elif isinstance(value, dict):
-            for verb, item in [(k, v) for k, v in value.items() if v]:
-                toks = verb.split(':', maxsplit=1)
-                verb = toks[-1]
-                transform = toks[0] if len(toks) > 1 else ''
-                if transform in {'NICKS', 'NUHS'}:
-                    nuhs = (self.db.users[id_] for id_ in item)
-                    item = ', '.join([
-                        (str(nuh) if transform == 'nuhs' else nuh.nick)
-                        for nuh in nuhs])
-                self.log(f'{verb}: {item}', **log_kwargs)
-        elif isinstance(value, str) and value.startswith('RAW:'):
-            self.log(value.split(':', maxsplit=1)[1], **log_kwargs)
-        else:
-            announcement = f'{log_path} set to:'
-            if isinstance(value, tuple):
-                self.log(announcement, **log_kwargs)
-                for item in value:
-                    self.log(f'  {item}', **log_kwargs)
+        for t in result:
+            scope, value = t
+            log_path = ':'.join(update.path)
+            log_kwargs: dict[str, Any] = {'scope': scope}
+            if scope in {LogScope.CHAT, LogScope.USER}:
+                log_kwargs |= {'target': update.path[1]}
+            if value is None:
+                self.log(f'{log_path} cleared', **log_kwargs)
+            elif isinstance(value, dict):
+                for verb, item in [(k, v) for k, v in value.items() if v]:
+                    toks = verb.split(':', maxsplit=1)
+                    verb = toks[-1]
+                    transform = toks[0] if len(toks) > 1 else ''
+                    if transform in {'NICKS', 'NUHS'}:
+                        nuhs = (self.db.users[id_] for id_ in item)
+                        item = ', '.join([
+                            (str(nuh) if transform == 'nuhs' else nuh.nick)
+                            for nuh in nuhs])
+                    self.log(f'{verb}: {item}', **log_kwargs)
+            elif isinstance(value, str) and value.startswith('RAW:'):
+                self.log(value.split(':', maxsplit=1)[1], **log_kwargs)
             else:
-                self.log(f'{announcement} [{value}]', **log_kwargs)
+                announcement = f'{log_path} set to:'
+                if isinstance(value, tuple):
+                    self.log(announcement, **log_kwargs)
+                    for item in value:
+                        self.log(f'  {item}', **log_kwargs)
+                else:
+                    self.log(f'{announcement} [{value}]', **log_kwargs)
         for win in [w for w in self.windows if isinstance(w, _ChatWindow)]:
             win.set_prompt_prefix()
         return bool([w for w in self.windows if w.tainted])
index 287dde1f03b33d915e16d9218b0e09f2e4993a3f..8fbff2234d52d581435a32412c8347b2665094f1 100644 (file)
--- a/test.txt
+++ b/test.txt
@@ -42,6 +42,7 @@
 
 # ETC.
 
+# on /connect init databases, log in new windows
 > /connect foo.bar.baz foo:bar baz
 1,2 $ isupport cleared
 1,2 $ isupport:CHANTYPES set to: [#&]
 1,2 $ caps cleared
 1,2 $ users cleared
 1,2 $ channels cleared
+
+# connect with values set by /connect
 1,2 $ hostname set to: [foo.bar.baz]
 1,2 $ port set to: [-1]
 1,2 $ nick_wanted set to: [foo]
 1,2 $ realname set to: [baz]
 1,2 $ password set to: [bar]
 1,2 $ port set to: [6697]
-
 1,2 $ connection_state set to: [connecting]
 
+1,2 $ users:me:nick set to: [?]
 1,2 $ ?!?@? renames ?
 1,2 $ users:me:user set to: [plom]
 1,2 $ connection_state set to: [connected]
@@ -95,6 +98,7 @@
 2 > AUTHENTICATE :Zm9vAGZvbwBiYXI=
 2 < :foo.bar.baz 900 foo foo!plom@baz.bar.foo foo :You are now logged in as foo
 
+1,2 $ users:me:nick set to: [foo]
 1,2 $ ?!plom@? renames foo
 1,2 $ users:me:host set to: [baz.bar.foo]
 
 2 < :foo.bar.baz 333 foo #test bar!~bar@bar.bar 1234567890
 4 $ bar!~bar@bar.bar set topic: foo bar baz
 2 < :foo.bar.baz 353 foo @ #test :foo @bar
+1,2 $ users:1:nick set to: [?]
 , $ ?!?@? renames ?
+1,2 $ users:1:nick set to: [bar]
 , $ ?!?@? renames bar
 2 < :foo.bar.baz 366 foo #test :End of /NAMES list.
 4 $ residents: bar, foo
 2 < :bar!~bar@bar.bar TOPIC #test :abc def ghi
 4 $ bar!~bar@bar.bar set topic: abc def ghi
 2 < :baz!~baz@baz.baz JOIN :#test
+1,2 $ users:2:nick set to: [?]
 , $ ?!?@? renames ?
+1,2 $ users:2:nick set to: [baz]
 , $ ?!?@? renames baz
 1,2 $ users:2:user set to: [~baz]
 1,2 $ users:2:host set to: [baz.baz]
 4 $ joining: baz
 2 < :baz!~baz@baz.baz NICK :bazbaz
+1,2 $ users:2:nick set to: [bazbaz]
 4 $ baz!~baz@baz.baz renames bazbaz
 2 < :bazbaz!~baz@baz.baz PART :#test
 4 $ bazbaz!~baz@baz.baz parts
 1,2 $ users:2 cleared
+1,2 $ users:2:nick set to: [?]
 , $ ?!?@? renames ?
 2 < :bazbaz!~baz@baz.baz JOIN :#test
+1,2 $ users:3:nick set to: [?]
 , $ ?!?@? renames ?
+1,2 $ users:3:nick set to: [bazbaz]
 , $ ?!?@? renames bazbaz
 1,2 $ users:3:user set to: [~baz]
 1,2 $ users:3:host set to: [baz.baz]
 4 $ bazbaz!~baz@baz.baz quits: Client Quit
 1,2 $ users:2 cleared
 1,2 $ users:3 cleared
+1,2 $ users:3:nick set to: [?]
 , $ ?!?@? renames ?
 2 < :foo!~plom@baz.bar.foo PART :#test
 1,2,3,4 $ foo!~plom@baz.bar.foo parts
 1,2,3,4 $ connection_state set to: [connecting]
 
 # except for two positions marked with NB comment, exactly same as on 1st time
+1,2 $ users:me:nick set to: [?]
 1,2,3,4 $ ?!?@? renames ?
 1,2 $ users:me:user set to: [plom]
 1,2,3,4 $ connection_state set to: [connected]
 2 < AUTHENTICATE +
 2 > AUTHENTICATE :Zm9vAGZvbwBiYXI=
 2 < :foo.bar.baz 900 foo foo!plom@baz.bar.foo foo :You are now logged in as foo
+1,2 $ users:me:nick set to: [foo]
 1,2,3,4 $ ?!plom@? renames foo
 1,2 $ users:me:host set to: [baz.bar.foo]
 1,2 $ sasl_account set to: [foo]
 2 < :foo!~plom@baz.bar.foo JOIN #test
 1,2 $ users:me:user set to: [~plom]
 2 < :foo.bar.baz 353 foo @ #test :foo @bar
+1,2 $ users:4:nick set to: [?]
 , $ ?!?@? renames ?
+1,2 $ users:4:nick set to: [bar]
 , $ ?!?@? renames bar
 2 < :foo.bar.baz 366 foo #test :End of /NAMES list.
 4 $ residents: bar, foo