iMind Developers Blog

iMind開発者ブログ

Pythonのclickでサブコマンドの実装

概要

Pythonのclickでgroupを使うと1つのスクリプトに複数のサブコマンドを実装できる。

バージョン情報

  • Python 3.7.4
  • Click==7.0

最小限のサンプルコード

引数に応じて2つのコマンドを出し分けるシンプルなコード。

import click

@click.group()
def cli():
    pass

@cli.command()
def create():
    print('create command')

@cli.command()
def delete():
    print('delete command')

if __name__ == '__main__':
    cli()

上記をexample.pyという名前で保存。

手始めにサブコマンドを付けずに実行してみる。

$ python example.py

Usage: example.py [OPTIONS] COMMAND [ARGS]...

Options:
  --help  Show this message and exit.

Commands:
  create
  delete

ヘルプが表示された。

createとdeleteという2つのメソッドを用意したので、それぞれメソッドと同じ名前を引数に指定すれば当該処理が呼び出される。

$ python example.py create

create command

$ python example.py delete

delete command

引数の指定

@click.optionで各コマンドが取る引数を指定する。

コマンドごとにそれぞれ違う引数のパターンを指定できる。

import click

@click.group()
def cli():
    pass

# createはnameを引数に取る
@cli.command()
@click.option('-n', '--name')
def create(name):
    print('create %s' % name)

# deleteは引数を取らない
@cli.command()
def delete():
    print('delete command')

if __name__ == '__main__':
    cli()

helpの参照。

$ python example.py create --help
Usage: example.py create [OPTIONS]

Options:
  -n, --name TEXT
  --help           Show this message and exit.

コードで@click.option('-n', '--name')と指定しているので、-nもしくは--nameで値を指定できる。

$ python example.py create --name foo

create foo

pass_contextの利用

@click.pass_contextを指定するとclick.core.Contextが引数で渡される。

Contextのobjに引数を登録することで、pass_contextを指定した各コマンドに対して、共通の値を送ることができる。

import click

@click.group()
@click.option('--debug/--no-debug', default=False)
@click.pass_context
def cli(ctx, debug):
    ctx.obj = {'debug': debug}

@cli.command()
@click.option('-n', '--name')
@click.pass_obj
def create(obj, name):
    print('debug -> %b, name -> %s', (obj['debug'], name))

@cli.command()
@click.pass_obj
def delete(obj):
    print('debug -> %r' % obj['debug'])

if __name__ == '__main__':
    cli()

呼び出し方は、cliの引数、コマンド名、指定コマンドの引数の順。

$ python example.py --debug create --name foo

debug -> True, name -> foo

invoke_without_commandの指定

invoke_without_command=Trueを指定すると、サブコマンドを指定しない場合でも処理が呼び出される。

例えば下記のコード。

invoke_without_commandの指定がない場合はヘルプが表示されたが、指定するとcli()が呼び出されて「cliが呼ばれました」と標準出力に表示される。

import click

@click.group()
def cli():
    print('cliが呼ばれました')

@cli.command()
def create():
    print('create command')

@cli.command()
def delete():
    print('delete command')

if __name__ == '__main__':
    cli()

サブコマンドの記述を複数のファイルに分ける

サブコマンドが増えていって記述を分けたい気分になってきた場合の対処方法。

commands.py(仮名)というファイル名で、コマンドのところだけ書いたPythonファイルを用意する。

import click

@click.command()
@click.option('--name', required=True)
def create(name):
    print('create command : name=%s' % name)

@click.command()
def delete():
    print('delete command')

mainを記述している example.py の方で、command.pyにimportして記述したコマンドをadd_commandする。

import click

from commands import create, delete

@click.group()
def cli():
    pass

cli.add_command(create)
cli.add_command(delete)

if __name__ == '__main__':
    cli()

実行してみる。

$ python example.py  create --name foo

create command : name=foo

改定履歴

Author: Masato Watanabe, Date: 2019-11-16, 記事投稿