argparse – コマンドラインオプションと引数の解析

目的:コマンドラインオプションと引数の解析
利用できるバージョン:2.7 以上

argparse モジュールは optparse に置き換わるものとして Python 2.7 で追加されました。 argparse の実装は optparse へ簡単に追加されない機能を提供しますが、後方互換性のない API の変更を必要とします。そのために optparse に置き換わる新たなライブラリとして設けられました。 optparse もそのままサポートされますが、新しい機能が実装されることはないと思われます。

optparse と比較する

argparse の API は optparse が提供するものとよく似ています。そして、多くのケースでは argparse はクラス名やメソッドを変更すると、そのまま置き換えて使用できます。そうとは言え、そのままでは使用できない互換性のない新機能も少しだけあります。

既存のプログラムをアップグレードするかどうかは、基本的にケースバイケースで決めます。 optparse では機能不足のためにワークアラウンドとしてのコードを書くぐらいなら、自分でメンテナンスするコード量を減らすために argparse へアップグレードした方が良いです。デプロイ対象の全てのプラットフォームで動作する場合、新しいプログラムには argparse を使った方が良いです。

パーサを設定する

argparse を使用するときに最初にすることは、パーサオブジェクトを作成して指定される引数をそのパーサオブジェクトへ伝えます。パーサオブジェクトはプログラムの実行時にコマンドライン引数を処理するために使用されます。

パーサクラスは ArgumentParser です。そのコンストラクタは、プログラムのヘルプ内容を作成したり、グローバルな動作や設定を行う複数の引数を取ります。

import argparse
parser = argparse.ArgumentParser(description='This is a PyMOTW sample program')

引数を定義する

argparse は完全な引数 処理 ライブラリです。引数は add_argument() への action 引数で指定されて様々なアクションのトリガーになります。サポートされるアクションは、(符号付き、またはリストの一部として)引数を格納する、引数が指定されたときに定数を格納する(true/false といったブーリアンに特化した操作を含む)、引数が指定された回数を数える、コールバック関数を呼び出す等があります。

デフォルトのアクションは引数の値を格納します。このケースでは、ある型を指定すると、その値は指定された型で変換してから格納されます。 dest 引数が指定された場合、コマンドライン引数が解析されるときに返す Namespace オブジェクトの属性名としてその値が保存されます。

コマンドラインを解析する

全ての引数を定義したら parse_args() へ引数文字列のシーケンスを渡すことでコマンドラインを解析します。デフォルトでは、引数は sys.argv[1:] から取得しますが、独自のリストを渡すこともできます。オプションは GNU/POSIX 構文で処理されるので、オプションと引数の値はシーケンスに混在できます。

parse_args() からの返り値はコマンドへの引数を含む Namespace オブジェクトです。 Namespace オブジェクトは引数の値を属性として保持します。つまり dest"myoption" の場合 args.myoption でその引数の値にアクセスできます。

簡単なサンプル

ここに3つの異なるオプションを持つサンプルがあります。1つ目はブーリアンオプション(-a)、2つ目は文字列オプション(-b)、最後は整数オプション(-c)です。

import argparse

parser = argparse.ArgumentParser(description='Short sample app')

parser.add_argument('-a', action="store_true", default=False)
parser.add_argument('-b', action="store", dest="b")
parser.add_argument('-c', action="store", dest="c", type=int)

print parser.parse_args(['-a', '-bval', '-c', '3'])

一文字オプションへ値を渡す方法は複数あります。このサンプルでは -bval-c val の2つの形態で渡しています。

$ python argparse_short.py
Namespace(a=True, b='val', c=3)

出力結果の 'c' に関連付けられた値の型は整数です。その値は ArgumentParser が変換してから格納するからです。

一文字オプションよりも “長い” オプション名も同様に処理されます。

import argparse

parser = argparse.ArgumentParser(description='Example with long option names')

parser.add_argument('--noarg', action="store_true", default=False)
parser.add_argument('--witharg', action="store", dest="witharg")
parser.add_argument('--witharg2', action="store", dest="witharg2", type=int)

print parser.parse_args([ '--noarg', '--witharg', 'val', '--witharg2=3' ])

その出力結果もよく似ています。

$ python argparse_long.py
Namespace(noarg=True, witharg='val', witharg2=3)

