[IPython-User] Problem: Custom Tab-Completion through Pyreadline

yaluen@u... yaluen@u...
Wed Jun 30 02:08:15 CDT 2010


I'm helping build a cross-platform custom tab completer for a Python research project, and I'm using Pyreadline to substitute for the the lack of readline on Windows. However, upon installation, despite trying to set-up the custom tab completion through the use of readline's set_completer function, pyreadline automatically uses the completer under basemode.py, shown when it printed out a list of files in the directory when I tabbed despite changing the custom tab complete function to raise an exception instead. When I ran through the code using the Python Debugger, I saw that set_completer(completer.complete) was clearly called under rlmain.py, yet the custom tab complete function wasn't implemented in the readline somehow.

I'm using version 1.5 of Pyreadline, installed under a custom directory of the research project that essentially uses Python 2.5. I'm testing all this through Windows command prompt. I might have overlooked some details since I'm still fairly new to Python, but any help would be much appreciated.

For reference, I didn't write the TabCompleter code, and it works when used in a Unix system with their GNU Readline.

Alan Loh

This is the code for the custom tab completion part of the project:

tabcompletion = True
  import readline
except ImportError:
  print "Auto tab completion is off, because it is not available on your operating system."
  tabcompletion = False
  # Set up the tab completion environment
  if tabcompletion:
    completer = TabCompleter()
    readline.parse_and_bind("tab: complete")
    readline.set_completer_delims(" ")

...and this is the TabCompleter class I'm trying to help implement.

class TabCompleter:

  # Constructor that initializes all the private variables
  def __init__(self):

    # list of files that match the directory of the given prefix
    self._words = []

    # list of files that match the given prefix
    self._matching_words = []

    self._prefix = None

  # Returns the path from a given prefix, by extracting the string up to the
  # last forward slash in the prefix. If no forward slash is found, returns an
  # empty string.
  def _getpath(self, prefix):

    slashpos = prefix.rfind("/")
    currentpath = ""
    if slashpos > -1:
      currentpath = prefix[0 : slashpos+1]

    return currentpath

  # Returns the file name, or a part of the file name, from a given prefix, by
  # extracting the string after the last forward slash in the prefix. If no
  # forward slash is found, returns an empty string.
  def _getfilename(self, prefix):

    # Find the last occurrence of the slash (if any), as it separates the path
    # and the file name.
    slashpos = prefix.rfind("/")
    filename = ""

    # If slash exists and there are characters after the last slash, then the
    # file name is whatever that follows the last slash.
    if slashpos > -1 and slashpos+1 <= len(prefix)-1:
      filename = prefix[slashpos+1:]

    # If no slash is found, then we assume that the entire user input is the
    # prefix of a file name because it does not contain a directory
    elif slashpos == -1:
      filename = prefix

    # If both cases fail, then the entire user input is the name of a
    # directory. Thus, we return the file name as an empty string.

    return filename

  # Returns a list of file names that start with the given prefix.
  def _listfiles(self, prefix):

    # Find the directory specified by the prefix
    currentpath = self._getpath(prefix)
    if not currentpath:
      currentpath = "./"
    filelist = []

    # Attempt to list files from the directory
      currentpath = os.path.expanduser(currentpath)
      filelist = os.listdir(currentpath)

      # We are silently dropping all exceptions because the directory specified
      # by the prefix may be incorrect. In this case, we're returning an empty
      # list, similar to what you would get when you TAB a wrong name in the
      # Unix shell.

      return filelist

  # The completer function required as a callback by the readline module. See
  # also http://docs.python.org/library/readline.html#readline.set_completer
  def complete(self, prefix, index):

    # If the user updates the prefix, then we list files that start with the
    # prefix.
    if prefix != self._prefix:

      self._words = self._listfiles(prefix)
      fn = self._getfilename(prefix)

      # Find the files that match the prefix
      self._matching_words = []
      for word in self._words:
        if word.startswith(fn):

      self._prefix = prefix

      return self._getpath(prefix) + self._matching_words[index]

    except IndexError:
      return None

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.scipy.org/pipermail/ipython-user/attachments/20100630/59c95abf/attachment.html 

More information about the IPython-User mailing list