【Python】clickでTODO管理CLIツールを作ってみた

【Python】clickでTODO管理CLIツールを作ってみた

公開: 更新:
CodeCampが提供するDX人材育成が可能なプログラミングやITが学べる公開講座

Pythonのclickライブラリを使って、ターミナルから使えるTODO管理CLIツールを作ってみました。add・list・done・deleteの4つのサブコマンドでタスクを登録・確認・完了・削除でき、データはtodo.jsonに保存するシンプルな構成です。実際にコードを書いて動かした結果とあわせて、初心者の方でも追体験できるように解説します。

【Python】clickでTODO管理CLIツールを作ってみた|概要動画
動画の内容をテキストで確認する

オープニング。clickを使った実践内容を約34秒で紹介します。概要紹介。

この動画では、Pythonのclickで、タスクの追加・一覧・完了・削除ができるTODO管理CLIツールを作ります。具体的にやること。具体的には、clickの@click.groupで4つのサブコマンドを定義し、タスクをtodo.jsonに保存して実行をまたいで状態を保持します。

実装環境・必須アプリ。実装環境と必須アプリは、Windows 11 Pro / Python 3.13.3 / clickです。実際のキャプチャ動画。

実際の画面で操作を確認します。検証の結果、CLIツールを4回のコマンドで実行し、全て正常(終了コード0)に動作しました。エンディング。

研修ならCodeCampで検索。

PythonとclickでTODO管理CLIを作る方針

今回作るのは、ターミナルでtodo add "牛乳を買う"のように打つとタスクを登録できるTODO管理ツールです。追加(add)・一覧(list)・完了(done)・削除(delete)の4つのサブコマンドを用意します。

CLIの骨組みにはclickを採用しました。デコレータを付けるだけで関数がそのままコマンドになるため、標準ライブラリのargparseより記述量が少なく、サブコマンドを持つツールを直感的に組み立てられるのが選定理由です。

採用の裏づけとして、公式ドキュメントも「必要最小限のコードで組み立てられること」をclickの設計思想として掲げています。

データの保存先はカレントディレクトリのtodo.jsonにしました。コマンドを実行するたびに「読み込み→更新→保存」を繰り返す素直な構成なら、データベースを使わなくても状態を持つCLIを最小限のコードで実現できます。

このセクションの用語

CLI
Command Line Interfaceの略で、ターミナルに文字でコマンドを打って操作するタイプのプログラムのこと。
click
PythonでCLIツールを作るためのライブラリ。関数にデコレータを付けるだけでコマンド化できる。
サブコマンド
git addaddのように、メインコマンドの後ろに続けて指定する個別の機能名。
JSON
データを人間にも読める文字列として保存する形式。Pythonの辞書やリストと相性が良い。
TODO管理CLIツールの処理の流れ ターミナルでコマンドを入力clickが引数を解析して対応する関数を呼ぶtodo.jsonからタスク一覧を読み込む追加・完了・削除などの更新を実行todo.jsonへ保存し結果をclick.echoで表示
TODO管理CLIツールの処理の流れ

参考:

©click公式ドキュメント(Pallets Projects)

Click is a Python package for creating beautiful command line interfaces in a composable way with as little code as necessary.

さらに詳しく学べるPython研修の詳細はこちら

Pythonでclickのコードを生成したプロンプトと作り方

この記事のコードはClaude Codeに書かせたもので。以下は、その際に使った指示の内容です。

clickはデコレータでコマンドを定義できる反面、指示があいまいだとLLMは期限や優先度など頼んでいない機能まで盛り込みがちです。そこでサブコマンドを4つに限定し、「だけ実装する」と機能範囲を先に固定しました。

保存形式は動作確認のしやすさから逆算して指定しています。todo.jsonへの保存とensure_ascii=Falseを明記すれば、実行後にファイルを開くだけで日本語タスクの状態変化を目で追えます。

