web-dev-qa-db-ja.com

引数のいくつかのリストを@ click.optionに渡す方法

この種のパラメーターを使用して、コマンドラインからpythonスクリプトを呼び出したい(リストは3のように、任意のサイズにできます):

python test.py --option1 ["o11", "o12", "o13"] --option2 ["o21", "o22", "o23"]

クリックを使用します。ドキュメントから、リストを @ click.option のパラメーターとして使用できることはどこにも記載されていません

そして、私がこれをしようとすると:

#!/usr/bin/env python
import click

@click.command(context_settings=dict(help_option_names=['-h', '--help']))
@click.option('--option', default=[])
def do_stuff(option):

    return

# do stuff
if __name__ == '__main__':
    do_stuff()

私のtest.py、コマンドラインから呼び出すことにより:

python test.py --option ["some option", "some option 2"]

エラーが発生します:

エラー:予期しない余分な引数を取得しました(オプション2])

コマンドごとに1つの可変引数しか使用できないため、可変引数を実際に使用することはできません( http://click.pocoo.org/5/arguments/#variadic-arguments

だから、誰かが正しい方向を指し示すことができれば(できればクリックを使用して)、非常に高く評価されます。

10
downstroy

次のようなカスタムオプションクラスを使用して、リストがpythonリストの文字列リテラルとしてフォーマットされている場合、複数のリスト引数を強制的にクリックできます。

カスタムクラス:

_import click
import ast

class PythonLiteralOption(click.Option):

    def type_cast_value(self, ctx, value):
        try:
            return ast.literal_eval(value)
        except:
            raise click.BadParameter(value)
_

このクラスは、Pythonの Abstract Syntax Tree モジュールを使用して、パラメーターをpythonリテラルとして解析します。

カスタムクラスの使用法:

カスタムクラスを使用するには、clsパラメーターを@click.option()デコレーターに渡します。

_@click.option('--option1', cls=PythonLiteralOption, default=[])
_

これはどのように作動しますか?

これは、クリックが適切に設計されたOOフレームワークです。@click.option()デコレーターは通常_click.Option_オブジェクトをインスタンス化しますが、この動作をclsパラメータ:したがって、独自のクラスで_click.Option_を継承し、目的のメソッドをオーバーライドすることは比較的簡単です。

この場合、click.Option.type_cast_value()をオーバーライドしてから ast.literal_eval() を呼び出してリストを解析します。

テストコード:

_@click.command(context_settings=dict(help_option_names=['-h', '--help']))
@click.option('--option1', cls=PythonLiteralOption, default=[])
@click.option('--option2', cls=PythonLiteralOption, default=[])
def cli(option1, option2):
    click.echo("Option 1, type: {}  value: {}".format(
        type(option1), option1))
    click.echo("Option 2, type: {}  value: {}".format(
        type(option2), option2))

# do stuff
if __name__ == '__main__':
    import shlex
    cli(shlex.split(
        '''--option1 '["o11", "o12", "o13"]' 
        --option2 '["o21", "o22", "o23"]' '''))
_

試験結果:

_Option 1, type: <type 'list'>  value: ['o11', 'o12', 'o13']
Option 2, type: <type 'list'>  value: ['o21', 'o22', 'o23']
_
14
Stephen Rauch

以下は、より簡単なハック修正です。

#!/usr/bin/env python
import click
import json

@click.command(context_settings=dict(help_option_names=['-h', '--help']))
@click.option('--option', help='Whatever')
def do_stuff(option):
    try:
        option = json.loads(option)    
    except ValueError:
        pass

# do stuff
if __name__ == '__main__':
    do_stuff()

これは、listまたはstrとして「オプション」を使用するのに役立ちます。

1
Murphy

@Murphyの「簡単なハック」はほとんど役に立ちましたが、問題はoptionstringになることです。オプションを単一引用符で囲まない限り、リストを再構成するためにこれを行わなければなりませんでした:

#!/usr/bin/env python
import click
import json

@click.command(context_settings=dict(help_option_names=['-h', '--help']))
@click.option('--option', help='Whatever')
def do_stuff(option):
    try:
        option = json.loads(option)
        # option = str(option)  # this also works
    except ValueError:
        pass

    option = option[1:-1]  # trim '[' and ']'

    options = option.split(',')

    for i, value in enumerate(options):
        # catch integers
        try:
            int(value)
        except ValueError:
            options[i] = value
        else:
            options[i] = int(value)

    # Here use options as you need

# do stuff
if __name__ == '__main__':
    do_stuff()

他のタイプをキャッチできます

使用するには、リストを引用符で囲みます。

python test.py --option "[o11, o12, o13]"

または、スペースを残さないことで引用符を避けることができます:

python test.py --option [o11,o12,o13]
0