Tuesday, July 2, 2013

Add History and Tab Completion to the Default Python Shell

So I've in the python shell quite a bit lately and figured it would be nice to be able to have readline history between sessions as well as tab completion. Yes I know things like iPython exist as well as others but wanted to keep it simple. It turns out its not that hard to enable and here is how you do it.

First you'll need to add a line to your .bashrc or .profile setting your PYTHONSTARTUP environment variable

# ~/.bashrc
export PYTHONSTARTUP=$HOME/.pythonstartup.py

Then copy this scripts contents to file your new environment variable points to

# ~/.pythonstartup.py
try:
    import readline
    import rlcompleter
    import atexit
    import os
except ImportError:
    print "Python shell enhancement modules not available."
else:
    histfile = os.path.join(os.environ["HOME"], ".pythonhistory")
    import rlcompleter
    readline.parse_and_bind("tab: complete")
    if os.path.isfile(histfile):
        readline.read_history_file(histfile)
    atexit.register(readline.write_history_file, histfile)
    del os, histfile, readline, rlcompleter, atexit
    print "Python shell history and tab completion are enabled."

That's it. Now anytime you pop into the python shell you'll have history from previous sessions as well as tab completion. Enjoy!

Edit: If you're not running the latest version of OSX you may need to change the readline.pasre_and_bind line to the following

#readline.parse_and_bind("tab: complete")
readline.parse_and_bind("bind ^I rl_complete")

5 comments:

  1. Thanks, really useful. I usually use bpython, it has syntax highlighting in addition. In the source you can remove "import rlcompleter", you don't seem to use it. Remove in the "del" line too.

    ReplyDelete
    Replies
    1. Sorry, I just realized that rlcompleter is necessary. However, you import it twice.

      Delete
    2. Ha, nice catch! I'll fix it! The del was so they didn't pollute the global namespace but it really isn't that big of a deal. Someone else had it in their example and thought it was a best practice.

      I think I'll be using bpython as well. Thanks for your feedback!

      Delete
  2. There's something wrong with this idea in a virtualenv:

    Python 2.7.4 (v2.7.4:026ee0057e2d, Apr 6 2013, 11:43:10)
    [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
    Type "help", "copyright", "credits" or "license" for more information.
    Traceback (most recent call last):
    File "/Users/emptysquare/bin/pythonstartup.py", line 14, in
    readline.read_history_file(histfile)
    IOError: [Errno 2] No such file or directory
    >>> histfile
    '/Users/emptysquare/.pythonhistory'

    The file exists, and I can read it with open(histfile).read(), and it works with no virtualenv active. Haven't investigated much further than this -- readline.c in the Python source doesn't do anything unexpected in its read_history_file() implementation. In any case, this seems to work, both in a virtualenv and not in one:

    if os.path.isfile(histfile):
    for line in open(histfile):
    readline.add_history(line)

    ReplyDelete
    Replies
    1. You need to print out your histfile variable and see what its contents are. My guess for some reason your HOME environment variable isn't defined.

      Delete