さらにID採番とエラー時の振る舞いも決め打ちにしました。ここを曖昧にすると生成のたびに仕様が揺れて、記事の実行例が再現できなくなるためです。

生成プロンプトで工夫したポイント

  • サブコマンドは次の4つだけ実装する:機能範囲を「だけ」で固定すると、LLMが期限や優先度など頼んでいない機能を足すのを防げます。
  • IDは既存の最大IDに1を足して自動で振る:採番ルールを決め打ちにすることで、doneやdeleteの実行例が何度生成しても同じ結果で再現できるようになります。
  • 存在しないIDを指定されたときはエラーで落とさず:異常系の方針を先に与えておくと、例外で止まらない実用的なCLIが最初の生成から出てくるのがポイントです。

同じコードを作れる生成プロンプト(コピペ用・全文)

以下のプロンプトは、そのままコピペして同じものが作れるように整えた全文です。

Pythonとclickライブラリで、コマンドラインから使えるTODO管理CLIツール「todo_cli.py」を作ってください。 仕様: - clickの@click.group()を使ったサブコマンド方式にする - サブコマンドは次の4つだけ実装する - add <タスク名>: タスクを追加する。IDは既存の最大IDに1を足して自動で振る - list: タスクを一覧表示する。完了済みは[x]、未完了は[ ]の印を付ける - done <ID>: 指定したIDのタスクを完了にする - delete <ID>: 指定したIDのタスクを削除する - タスクはカレントディレクトリのtodo.jsonに保存し、実行をまたいで状態を保持する - 保存はjson.dumpsのensure_ascii=Falseとindent=2で、日本語のまま読めるJSONにする - 存在しないIDを指定されたときはエラーで落とさず、見つからない旨をclick.echoで表示する - python todo_cli.py add牛乳を買う のように実行でき、結果が毎回メッセージで表示されるようにする
さらに詳しく学べるPython研修の詳細はこちら

Python・clickの環境構築(Windows 11/PowerShellで検証)

この記事のセットアップ手順と掲載コードは、Windows 11 Pro、PowerShell 5.1、Python 3.13.3で動作確認しています。仮想環境を有効化せず、その中のPythonを直接指定するため、以下のコマンドはPowerShellとコマンドプロンプト(cmd)の両方で使えます。

python -m venv .venv
.\.venv\Scripts\python.exe -m pip install click

macOS・Linuxでは仮想環境内のPythonパスが異なります。今回の動作確認環境とは異なるため、以下は環境差分を補う参考手順です。

python3 -m venv .venv
./.venv/bin/python -m pip install click
  • todo.jsonは実行したフォルダに作成されるため、同じフォルダでコマンドを実行すれば状態が引き継がれます。
  • 「No module named click」と出る場合は、仮想環境のPython(.\.venv\Scripts\python.exe)で実行しているか確認してください。

Python研修一覧はこちら

目的に合うPython研修を一覧形式から探したい方は、ぜひご利用ください。

Python研修を比較する

Java研修一覧はこちら

目的に合うJava研修を一覧形式から探したい方は、ぜひご利用ください。

Java研修を比較する

PHP研修一覧はこちら

目的に合うPHP研修を一覧形式から探したい方は、ぜひご利用ください。

PHP研修を比較する

新入社員研修

目的に合う新入社員研修を一覧形式から探したい方は、ぜひご利用ください。

新入社員研修を比較する

全ての研修からも探したい方はこちら

Python clickの@click.groupで4コマンドを実装する

コード全体は約80行で、構成は大きく3つに分かれます。JSONの読み書きを担うload_todos/save_todos、コマンドの入り口になるcliグループ、そして4つのサブコマンド関数です。

どのコマンドも「load_todos()で読み込み→リストを操作→save_todos()で保存→click.echo()で結果表示」という同じ型で書かれています。この型さえ押さえれば、あとから新しいコマンドを追加するのも簡単です。

全行を暗記する必要はありません。次の6か所だけ理解すれば、このツールの仕組みは把握できます。

このセクションの用語

