pythonで簡易コンソールを作る
pythonで簡易コンソールを作る機会があったのでメモしておく。
コンソールを作る場合、以下のように標準入力でループさせて処理させる方法がある。
参考:http://python.g.hatena.ne.jp/mhrs/20060522/p2
#!/usr/bin/env python # -*- coding:utf-8 -*- # # easy_console.py # import sys sys.stdout.write(">> ") for line in iter(sys.stdin.readline, ""): if line.strip() != "": sys.stdout.write(line) sys.stdout.write(">> ") sys.stdout.write("\n")
$ python easy_console.py
>>
>> hoge
hoge
>> fuga
fuga
>> ^D
一方で、pythonには標準でコンソールがあり、それをうまく流用できないか考えてみる。
調べてみると、コンソールをエミュレートするcodeというモジュールがあるらしい。
http://www.python.jp/doc/release/library/code.html
codeでは
- InteractiveInterpreter : python構文解析とインタプリタ状態を担当
- InteractiveConsole : コンソールエミュレータ (InteractiveInterpreterの子クラス)
の2つが定義されている。
例えば、以下の様なコードを書くとコンソールもどきが起動できる。
#!/usr/bin/env python # -*- coding:utf-8 -*- # # code_console.py # import code console = code.InteractiveConsole() console.interact()
$ python codo_consoele.py
Python 2.7.1 (r271:86832, Jun 25 2011, 05:09:01)
[GCC 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2335.15.00)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> 1 + 1 * 2
3
>>> for i in range(3):
... print i
...
0
1
2
>>> ^D
デフォルトではpythonのインタプリタとして動作する。これは、InteractiveConsoleのpushメソッドでInteractiveInterpretarに入力した文字列を渡しているから。
pushをオーバライドすることで独自処理が実装できる。
#!/usr/bin/env python # -*- coding:utf-8 -*- # # my_console.py # import sys import code class MyConsole(code.InteractiveConsole): def __init__(self, local=None, filename="<console>"): code.InteractiveConsole.__init__(self, local, filename) def push(self, line): if line == "nullpo": print "ga!!!" my_console = MyConsole() sys.ps1 = "(test)>> " sys.ps2 = "------>> " my_console.interact("### welcome to my console!!! ###")
$ python my_console.py
### welcome to my console!!! ###
(test)>> hoge
(test)>> nullpo
ga!!!
(test)>> ^D
ところで、InteractiveConsoleをそのまま使うと上下とかでコマンド履歴を呼び出す機能がない。
pythonの標準コンソールでは出来るので、"python -vvv"とかして調べてたら以下のドキュメントを見つけた。
http://www.python.jp/doc/release/library/readline.html
早速拡張してみる。
#!/usr/bin/env python # -*- coding:utf-8 -*- # # my_console.py # import sys import code import readline import atexit import os class MyConsole(code.InteractiveConsole): def __init__(self, local=None, filename="<console>", histfile=os.path.expanduser("~/.console-history")): code.InteractiveConsole.__init__(self, local, filename) self.init_history(histfile) def init_history(self, histfile): readline.parse_and_bind("tab: complete") if hasattr(readline, "read_history_file"): try: readline.read_history_file(histfile) except IOError: pass atexit.register(self.save_history, histfile) def save_history(self, histfile): readline.write_history_file(histfile) def push(self, line): if line == "nullpo": print "ga!!!" my_console = MyConsole() sys.ps1 = "(test)>> " sys.ps2 = "------>> " my_console.interact("### welcome to my console!!! ###")
これで上下での履歴の呼び出しができるようになる。