Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get a Python console to access the vim module

Tags:

python

vim

Recently I have been looking into vim plugin development, and I found I missed the ability to use a Python REPL (ipython/bpython for eg) to inspect the vim module, and generally the environment (current open document, line number, selection etc).

This is in principle - not very advanced and something I've done from other applications that embed Python...

Typically you could do this:

import code
code.interact(local=locals())

Or with IPython:

import IPython
IPython.embed()

However when vim embeds Python, it replaces sys.stdin/stdout/stderr, I managed to temp restore these and it very nearly works but there is still some problems with scrambled line endings and stdin isnt reading properly.

eg,

std_back = sys.stderr, sys.stdin, sys.stdout
sys.stderr = sys.__stderr__
sys.stdout = sys.__stdout__
sys.stdin = sys.__stdin__

import IPython
IPython.embed()

sys.stderr, sys.stdin, sys.stdout = std_back

I tried with both vim and gvim on linux, and the stdin/stdout was not working right. (hard to explain, but only every second key input was accepted and newlines were not printed, text wrapping).

So my final attempt was to use idle, which bypasses the terminal and opens up a tk interface with a command line.

import idlelib
import idlelib.PyShell
idlelib.PyShell.main()

This loads the console, but in a sub-process so no access to vim module, I checked on the idlelib source code, and found you can disable subprocess use by faking a command line argument which idle would normally access from being launched from the command line directly.

import sys
sys.argv.append("-n")

import idlelib
import idlelib.PyShell
idlelib.PyShell.main()

Ok, so this works, but, Python devs are going to remove idlelib's option to run inside the process (currently its deprecated). Do any other devs know a way of using a Python REPL within vim called from pyfile / py3file ?

like image 285
ideasman42 Avatar asked Aug 14 '26 07:08

ideasman42


1 Answers

This is a simple interpreter shell that listens on a network socket:

import socket
import sys
import code
from threading import Thread


def remote_shell(local, host='localhost', port=5555):
    serv = socket.socket()
    serv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    serv.bind((host, port))
    serv.listen(1)
    streams = sys.stdout, sys.stderr
    print('listening on %s:%d' % (host, port))
    try:
        sock, addr = serv.accept()
        f = sock.makefile('rw')
        sys.stdout = sys.stderr = f

        def read(prompt):
                f.write(prompt)
                f.flush()
                return f.readline().rstrip('\n')

        code.interact(readfunc=read, local=local)
    finally:
        sys.stdout, sys.stderr = streams
        serv.close()
        sock.close()


if __name__ == '__main__':
    remote_shell(local=locals())
    # or if it should run in background:
    # Thread(target=remote_shell, kwargs={'local': locals()}).start()

To connect, simply use: nc localhost 5555 - but it's probably too restricted for advanced use. With a tool like rlwrap at least it would be possible to have history, but the more advanced readline features like tab completion won't work. But at least it's something...

like image 137
mata Avatar answered Aug 16 '26 20:08

mata