Welcome, guest | Sign In | My Account | Store | Cart

I hacked up this useful wrapper around the python command line shell to allow editing of the last typed in lines of code in an external editor.

Python, 36 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# Python Console with an editable buffer.
import os
from tempfile import mkstemp
from code import InteractiveConsole

EDITOR = os.environ.get('EDITOR', 'vim')
EDIT_CMD = '\e'

class EditableBufferInteractiveConsole(InteractiveConsole):
    def __init__(self, *args):
        self.last_buffer = [] # This holds the last executed statement
        InteractiveConsole.__init__(self, *args)

    def runsource(self, source, *args):
        self.last_buffer = [ source ]
        return InteractiveConsole.runsource(self, source, *args)

    def raw_input(self, *args):
        line = InteractiveConsole.raw_input(self, *args)
        if line == EDIT_CMD:
            fd, tmpfl = mkstemp()
            os.write(fd, '\n'.join(self.last_buffer))
            os.close(fd)
            os.system('%s %s' % (EDITOR, tmpfl))
            line = open(tmpfl).read()
            os.unlink(tmpfl)
            tmpfl = ''
        return line

c = EditableBufferInteractiveConsole()
c.write("""
Starting the editable interactive console.
Edit command is '%s'.

""" % EDIT_CMD)
c.interact(banner='')

When working in the python command shell, I often end up writing more than 10+ lines of indented code before making a stupid typo. This got irritating enough for me to do something about it. So, here's an 'Interactive Console with an editable buffer'. Hope that people find it useful. To load it up automatically put it in your python startup file [ http://docs.python.org/tut/node4.html#SECTION004240000000000000000 ].

Notes: a) This code works with python 2.3 only due to it's use of tempfile.msktemp(). Maybe somebody could modify it to work with other python versions. b) The editor is selected using the EDITOR environment variable and defaults to 'vim' if it has not been defined. c) The command to invoke the editor is '\e' by default. It may be customized by changing the 'EDIT_CMD' variable.

6 comments

Bill Mill 16 years, 4 months ago  # | flag

Use ipython. Get ipython at http://ipython.scipy.org .

Karl Waedt 15 years, 11 months ago  # | flag

Editing multiple lines. Works fine, even with gvim.exe. However, for editing multiple lines like:

print 1
def f( x ):
    return x + 2

a small extension is required before 'return line'. Only the last line entered via the editor is returned by raw_input(). The other lines are handed over to InteractiveConsole.push(), e.g.

def raw_input(self, *args):
    line = InteractiveConsole.raw_input(self, *args)
    if line == EDIT_CMD:
        ...
        lines = line.split( '\n' )
        for i in range(len(lines) - 1): self.push( lines[i] )
        line = lines[-1]
    return line
Nathaniel Whiteinge 14 years, 6 months ago  # | flag

Python file extension. Awesome tip! This brings the standard Python shell to 90% of IPython's functionality (for me).

If you change the following line, the temporary file will have a .py file extension which might be helpful::

fd, tmpfl = mkstemp('.py')
Nathaniel Whiteinge 13 years, 7 months ago  # | flag

Make namespace available to the sub-shell. If you add kwargs to __init__ you can easily pass the local namespace to the InteractiveConsole.

def __init__(self, *args, **kwargs):
    ...
    InteractiveConsole.__init__(self, *args, **kwargs)

then

c = EditableBufferInteractiveConsole(locals=locals())
Gary Robinson 12 years, 4 months ago  # | flag

For some reason, I've never liked ipython. Just never felt comfortable to me. So this script is a nice find.

Stephen says above that it only works in Python 2.3 -- I guess he meant 2.3+ because it seems fine in 2.5.

The one thing I haven't liked about it so far is that, on my Linux box, the interactive mode it creates don't have regular readline functionality.

However, that is easily fixed! Just add

import readline

to the imports at the top of the recipe. Works for me anyway.

lonetwin (author) 7 years, 2 months ago  # | flag

Update: Over the years I've improved on this quite a bit. Here's a 'pimped up' version of this:

https://gist.github.com/lonetwin/5902720