web-dev-qa-db-ja.com

Python argparse:多くの選択肢があると、ugいヘルプ出力が表示されます

私は一般的に満足しているこのコードを持っています:

import argparse

servers = [ "ApaServer", "BananServer", "GulServer", "SolServer", "RymdServer",
            "SkeppServer", "HavsServer", "PiratServer", "SvartServer", "NattServer", "SovServer" ]

parser = argparse.ArgumentParser(description="A program to update components on servers.")
group = parser.add_mutually_exclusive_group()
group.add_argument('-l', '--list', dest="update", action='store_false', default=False, help='list server components')
group.add_argument('-u', '--updatepom', dest="update", action='store_true', help='update server components')
parser.add_argument('-o', '--only', nargs='*', choices=servers, help='Space separated list of case sensitive server names to process')
parser.add_argument('-s', '--skip', nargs='*', choices=servers, help='Space separated list of case sensitive server names to exclude from processing')
args = parser.parse_args()

Choice = serversが入力のサーバー名を検証するので、必要はありません。ただし、非常に多くの有効な選択肢があると、ヘルプ出力がひどく見えます。

usage: args.py [-h] [-l | -u]
               [-o [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} ...]]]
               [-s [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} ...]]]

A program to update components on servers.

optional arguments:
  -h, --help            show this help message and exit
  -l, --list            list server components
  -u, --updatepom       update server components
  -o [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} ...]], --only [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} ...]]
                        Space separated list of case sensitive server names to
                        process
  -s [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} ...]], --skip [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} ...]]
                        Space separated list of case sensitive server names to
                        exclude from processing

私が望むなら、どの方法をお勧めしますか:

  • 素敵な(主に)自動生成されたヘルプ出力
  • -oまたは-sオプションに指定されたエントリがserversにあることの検証。

ボーナス:

  • サーバー名の大文字と小文字を区別しない文字列照合を行うことは可能ですか?

追加

-o-sオプションは上記の出力から削除され、この部分が追加されます。

server optional arguments:
  Valid server names are: ApaServer, BananServer, GulServer, SolServer,
  RymdServer, SkeppServer, HavsServer, PiratServer, SvartServer,
  NattServer, SovServer

かなり良さそうですが、本当に-oおよび-sそれ以外の場合はユーザーが知らないオプション。したがって、私はまだこのアプローチを使用しているわけではありません。

45
Deleted

私は基本的にアーネストが言ったことを繰り返しています-選択肢のlongい長いリストを避けるために、選択ベースの引数にmetavar = ''を設定します(ただし、引数とコンマの間の空白を取り除くことはできません(例:-o , の代わりに -o,)。次に、一般的な説明で利用可能な選択肢を詳細に説明できます(RawDescriptionHelpFormatterは、明確なインデントでリストしたい場合に役立ちます)。

アーネストの答えがなぜ投票されなかったのかはわかりません。このコード

import argparse

servers = [ "ApaServer", "BananServer", "GulServer", "SolServer", "RymdServer",
            "SkeppServer", "HavsServer", "PiratServer", "SvartServer", "NattServer", "SovServer" ]

parser = argparse.ArgumentParser(description="A program to update components on servers.")
group = parser.add_mutually_exclusive_group()
group.add_argument('-l', '--list', dest="update", action='store_false', default=False, help='list server components')
group.add_argument('-u', '--updatepom', dest="update", action='store_true', help='update server components')
parser.add_argument('-o', '--only', choices=servers, help='Space separated list of case sensitive server names to process.  Allowed values are '+', '.join(servers), metavar='')
parser.add_argument('-s', '--skip', choices=servers, help='Space separated list of case sensitive server names to exclude from processing.  Allowed values are '+', '.join(servers), metavar='')
args = parser.parse_args()

次のヘルプ出力を生成します

usage: run.py [-h] [-l | -u] [-o] [-s]

A program to update components on servers.

optional arguments:
  -h, --help       show this help message and exit
  -l, --list       list server components
  -u, --updatepom  update server components
  -o , --only      Space separated list of case sensitive server names to
                   process. Allowed values are ApaServer, BananServer,
                   GulServer, SolServer, RymdServer, SkeppServer, HavsServer,
                   PiratServer, SvartServer, NattServer, SovServer
  -s , --skip      Space separated list of case sensitive server names to
                   exclude from processing. Allowed values are ApaServer,
                   BananServer, GulServer, SolServer, RymdServer, SkeppServer,
                   HavsServer, PiratServer, SvartServer, NattServer, SovServer

これは、元の投稿が探していたものです。

46
user2463717

サブクラス化する必要はありません。ヘルプメッセージに表示する文字列をmetavar引数に渡すだけです。

詳細については、 argparse documentation をご覧ください。

23
Ernest A

これと同じ問題があり、回避策として、エピローグを使用して各オプションの選択肢を説明しました。 argparse.RawTextHelpFormatterを使用する必要がありました。これにより、エピローグが事前にフォーマットされていることを指定できます。

def choicesDescriptions():
   return """
Choices supports the following: 
   choice1         - the FIRST option
   choice2         - the SECOND option
   ...
   choiceN         - the Nth option
"""

def getChoices():
   return ["choice1", "choice2", ..., "choiceN"]

parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, epilog=choicesDescriptions())
parser.add_argument(
   'choices', 
   choices=getChoices(),
   help='Arg choice.  See the choices options below'
   )

