PEP 389: argparse - 新しいコマンドラインparseモジュール

原文:http://www.python.org/dev/peps/pep-0389/

訳注: argparseの公式ドキュメントはhttp://argparse.googlecode.com/svn/trunk/doc/index.html

PEP 389
Title argparse - New Command Line Parsing Module
Version 78618
Last-Modified 2010-03-03 03:55:24 +0100(Wed, 03 Mar 2010)
Author Steven Bethard <steven.bethard@gmail.com>
Status Accepted
Type Standards Track
Content-Type text/x-rst
Created 25-Sep-2009
Python-Version 2.7 and 3.2
Post-History 27-Sep-2009, 24-Oct-2009

承認

このPEPはGuidoによって、2010年4月21日のpython-devにて承認されました。[17]

概要

このPEPでは、Python 2.7と3.2で標準ライブラリに加わったargparse[1]モジュールに関して説明します。

モチベーション

argparseモジュールは既存のコマンドライン引数を解釈するモジュールであるgetopt[2]とoptparse[3]よりも高機能なparseライブラリです。argparseは、単なるオプションだけでなく固定引数(positional argument)、サブコマンド、必須オプション、”/f”や”+rgb”といったオプション文法、0以上や1以上という形式の引数、その他getoptとoptparseが持っていない多数の機能を提供します。

argparseモジュールはこれらのモジュールを置き換えるサードパーティ製のモジュールとしてすでに人気があります。argparseはIPython(Scipy PythonShell)[4]で使われており、Debianのtestingとusntable[5]に含まれており、2007年から標準ライブラリに組み込むようたくさんのリクエストを受け取って来ました[6][7][8]。これはPythonのライブラリに追加する価値があることを示唆しています。

なぜgetoptとoptparseは十分ではないの?

argparseに対する議論の一つとして「すでにもう二つの異なるOptionパーザがあるじゃないか[9]」というものがあります。以下にargparseにはあるが他のgetoptとoptparseにはない機能のリストを記します。

  • 既に二つの*option* パーザライブラリがあることは確かですが、完全なコマンドラインパーズライブラリはありません。getoptとoptparseの両方とも、optionしかサポートしておらず、固定引数(positional argument)をサポートしていません。argparseは両方を扱うことができ、結果として、より良いヘルプメッセージを生成でき、optparseで必要になってくるusage=文字列といった冗長な記述を避けられます。
  • argparseモジュールは純正よりも実用性を評価します。従って、optparseが「 ‘required option’は自己矛盾であり、-pf,-file,+f,+rgb,/f,/fileはoptparseでサポートされず、今後もサポートされない」と明確に記載されているのと異なり、argparseは必要となるオプションとオプションを識別するのに使われる文字のカスタマイズができます。
  • argparseはnargs='?',nargs='*'あるいはnargs='+'を使って可変長引数を記述できます。optparseはこの機能の一部をテストされていないレシピを使って提供していますが[10]、「可変長引数を使おうとするとやばいことになりかねないよ」と認めています。
  • argparseはサブコマンドをサポートしています。これは、主となるコマンドラインパーザが依存する引数を他のコマンドラインパーザに送ります。これは、コマンドラインインタフェースではよく使われています。 例:svn cosvn up

optparseに機能を追加するのではなぜだめなの?

明らかに、以上述べてきた機能追加はoptparseに追加して実現できる許容量を越えています。利にかなった質問は、これらの機能はまったく新しいモジュールとしてではなく、なぜ単純にoptparseへのpatchとして提供されないのですか、です。事実、argparseの開発初期段階はpatchとして行われていたのですが、optparseの設計からくるさまざまな制約により、これは不可能だったのです。以下に示すいくつかの問題がありました。

  • optparseモジュールは内部のパースアルゴリズムを公開しています。特に、parser.largsparser.rargsはコールバックがあることを前提としています[11]。このことは、固定引数や可変長引数に対応するためにargparseでパースアルゴリズムを改良することを阻害しています。例えば、argparseでnargs='+'を使って正規表現にマッチさせる時は、parser.largsなどを使う意図はありません。(訳注:訳に自信なし)
  • optparseの拡張APIは非常に複雑です。例えば、簡単なstringからobjectへと変換する自作の関数を使おうとするときでも、Optionサブクラスを作り、クラス変数をいじり、自作のオプションタイプをparserに指定する必要があるのです。例を示します。:

    class MyOption(Option):
        TYPES = Option.TYPES + ("mytype",)
        TYPE_CHECKER = copy(Option.TYPE_CHECKER)
        TYPE_CHECKER["mytype"] = check_mytype
    parser = optparse.OptionParser(option_class=MyOption)
    parser.add_option("-m", type="mytype")
    
  • optparseとargparseは両方共コマンドラインの引数をパーズし、parse_argsによって返されたオブジェクトの属性を設定します。しかし、optparseモジュールは自作のアクションであるtake_actionメソッドにensure_valueメソッドを持つvaluesオブジェクトが常に渡されることを前提としています[12]。一方、argparseはどんなオブジェクトにでも属性を設定することができます。例

    foo_object = ...
    parser.parse_args(namespace=foo_object)
    foo_object.some_attribute_parsed_from_command_line
    

このような問題により、argparseがoptparse APIと互換性を保ったままにすることは難しく、argparseは独立したモジュールとして開発されました。付け加えると、同じ理由によりargparseの全ての機能を後方互換性を保ったままoptparseに入れることは考えられません。

optparseの廃止

