Pythonのclickで共通オプション

またもやPythonねたです。前回言っていたimportのフォーマットは、dirimportという形で公開してみました。pip install dirimport …使ってみてもらえればと。今後の自分のrequirements-dev.txtの常連になるか?

今回はclickについて。我々の日常はPythonでclickを使ってサブコマンドを作っていく毎日だが、、、そこにはパターンというものがある。

まずはサブコマンドの定型句がある。君たちはこれを何度書かせるつもりかね?

@click.group(invoke_without_command=True)
@click.pass_context
def cli(ctx):
    if ctx.invoked_subcommand is None:
        print(ctx.get_help())

で、こんな感じでサブコマンドを書いていく。

@cli.command()
@click.option("--verbose/--no-verbose")
def subcmd(verbose):
    pass

ここで、サブコマンド間の共通のオプションっていうものが考えられる。これは毎回いくつもの共通オプションを書くのはめんどくさいので、1つのデコレータにマージするテクニックがある。デコレータ…毎回ググらないと書ける気がしないやつね。

_common_options = [
  click.option("--verbose/--no-verbose"),
  click.option("--arg1"),
  click.option("--arg2"),
]

def common_option(func):
    for option in reversed(_common_options):
        func = option(func)
    return func


@cli.command()
@common_option
@click.option("--other-option")
def subcmd(verbose, arg1, arg2, other_option):
   pass

しかし全部のサブコマンドに共通ってわけじゃなくて、複数の共通セットがあってサブコマンドの種類ごとに変わります、みたいなことになると、、、これで個別にデコレータを増やしていくのもきつくなる。どうするか…

そこで、引数のオプション配列を処理してくれるデコレータにする、というテクニックが出てくる。

def multi_option(decs):
    def deco(f):
        for dec in reversed(decs):
            f = dec(f)
        return f
    return deco


@cli.command()
@multi_option(_common_options)
@click.option("--other-option")
def subcmd(**kwargs):
   pass

実際はmulti_optionsからcommon_optionを作ることになるんだろう。

def common_option1(func):
    return multi_option(_common_options)(func)

@cli.command()
@common_option1
@click.option("--other-option")
def subcmd(**kwargs):
    pass

ここまでやるとclickにこだわるんじゃなくて、argparseでいいような気もするね。

def common_parser(parser=None):
    if parser is None:
        parser = argparse.ArgumentParser()
    parser.add_argument(...)
    parser.add_argument(...)
    return parser

def main():
    p = common_parser()
    p.add_argument("--other-option")
    opt = p.parse_args()
     :

もうちょっと頑張って何か作ったら、うまいこと省力化できそうな気もしますよね。どうしようかな。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です