indygreg / python-build-standalone

Produce redistributable builds of Python
BSD 3-Clause "New" or "Revised" License
1.71k stars 107 forks source link

cmd module tab-complete is broken #159

Closed bkad closed 1 year ago

bkad commented 1 year ago

I'm unable to get tab-completion to work on the built-in cmd module using the following steps:

  1. Using either the standalone build, run python3 -c 'import cmd; cmd.Cmd().cmdloop()'.
  2. Try to tab-complete the "help" command. It fails on my machines using the standalone build, but works using my installation from brew.

I've tested this on the following, although it's possibly broken on all of them:

Tab-completion works just fine in the standalone build's standard python REPL. Setting TERMINFO_DIRS=/usr/share/terminfo doesn't seem to help.

bkad commented 1 year ago

Think this is due to readline being compiled against libedit instead of GNU readline. If you override the default cmdloop implementation with one compatible with libedit, it seems to work just fine.

import cmd
import readline

class MyPrompt(cmd.Cmd):
    def cmdloop(self, intro=None):
        self.preloop()
        self.old_completer = readline.get_completer()
        readline.set_completer(self.complete)

        # use the libedit incantation rather than GNU readline
        readline.parse_and_bind("bind ^I rl_complete")

        try:
            if intro is not None:
                self.intro = intro
            if self.intro:
                self.stdout.write(str(self.intro) + "\n")
            stop = None
            while not stop:
                if self.cmdqueue:
                    line = self.cmdqueue.pop(0)
                else:
                    if self.use_rawinput:
                        try:
                            line = input(self.prompt)
                        except EOFError:
                            line = "EOF"
                    else:
                        self.stdout.write(self.prompt)
                        self.stdout.flush()
                        line = self.stdin.readline()
                        if not len(line):
                            line = "EOF"
                        else:
                            line = line.rstrip("\r\n")
                line = self.precmd(line)
                stop = self.onecmd(line)
                stop = self.postcmd(stop, line)
            self.postloop()
        finally:
            readline.set_completer(self.old_completer)

if __name__ == '__main__':
    MyPrompt().cmdloop()
bkad commented 1 year ago

I'm convinced now that this is an issue with python's implementation of the cmd module and not with this project.