argparseで、optparseの全ての機能が実現されているため、optparseモジュールは将来的に廃止されます。しかし、optparseは広く使用されているため、廃止の道筋は単に文書の変更だけであり、標準では警告は表示されません。

  • Python 2.7以降と3.2以降 – optparseの文章に以下の注意書きが付け加えられます。

    optparseモジュールは廃止され、以降開発されません。以降の開発はargparseモジュールで継続されます。

  • Python 2.7以降 – もしPython 3の互換性フラグ(-3)が与えられた場合、optparseはDeprecationWarningという警告を出します。そうでなければ、警告はなにも出ません。
  • Python 3.2以上 – optparseをimportすると、PendingDeprecationWarningという、標準では表示されない警告が出ます。

optparseが削除される日付に関してはなにも決まっていないことに注意してください。

getoptの文書の更新

getoptモジュールは廃止されません。しかし、その文書のいくつかの場所ではargparseを示すように更新されます。モジュールの先頭には以下の注意書きが付け加えられます。

getoptモジュールはC言語のgetopt関数のAPIに慣れ親しんだユーザのために作
られたコマンドラインオプションのパーズモジュールです。C言語のgetopt関数
に不慣れな、あるいは少ないコード量でより良いヘルプとエラーメッセージを
得たいユーザは、代わりにargparseモジュールを使ってください。

さらに、最後のgetoptの例の後に、以下の注意書きが付け加えられます。

少ないコード量で同等のコマンドラインインタフェースが、argparseモジュールを使うことで得られます。

  import argparse

  if __name__ == '__main__':
      parser = argparse.ArgumentParser()
      parser.add_argument('-o', '--output')
      parser.add_argument('-v', dest='verbose', action='store_true')
      args = parser.parse_args()
      # ... do something with args.output ...
      # ... do something with args.verbose ..

延期: 文字列フォーマット

argparseモジュールはPython 2.3から3.2までサポートしますが、そのため、伝統的な%(foo)s形式の文字列フォーマットに依存します。これは新しい形式の{foo}フォーマットを使うように推奨されています[13]。標準ライブラリではこの問題について議論され[14]、何人かは%形式から{}形式に自動的に変換する機能を開発しています[15][16]。この変換機能が標準ライブラリに付け加えられた時には、argparseもこの両方のフォーマット形式をサポートします。

getopt互換のメソッドは取り除かれました

以前、このPEPがoptparseと同じくgetoptの廃止も提案したとき、以下のようなメソッドの追加に関する議論がありました。

ArgumentParser.add_getopt_arguments(options[, long_options])

しかし、このメソッドはこれらの理由により追加されません。

  • getoptモジュールは廃止されないため、必要性は低いです。
  • このメソッドはすでにusageのメッセージを管理しているgetoptユーザが移行に使うには簡単ではありません。なぜならこのAPIは引数にヘルプメッセージを追加する機能を提供していないからです。
  • getoptが想定しているユーザのうち何人かは、関数を一回だけ呼べばいいということを重要視しています。このAPIではArgumentParser()parse_args()の両方を呼ぶ必要があるため、この要求を満たせません。

スコープ外: 機能要求

argparseに対する機能要求のいくつかは、このPEPですでに議論されています。

  • 環境変数からデフォルトの引数を設定する機能
  • 設定ファイルからデフォルトの引数を設定する機能
  • 現在の”foo subcommand –help”に加えて”foo –help subcommand”を解釈する機能

これらは全て正当な理由がある機能要求ですが、このPEPのスコープ外であり、argparseのIssue Trackerで議論されます。

議論: sys.stderrとsys.exit

argparseを使うと、標準では常にsys.stderrに書き出し、不正な引数が与えられた時に常にsys.exitを呼んでいます。これは簡単なコマンドラインインタフェースを提供するargparseのユースケースの大部分にとって期待された振る舞いです。しかし、いくつかの場合では、argparseがexitしないようにして欲しかったり、あるいはsys.stderr以外に書き出して欲しかったりします。このような場合、ArgumentParserというサブクラスを作り、``exit`` や_print_messageをオーバーライドすることで欲しい機能を実現できます。後者は文書化されていない実相の詳細ですが、よく使われるようになれば公開します。

参考文献

[1]argparse(http://code.google.com/p/argparse/)
[2]getopt(http://docs.python.org/library/getopt.html)
[3]optparse(http://docs.python.org/library/optparse.html)
[4]argparse in IPython(http://mail.scipy.org/pipermail/ipython-dev/2009-April/005102.html)
[5]argparse in Debian(http://packages.debian.org/search?keywords=argparse)
[6](1, 2) 2007-01-03 request for argparse in the standard library(http://mail.python.org/pipermail/python-list/2007-January/472276.html)
[7]2009-06-09 request for argparse in the standard library(http://bugs.python.org/issue6247)
[8]2009-09-10 request for argparse in the standard library(http://mail.python.org/pipermail/stdlib-sig/2009-September/000342.html)
[9]Fredrik Lundh response to[6](http://mail.python.org/pipermail/python-list/2007-January/1086892.html)
[10]optparse variable args(http://docs.python.org/library/optparse.html#callback-example-6-variable-arguments)
[11]parser.largs and parser.rargs(http://docs.python.org/library/optparse.html#how-callbacks-are-called)
[12]take_action values argument(http://docs.python.org/library/optparse.html#adding-new-actions)
[13]use {}-formatting instead of %-formatting(http://bugs.python.org/msg89279)
[14]transitioning from % to {} formatting(http://mail.python.org/pipermail/pytho
[15]Vinay Sajip’s %-to-{} converter(http://gist.github.com/200936)
[16]Benjamin Peterson’s %-to-{} converter(http://bazaar.launchpad.net/~gutworth/+junk/mod2format/files)
[17]Guido’s approval(http://mail.python.org/pipermail/python-dev/2010-February/097839.html)

著作権

このドキュメントはパブリック・ドメインに属します。