dynamic cmd.Cmd tab-completion

Solution 1:

Your script does not work with Python 3.8.10 probably because of this line

setattr(C, 'do_' + k, k)

k is not a method. By the way, the mistake is in this line:

for k, v in self.commands.items():
     self.k = k  # <--

You are continuously reassigning self.k until the last key of the commands dictionary, that is bb. Later, the complete_ method is always evaluated as self.commands['bb']. Here is a possible working version:

import cmd

class C(cmd.Cmd):

    commands = {
            'aa': ['aa1', 'aa2'],
            'bb': ['bb1', 'bb2']
        }
        
    def monad_print(self, *args):
        print(args)

    def preloop(self):
        for k, v in self.commands.items():
            setattr(C, 'do_' + k, lambda self, arg, k=k: self.monad_print(k, arg))
            setattr(C, 'complete_' + k, self.complete_)

    def complete_(self, text, line, start_index, end_index):
        command = line.split(' ', maxsplit=1)[0]
        if text:
            return [
                command for command in self.commands[command]
                if command.startswith(text)
            ]
        else:
            return self.commands[command]


if __name__ == '__main__':
    C().cmdloop()

The only difference is that the command is now taken from the entire read-line, in particular the first token of str.split().