デコレータ
関数定義の上に@名前と書いて機能を後付けする仕組み。clickではこれでコマンド登録を行う。
docstring
関数の先頭に書く説明用の文字列。clickでは--help実行時の説明文として利用される。
内包表記
[t for t in todos if ...]のように、条件を満たす要素からリストを1行で作るPythonの書き方。
1コマンドあたりのデータの流れ load_todos()がtodo.jsonを読み込む辞書のリストとしてメモリ上で操作save_todos()でJSONに書き戻すclick.echo()で結果をターミナルに表示
1コマンドあたりのデータの流れ
"""clickで作るTODO管理CLIツール"""

import json
from pathlib import Path

import click

# 保存先ファイルの設定
TODO_FILE = Path("todo.json")


# TODOデータの読み込み
def load_todos():
    if TODO_FILE.exists():
        return json.loads(TODO_FILE.read_text(encoding="utf-8"))
    return []


# TODOデータの保存
def save_todos(todos):
    TODO_FILE.write_text(
        json.dumps(todos, ensure_ascii=False, indent=2), encoding="utf-8"
    )


# コマンドグループの定義
@click.group()
def cli():
    """clickで作るシンプルなTODO管理CLIツール"""


# タスクの追加(add)
@cli.command()
@click.argument("task")
def add(task):
    """タスクを追加する"""
    todos = load_todos()
    next_id = max((t["id"] for t in todos), default=0) + 1
    todos.append({"id": next_id, "task": task, "done": False})
    save_todos(todos)
    click.echo(f"追加しました: [{next_id}] {task}")


# タスクの一覧表示(list)
@cli.command("list")
def list_todos():
    """タスクを一覧表示する"""
    todos = load_todos()
    if not todos:
        click.echo("タスクはありません。addコマンドで追加してください。")
        return
    for t in todos:
        mark = "x" if t["done"] else " "
        click.echo(f"[{mark}] {t['id']}. {t['task']}")


# タスクの完了(done)
@cli.command()
@click.argument("todo_id", type=int)
def done(todo_id):
    """指定したIDのタスクを完了にする"""
    todos = load_todos()
    for t in todos:
        if t["id"] == todo_id:
            t["done"] = True
            save_todos(todos)
            click.echo(f"完了しました: [{todo_id}] {t['task']}")
            return
    click.echo(f"ID {todo_id} のタスクが見つかりません。")


# タスクの削除(delete)
@cli.command()
@click.argument("todo_id", type=int)
def delete(todo_id):
    """指定したIDのタスクを削除する"""
    todos = load_todos()
    remaining = [t for t in todos if t["id"] != todo_id]
    if len(remaining) == len(todos):
        click.echo(f"ID {todo_id} のタスクが見つかりません。")
        return
    save_todos(remaining)
    click.echo(f"削除しました: ID {todo_id}")


# エントリーポイント
def main():
    cli()


if __name__ == "__main__":
    main()

コード全文は上の折り畳みに入れてあるので、全部を上から読む必要はありません。ここでは特に重要な部分だけを抜き出して、何をしているのか順番に見ていきます。

load_todosとsave_todosでJSONを読み書き

def load_todos():
    if TODO_FILE.exists():
        return json.loads(TODO_FILE.read_text(encoding="utf-8"))
    return []

todo.jsonが存在すれば読み込み、無ければ空リストを返します。初回実行でファイルがまだ無くてもエラーにならないのがポイントです。保存側のsave_todosではensure_ascii=Falseを指定し、日本語がそのまま読める形で書き出しています。

@click.groupでコマンドの親を作る

@click.group()
def cli():
    """clickで作るシンプルなTODO管理CLIツール"""

@click.group()を付けたcli関数が、4つのサブコマンドをぶら下げる親になります。中身は空でも動作し、docstringはそのまま--helpの説明文として表示されるので書いておくと親切です。

addコマンドと自動採番の仕組み