args = parser.parse_args()
print(args)
9
Mike

これは、元の質問のようにオプションリストが極端に長い状況では役に立ちませんが、私のように、適度に長いオプション文字列を2行に分割する方法を探してこの質問に出会った人のために、ここにあります解決:

import argparse

class CustomFormatter(argparse.HelpFormatter):
    """Custom formatter for setting argparse formatter_class. Identical to the
    default formatter, except that very long option strings are split into two
    lines.
    """

    def _format_action_invocation(self, action):
        if not action.option_strings:
            metavar, = self._metavar_formatter(action, action.dest)(1)
            return metavar
        else:
            parts = []
            # if the Optional doesn't take a value, format is:
            #    -s, --long
            if action.nargs == 0:
                parts.extend(action.option_strings)
            # if the Optional takes a value, format is:
            #    -s ARGS, --long ARGS
            else:
                default = action.dest.upper()
                args_string = self._format_args(action, default)
                for option_string in action.option_strings:
                    parts.append('%s %s' % (option_string, args_string))
            if sum(len(s) for s in parts) < self._width - (len(parts) - 1) * 2:
                return ', '.join(parts)
            else:
                return ',\n  '.join(parts)

このコードは、デフォルトのargparse.HelpFormatterメソッド_format_action_invocationをオーバーライドし、最後の4行を除いてデフォルトの実装と同じです。

デフォルトのフォーマッターの動作:

parser = argparse.ArgumentParser(description="Argparse default formatter.")
parser.add_argument('-a', '--argument', help='not too long')
parser.add_argument('-u', '--ugly', choices=range(20), help='looks messy')
parser.print_help()

出力:

usage: test.py [-h] [-a ARGUMENT]
               [-u {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19}]

Argparse default formatter.

optional arguments:
  -h, --help            show this help message and exit
  -a ARGUMENT, --argument ARGUMENT
                        not too long
  -u {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19}, --ugly {0,1,2,3,4,5,6,
7,8,9,10,11,12,13,14,15,16,17,18,19}
                        looks messy

カスタムフォーマッタの動作:

parser = argparse.ArgumentParser(description="Argparse custom formatter.",
                                 formatter_class=CustomFormatter)
parser.add_argument('-a', '--argument', help='not too long')
parser.add_argument('-l', '--less-ugly', choices=range(20), help='less messy')

出力:

usage: test.py [-h] [-a ARGUMENT]
               [-l {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19}]

Argparse custom formatter.

optional arguments:
  -h, --help            show this help message and exit
  -a ARGUMENT, --argument ARGUMENT
                        not too long
  -l {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19},
  --less-ugly {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19}
                        less messy
5
vamin

サーバーベースのオプションのグループを作成するためにparser.add_argument_groupを使用し、説明argが可能な選択肢のリストを表示するようにしないのはなぜですか?次に、argparse.SUPPRESSを各​​オプションのヘルプに渡します。私はあなたが望むものをあなたに与えると信じています。

3
michaelfilms

http://bugs.python.org/issue16468argparse only supports iterable choicesは、選択肢のフォーマットを議論するバグの問題です。選択肢のリストは、使用法ライン、ヘルプライン、エラーメッセージの3つの場所に表示できます。

パーサーが気にするのは、in__contains__)テスト。ただし、書式設定、長いリスト、無制限の「リスト」(100を超える整数など)、および反復可能でない他のオブジェクトでは問題が発生します。 metavarは、現在のユーザーがほとんどの書式設定の問題を回避する方法です(エラーメッセージが役に立たない場合があります)。自分のバージョンのargparseを変更する方法のアイデアを得るには、問題を見てください。

1
hpaulj