引数なしでスクリプトが呼び出されたときにpython argparseでヘルプメッセージを表示する


226

これは簡単なものかもしれません。argparseを使用してコマンドラインの引数/オプションを処理するプログラムがあるとします。以下は「ヘルプ」メッセージを出力します:

./myprogram -h

または:

./myprogram --help

ただし、引数なしでスクリプトを実行しても、何も実行されません。引数なしで呼び出されたときに使用方法のメッセージを表示することです。それはどのように行われますか?

回答:


273

この回答は、Googleグループの Steven Bethardからのものです。Googleアカウントを持たない人が簡単にアクセスできるように、ここに再投稿しています。

errorメソッドのデフォルトの動作をオーバーライドできます。

import argparse
import sys

class MyParser(argparse.ArgumentParser):
    def error(self, message):
        sys.stderr.write('error: %s\n' % message)
        self.print_help()
        sys.exit(2)

parser = MyParser()
parser.add_argument('foo', nargs='+')
args = parser.parse_args()

上記のソリューションでは、error メソッドがトリガーされるたびにヘルプメッセージが出力されることに注意してください。たとえば、test.py --blah--blah有効なオプションでない場合は、ヘルプメッセージも出力されます。

コマンドラインで引数が指定されていない場合にのみヘルプメッセージを出力する場合は、おそらくこれが最も簡単な方法です。

import argparse
import sys

parser=argparse.ArgumentParser()
parser.add_argument('foo', nargs='+')
if len(sys.argv)==1:
    parser.print_help(sys.stderr)
    sys.exit(1)
args=parser.parse_args()

parser.print_help()デフォルトでstdoutに出力することに注意してください。以下のようinit_jsが示唆、使用がparser.print_help(sys.stderr)stderrに出力します。


ええ.. argparseがこのシナリオを処理する方法があったかどうか、私はそれについて疑問に思っていました。ありがとう!
musashiXXX

6
parser.print_usage()代わりに使用する2番目のソリューションではparser.print_help()、ヘルプメッセージに使用法が含まれていますが、より冗長です。
user2314737 2015

5
私は回答の2番目の部分に投票したでしょうが、オーバーライドerror()することは私には恐ろしい考えのようです。それは別の目的を果たします、それはフレンドリーな使用法やヘルプを印刷するために設計されていません。
Peterino、

@Peterino-オーバーライドは子クラスで発生しているため、これは問題になりません。それは明白です。
Marcel Wilson、

