CLI フレームワークの Cliff がいい感じ

PythonCLI フレームワークに Cliff というのがある。
http://readthedocs.org/docs/cliff/en/latest/
ドキュメントを読んでコマンドラインなアプリケーションを作る場合に使えそうだったので、試してみたら Python2.6 では動かなかった。
ちょっとソースを読んでみた所簡単に直せたので、修正して Pull Request したら取り込んでくれた。


で、ドキュメントでは cliffdemo というアプリケーションがサンプルであるが、setup.py を実行して cliffdemo という実行ファイルを作成する。
cliffdemo から main.py を呼び出して実行する。
が、いちいち setup.py するの手間かかるし、作成しなくても直接 main.py を実行して動かないかなと思って色々試してみた。
GitHub - heavenshell/cliff-samples: Cliff sample application


まず実行するスクリプトないで、コマンド類を定義する必要がある。
cliff を引数なしで実行すると、インタラクティブシェルが起動する。
そこで help と入力すると以下のように表示される。

$ python manage.py 
(manage) help

Shell commands (type help <topic>):
===================================
cmdenvironment  edit  hi       l   list  pause  r    save  shell      show
ed              help  history  li  load  py     run  set   shortcuts

Undocumented commands:
======================
EOF  eof  exit  q  quit

Application commands (type help <topic>):
=========================================
help

一番下の Application commands に表示されているコマンドが実行可能なコマンドとなる。
初期状態だとデフォルトの help しか定義されてない。


ここにコマンドを追加していく。

class GeneApp(App):
    log = logging.getLogger(__name__)

    def __init__(self):
        command = CommandManager('gene.app')
        super(GeneApp, self).__init__(
            description='sample app',
            version='0.1',
            command_manager=command,
        )
        commands = {
            'simple': Simple,
            'file': File,
            'files': Files,
            'sample': MyCommand
        }
        for k, v in commands.iteritems():
            command.add_command(k, v)

コマンドの追加は CommandManager クラスの add_command() を使用する。
第一引数に入力時に使用するコマンド名、第二引数にコマンドとして実行するオブジェクトを渡す。
この場合、simple, file, files, sample を追加して使用できるようにした。


次にコマンドに引数を付けたい場合は次のよう定義したクラスに引数を追加する。

class MyCommand(Command):
    def get_parser(self, prog_name):
        parser = super(MyCommand, self).get_parser(prog_name)
        parser.add_argument('arg', nargs='?', default=None)

        return parser

    def run(self, parsed_args):
        self.app.stdout.write(parsed_args.arg + "\n")

get_parser() をオーバライドし、その中で parser.add_argument() で引数を定義する。
で、実際に実行する処理を run() 内に定義すれば動作する。


ドキュメントに書かれていないので、ちょっと色々試行錯誤して出来るようになった。
インタラクティブシェルが付いていたりととても便利なので、ちょっとしたコマンドラインなアプリケーションを書く時には重宝しそう。


あ、言うまでもなく CLI フレームワークは起動時に色々処理をしているので、スクリプト内で argparse とかを使ってベタに書くよりも起動は遅い。
そこはトレードオフかな。