Pythonのargparseを使ってみた

 Pythonでコマンドライノプションの解析をするには、次のような方法があります。

  • sys.argvをまんま使ってゴリゴリ書く。
  • getopt モジュール (gnuっぽい書き方ができる)
  • optparse モジュール (割と新しい)
  • argparse モジュール (もっと新しい)

argparseはPython2.7からだそうですが、他人に使ってもらうようなコマンドラインツールを書く機会がしばらくありませんでしたから、(optparseも)使ったことがありません。

今回、そういう機会があったので、argparseを使ってみました。
基本的な使い方は、次サイトの説明で学びました。

getoptと比べると次のような特徴があり、とってもお利口さんです。

  • usageを勝手に作ってくれる。
  • positional argument はどこにあってもいい
  • パースした引数の値を、指定したnamespaceにセットしてくれる。
  • 引数の型を指定できる(ファイルならオープンまでしてくれる)

ちなみに、今回作成したツールのargparseに関係する部分だけ、抜粋します。

     parser = argparse.ArgumentParser(
      description="""ログ定義ファイルユーティリティ""")
     parser.add_argument('input', metavar='infile',
      type=argparse.FileType('r'),
      default=sys.stdin,
      help='入力ファイル(ログ定義ファイル)')
     parser.add_argument('-o', '--output', metavar='outfile', nargs='?',
      type=argparse.FileType('w'),
      default=sys.stdout,
      dest='output',
      help='出力ファイル(default:stdout)')
     parser.add_argument('-p', '--prefix', default='PREFIX',
      dest='prefix',
      help='enumプレフィクス(default:PREFIX)')
     parser.add_argument('-c', '--cpp', default='logdefine.h',
      dest='header',
      help='C++ヘッダファイル名(default:logdefine.h)')
     parser.add_argument('-v','--verbose', action='store_true',
      dest='verbose',
      help='冗長メッセージフラグ(default:False)')
     parser.add_argument('-d', '--debug', action='store_true',
      dest='debug',
      help='デバッグフラグ(default:False)')
     converter = Converter()
     args = parser.parse_args(namespace = converter)
     converter.convert()


とても使い勝手がよいのですが、default値の設定のところでハマりました。

各引数には、add_argumentでdefault値を指定できます。引数を省略した場合、parse_argsで指定したnamespaceにdefault値がセットされることになっています。

上のコードで、オプションを指定しないでparse_argsを実行したところ、

    converter.header='logdefine.h'
    converter.prefix='PREFIX'

となってほしかったのですが、そうなりません。

よくよく調べてみると、次のように、converterのコンストラクタでその変数をセットしているからでした。

     class Converter(object):
      def __init__(self):
       etc
       ・・・
       self.header = ''
       self.prefix = ''
       ・・・
       etc

初期値をセットするコードを取除くと、parse_argsでdefault値がセットさ
れるようになりました。

    class Converter(object):
      def __init__(self):
       ・・・
       #self.header = ''
       #self.prefix = ''
       ・・・

すでにnamespaceにメンバがある場合には設定しない、ということのようです。ドキュメントのどこかにしっかり書いてあるのかも知れませんが、斜め読みしただけでは分からなかったので、メモしときます。