Python REPL tab completion on MacOS
Asked Answered
E

2

53

Before upgrading to lion, I had tab complete working in a python shell via terminal. Following these instructions, it was possible to have tab complete working.

Since upgrading to Lion, I am now unable to get tab complete working in a terminal session of Python. I've followed the above instructions to the letter, and it still does not work.

Is there a difference with the readline module in Lion? Hooking in to the 'tab:complete' option no longer seems to work. I'm wondering if it is terminal that is ignoring readline, or if it is python itself.

Python version: 2.7.1

Edit:

By tab complete, I mean I could do something like the following:

# django
import MyModel
MyModel.objects.a[TAB] # will complete to all()
Excurved answered 19/8, 2011 at 1:30 Comment(4)
What kind of stuff did you have it completing before?Bergschrund
Have you considered using something like ipython or bpython?Bergschrund
If it still doesn't work, see my answer in another post.Grenade
Still happens on Big Sur+ and Py 3.8, but there is an answer below. :-/Teocalli
L
117

Apple does not ship GNU readline with OS X. It does ship BSD libedit which includes a readline compatibility interface. The system Pythons shipped by Apple and the 64-bit/32-bit Pythons from python.org installers are built with libedit. The problem is that the commands supported by libedit are completely different from those of readline (see for example the discussion here). The traditional 32-bit-only python.org installers do use GNU readline as do some other 3rd-party distributors of Python for OS X, like MacPorts. Chances are that you were previously using such a Python and not a recent Apple one. You do have a few options, besides modifying Django: you can install the third-party replacement readline module; or you can use another Python that comes with GNU readline. However, you should not use the python.org 32-bit-only Pythons on 10.7 because, unfortunately, Xcode 4 on 10.7 no longer includes gcc-4.0 and the OS X 10.4u SDK which those Pythons need to build and install packages with C extension modules.

Putting the following in the python startup file will enable tab completion for both the libedit interface and the typical readline module. For more information on the python startup file, see here

import readline
import rlcompleter
if 'libedit' in readline.__doc__:
    readline.parse_and_bind("bind ^I rl_complete")
else:
    readline.parse_and_bind("tab: complete")
Langtry answered 19/8, 2011 at 4:38 Comment(2)
I used to use python 2.7 with osx 10.6, so it wasn't the system python. I believe you're right in what you're saying though. I have to use the system python, due to some oracle libraries I'm using for django.Excurved
I added the solution to get tab completion working to your answer rather than creating a new one as the information in your answer is much better. Feel free to remove it if you like, and I'll create a new answer. CheersExcurved
F
16

As it uses libedit/editline, the syntax to enable autocompletion is a little bit different. You can first force emacs bindings (as it is with readline if I'm not wrong) by typing :

readline.parse_and_bind("bind -e")

Then you can add autocompletion linked to your TAB button (man editrc) :

readline.parse_and_bind("bind '\t' rl_complete")

And if you want to support indenting and has a history (found on internet), it should look like that (unless I made a mistake) :

import readline,rlcompleter

### Indenting
class TabCompleter(rlcompleter.Completer):
    """Completer that supports indenting"""
    def complete(self, text, state):
        if not text:
            return ('    ', None)[state]
        else:
            return rlcompleter.Completer.complete(self, text, state)

readline.set_completer(TabCompleter().complete)

### Add autocompletion
if 'libedit' in readline.__doc__:
    readline.parse_and_bind("bind -e")
    readline.parse_and_bind("bind '\t' rl_complete")
else:
    readline.parse_and_bind("tab: complete")

### Add history
import os
histfile = os.path.join(os.environ["HOME"], ".pyhist")
try:
    readline.read_history_file(histfile)
except IOError:
    pass
import atexit
atexit.register(readline.write_history_file, histfile)
del histfile
Flooded answered 9/11, 2011 at 21:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.