CLI設計(argparse・click)

なぜCLIの設計が要るのか

CLI ツールは人が打つだけでなく、他のツールから呼ばれます。シェルスクリプトやCIから呼ばれることを前提にすると、守るべき約束事が決まってきます。

約束事理由
終了コード成功は0、失敗は0以外。呼び出し側が成否を判定する
標準出力と標準エラーの分離結果は標準出力、進捗やログは標準エラーへ。パイプでつなげる
引数の一貫性--dry-run などの命名を全サブコマンドで揃える
破壊的操作の確認削除・上書きは既定で確認、--yes で省略可能にする

特に終了コードは忘れられがちです。エラーメッセージを出して 0 で終わると、呼び出し側は成功したと判断します。CI が緑のまま処理が失敗する事故はこれが原因になりやすいところです。

argparse と click

[[Python]] では標準ライブラリの argparse と、外部ライブラリの click が主な選択肢です。

# argparse: 標準ライブラリだけで完結する
parser = argparse.ArgumentParser()
parser.add_argument("path")
parser.add_argument("--dry-run", action="store_true")
args = parser.parse_args()
# click: デコレータで宣言する。サブコマンドの構成が楽
@click.command()
@click.argument("path")
@click.option("--dry-run", is_flag=True)
def main(path, dry_run):
    ...
観点argparseclick
依存追加なし(標準)外部パッケージが必要
記述量やや冗長デコレータで簡潔
サブコマンド手で組み立てるグループ機能で素直に書ける
配布依存が増えないため軽い同梱の対象が増える

依存を増やしたくない小さなツールは argparse、サブコマンドが複数ある本格的なツールは click、という選び方が実務的です。

設定の優先順位を決めておく

引数以外にも、環境変数や設定ファイルから値が来ることがあります。同じ項目が複数の経路から与えられたときにどれを採るかを、あらかじめ一方向に決めて文書化します。

慣例的な優先順位は次のとおりです。

  1. コマンドライン引数(その場の指定が最優先)
  2. 環境変数(実行環境ごとの差分)
  3. 設定ファイル(既定値の集約)
  4. コード内の既定値

この順序が曖昧だと、「設定ファイルを直したのに反映されない」という調査に時間を取られます。--help か README のどちらかに明記しておくと、利用者も自分も迷いません。

テストしやすい形にする

CLI をテストしやすくする鍵は、引数の解釈と処理本体を分けることです。

def convert(path: str, dry_run: bool) -> int:  # ここをテストする
    ...

def main() -> int:                              # 引数解釈だけ
    args = parser.parse_args()
    return convert(args.path, args.dry_run)

処理本体が普通の関数なら [[自動テスト]] からそのまま呼べます。同じ関数を [[Tkinter/ttkbootstrap(デスクトップGUI)]] から呼べば、GUI版も追加できます。

--help の文面はそのまま利用者向けの説明になります。[[ドキュメンテーション]] を別に用意する前に、ヘルプが単体で読めるかを確認すると、書くべき文書の量が減ります。

関連技術とのつながり

  • [[Python]] — argparse は標準ライブラリに含まれる
  • [[自動テスト]] — 処理本体を関数として切り出せばそのままテストできる
  • [[Tkinter/ttkbootstrap(デスクトップGUI)]] — 同じ処理本体に別の入口を足す選択肢
  • [[ドキュメンテーション]] — --help は最初に読まれる説明書
Q: CLIツールで終了コードを正しく返す必要があるのはなぜ?
- [ ] 実行速度が上がるから
- [x] 呼び出し側(シェルやCI)が成否を判定するから
- [ ] エラーメッセージが色付きになるから
解説: エラーを出しても0で終了すると、呼び出し側は成功と判断します。CIが緑のまま失敗が見逃される原因になります。

Q: 標準出力と標準エラーを分ける理由はどれ?
- [x] 結果だけをパイプで次のコマンドへ渡せるようにするため
- [ ] 出力量を減らすため
- [ ] 文字コードを変換するため
解説: 結果を標準出力、進捗やログを標準エラーへ分けると、他のツールと組み合わせやすくなります。

Q: argparseとclickの使い分けとして適切なものはどれ?
- [ ] どんな場合もclickを使うべき
- [x] 依存を増やしたくない小さなツールはargparse、サブコマンドが多い場合はclick
- [ ] argparseはサブコマンドを扱えない
解説: argparse は標準ライブラリで依存が増えず、click はサブコマンドの構成を簡潔に書けます。