argparseoptparse の違いの1つは、任意選択ではないオプションの扱いです。 optparse の場合、オプションを解析するために固定しますが、 argparse は完全なコマンドライン解析ツールなので、任意選択ではないオプションの引数をうまく扱います。

import argparse

parser = argparse.ArgumentParser(description='Example with non-optional arguments')

parser.add_argument('count', action="store", type=int)
parser.add_argument('units', action="store")

print parser.parse_args()

このサンプルでは、”count” 引数は整数で “units” 引数は文字列として保存されます。コマンドライン上でどちらも指定されないか、その値が適切な型に変換できない場合にエラーが発生します。

$ python argparse_arguments.py 3 inches
Namespace(count=3, units='inches')

$ python argparse_arguments.py some inches
usage: argparse_arguments.py [-h] count units
argparse_arguments.py: error: argument count: invalid int value: 'some'

$ python argparse_arguments.py
usage: argparse_arguments.py [-h] count units
argparse_arguments.py: error: too few arguments

引数のアクション

引数が指定されたときにトリガーとなる6つの組み込みアクションがあります。

store
値を保存する、オプションで型が指定されたときは違う型へ変換する。これは明示的に指定されなかったときのデフォルトアクションです。
store_const
引数の解析結果からの値というよりも引数の仕様の一部として定義された値を保存する。これは通常ブーリアンではないコマンドラインのフラグの実装に使用される。
store_true / store_false
適切なブーリアン値を保存する。これらのアクションはブーリアンスイッチの実装に使用される。
append
リストへ値を保存する。引数が繰り返されるときに複数の値が保存される。
append_const
リストへの引数の仕様で定義された値を保存する。
version
プログラムに関するバージョンの詳細を表示して終了する。
import argparse

parser = argparse.ArgumentParser()

parser.add_argument('-s', action='store', dest='simple_value',
                    help='Store a simple value')

parser.add_argument('-c', action='store_const', dest='constant_value',
                    const='value-to-store',
                    help='Store a constant value')

parser.add_argument('-t', action='store_true', default=False,
                    dest='boolean_switch',
                    help='Set a switch to true')
parser.add_argument('-f', action='store_false', default=False,
                    dest='boolean_switch',
                    help='Set a switch to false')

parser.add_argument('-a', action='append', dest='collection',
                    default=[],
                    help='Add repeated values to a list',
                    )

parser.add_argument('-A', action='append_const', dest='const_collection',
                    const='value-1-to-append',
                    default=[],
                    help='Add different values to list')
parser.add_argument('-B', action='append_const', dest='const_collection',
                    const='value-2-to-append',
                    help='Add different values to list')

parser.add_argument('--version', action='version', version='%(prog)s 1.0')

results = parser.parse_args()
print 'simple_value     =', results.simple_value
print 'constant_value   =', results.constant_value
print 'boolean_switch   =', results.boolean_switch
print 'collection       =', results.collection
print 'const_collection =', results.const_collection
$ python argparse_action.py -h
usage: argparse_action.py [-h] [-s SIMPLE_VALUE] [-c] [-t] [-f]
                          [-a COLLECTION] [-A] [-B] [--version]

optional arguments:
  -h, --help       show this help message and exit
  -s SIMPLE_VALUE  Store a simple value
  -c               Store a constant value
  -t               Set a switch to true
  -f               Set a switch to false
  -a COLLECTION    Add repeated values to a list
  -A               Add different values to list
  -B               Add different values to list
  --version        show program's version number and exit

$ python argparse_action.py -s value
simple_value     = value
constant_value   = None
boolean_switch   = False
collection       = []
const_collection = []

$ python argparse_action.py -c
simple_value     = None
constant_value   = value-to-store
boolean_switch   = False
collection       = []
const_collection = []

$ python argparse_action.py -t
simple_value     = None
constant_value   = None
boolean_switch   = True
collection       = []
const_collection = []

$ python argparse_action.py -f
simple_value     = None
constant_value   = None
boolean_switch   = False
collection       = []
const_collection = []

$ python argparse_action.py -a one -a two -a three
simple_value     = None
constant_value   = None
boolean_switch   = False
collection       = ['one', 'two', 'three']
const_collection = []

$ python argparse_action.py -B -A
simple_value     = None
constant_value   = None
boolean_switch   = False
collection       = []
const_collection = ['value-2-to-append', 'value-1-to-append']

$ python argparse_action.py --version
argparse_action.py 1.0

オプションの接頭辞