@cli.command()
@click.argument("task")
def add(task):
    """タスクを追加する"""
    todos = load_todos()
    next_id = max((t["id"] for t in todos), default=0) + 1
    todos.append({"id": next_id, "task": task, "done": False})

@click.argument("task")で受け取った文字列が、そのまま引数taskに渡ってきます。IDは既存の最大値に1を足す自動採番で、max()default=0のおかげでタスクが0件のときも1番から始まります。

@cli.command("list")で名前の衝突を回避

@cli.command("list")
def list_todos():
    """タスクを一覧表示する"""

関数名をlistにすると組み込み関数を上書きしてしまうため、関数名はlist_todosにしています。@cli.command("list")と名前を明示すれば、コマンド名だけをlistにできるのがclickの便利なところです。

doneコマンドはtype=intでIDを検証

@cli.command()
@click.argument("todo_id", type=int)
def done(todo_id):
    """指定したIDのタスクを完了にする"""

type=intを指定すると、clickが入力を自動で整数に変換してくれます。数値以外を渡した場合は関数が呼ばれる前にclick側がエラーメッセージを出すため、自前の検証コードが不要になるのが利点です。

deleteは内包表記で残すタスクを絞り込む

    remaining = [t for t in todos if t["id"] != todo_id]
    if len(remaining) == len(todos):
        click.echo(f"ID {todo_id} のタスクが見つかりません。")
        return
    save_todos(remaining)

削除は「指定したID以外を残す」という内包表記で実現しています。削除前後で件数が変わらなければ該当IDが存在しなかったと判断でき、存在チェックを別途書かずに済むのが上手い点です。


Python基礎・実践(Django)

企業・法人向けのPython研修では、基礎から応用まで体系的に学べます。

Python研修の詳細

DX社員研修

企業・法人向けのDX研修では、実務に繋がるリスキリングでITレベルを向上させます。

DX研修の詳細

Javaエンジニア育成研修

企業・法人向けのJavaエンジニア育成研修では、Javaの基礎から応用まで確実に習得できます。

Java研修の詳細

新卒・新入社員向け研修

企業・法人に新入社員・新卒社員に向けたプログラミング研修を提供しています。

新入社員研修の詳細

コードキャンプのIT研修を全て見る

PythonでTODO管理CLIを動かしてみた結果

実装したCLIツールを、実際に4回のコマンドで動かしてみました。addでタスクを追加し、listで一覧を確認、doneで完了にして、最後にdeleteで削除するという一連の流れです。

4回の実行はすべて終了コード0で正常に完了しています。下のキャプチャが実際のセッションで、コマンドを打つたびにclick.echoのメッセージが返ってくる様子が分かります。

引数の解析やエラーメッセージの整形はclickに任せているので、操作中に迷う場面はありませんでした。約80行のコードでも、日常づかいのTODO管理として十分に機能します。

このセクションの用語

終了コード
プログラムが終了時にOSへ返す数値。0なら正常終了、0以外は何らかの異常を意味する。
CLIツールの実行セッション(実出力)
CLIツールの実行セッション(実出力)
さらに詳しく学べるPython研修の詳細はこちら

Python×clickでよくあるエラーと対処法

同じ構成のCLIを自分で作るときに、初心者が遭遇しやすいエラーを整理しました。clickは親切なエラーメッセージを出してくれるため、メッセージを読めば原因の見当が付くものがほとんどです。

特に多いのはライブラリの入れ忘れと引数まわりの間違いです。下の表の対処を上から順に確認すれば、たいていはすぐ解決できます。

このセクションの用語

