home · contact · privacy
Refactor commands database. master
authorChristian Heller <c.heller@plomlompom.de>
Wed, 6 Aug 2025 13:18:36 +0000 (15:18 +0200)
committerChristian Heller <c.heller@plomlompom.de>
Wed, 6 Aug 2025 13:18:36 +0000 (15:18 +0200)
ircplom/tui_base.py

index 25152ca4d938c05c6f134e6a9af7e8dbb16f7b01..204008c982d739977e99a2a8a64d525249cd50de 100644 (file)
@@ -381,22 +381,6 @@ class BaseTui(QueueMixin):
             window.set_geometry()
         self.redraw_affected()
 
-    def _cmd_name_to_cmd(self, cmd_name: str) -> Optional[Callable]:
-        'Map cmd_name to executable TUI element method.'
-        cmd_name = CMD_SHORTCUTS.get(cmd_name, cmd_name)
-        ancestors = [self]
-        steps = cmd_name.split('.')
-        method_name = f'cmd__{steps[-1]}'
-        for step in steps[:-1]:
-            parent = ancestors[-1]
-            if not hasattr(parent, step):
-                break
-            ancestors += [getattr(parent, step)]
-        for ancestor in reversed(ancestors):
-            if hasattr(ancestor, method_name):
-                return getattr(ancestor, method_name)
-        return None
-
     @property
     def window(self) -> Window:
         'Currently selected Window.'
@@ -406,6 +390,31 @@ class BaseTui(QueueMixin):
         self._window_idx = idx
         self.window.draw()
 
+    @property
+    def _commands(self) -> dict[str, tuple[Callable[..., None],
+                                           int, tuple[str, ...]]]:
+        cmds = {}
+        method_name_prefix = 'cmd__'
+        base = 'self'
+        for path in (base, f'{base}.window', f'{base}.window.prompt',
+                     f'{base}.window.log'):
+            for cmd_method_name in [name for name in dir(eval(path))
+                                    if name.startswith(method_name_prefix)]:
+                path_prefix = f'{path}.'
+                cmd_name = (path_prefix[len(base)+1:]
+                            + cmd_method_name[len(method_name_prefix):])
+                method = eval(f'{path_prefix}{cmd_method_name}')
+                n_args_min = 0
+                arg_names = []
+                for arg_name, param in signature(method).parameters.items():
+                    arg_names += [arg_name]
+                    n_args_min += int(param.default == inspect_empty)
+                cmds[cmd_name] = (method, n_args_min, tuple(arg_names))
+        for key, target in CMD_SHORTCUTS.items():
+            if target in cmds:
+                cmds[key] = cmds[target]
+        return cmds
+
     def handle_keyboard_event(self, typed_in: str) -> None:
         'Translate keyboard input into appropriate actions.'
         if typed_in[0] == _CHAR_RESIZE:
@@ -413,9 +422,7 @@ class BaseTui(QueueMixin):
             return
         if typed_in in _KEYBINDINGS:
             cmd_data = _KEYBINDINGS[typed_in]
-            cmd = self._cmd_name_to_cmd(cmd_data[0])
-            if cmd:
-                cmd(*cmd_data[1:])
+            self._commands[cmd_data[0]][0](*cmd_data[1:])
         elif typed_in.startswith(_B64_PREFIX):
             encoded = typed_in[len(_B64_PREFIX):]
             to_paste = ''
@@ -444,19 +451,17 @@ class BaseTui(QueueMixin):
         if to_parse[0] == '/':
             toks = to_parse.split(maxsplit=1)
             cmd_name = toks.pop(0)
-            cmd = self._cmd_name_to_cmd(cmd_name[1:])
+            cmd, n_args_min, arg_names = self._commands.get(cmd_name[1:],
+                                                            (None, 0, ()))
             if not cmd:
                 alert = f'{cmd_name} unknown'
             elif cmd.__name__ == stack()[0].function:
                 alert = f'{cmd_name} would loop into ourselves'
             else:
-                params = signature(cmd).parameters
-                n_args_max = len(params)
+                n_args_max = len(arg_names)
                 if toks and not n_args_max:
                     alert = f'{cmd_name} given argument(s) while none expected'
                 else:
-                    n_args_min = len([p for p in params.values()
-                                      if p.default == inspect_empty])
                     args: list[str] = []
                     while toks and len(args) < n_args_max:
                         toks = toks[0].split(maxsplit=1)
@@ -474,28 +479,14 @@ class BaseTui(QueueMixin):
     def cmd__help(self) -> None:
         'Print available commands.'
         self._log.add('commands available in this window:')
-        cmds = {}
-        cmd_prefix = 'cmd__'
-        base = 'self'
-        for path in (base, f'{base}.window', f'{base}.window.prompt'):
-            for cmd_method_name in [attr_name for attr_name in dir(eval(path))
-                                    if attr_name.startswith(cmd_prefix)]:
-                path_prefix = f'{path}.'
-                cmd_name = (path_prefix[len(base)+1:]
-                            + cmd_method_name[len(cmd_prefix):])
-                method = eval(f'{path_prefix}{cmd_method_name}')
-                args = []
-                for key, param in signature(method).parameters.items():
-                    arg = key.upper()
-                    if param.default != inspect_empty:
-                        arg = f'[{arg}]'
-                    args += [arg]
-                cmds[cmd_name] = args
         to_log = []
-        for cmd_name, args in cmds.items():
-            for key, target in CMD_SHORTCUTS.items():
-                if target == cmd_name:
-                    cmd_name = key
+        for cmd_name, cmd_data in self._commands.items():
+            args = []
+            for idx, arg in enumerate(cmd_data[2]):
+                arg = arg.upper()
+                if idx >= cmd_data[1]:
+                    arg = f'[{arg}]'
+                args += [arg]
             to_log += [f'{cmd_name} {" ".join(args)}']
         for item in sorted(to_log):
             self._log.add(f'  /{item}')