オプションのデフォルト構文は、”-” の接頭辞でコマンドラインスイッチを表す Unix の慣習に基づいています。 argparse はその他の接頭辞をサポートするので、プラットフォーム独自のデフォルトに準拠させる(例えば Windows では “/” を使う)といった異なる慣習をサポートできます。

import argparse

parser = argparse.ArgumentParser(description='Change the option prefix characters',
                                 prefix_chars='-+/',
                                 )

parser.add_argument('-a', action="store_false", default=None,
                    help='Turn A off',
                    )
parser.add_argument('+a', action="store_true", default=None,
                    help='Turn A on',
                    )
parser.add_argument('//noarg', '++noarg', action="store_true", default=False)

print parser.parse_args()

ArgumentParserprefix_chars パラメータにオプションを表す文字を全て含む文字列をセットしてください。 prefix_chars はコマンドラインスイッチの文字を許容しますが、個別の引数定義はそのスイッチ文字の構文を指定することを理解することが重要です。これにより、明示的に違う接頭辞で使用するオプションがエイリアスなのか(プラットフォーム独自のコマンドライン構文のようなもの)、他の代替処理なのか(例えば “+” はフラグをオンにして “-” はオフにする)を管理します。上述したこのサンプルでは、 +a-a は独立した別の引数であり、 //noarg++noarg は同じ引数ですが --noarg は違います。