仮想環境
プロジェクトごとにライブラリを分けて管理するPythonの仕組み。venvなどのツールで作成する。
エラー例 原因 対処
ModuleNotFoundError: No module named 'click' clickが未インストール、または別の仮想環境で実行している pip install clickを実行する。仮想環境を使っている場合は有効化してから入れ直す
Error: No such command 'lst' サブコマンド名のタイプミス python todo.py --helpでコマンド一覧を確認し、正しい名前で実行し直す
Error: Missing argument 'TASK' addに引数を付けずに実行した add "タスク名"のように引数を渡す。スペースを含むタスクは引用符で囲む
Error: Invalid value for 'TODO_ID' donedeleteに数値以外を渡した type=intが入力を検証しているので、listで確認した整数のIDを指定する
json.decoder.JSONDecodeError todo.jsonを手で編集して形式が壊れた カンマや括弧の対応を直すか、todo.jsonを削除していったん作り直す
さらに詳しく学べるPython研修の詳細はこちら

Python click入門でつまずきやすい4つの落とし穴

文法エラーにはならないのに動きが期待と違う、という「静かなハマりどころ」がclickにはいくつかあります。今回の実装で意識した回避策とあわせて紹介します。

このセクションの用語

組み込み関数
list()max()のように、インポートせず最初から使えるPython標準の関数。
シャドーイング
既存の名前と同じ名前の変数や関数を定義して、元の名前を隠してしまうこと。気づきにくいバグの原因になる。
POINT

関数名をlistにすると組み込み関数のlist()を上書き(シャドーイング)してしまいます。関数名はlist_todosのままにして、@cli.command("list")でコマンド名だけをlistにするのが安全です。

clickは関数名のアンダースコアをハイフンに変換するため、名前を指定しないとlist_todosのコマンド名はlist-todosになります。意図した名前で呼ばせたいときは@cli.command("名前")で明示しましょう。

todo.jsonはカレントディレクトリに作られるため、実行する場所が変わると別のTODOリストを見ることになります。常に同じデータを使いたい場合は、保存先をホームディレクトリなど固定のパスに変える改造が有効です。

@click.argument@cli.command()より下(関数に近い側)に書きます。デコレータの順番を逆にすると引数が正しく登録されず、原因の分かりにくいエラーにつながります。

さらに詳しく学べるPython研修の詳細はこちら

TODO管理CLIツールが実務で活きる場面

「ターミナルから離れずにタスクを記録できる」「データがただのJSONファイル」という2つの性質は、実務でも応用が利きます。代表的な使い方を表にまとめました。

いずれも今回の約80行のコードをベースに、少しの改造で実現できる範囲です。まずは自分の開発フローに1つ組み込んでみるのがおすすめです。

このセクションの用語

リポジトリ
Gitでソースコードを管理する単位となるフォルダのこと。
シェルスクリプト
ターミナルで実行する一連のコマンドをファイルにまとめ、自動実行できるようにしたもの。
使える場面 具体的な使い方
開発中のメモ・積み残し管理 コーディング中に気づいた修正点をtodo addで即記録し、切りのいいタイミングでtodo listを見ながら消化していく
プロジェクト単位のTODO共有 todo.jsonはカレントディレクトリに作られるため、リポジトリごとにタスクを分けて管理できる。Gitコミットすればチームでの共有も可能
シェルスクリプトとの連携 python todo.py add "デプロイ後の動作確認"デプロイスクリプトの末尾に仕込み、やるべき確認作業を自動でタスク化する
自作CLIツールの雛形 「読み込み→更新→保存」の型はそのまま流用できるので、ブックマーク管理や作業ログ記録など、別のCLIを作るときの土台にする
さらに詳しく学べるPython研修の詳細はこちら

Pythonのclick製TODO管理CLIまとめ

click@click.group@cli.commandを組み合わせるだけで、add・list・done・deleteの4コマンドを持つTODO管理CLIを約80行で作れました。実際に実行した4回のコマンドは、すべて終了コード0で正常に動作しています。

「JSONを読み込み→更新→保存」という単純な型と、click.echoに統一した出力のおかげで、初心者でも追いやすいコードになったと思います。組み込み関数との衝突を@cli.command("list")で回避するテクニックも、覚えておいて損はないポイントです。

次のステップとしては、期限や優先度フィールドの追加、完了済みだけを表示する--allのようなオプション対応などが考えられます。まずは本記事のコードを写経して、自分だけのコマンドを1つ足すところから始めてみてください。

