None
Remote Interactive Python
Going Interactive during Threaded Development
One of Python's best kept secrets is the ability to regain an interactive console in the middle of non-interactive code. Using code.interact, we've saved thousands of development hours by instantly checking assumptions and playing with data in ways logging just can't match. It's been particularly effective inside error handling routines to quickly identify what went wrong and develop a fix on the spot. Here's what we've been using:

def interactiveShell(local, banner='interactive shell'):
    import code
    local = dict(globals(),**local) if local else globals()
    code.interact(banner,local=local)

Unfortunately, it can't handle daemonized services like web2py (a great python-based MVC framework). Several heavy-handed solutions exist (potentially pydbgr), but nothing that matched the simplicity and availability of code.interact. A little googling started to approach an answer in the form of this post in March 2010. That led to developing the functions below to provide a fully interactive Python console locally or remotely via TCP sockets. The best part is the console is so intuitive that netcat and telnet can talk with the server with ease.

import sys, socket, logging
import contextlib

@contextlib.contextmanager
def std_redirector(stdin, stdout, stderr):
    orig_fds = sys.stdin, sys.stdout, sys.stderr
    sys.stdin, sys.stdout, sys.stderr = stdin, stdout, stderr
    yield
    sys.stdin, sys.stdout, sys.stderr = orig_fds

class socketWrapper():
    def __init__(self, s): self.s = s
    def read(self, len):   return self.s.recv(len)
    def write(self, str):  return self.s.send(str)
    def readline(self):
    	data = ''
    	while True:
    		iota = self.read(1)
    		if not iota: break
    		else: data += iota
    		if iota in '\n': break
    	return data

def ishell(local=None, banner='interactive shell'):
    import code
    local = dict(globals(),**local) if local else globals()
    code.interact(banner,local=local)

def linkup(local,link,banner):
    import traceback
    link = socketWrapper(link)
    banner += '\nStack Trace\n'
    banner += '----------------------------------------\n'
    banner += ''.join(traceback.format_stack()[:-2])
    banner += '----------------------------------------\n'
    with std_redirector(link,link,link):
    	ishell(local,banner)

def listen(local=None, host='127.0.0.1', port=2000):
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server.bind((host,port))
    server.listen(1)
    client,addr = server.accept()
    linkup(local,client,'connected to %s:%d'%(host,port))
    client.shutdown(socket.SHUT_RDWR)
    server.shutdown(socket.SHUT_RDWR)

def connect(local=None, host='127.0.0.1', port=2000):
    link = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    link.connect((host,port))
    linkup(local,link,'connected to %s:%d'%(host,port))
    link.shutdown(socket.SHUT_RDWR)

if __name__ == '__main__':
    listen() # nc localhost 2000
    connect() # nc -l 2000

Currently, the code doesn't provide any fancy features like command history or tab completion, but readline with rlcompleter look promising.

- Kelson (kelson@shysecurity.com)