$ python argparse_prefix_chars.py -h
usage: argparse_prefix_chars.py [-h] [-a] [+a] [//noarg]

Change the option prefix characters

optional arguments:
  -h, --help        show this help message and exit
  -a                Turn A off
  +a                Turn A on
  //noarg, ++noarg

$ python argparse_prefix_chars.py +a
Namespace(a=True, noarg=False)

$ python argparse_prefix_chars.py -a
Namespace(a=False, noarg=False)

$ python argparse_prefix_chars.py //noarg
Namespace(a=None, noarg=True)

$ python argparse_prefix_chars.py ++noarg
Namespace(a=None, noarg=True)

$ python argparse_prefix_chars.py --noarg
usage: argparse_prefix_chars.py [-h] [-a] [+a] [//noarg]
argparse_prefix_chars.py: error: unrecognized arguments: --noarg

引数のソース

これまでのサンプルでは、パーサへ渡される引数のリストは明示的に渡したリストか、 sys.argv から暗黙的に受け取ります。パーサへ渡すリストを明示的に渡すことは argparse で(設定ファイルのような)コマンドラインではない、よく似た命令を処理するのに便利です。

import argparse
from ConfigParser import ConfigParser
import shlex

parser = argparse.ArgumentParser(description='Short sample app')

parser.add_argument('-a', action="store_true", default=False)
parser.add_argument('-b', action="store", dest="b")
parser.add_argument('-c', action="store", dest="c", type=int)

config = ConfigParser()
config.read('argparse_witH_shlex.ini')
config_value = config.get('cli', 'options')
print 'Config  :', config_value

argument_list = shlex.split(config_value)
print 'Arg List:', argument_list

print 'Results :', parser.parse_args(argument_list)

shlex モジュールは設定ファイルから読み込んだ文字列を簡単に分割します。

$ python argparse_with_shlex.py
Config  : -a -b 2
Arg List: ['-a', '-b', '2']
Results : Namespace(a=True, b='2', c=None)

設定ファイルを処理する代替方法として fromfile_prefix_chars で処理対象の引数セットを含む入力ファイルを指定する引数の認識方法を argparse へ伝えます。

import argparse
from ConfigParser import ConfigParser
import shlex

parser = argparse.ArgumentParser(description='Short sample app',
                                 fromfile_prefix_chars='@',
                                 )

parser.add_argument('-a', action="store_true", default=False)
parser.add_argument('-b', action="store", dest="b")
parser.add_argument('-c', action="store", dest="c", type=int)

print parser.parse_args(['@argparse_fromfile_prefix_chars.txt'])

このサンプルは @ の接頭辞を持つ引数を見つけると停止して、その名前のファイルを読み込んで引数を探します。例えば argparse_fromfile_prefix_chars.txt という入力ファイルは、1行につき1引数の引数セットを持ちます。

-a
-b
2

この入力ファイルを処理した出力結果です。

$ python argparse_fromfile_prefix_chars.py
Namespace(a=True, b='2', c=None)

自動生成されるオプション

argparse は、設定済みの場合、自動的にヘルプを生成するオプションを追加してアプリケーションのバージョン情報を表示します。

ArgumentParseradd_help 引数はヘルプ関連のオプションを管理します。

import argparse

parser = argparse.ArgumentParser(add_help=True)

parser.add_argument('-a', action="store_true", default=False)
parser.add_argument('-b', action="store", dest="b")
parser.add_argument('-c', action="store", dest="c", type=int)

print parser.parse_args()

ヘルプオプション (-h--help) はデフォルトで追加されますが、 add_help に false をセットすることで非表示にできます。

import argparse

parser = argparse.ArgumentParser(add_help=False)

parser.add_argument('-a', action="store_true", default=False)
parser.add_argument('-b', action="store", dest="b")
parser.add_argument('-c', action="store", dest="c", type=int)

print parser.parse_args()

-h--help はヘルプを見たいときのデファクトスタンダードなオプション名ですが、アプリケーションや argparse の用途によっては、ヘルプを提供する必要がないときや他の目的でそのオプション名を使用したいときがあります。

$ python argparse_with_help.py -h
usage: argparse_with_help.py [-h] [-a] [-b B] [-c C]

optional arguments:
  -h, --help  show this help message and exit
  -a
  -b B
  -c C

$ python argparse_without_help.py -h
usage: argparse_without_help.py [-a] [-b B] [-c C]
argparse_without_help.py: error: unrecognized arguments: -h

バージョンオプション (-v--version) は ArgumentParser のコンストラクタに version がセットされるときに追加されます。

import argparse

parser = argparse.ArgumentParser(version='1.0')

parser.add_argument('-a', action="store_true", default=False)
parser.add_argument('-b', action="store", dest="b")
parser.add_argument('-c', action="store", dest="c", type=int)

print parser.parse_args()

print 'This is not printed'

どちらのオプションを使用してもプログラムのバージョン文字列を表示してから終了します。

$ python argparse_with_version.py -h
usage: argparse_with_version.py [-h] [-v] [-a] [-b B] [-c C]

optional arguments:
  -h, --help     show this help message and exit
  -v, --version  show program's version number and exit
  -a
  -b B
  -c C

$ python argparse_with_version.py -v
1.0

$ python argparse_with_version.py --version
1.0

パーサ構築

argparse は、ヘルプの内容をもっと分かり易くしたり、引数パーサを簡単に実装する機能をいくつか提供します。

パーサルールを共有する

全ての引数セットを受け取るコマンドラインプログラムを実装する必要があるのは同じです。その後で様々な方法で定義します。例えば、任意の実アクションを行う前にユーザを認証する必要があるなら、そのプログラムは --user--password オプションをサポートする必要があります。全ての ArgumentParser クラスへ明示的にそういったオプションを追加するよりも、共有オプションとして “親” パーサを定義します。そうして、その親パーサから継承された個別のプログラムが引数を解析します。

まず行うことは共有の引数定義として親パーサを設定することです。親パーサがそれぞれの子パーサで同じヘルプオプションを追加しようとして例外が発生するので、親パーサの自動ヘルプ生成をオフにします。

import argparse

parser = argparse.ArgumentParser(add_help=False)

parser.add_argument('--user', action="store")
parser.add_argument('--password', action="store")

次に parents をセットして別のパーサを作成してください。

import argparse
import argparse_parent_base

parser = argparse.ArgumentParser(parents=[argparse_parent_base.parser])

parser.add_argument('--local-arg', action="store_true", default=False)

print parser.parse_args()

結果としてこのプログラムは3つのオプションがあります。

$ python argparse_uses_parent.py -h
usage: argparse_uses_parent.py [-h] [--user USER] [--password PASSWORD]
                               [--local-arg]

optional arguments:
  -h, --help           show this help message and exit
  --user USER
  --password PASSWORD
  --local-arg

オプションの競合

前節で紹介したサンプルは、同じ引数の名前でパーサへ2つの引数ハンドラを追加することで例外を発生させると説明しました。 conflict_handlerresolve を渡すことで競合を解決するように変更してください。2つの組み込みハンドラ error (デフォルト) と resolve があり、追加される順番に基づいてハンドラを選択します。

import argparse

parser = argparse.ArgumentParser(conflict_handler='resolve')

parser.add_argument('-a', action="store")
parser.add_argument('-b', action="store", help='Short alone')
parser.add_argument('--long-b', '-b', action="store", help='Long and short together')

print parser.parse_args(['-h'])

指定した引数名は最後に追加されたハンドラで使用されるので、このサンプルの独自オプション -b--long-b のエイリアスとして扱われます。

$ python argparse_conflict_handler_resolve.py
usage: argparse_conflict_handler_resolve.py [-h] [-a A] [--long-b LONG_B]

optional arguments:
  -h, --help            show this help message and exit
  -a A
  --long-b LONG_B, -b LONG_B
                        Long and short together

add_argument() を呼び出す順番を変更することで独立したオプションとして扱います。

import argparse

parser = argparse.ArgumentParser(conflict_handler='resolve')

parser.add_argument('-a', action="store")
parser.add_argument('--long-b', '-b', action="store", help='Long and short together')
parser.add_argument('-b', action="store", help='Short alone')

print parser.parse_args(['-h'])

これで両方のオプションを一緒に使用できます。

$ python argparse_conflict_handler_resolve2.py
usage: argparse_conflict_handler_resolve2.py [-h] [-a A] [--long-b LONG_B]
                                             [-b B]

optional arguments:
  -h, --help       show this help message and exit
  -a A
  --long-b LONG_B  Long and short together
  -b B             Short alone

引数グループ

argparse は “groups” 内に引数定義を組み合わせます。デフォルトは2つのグループを使用します。1つはオプションのため、もう1つは必須の位置ベースの引数のためです。

import argparse

parser = argparse.ArgumentParser(description='Short sample app')

parser.add_argument('--optional', action="store_true", default=False)
parser.add_argument('positional', action="store")

print parser.parse_args()

このグルーピングは、ヘルプ内容で “positional arguments” と “optional arguments” の独立したセクションに分割されます。

$ python argparse_default_grouping.py -h
usage: argparse_default_grouping.py [-h] [--optional] positional

Short sample app

positional arguments:
  positional

optional arguments:
  -h, --help  show this help message and exit
  --optional

ヘルプ内容に関連するオプション、もしくは値が一緒に説明されるように論理的に分かり易いグルーピングにできます。前節で紹介した共有オプションのサンプルを、ヘルプの認証オプションの説明が一緒に表示されるようにカスタムグルーピングで書き直してみます。

add_argument_group() で “authentication” グループを作成して、基本パーサではなく、そのグループへ認証関連のオプションを追加してください。

import argparse

parser = argparse.ArgumentParser(add_help=False)

group = parser.add_argument_group('authentication')

group.add_argument('--user', action="store")
group.add_argument('--password', action="store")

グループベースの親パーサを使用するこのプログラムは、 parents に親パーサを指定して変更前のプログラム同様に表示します。

import argparse
import argparse_parent_with_group

parser = argparse.ArgumentParser(parents=[argparse_parent_with_group.parser])

parser.add_argument('--local-arg', action="store_true", default=False)

print parser.parse_args()

ヘルプ内容は認証オプションを一緒に表示するようになりました。

$ python argparse_uses_parent_with_group.py -h
usage: argparse_uses_parent_with_group.py [-h] [--user USER]
                                          [--password PASSWORD] [--local-arg]

optional arguments:
  -h, --help           show this help message and exit
  --local-arg

authentication:
  --user USER
  --password PASSWORD

相互排他オプション

相互排他オプションの定義はグルーピング機能の特殊ケースです。 add_argument_group() ではなく add_mutually_exclusive_group() を使用してください。

import argparse

parser = argparse.ArgumentParser()

group = parser.add_mutually_exclusive_group()
group.add_argument('-a', action='store_true')
group.add_argument('-b', action='store_true')

print parser.parse_args()

argparse は、グループのどれか1つだけが指定されるように相互排他性を強制します。

$ python argparse_mutually_exclusive.py -h
usage: argparse_mutually_exclusive.py [-h] [-a | -b]

optional arguments:
  -h, --help  show this help message and exit
  -a
  -b

$ python argparse_mutually_exclusive.py -a
Namespace(a=True, b=False)

$ python argparse_mutually_exclusive.py -b
Namespace(a=False, b=True)

$ python argparse_mutually_exclusive.py -a -b
usage: argparse_mutually_exclusive.py [-h] [-a | -b]
argparse_mutually_exclusive.py: error: argument -b: not allowed with argument -a

パーサをネストする

前節で説明した親パーサの方法は関連コマンド間でオプションを共有する1つの方法です。代替の方法として、1つのプログラム内にコマンドを組み合わせて、コマンドラインの各部分を扱うサブパーサを使用する方法があります。その結果は、複数のコマンドラインアクション、もしくはサブコマンドが行う svnhg コマンドのように動作します。

ファイルシステム上のディレクトリを扱うプログラムは、次のように作成、削除、そしてディレクトリ内のコンテンツを表示するコマンドを定義します。

import argparse

parser = argparse.ArgumentParser()

subparsers = parser.add_subparsers(help='commands')

# list コマンド
list_parser = subparsers.add_parser('list', help='List contents')
list_parser.add_argument('dirname', action='store', help='Directory to list')

# create コマンド
create_parser = subparsers.add_parser('create', help='Create a directory')
create_parser.add_argument('dirname', action='store', help='New directory to create')
create_parser.add_argument('--read-only', default=False, action='store_true',
                           help='Set permissions to prevent writing to the directory',
                           )

# delete コマンド
delete_parser = subparsers.add_parser('delete', help='Remove a directory')
delete_parser.add_argument('dirname', action='store', help='The directory to remove')
delete_parser.add_argument('--recursive', '-r', default=False, action='store_true',
                           help='Remove the contents of the directory, too',
                           )

print parser.parse_args()

ヘルプ内容は、位置引数としてコマンドライン上で指定できる “commands” と名付けられたサブパーサを表示します。

$ python argparse_subparsers.py -h
usage: argparse_subparsers.py [-h] {list,create,delete} ...

positional arguments:
  {list,create,delete}  commands
    list                List contents
    create              Create a directory
    delete              Remove a directory

optional arguments:
  -h, --help            show this help message and exit

さらに、それぞれのサブパーサはそのコマンドのオプションと引数を説明する独自のヘルプを持ちます。

$ python argparse_subparsers.py create -h
usage: argparse_subparsers.py create [-h] [--read-only] dirname

positional arguments:
  dirname      New directory to create

optional arguments:
  -h, --help   show this help message and exit
  --read-only  Set permissions to prevent writing to the directory

そして、引数が解析されるときに parse_args() が返す Namespace オブジェクトは指定されたそのコマンドのみに関連する値を含みます。

$ python argparse_subparsers.py delete -r foo
Namespace(dirname='foo', recursive=True)

高度な引数処理

これまでのサンプルはシンプルなブーリアンフラグ、文字か数値のオプション、位置引数を紹介しました。 argparse は、可変長の引数リスト、列挙、定数値を便利に洗練された仕様でサポートします。

変数の引数リスト

コマンドライン上で複数の引数を解析するのに1つの引数定義で設定できます。必須引数、もしくは期待される引数の数に基づいて次のフラグ値の1つを nargs にセットしてください。

説明
N 引数の絶対数 (例 3)
? 0 か 1 の引数
* 0 か任意の引数
+ 少なくとも1つ以上の任意の引数
import argparse

parser = argparse.ArgumentParser()

parser.add_argument('--three', nargs=3)
parser.add_argument('--optional', nargs='?')
parser.add_argument('--all', nargs='*', dest='all')
parser.add_argument('--one-or-more', nargs='+')

print parser.parse_args()

このパーサは引数の数を強制して、ヘルプ内容に正確な構文図を生成します。

$ python argparse_nargs.py -h
usage: argparse_nargs.py [-h] [--three THREE THREE THREE]
                         [--optional [OPTIONAL]] [--all [ALL [ALL ...]]]
                         [--one-or-more ONE_OR_MORE [ONE_OR_MORE ...]]

optional arguments:
  -h, --help            show this help message and exit
  --three THREE THREE THREE
  --optional [OPTIONAL]
  --all [ALL [ALL ...]]
  --one-or-more ONE_OR_MORE [ONE_OR_MORE ...]

$ python argparse_nargs.py
Namespace(all=None, one_or_more=None, optional=None, three=None)

$ python argparse_nargs.py --three
usage: argparse_nargs.py [-h] [--three THREE THREE THREE]
                         [--optional [OPTIONAL]] [--all [ALL [ALL ...]]]
                         [--one-or-more ONE_OR_MORE [ONE_OR_MORE ...]]
argparse_nargs.py: error: argument --three: expected 3 argument(s)

$ python argparse_nargs.py --three a b c
Namespace(all=None, one_or_more=None, optional=None, three=['a', 'b', 'c'])

$ python argparse_nargs.py --optional
Namespace(all=None, one_or_more=None, optional=None, three=None)

$ python argparse_nargs.py --optional with_value
Namespace(all=None, one_or_more=None, optional='with_value', three=None)

$ python argparse_nargs.py --all with multiple values
Namespace(all=['with', 'multiple', 'values'], one_or_more=None, optional=None, three=None)

$ python argparse_nargs.py --one-or-more with_value
Namespace(all=None, one_or_more=['with_value'], optional=None, three=None)

$ python argparse_nargs.py --one-or-more with multiple values
Namespace(all=None, one_or_more=['with', 'multiple', 'values'], optional=None, three=None)

$ python argparse_nargs.py --one-or-more
usage: argparse_nargs.py [-h] [--three THREE THREE THREE]
                         [--optional [OPTIONAL]] [--all [ALL [ALL ...]]]
                         [--one-or-more ONE_OR_MORE [ONE_OR_MORE ...]]
argparse_nargs.py: error: argument --one-or-more: expected at least one argument

引数の型

argparse は、明示的に文字列を別の型に変換しない限り、文字列として全ての引数の値を扱います。 add_argument() への type パラメータは、文字列から別の型へ引数の値を変換するために ArgumentParser が使用する変換関数を指定します。

import argparse

parser = argparse.ArgumentParser()

parser.add_argument('-i', type=int)
parser.add_argument('-f', type=float)
parser.add_argument('--file', type=file)

try:
    print parser.parse_args()
except IOError, msg:
    parser.error(str(msg))

一文字引数を取る任意の呼び出し可能オブジェクトは、 int(), float(), file() といった組み込み型を含めて type に渡されます。

$ python argparse_type.py -i 1
Namespace(f=None, file=None, i=1)

$ python argparse_type.py -f 3.14
Namespace(f=3.14, file=None, i=None)

$ python argparse_type.py --file argparse_type.py
Namespace(f=None, file=<open file 'argparse_type.py', mode 'r' at 0x1004de270>, i=None)

型変換が失敗した場合、 argparse は例外を発生させます。 TypeErrorValueError 例外は、自動的にトラップされてユーザへ簡易エラーメッセージを表示します。次のサンプルの、入力ファイルが存在しないときに発生する IOError といったその他の例外は呼び出し側で扱わなければなりません。

$ python argparse_type.py -i a
usage: argparse_type.py [-h] [-i I] [-f F] [--file FILE]
argparse_type.py: error: argument -i: invalid int value: 'a'

$ python argparse_type.py -f 3.14.15
usage: argparse_type.py [-h] [-i I] [-f F] [--file FILE]
argparse_type.py: error: argument -f: invalid float value: '3.14.15'

$ python argparse_type.py --file does_not_exist.txt
usage: argparse_type.py [-h] [-i I] [-f F] [--file FILE]
argparse_type.py: error: [Errno 2] No such file or directory: 'does_not_exist.txt'

あらかじめ定義された値セットに入力する引数を制限するには choices パラメータを使用してください。

import argparse

parser = argparse.ArgumentParser()

parser.add_argument('--mode', choices=('read-only', 'read-write'))

print parser.parse_args()

--mode への引数が定義済みの値ではない場合、エラーが発生して処理を中断します。

$ python argparse_choices.py -h
usage: argparse_choices.py [-h] [--mode {read-only,read-write}]

optional arguments:
  -h, --help            show this help message and exit
  --mode {read-only,read-write}

$ python argparse_choices.py --mode read-only
Namespace(mode='read-only')

$ python argparse_choices.py --mode invalid
usage: argparse_choices.py [-h] [--mode {read-only,read-write}]
argparse_choices.py: error: argument --mode: invalid choice: 'invalid'
(choose from 'read-only', 'read-write')

ファイル引数

file オブジェクトは一文字引数でインスタンス化できますが、アクセスモードの指定ができません。 FileType は、引数として渡されるファイルのアクセスモードやバッファサイズを指定するといったもっと柔軟な方法を提供します。

import argparse

parser = argparse.ArgumentParser()

parser.add_argument('-i', metavar='in-file', type=argparse.FileType('rt'))
parser.add_argument('-o', metavar='out-file', type=argparse.FileType('wt'))

try:
    results = parser.parse_args()
    print 'Input file:', results.i
    print 'Output file:', results.o
except IOError, msg:
    parser.error(str(msg))
    

引数名で関連付けられた値はオープンされたファイルハンドラです。そのファイルを使い終えたときに自分でクローズする責任があります。

$ python argparse_FileType.py -h
usage: argparse_FileType.py [-h] [-i in-file] [-o out-file]

optional arguments:
  -h, --help   show this help message and exit
  -i in-file
  -o out-file

$ python argparse_FileType.py -i argparse_FileType.py -o temporary_file.txt
Input file: <open file 'argparse_FileType.py', mode 'rt' at 0x1004df270>
Output file: <open file 'temporary_file.txt', mode 'wt' at 0x1004df300>

$ python argparse_FileType.py -i no_such_file.txt
usage: argparse_FileType.py [-h] [-i in-file] [-o out-file]
argparse_FileType.py: error: argument -i: can't open 'no_such_file.txt': [Errno 2] No such file or directory: 'no_such_file.txt'

カスタムアクション

前節で説明した組み込みアクションに加えて、アクション API を実装するオブジェクトを提供することでカスタムアクションを定義できます。 action として add_argument() へ渡すオブジェクトは、定義された引数(add_argument() へ渡される全ての引数と同じ)を表示するパラメータを取ります。そして、引数を処理する parser 、解析した結果を保持する namespace 、実行後の引数の value 、アクションをトリガーする option_string をパラメータとして受け取る呼び出し可能オブジェクトを返します。

Action クラスは新しいアクションを定義するために便利な仕組みを提供します。そのコンストラクタは引数定義を扱います。そのため、サブクラスで __call__() のみオーバーライドする必要があります。

import argparse

class CustomAction(argparse.Action):
    def __init__(self,
                 option_strings,
                 dest,
                 nargs=None,
                 const=None,
                 default=None,
                 type=None,
                 choices=None,
                 required=False,
                 help=None,
                 metavar=None):
        argparse.Action.__init__(self,
                                 option_strings=option_strings,
                                 dest=dest,
                                 nargs=nargs,
                                 const=const,
                                 default=default,
                                 type=type,
                                 choices=choices,
                                 required=required,
                                 help=help,
                                 metavar=metavar,
                                 )
        print
        print 'Initializing CustomAction'
        for name,value in sorted(locals().items()):
            if name == 'self' or value is None:
                continue
            print '  %s = %r' % (name, value)
        return

    def __call__(self, parser, namespace, values, option_string=None):
        print
        print 'Processing CustomAction for "%s"' % self.dest
        print '  parser = %s' % id(parser)
        print '  values = %r' % values
        print '  option_string = %r' % option_string
        
        # 入力値に任意の処理を行う
        if isinstance(values, list):
            values = [ v.upper() for v in values ]
        else:
            values = values.upper()
        # コンストラクタへ渡された dest 値で namespace に結果を保存する
        setattr(namespace, self.dest, values)

parser = argparse.ArgumentParser()

parser.add_argument('-a', action=CustomAction)
parser.add_argument('-m', nargs='*', action=CustomAction)
parser.add_argument('positional', action=CustomAction)

results = parser.parse_args(['-a', 'value', '-m' 'multi-value', 'positional-value'])
print
print results

values の型は nargs の値に依存します。その引数が複数の値を許容する場合、 values は1つの要素のみであってもリストになります。

さらに option_string の値もオリジナルの引数仕様に依存します。位置、必須、引数、 option_string は常に None です。

$ python argparse_custom_action.py

Initializing CustomAction
  dest = 'a'
  option_strings = ['-a']
  required = False

Initializing CustomAction
  dest = 'm'
  nargs = '*'
  option_strings = ['-m']
  required = False

Initializing CustomAction
  dest = 'positional'
  option_strings = []
  required = True

Processing CustomAction for "a"
  parser = 4299620560
  values = 'value'
  option_string = '-a'

Processing CustomAction for "m"
  parser = 4299620560
  values = ['multi-value']
  option_string = '-m'

Processing CustomAction for "positional"
  parser = 4299620560
  values = 'positional-value'
  option_string = None

Namespace(a='VALUE', m=['MULTI-VALUE'], positional='POSITIONAL-VALUE')

See also

argparse
本モジュールの標準ライブラリドキュメント
original argparse
標準ライブラリではない argparse の PyPI ページ、これは古い Python バージョンと互換性があり独立してインストールされる
ConfigParser
設定ファイルを読み書きする
Bookmark and Share