さらに詳しく学べるPython研修の詳細はこちら

参考にした一次情報

  1. ^ click公式ドキュメント. https://click.palletsprojects.com/, (参照26-07-17).
  2. ^ json --- JSONエンコーダおよびデコーダ — Python公式ドキュメント. https://docs.python.org/ja/3/library/json.html, (参照26-07-17).
  3. ^ pathlib --- オブジェクト指向のファイルシステムパス — Python公式ドキュメント. https://docs.python.org/ja/3/library/pathlib.html, (参照26-07-17).

※内容は執筆時点のものです。ライブラリやサイトの仕様は変わる可能性があるため、公式ドキュメントもあわせてご確認ください。

ITやプログラミングに関するコラム


ITやプログラミングに関するニュース

ブログに戻る

コメントを残す

コメントは公開前に承認される必要があることにご注意ください。

企業・法人向けのIT・プログラミング・生成AI研修を探す、比較する - IT・プログラミングを知って学べるコネクトメディア CodeCampが提供するDX人材育成が可能なプログラミングやITが学べる公開講座 - IT・プログラミングを知って学べるコネクトメディア コードキャンプが提供する無料で学べるプログラミングスクール講座 - IT・プログラミングを知って学べるコネクトメディア コードキャンプDX人材育成研修 - IT・プログラミングを知って学べるコネクトメディア 3.5日の研修で、年間1,600時間の削減効果が見込まれる。東京きらぼしフィナンシャルグループのDX人材育成事例 - IT・プログラミングを知って学べるコネクトメディア 配属3ヶ月で30%の生産性向上を実現するいよぎんコンピュータサービスの新人研修に迫る - IT・プログラミングを知って学べるコネクトメディア 金融業界の業務効率化を加速するニッセイアセットマネジメントの生成AI×GAS活用研修事例 - IT・プログラミングを知って学べるコネクトメディア 【製造業のDX人材育成事例】デジタル人材の即戦力化を実現する、日本ガイシ株式会社の異動者向オンボーディング研修 - ITやプログラミングを知って学べるコネクトメディア フューチャーアーキテクト株式会社が実現した新入社員向けIT研修プログラムでタスクフォース制度が主体的な学びと成長を生み出す - IT・プログラミングを知って学べるコネクトメディア コードキャンプDX人材育成研修 - IT・プログラミングを知って学べるコネクトメディア コードキャンプIT・プログラミング研修事例/【IT新入社員研修】オンラインとオフラインの最適バランスを実現したFutureOneの導入事例 - IT・プログラミングを知って学べるコネクトメディア コードキャンプIT・プログラミング研修事例/【新入社員研修】柔軟なハイブリッド型Java研修で実現した新卒20名の成長と成果|サークレイス株式会社 - ITやプログラミングを知って学べるコネクトメディア コードキャンプIT・プログラミング研修事例/現場により近いところにデジタルを根付かせるDX基礎講座研修|株式会社ブリヂストン - ITやプログラミングを知って学べるコネクトメディア コードキャンプIT・プログラミング研修事例/業務の効率化・DX推進に向けたIT人材育成への第一歩|株式会社カナエ - ITやプログラミングを知って学べるコネクトメディア 企業・法人向けのIT・プログラミング研修 - ITやプログラミングを知って学べるコネクトメディア

新着記事

対象者別で探す

子供(小学生・中学生・高校生)向け
プログラミング教室検索する

子供(小学生・中学生・高校生)がロボットやプログラミング言語を学ぶことができるオフラインからオンラインスクールを検索、比較することが可能です。

子供(小学生・中学生・高校生)
プログラミング教室検索する

ITやプログラムなどの
最新情報を検索する

日々、新しいITやプログラミング言語の情報が流れていきますが、特定の情報を時系列でニュースやコラムを確認することができます。

ITやプログラムなどの
最新情報を検索する