Date: 2007-10-25
Tags: python, turbogears

ソースコード探訪:turbogears.toolbox.shell.WebConsole

turbogears.toolbox.shell.WebConsole クラスのコードを読む。 Web画面上でPython対話コンソール(InteractiveShell)を実現するためのサーバー側実装コードは100行無かった!こんなに短いとは‥‥。全ては code.InteractiveConsole クラスのお力か。

で、このコードを発展させて、自分でソケットを開いてリモート対話シェルを作ってみる。

import sys
import StringIO
from code import InteractiveConsole
import socket

def init_ps():
    try:
        sys.ps1
    except AttributeError:
        sys.ps1 = '>>> '
    try:
        sys.ps2
    except AttributeError:
        sys.ps2 = '... '


class RemoteConsole(object):
    """Remote Python interpreter"""

    def __init__(self):
        self.console = None
        init_ps()

    def process_request(self, line):
        more, output = self._process_request(line)
        return dict(more=more, output=output)

    def _process_request(self, line):
        myout = StringIO.StringIO()
        try:
            sys.stdout = myout
            sys.stderr = myout
            more = self.console.push(line)
        finally:
            sys.stdout = sys.__stdout__
            sys.stderr = sys.__stderr__

        output = myout.getvalue()
        return ( more, output.rstrip() )

    def new_console(self):
        locs = dict(__name__='RemoteConsole',
                    __doc__=None,
                    reload_console=self.new_console,
                    )
        self.console = InteractiveConsole(locals=locs)

    def connect(self):
        if not self.console:
            self.new_console()

    def disconnect(self):
        if self.console:
            self.console = None


def server():
    rc = RemoteConsole()
    s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    s.bind(('',10000))
    s.listen(1)
    conn,addr=s.accept()
    rc.connect()
    while 1:
        data = conn.recv(1000000)
        #print 'r',len(data)
        ret = rc.process_request(data)
        ret = unicode(ret)
        #print 's',len(ret)
        conn.send(ret)


def client():
    init_ps()
    s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    s.connect(('localhost',10000))
    more = False
    while 1:
        data = raw_input(more and sys.ps2 or sys.ps1)
        if len(data) == 0:
            data = '\n'
        s.send(data)
        ret = s.recv(1000000)
        ret = eval(ret)
        output = ret.get('output')
        more = ret.get('more', False)
        if output:
            print output


if __name__ == '__main__':
    if len(sys.argv) == 1:
        print 'Run as server'
        server()
    else:
        print 'Run as client'
        client()

ちょー適当だけど、とりあえず動くだけなら出来た。97行。

このコードをtestconsole.pyとして保存して実行してみる。

server起動

C:\> python2.4 testconsole.py
Run as server

client起動

C:\> python2.4 testconsole.py client
Run as client
>>> a=1
>>> b=2
>>> a+b
3
>>> def foo(n):
... return n*n
...
>>> foo(2)
4
>>>

見た目分かりづらいけど、ネットワーク越しにPython対話シェルもどきが動いている。この先にあるのは、パクり元のWebConsoleのネットワーク版。

Pythonで書かれたサーバーにモニタリング用ポートを開けて、好きなタイミングでサーバー稼働中のPythonプロセスで対話シェルを操作できるようになる‥‥といいなあ。モデルの状態を見たり、メモリ利用状態の調査をしたり色々できるんじゃなかろうか。