google-apputilsって何者?

Operations Research関連のオープンソースソフトウェアを探していたら,2010年9月から始まってgooglerが中心となって開発しているようなor-toolsというプロジェクトを見つけました.or-toolsはswigを使ったPythonのライブラリになっていてサンプルコードも多数含まれているのですが,サンプルコード一部(例えばknapsack.py)の中に下のような見慣れないコードがありました.

from google.apputils import app
# 省略
def main(unused_argv):
    # 省略

if __name__ == '__main__':
    app.run()

google-apputils-pythonとはGoogle内製のアプリでも使用されてる便利ツールのようです.残念ながらまだドキュメントが充実していないようなのですが,どんなことができるのか簡単に見ていきましょう.まずは次のようなシンプルなPythonファイルを用意します.

#!/usr/bin/env python

'''How to use this module'''
from google.apputils import app

def main(argv):
    if len(argv) < 2:
        raise app.UsageError('usage error')
    print argv

if __name__ == '__main__':
    app.run()

sample.pyとして実行します.

$ sample.py --help
How to use this module
flags:

google.apputils.app:
  -?,--[no]help: show this help
  --[no]helpshort: show usage only for this module
  --[no]helpxml: like --help, but generates XML output
  --[no]run_with_pdb: Set to true for PDB debug mode
    (default: 'false')
  --[no]run_with_profiling: Set to true for profiling the script. Execution will
    be slower, and the output format might change over time.
    (default: 'false')
  --[no]show_build_data: show build data and exit

gflags:
  --flagfile: Insert flag definitions from the given file into the command line.
    (default: '')
  --undefok: comma-separated list of flag names that it is okay to specify on
    the command line even if the program does not define a flag with that name.
    IMPORTANT: flags in this list that have arguments MUST use the --flag=value
    format.
    (default: '')
$ sample.py --helpshort
How to use this module
$ sample.py
How to use this module

usage error
$ sample.py arg
['./sample.py', 'arg']
$ sample.py --run_with_profiling arg
['./sample.py', 'arg']
         4 function calls in 0.002 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.000    0.000 :0(len)
        1    0.002    0.002    0.002    0.002 :0(setprofile)
        1    0.000    0.000    0.002    0.002 profile:0(<function main at 0x1010679b0>)
        0    0.000             0.000          profile:0(profiler)
        1    0.000    0.000    0.000    0.000 sample.py:6(main)


$ sample.py --run_with_pdb arg
> /Users/likr/python/test/sample.py(7)main()
-> if len(argv) < 2:
(Pdb) 

このようにapputilsを使えば,モジュールのdocstringをusageとして表示させたり,コマンドライン引数を渡すだけでプロファイラやデバッガの起動ができるようになります.apputilsでハンドルされるフラグはmainには渡ってきません.また,以下のようにフラグを自分で追加することもできます.

#!/usr/bin/env python

'''How to use this module'''
from google.apputils import app
import gflags

class MyFlag(gflags.BooleanFlag):

    def __init__(self):
        gflags.BooleanFlag.__init__(self, 'myflag', 0, 'my flag',
                                    short_name='m', allow_override=1)

    def Parse(self, arg):
        if arg:
            print 'myflag is used'

def main(argv):
    if len(argv) < 2:
        raise app.UsageError('usage error')
    print argv

if __name__ == '__main__':
    gflags.DEFINE_flag(MyFlag())
    app.run()
$ sample.py -?
How to use this module
flags:

./sample.py:
  -m,--[no]myflag: my flag

google.apputils.app:
  -?,--[no]help: show this help
  --[no]helpshort: show usage only for this module
  --[no]helpxml: like --help, but generates XML output
  --[no]run_with_pdb: Set to true for PDB debug mode
    (default: 'false')
  --[no]run_with_profiling: Set to true for profiling the script. Execution will
    be slower, and the output format might change over time.
    (default: 'false')
  --[no]show_build_data: show build data and exit

gflags:
  --flagfile: Insert flag definitions from the given file into the command line.
    (default: '')
  --undefok: comma-separated list of flag names that it is okay to specify on
    the command line even if the program does not define a flag with that name.
    IMPORTANT: flags in this list that have arguments MUST use the --flag=value
    format.
    (default: '')
$ sample.py --myflag aaa
myflag is used
['./sample.py', 'aaa']
$ sample.py -m aaa
myflag is used
['./sample.py', 'aaa']

gflags.BooleanFlag.__init__の第2引数が--xxxのxxxに,shortname引数が-yのyに対応します.
普通のmainの書き方と大きく変わらない書き方で,コマンドライン引数によるプロファイラの起動ができるのは便利そうだと思いました.google-apputilsはPyPIにも登録されているのでeasy_installやpipでもインストールできます.google-apputilsには,今回紹介した以外にもアプリケーションのサブコマンド,デバッグ,プロファイリング,日付などを便利に扱う機能があるようです.他の機能の紹介は機会があれば後日.