Javaでリストとかfor文とか [Main] PyCodeObjectを書き換える

[Lingr(1)]

Pythonでreduce(l|r)[Python(18)]PyCodeObjectを書き換える

コーディング過程をLingrで中継

lingr.png

右半分がPythonの対話的インタプリタIdleで、左半分がチャットのLingrです。 対話的にコードを書いて試している過程がチャットでどんどん公開されていきます。

Lingrのボット(チャットを読み書きするプログラム)は inforno :: Python版Lingr APIライブラリ を使うと簡単に書けます。 リンク先の例では入退室と発言しかありませんが、 他人の発言をウォッチするのも User Observe in Lingr Developer Wiki を使うだけで簡単にできます。

しかし、Lingrにコードを書くのはオートインデントとかがなくて面倒なので、 対話的インタプリタの側からLingrに「実行する内容」と「実行した結果」を送信するようにしました。 チャットに書いたコードが自動的に実行されるとなるとセキュリティが心配ですが、 これは自分のマシンで走るのは自分の書いたコードだけなので安心です。

一番時間がかかったのは実は 「コンパイル時に"single"と指定すれば、与えられたコードが式である場合に評価した結果を表示するバイトコードが付加される」 と気づくところです。 ライブラリリファレンスの組み込み関数のところに書いてあるのに気がつかず、PyShell.pyを読みながら 「execはあるけども式かどうか判別するコードも評価した値を表示するコードもないぞ?」 と悩んでいたのでした。まさかバイトコードに入っているとは。

下のコードはまだ荒削りですが、 とりあえずこれで上図のような結果が出ます。 複数行のコードの場合、インデントがデフォルトでは表示されませんが、 これはLingrの方で「view paste」というリンクをクリックするときちんとインデントされます。 後は対話的インタプリタの評価のところでLingrと通信しているせいで反応が遅くなっていますが… それは通信部分だけ別スレッドで動かすなどの解決方法がありそうです。

import idlelib

from lingr import *
lingr = Lingr("***YOUR LINGR API KEY***")
lingr.api.session.create()
r = lingr.api.room
r.enter(id="***id***", nickname="***YOUR NICKNAME***", password="***YOUR PASSWORD***")

LOCALS = {}

class Tee(object):
	def __init__(self, stdout):
		self.buf = []
		self.stdout = stdout
	def write(self, s):
		self.buf.append(s)
		self.stdout.write(s)
		
import codeop
CC = codeop.CommandCompiler()

def wrap(f):
	def foo(self, s):
		result = f(self, s)
		if result == False:
			r.say(message = ">>> " + s + "\n\n")
			import sys
			sys.stdout = Tee(sys.stdout)
			exec CC(s, "single") in LOCALS
			r.say(message = "".join(sys.stdout.buf) + "\n" )
			sys.stdout = sys.stdout.stdout
		return result
	return foo

MI = idlelib.PyShell.ModifiedInterpreter
MI.runsource = wrap(MI.runsource)
idlelib.PyShell.main()

追記:r.enter(id="***id***", nickname="***YOUR NICKNAME***", password="***YOUR PASSWORD***")のnicknameとpasswordはあなたのアカウントのものではなく、「ボットが名乗るてきとうなニックネーム」と「チャットルームのパスワード」です。テスト用にパスワードでアクセス制限をしたチャットルームを使っていたのでパスワードを指定していますが、普通のチャットルームなら必要ありません。

あとCommandCompilerを使わなくてもcompile(source, "<string>", "single")でよさそうです。