1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Pythonで本格CLIツールを作るならclick — デコレータから理解する

1
Last updated at Posted at 2026-07-01

はじめに

最近、UDS診断ツール(uds-client)をClaude.aiと一緒に開発したところ、click というライブラリが使われていました。

コードを見ると @ 記号が至るところに出てきます。

@click.group()
@click.option("--ip", required=True, help="ECU IP address")
@click.pass_context
def cli(ctx, ip):
    ...

「この @ は何?」「clickって何をするライブラリ?」というところから整理します。


@ はデコレータ

Pythonの @ 記号はデコレータと呼ばれる構文です。「関数に機能を追加する」ための仕組みで、@xxx を関数の直前に書くと、その関数の振る舞いを変えたり拡張したりできます。

clickはこのデコレータをうまく活用して、CLIツールを宣言的に書けるように設計されています。


clickとは

click はPythonでCLIツール(コマンドラインツール)を作るためのライブラリです。

CLIツールとは、こういうコマンドで動くプログラムのことです。

$ python main.py --ip 192.168.1.1 rdbi F190

clickを使うと、オプションの定義・型チェック・ヘルプ生成・サブコマンドの管理などを、シンプルなコードで実現できます。

インストールは以下のコマンドで行います。

pip install click

clickで何ができるか

uds-clientの実際のコードをもとに説明します。

コマンドグループの定義

import click

DOIP_PORT = 13400

@click.group()
@click.option("--ip", required=True, help="ECU IP address")
@click.option("--port", default=DOIP_PORT, show_default=True, help="DoIP port")
@click.option("--log", default="INFO", show_default=True,
              type=click.Choice(["DEBUG", "INFO", "WARNING"], case_sensitive=False),
              help="Log level")
@click.pass_context
def cli(ctx, ip, port, log):
    """UDS診断ツール (DoIP)"""
    ctx.ensure_object(dict)
    ctx.obj["ip"] = ip
    ctx.obj["port"] = port
    ctx.obj["log"] = log

@click.group() を付けると、この関数が「複数のサブコマンドを束ねる親」になります。

@click.option() でオプションを定義します。required=True で必須チェック、default= でデフォルト値、type=click.Choice(...) で選択肢の制限が自動でかかります。

@click.pass_context を付けると、ctx(コンテキスト)オブジェクトが関数に渡されます。ctx.obj に値を入れておくことで、後述するサブコマンドからその値を参照できます。

サブコマンドの定義

@cli.command()
@click.argument("did")
@click.pass_context
def rdbi(ctx, did):
    """0x22 ReadDataByIdentifier 例: rdbi F190"""
    ip = ctx.obj["ip"]  # 親で受け取った --ip の値が使える
    ...
    click.echo(f"[OK] DID={did} IP={ip}")

@cli.command() を付けると、この関数がサブコマンドになります。関数1つ=サブコマンド1つという対応です。

@click.argument() は位置引数(rdbi F190F190 の部分)を受け取ります。オプション(--ip)と違い、-- を付けずに渡す引数です。

ctx.obj["ip"] で親コマンドで受け取った --ip の値を参照できます。

フラグオプション

@cli.command("tester-present")  # Pythonの関数名はアンダースコア、CLIコマンド名はハイフンが慣習
@click.option("--suppress", is_flag=True, default=False, help="suppress response")
@click.pass_context
def tester_present(ctx, suppress):
    """0x3E TesterPresent"""
    ...

is_flag=True にすると、オプションが「付けたか・付けなかったか」の True / False で受け取れます。--suppress を付ければ True、付けなければ False です。

エントリーポイント

if __name__ == "__main__":
    cli()

最後にこの1行を書くだけで、python main.py で動くCLIツールになります。


ヘルプが自動生成される

clickのもう一つの便利な点は、--help が自動で生成されることです。関数のdocstringがそのまま説明文として使われます。

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

  UDS診断ツール (DoIP)

Options:
  --ip TEXT                    ECU IP address  [required]
  --port INTEGER               DoIP port  [default: 13400]
  --log [DEBUG|INFO|WARNING]   Log level  [default: INFO]
  --help                       Show this message and exit.

Commands:
  clear-dtc       0x14 ClearDiagnosticInformation
  rdbi            0x22 ReadDataByIdentifier
  read-dtc        0x19 ReadDTCInformation
  reset           0x11 ECUReset
  routine         0x31 RoutineControl
  session         0x10 DiagnosticSessionControl
  tester-present  0x3E TesterPresent
  wdbi            0x2E WriteDataByIdentifier

同じことはargparseでもできる

実はここまで説明してきたことは、Pythonの標準ライブラリである argparse でも実現できます。

import argparse, sys

parser = argparse.ArgumentParser(description="UDS診断ツール")
parser.add_argument("--ip", required=True)
parser.add_argument("--port", type=int, default=13400)
subparsers = parser.add_subparsers(dest="command")

rdbi_parser = subparsers.add_parser("rdbi")
rdbi_parser.add_argument("did")

args = parser.parse_args()

if args.command == "rdbi":
    print(f"[OK] DID={args.did} IP={args.ip}")
elif args.command is None:
    parser.print_help()
    sys.exit(1)

ただし、argparseには次の課題があります。

  • サブコマンドの定義・処理・分岐が3箇所に分散する
  • サブコマンドが増えるたびに elif が増えていく
  • --ip のような共通オプションをサブコマンドに渡す仕組みが自前になる

clickはこれらをデコレータで解決しています。uds-clientのようにサブコマンドが8個あるケースでは、その差がはっきり出ます。


まとめ

観点 argparse click
依存 標準ライブラリ pip install必要
サブコマンド追加 定義・処理・分岐の3箇所 関数1つ追加するだけ
共通オプションの伝播 手動 ctx.objで自動
ヘルプ生成 自動 自動(docstringがそのまま)
向いている用途 小規模・単機能 サブコマンドが複数あるツール

小規模スクリプトならargparse、複数のサブコマンドを持つCLIツールを作るならclick、という使い分けが自分の中で整理できました。


参考

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?