Pythonパッケージ: 便利なコマンドラインパーサClickを使ってみる

概要

コマンドラインパーサとして、argparseと呼ばれるPythonの標準パッケージが存在するが、使い勝手がいまいちだと感じています(あくまで個人的な感想ですが…)。

今回は、たまたま見つけたclickと呼ばれるサードパーティ製のPythonパッケージを用いてコマンドラインの引数を解釈するコードを書いてみようと思います。

argparseと比較してclickの書き方だと、コードが書きやすく、あとから読み返しやすくなると感じます。

Clickのドキュメントはこちら。
http://click.pocoo.org/6/

内容

すでに、こちらのブログに詳しく解説されているのでここでは忘備録的な記載に留めようと思います。

blog.amedama.jp

インストール

AnacondaもしくはBiocondaがインストールされていることが前提。

conda install click

基本的な使い方

@click.command()デコレータを用いて関数を修飾するだけでOKprint関数の代わりにclick.echoを用いて文字列を標準出力することもできる。

@click.command()
def cmd():
    click.echo("Command was Done.")

続けて、@click.option()デコレータに任意のオプションを追加していくことができる。argparseと同様に、-i--ifastqなどのような-から始まるコマンドライン引数を指定することができる。

@click.command()
@click.option('-i', '--ifastq')
def cmd():
    click.echo("Command was Done.")

また、作成したオプションに代入された値を取得するために、ifastqのように-を付けない形で文字列を指定することで、関数の中で値が代入された変数として扱えます。

以下のように、cmd関数の引数にifastqを指定しておく必要がある。

@click.command()
@click.option('-i', '--ifastq', 'ifastq')
def cmd(ifastq):
    click.echo(ifastq)

helpでHelpが呼び出されたときの説明文を指定できる。

@click.command()
@click.option('-i', '--ifastq', 'ifastq', help='Input file in FASTQ file.')
def cmd(ifastq):
    click.echo(ifastq)
実行例

具体的にPythonスクリプト内で記述したときの内容。

#/usr/bin/env python

import click

@click.command()
@click.option('-i', '--ifastq', 'ifastq', help='Input file in FASTQ file.')
@click.option('-o', '--output-prefix', 'output', help='Prefix of output file.')
def cmd(ifastq, output):
    click.echo(ifastq)

def main():
    cmd()

if __name__ == '__main__':
    main()

実行すると、

$ python test.py -i test.fastq
test.fastq

$ python test.py  --help
Usage: test.py [OPTIONS]

Options:
  -i, --ifastq TEXT         Input file in FASTQ file.
  -o, --output-prefix TEXT  Prefix of output file.
  --help                    Show this message and exit.

コマンドライン引数のタイプを指定したい

INT型やFLOAT型など、引数のタイプがあらかじめわかっている場合、引数のタイプを指定しておきたいケースが出てくる。

そういった場合、以下のようにargparseのときのように@click.option()デコレータ内で指定することが可能。

type=<type>で指定。

@click.option('-m', '--minimum-length', 'minimum_length', type=int, default=18, help='Discard trimmed reads that are shorter than LENGTH. Default: 18')

パラメータタイプは、int、float、bool、strが指定できるようです。
http://click.pocoo.org/6/parameters/

デフォルトの値を決めておきたい

特定のオプションでデフォルト値を指定しておきたいケースがある。 その場合、デフォルト値の設定は以下のように行う。

default='hogehoge'で指定。

@click.option('-nl', '--nucleotide-trimming-list', 'nucleotide_trimming_list', default='A,N', help='Trimmed nucleotide list. Default: A,N')

指定する引数を限定したい

オプションの引数として、特定の文字列や数値しか受け付けたくない場合、以下のように指定できる引数を限定することができる。

type=click.Choice(['hoge', 'huga'])で指定。

@click.option('-nd', '--nucleotide-trimming-direction', 'nucleotide_trimming_direction', type=click.Choice(['three', 'five']), help='Trimmed nucleotide direction. Default: three')

Helpパラメータをカスタマイズしたい

Clickのデフォルトでは、Help文は--helpを指定することで出力される。

一般的に、Help文は--helpだけでなく-hでも出力されるケースが多い。そこで、-hでもHelp文が出力されるように設定を変更する。

@click.command()デコレータ内で指定。

CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'])
@click.command(context_settings=CONTEXT_SETTINGS)

サブコマンド(後述)を利用している場合は、@click.groupデコレータ内で指定する。

CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'])
@click.group(context_settings=CONTEXT_SETTINGS)

コールバック関数で値を検知・Helpを表示

オプションの引数を評価したい場合、コールバック関数を利用すると便利です。

@click.option()デコレータ内でcallback=<function名>を指定する。 また、コールバック関数内で、ctx.get_help()でHelp文を表示させることができる。ctx.exit()Pythonスクリプトの実行を止めることができる。

def validate_ifastq(ctx, param, value):
    if not value:
        click.echo('Error: Do not choose your FASTQ file.\n')
        print(ctx.get_help())
        ctx.exit()
    return(value)

@cmd.command()
@click.option('-i', '--ifastq', 'ifastq', callback=validate_ifastq, help='Input file in FASTQ file. [required]')

サブコマンドを用意したい場合

@click.group()デコレータを用いて、サブコマンドを利用することができます。

#/usr/bin/env python

import click

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

@cmd.command()
@click.option('-i', '--ifastq', 'ifastq', help='Input file in FASTQ file.')
@click.option('-o', '--output-prefix', 'output', help='Prefix of output file.')
def trim_polya(ifastq, output):
    click.echo('Start trimming polyA sequence from reads...')

@cmd.command()
@click.option('-i', '--ifastq', 'ifastq', help='Input file in FASTQ file.')
@click.option('-o', '--output-prefix', 'output', help='Prefix of output file.')
def get_pass_read(ifastq, output):
    click.echo('Start getting polyA site-supporting reads...')

def main():
    cmd()

if __name__ == '__main__':
    main()

Help文は以下のとおり。

$ python test.py --help
Usage: test.py [OPTIONS] COMMAND [ARGS]...

Options:
  --help  Show this message and exit.

Commands:
  get_pass_read
  trim_polya

サブコマンドごとのオプションをHelp文で出力することもできる。

$ python test.py get_pass_read --help
Usage: test.py get_pass_read [OPTIONS]

Options:
  -i, --ifastq TEXT         Input file in FASTQ file.
  -o, --output-prefix TEXT  Prefix of output file.
  --help                    Show this message and exit.