1
@unutbu WONDERFUL!まさに私が必要としたもの。1つの質問、これはサブコマンドにも適用できますか?私は通常 `` Namespace(output = None) `を取得します。すべてのサブコマンドでエラーを簡単にトリガーするにはどうすればよいですか?そこでエラーをトリガーしたいのですが。
ジョナサンコマー

56

クラスを作成する代わりに、try / exceptを代わりに使用できます

try:
    options = parser.parse_args()
except:
    parser.print_help()
    sys.exit(0)

利点は、ワークフローがより明確になり、スタブクラスが不要になることです。欠点は、最初の「使用」行が2回印刷されることです。

これには、少なくとも1つの必須の引数が必要です。必須の引数がない場合、コマンドラインで引数を0にすることは有効です。


私も、受け入れられた答えよりもこれを好みます。引数が予期しない場合にヘルプを出力するために、クラスを追加することは過剰です。優れたモジュールargparseにエラーケースを処理させてください。
ニコールフィニー

7
このコード-hは、フラグを使用するとヘルプを2回出力し、フラグを使用すると不要なヘルプを出力します--version。:これらの問題を軽減するには、このようなエラーの種類を確認することができますexcept SystemExit as err: if err.code == 2: parser.print_help()
pkowalczyk

25

argparseを使用すると、次のことができます。

parser.argparse.ArgumentParser()
#parser.add_args here

#sys.argv includes a list of elements starting with the program
if len(sys.argv) < 2:
    parser.print_usage()
    sys.exit(1)

5
これは、次の電話の前に来る必要がありますparser.parse_args()
ボブスタイン

18

スクリプトを実行するために指定する必要がある引数がある場合-以下に示すように、ArgumentParser の必須パラメーターを使用します:-

parser.add_argument('--foo', required=True)

引数なしでスクリプトを実行すると、parse_args()はエラーを報告します。


2
これは最も簡単なソリューションであり、無効なオプションを指定しても機能します。
Steve Scherer

1
同意した。引数パーサーの組み込み機能を活用してから、何らかの種類の追加ハンドラーを作成する方が常に良いと思います。
クリストファーハンター

18

で説明されているように、(サブ)パーサーのデフォルト関数を関連付ける場合はadd_subparsers、単純にデフォルトアクションとして追加できます。

parser = argparse.ArgumentParser()
parser.set_defaults(func=lambda x: parser.print_usage())
args = parser.parse_args()
args.func(args)

位置引数がないために例外が発生する場合は、try-exceptを追加します。


1
この答えはとても過小評価されています。シンプルで、サブパーサーと非常にうまく機能します。
orodbhen

正解です。私が行った唯一の変更は、パラメータなしのラムダを使用することでした。
boh717

12

最もクリーンな解決策は、コマンドラインで何も指定されていない場合、デフォルトの引数を手動で渡すことです。

parser.parse_args(args=None if sys.argv[1:] else ['--help'])

完全な例:

import argparse, sys

parser = argparse.ArgumentParser()
parser.add_argument('--host', default='localhost', help='Host to connect to')
# parse arguments
args = parser.parse_args(args=None if sys.argv[1:] else ['--help'])

# use your args
print("connecting to {}".format(args.host))

引数なしで呼び出された場合、これは完全なヘルプ(短い使用法ではない)を出力します。


2
sys.argv[1:]非常に一般的なイディオムです。私はparser.parse_args(None if sys.argv[1:] else ['-h'])もっと慣用的できれいに見えます。
ヌーノ・アンドレ

1
@NunoAndréありがとう-回答を更新しました。確かにもっとパイソンのように感じます。
Ievgen Popovych 2018

10

ここに私のバージョンを山に投げ込みます:

import argparse

parser = argparse.ArgumentParser()
args = parser.parse_args()
if not vars(args):
    parser.print_help()
    parser.exit(1)

あなたは気づくかもしれparser.exitません-それがsysファイルの唯一の理由である場合はインポート行を保存するので、私は主にそのようにします...


parser.exit(1)はいいです!良い追加。
cgseller 2015年

4
残念ながら、位置引数がない場合、parser.parse_args()は終了します。したがって、これはオプションの引数を使用する場合にのみ機能します。
Marcel Wilson

1
@MarcelWilson、それは確かに-良いキャッチです!変更方法について考えます。
pauricthelodger 2016年

not vars(args)引数にdefaultメソッドがある場合は機能しない可能性があります。
funkid

5

仕事をすることができる一対のワンライナーsys.argv[1:](コマンドライン引数を参照する非常に一般的なPythonのイディオムsys.argv[0]、スクリプトの名前です)があります。

最初のものは自明で、きれいで、pythonicです:

args = parser.parse_args(None if sys.argv[1:] else ['-h'])

2つ目は少しハッカーです。空のリストがあることを以前に評価事実を組み合わせるFalseTrue == 1し、False == 0あなたがこれを取得同値を:

args = parser.parse_args([None, ['-h']][not sys.argv[1:]])

ブラケットが多すぎるかもしれませんが、前の引数の選択が行われた場合はかなり明確です。

_, *av = sys.argv
args = parser.parse_args([None, ['-h']][not av])

1
parser.print_help()
parser.exit()

このparser.exitメソッドは、status(戻りコード)とmessage値(自分で末尾の改行を含める!)も受け入れます。

独断的な例:)

#!/usr/bin/env python3

""" Example argparser based python file
"""

import argparse

ARGP = argparse.ArgumentParser(
    description=__doc__,
    formatter_class=argparse.RawTextHelpFormatter,
)
ARGP.add_argument('--example', action='store_true', help='Example Argument')


def main(argp=None):
    if argp is None:
        argp = ARGP.parse_args()  # pragma: no cover

    if 'soemthing_went_wrong' and not argp.example:
        ARGP.print_help()
        ARGP.exit(status=128, message="\nI just don't know what went wrong, maybe missing --example condition?\n")


if __name__ == '__main__':
    main()  # pragma: no cover

呼び出し例:

$ python3〜/ helloworld.py; エコー$?
使用法:helloworld.py [-h] [--example]

 argparserベースのPythonファイルの例

オプションの引数:
  -h、--helpこのヘルプメッセージを表示して終了します
  --example引数の例

何が問題だったかわからない、おそらく欠落している-例の条件?
128
$ python3〜/ helloworld.py-例; エコー$?
0

0

nargsを使用して位置引数を設定し、位置引数が空かどうかを確認します。

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('file', nargs='?')
args = parser.parse_args()
if not args.file:
    parser.print_help()

参照Python引数


0

これを行う別の方法を次に示します。特定のパラメーターが渡された場合にヘルプを表示する柔軟性のあるものが必要な場合は、まったくまたは1つ以上の競合する引数:

import argparse
import sys

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('-d', '--days', required=False,  help="Check mapped inventory that is x days old", default=None)
    parser.add_argument('-e', '--event', required=False, action="store", dest="event_id",
                        help="Check mapped inventory for a specific event", default=None)
    parser.add_argument('-b', '--broker', required=False, action="store", dest="broker_id",
                        help="Check mapped inventory for a broker", default=None)
    parser.add_argument('-k', '--keyword', required=False, action="store", dest="event_keyword",
                        help="Check mapped inventory for a specific event keyword", default=None)
    parser.add_argument('-p', '--product', required=False, action="store", dest="product_id",
                        help="Check mapped inventory for a specific product", default=None)
    parser.add_argument('-m', '--metadata', required=False, action="store", dest="metadata",
                        help="Check mapped inventory for specific metadata, good for debugging past tix", default=None)
    parser.add_argument('-u', '--update', required=False, action="store_true", dest="make_updates",
                        help="Update the event for a product if there is a difference, default No", default=False)
    args = parser.parse_args()

    days = args.days
    event_id = args.event_id
    broker_id = args.broker_id
    event_keyword = args.event_keyword
    product_id = args.product_id
    metadata = args.metadata
    make_updates = args.make_updates

    no_change_counter = 0
    change_counter = 0

    req_arg = bool(days) + bool(event_id) + bool(broker_id) + bool(product_id) + bool(event_keyword) + bool(metadata)
    if not req_arg:
        print("Need to specify days, broker id, event id, event keyword or past tickets full metadata")
        parser.print_help()
        sys.exit()
    elif req_arg != 1:
        print("More than one option specified. Need to specify only one required option")
        parser.print_help()
        sys.exit()

    # Processing logic here ...

乾杯!


私はあなたがサブパーサーまたは相互に排他的なグループを使う方がはるかに簡単だと思います
Tim Bray

0

コマンドが、ユーザーが何らかのアクションを選択する必要がある場合は、required = Trueを指定して相互に排他的なグループ使用します

これは、pd321によって提供される回答に対する一種の拡張です。

import argparse

parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument("--batch", action='store', type=int,  metavar='pay_id')
group.add_argument("--list", action='store_true')
group.add_argument("--all", action='store_true', help='check all payments')

args=parser.parse_args()

if args.batch:
    print('batch {}'.format(args.batch))

if args.list:
    print('list')

if args.all:
    print('all')

出力:

$ python3 a_test.py
使用法:a_test.py [-h](--batch pay_id | --list | --all)
a_test.py:エラー:引数の1つ--batch --list --allが必要です

これは基本的なヘルプのみを提供します。そして、他のいくつかの答えはあなたに完全な助けを与えます。しかし、少なくともユーザーは、自分ができることを知っています-h


0

これも(すべてのエラーをインターセプトするため)良くありませんが、次のようになります。

def _error(parser):
    def wrapper(interceptor):
        parser.print_help()

        sys.exit(-1)

    return wrapper

def _args_get(args=sys.argv[1:]):
    parser = argparser.ArgumentParser()

    parser.error = _error(parser)

    parser.add_argument(...)
    ...

これerrorArgumentParserクラスの関数の定義です:

https://github.com/python/cpython/blob/276eb67c29d05a93fbc22eea5470282e73700d20/Lib/argparse.py#L2374

。ご覧のように、シグネチャに従って、2つの引数を取ります。ただし、クラス外の関数は、最初の引数について何も知りませんself。これは、おおまかに言って、これがクラスのパラメーターだからです。(私はあなたが知っていることを知っています...)それによって、自分自身selfを渡すだけでmessage_error(...)できません(

def _error(self, message):
    self.print_help()

    sys.exit(-1)

def _args_get(args=sys.argv[1:]):
    parser = argparser.ArgumentParser()

    parser.error = _error
    ...
...

出力されます:

...
"AttributeError: 'str' object has no attribute 'print_help'"

)。parserself)を_error呼び出すことで、関数に渡すことができます。

def _error(self, message):
    self.print_help()

    sys.exit(-1)

def _args_get(args=sys.argv[1:]):
    parser = argparser.ArgumentParser()

    parser.error = _error(parser)
    ...
...

、しかし、今はプログラムを終了したくない。次にそれを返します:

def _error(parser):
    def wrapper():
        parser.print_help()

        sys.exit(-1)

    return wrapper
...

。それにもかかわらず、parserそれが変更されたことを知らないため、エラーが発生した場合、エラーの原因が送信されます(ところで、ローカライズされた翻訳)。さて、それを傍受します:

def _error(parser):
    def wrapper(interceptor):
        parser.print_help()

        sys.exit(-1)

    return wrapper
...

。さて、エラーが発生し、そのparser原因を送信すると、あなたはそれを傍受し、これを見て、そして捨てます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.