Friday, January 23, 2009

Tab-completion and history in the Python interpreter

The Interpreter

I usually use IPython as my interactive Python interpreter, but it has problems with Unicode decoding which can have detrimental effects for times when I need to deal with Unicode (such as when I'm working with FriendFeed PyAPI). When complaining about this on #python, one of the user told me I should use the standard Python interpreter anyway. When I told him I did not use the standard interpreter because I loved the convenience of tab-completion in the IPython shell, he informed me that, indeed, the standard interactive interpreter can do auto-complete.

After some Googling, I came upon this blog post. I wound up using a modified solution posted in the comments. Here's my .pythonrc file:

import atexit
import os.path

try:
   import readline
except ImportError:
   pass
else:
   import rlcompleter

   class IrlCompleter(rlcompleter.Completer):
       """
       This class enables a "tab" insertion if there's no text for
       completion.

       The default "tab" is four spaces. You can initialize with '\t' as
       the tab if you wish to use a genuine tab.

       """

       def __init__(self, tab='    '):
           self.tab = tab
           rlcompleter.Completer.__init__(self)


       def complete(self, text, state):
           if text == '':
               readline.insert_text(self.tab)
               return None
           else:
               return rlcompleter.Completer.complete(self,text,state)


   #you could change this line to bind another key instead tab.
   readline.parse_and_bind('tab: complete')
   readline.set_completer(IrlCompleter().complete)


# Restore our command-line history, and save it when Python exits.
history_path = os.path.expanduser('~/.pyhistory')
if os.path.isfile(history_path):
   readline.read_history_file(history_path)
atexit.register(lambda x=history_path: readline.write_history_file(x))

I then added the following line to my .bashrc:

export PYTHONSTARTUP="$HOME/.pythonrc"

Now I can remain a happy camper using the native interactive interpreter.

Update (2008-1-25): Thanks to Bob Erb's comments, I corrected some poor indentation (whoops!) and also added the final lines to remove the atexit and os.path modules from the main namespace.

Update (2009-4-18): I removed the deletion of atexit and os.path from the main namespace. That seemed to wreck any script that needed either of those; quite a few scripts rely on os.path, in particular.

4 comments:

  1. You've got a bug.

    If 'import readline' fails, you don't want to continue as you do. As written, 'import rlcompleter' won't execute, so the definition of IrlCompleter will fail. The references to readline below that would fail, too, if the interpreter got to them.

    Would be nice to del atexit and os.path when you're finished with them, too.

    ReplyDelete
  2. Thanks, Bob! Noted and corrected!

    ReplyDelete
  3. Nice post. I'm surprised, however, how many posts I've found recently about this and none of them reference the the section of the Python Tutorial that discusses this. It's been around since Python 2.2, at least.

    Also, just an FYI for anyone. If you want to get tabbed completion working on OS X (10.5) without using fink/macports/etc. use:

    readline.parse_and_bind ("bind ^I rl_complete")

    ReplyDelete