Ruff更新で警告が増えたら? Python CLIで差分確認と自動修正を試す

Ruffの更新後に増えた警告を確認する記事のアイキャッチ。「警告が増えた。どこを直す?」の文字と、List[int]からlist[int]への変更例 Python
カテゴリー
Python
公開日
2026.09.09

はじめに

Ruffを更新したら、昨日まで通っていたPythonコードに指摘が出るようになった。そんなときは、すべてを一括修正する前に、何が変わったのか、どの変更を受け入れるのかを確認しましょう。

この記事は、Pythonの関数とコマンド実行に触れたことがあり、Ruffの更新後に増えた指摘へ対応したい人向けです。単価と個数から合計を求める小さなCLIを使い、更新前後の診断、修正差分、修正後の実行結果を順番に比べます。CLIは、ターミナルから引数を渡して使うプログラムです。

ゴールは、Ruffの指摘を読み、差分を見て修正し、入力エラーも試して変更を判断できることです。

今回扱う内容と完成物

次の3ファイルを作ります。最初の2つには、入力数が違っても計算を続ける問題を意図的に残しています。

ファイル 役割
order_total.py 更新前後の指摘を比べる、修正前の教材
order_total_fixed.py Ruffの自動修正を適用したコピー
order_total_checked.py 入力数が合わない場合にエラーで止める完成版

正常な入力では、100円の商品を2個、200円の商品を3個として、合計800円を出力します。完成版では、単価が2件なのに個数が1件しかない場合を受け付けません。

これは修正の判断を学ぶための教材です。税、割引、金額の小数、負の値の業務上の扱いは実装していないため、そのまま請求処理には使えません。

Ruffの更新で指摘が増える仕組み

RuffはPythonコードを実行せずに調べ、ルールに合わない箇所を指摘するツールです。この検査をlintと呼びます。

AstralはRuff 0.16.0の発表で、標準で有効になるルールの変更を案内しています。同じコードでも、バージョンと有効なルールが変われば診断結果が変わります。

本記事ではRuff 0.15.0と0.16.0を固定して比較します。後続バージョンでも出力が同じになるとは限りません。また、設定ファイルでルールを選択しているプロジェクトでは、今回の標準設定の比較とは結果が異なります。

使う操作の違いを先に整理します。

操作 ファイルを書き換えるか 用途
ruff check いいえ 指摘を読む
ruff check --diff いいえ 自動修正で変わる行を先に見る
ruff check --fix はい 自動修正を適用する

Ruffには見た目を整えるruff formatもありますが、ここでは指摘と修正を扱うruff checkに絞ります。

必要な環境

掲載コマンドはWindowsのPowerShell、Python 3.13向けです。Python本体とpyコマンドが使えることを前提にしています。macOS・Linux向けのコマンドは扱いません。

Ruffのインストールにはインターネット接続が必要です。APIキーや有料サービスは使いません。検査対象のコードをクラウドサービスへ送る構成にもしていません。

既存プロジェクトと混ぜず、新しい作業用フォルダーを作り、その中へ移動します。同名のフォルダーやファイルがある場合は別の名前を使ってください。

mkdir ruff-update-demo
cd ruff-update-demo
py -3.13 -m venv .venv-old
py -3.13 -m venv .venv-new
.\.venv-old\Scripts\python.exe -m pip install "ruff==0.15.0"
.\.venv-new\Scripts\python.exe -m pip install "ruff==0.16.0"
.\.venv-old\Scripts\python.exe -m ruff --version
.\.venv-new\Scripts\python.exe -m ruff --version

最後の2コマンドで、それぞれruff 0.15.0ruff 0.16.0が表示されれば準備完了です。仮想環境は、依存ツールを作業ごとに分ける場所です。ここでは実行ファイルを直接指定するため、仮想環境の有効化は不要です。

手順1:比較用のCLIを保存する

作業フォルダーにorder_total.pyを作り、次の内容をUTF-8で保存してください。これはまだ修正前のコードです。

import argparse
from typing import List


def subtotal(prices: List[int], quantities: List[int]) -> int:
    return sum(price * quantity for price, quantity in zip(prices, quantities))


def main():
    parser = argparse.ArgumentParser(description="単価と個数から合計を求めます")
    parser.add_argument("--prices", nargs="+", type=int, required=True)
    parser.add_argument("--quantities", nargs="+", type=int, required=True)
    args = parser.parse_args()
    try:
        total = subtotal(args.prices, args.quantities)
    except ValueError as error:
        parser.error(str(error))
    print(f"合計: {total}円")


if __name__ == "__main__":
    main()

argparseは、--pricesなどの引数を受け取る標準ライブラリです。nargs="+"で1個以上の値を受け取り、type=intで整数へ変換します。

subtotal()は、単価と個数をzip()で順番に組み合わせ、掛けた値をsum()で合計します。List[int]は「整数のリスト」という型注釈であり、入力値を検証する処理ではありません。

まず正常な入力を実行します。

.\.venv-new\Scripts\python.exe order_total.py --prices 100 200 --quantities 2 3
合計: 800円

手順2:同じコードを更新前後で検査する

次の2コマンドで、同じファイルを検査します。

.\.venv-old\Scripts\python.exe -m ruff check --isolated --target-version py313 --output-format concise order_total.py
.\.venv-new\Scripts\python.exe -m ruff check --isolated --target-version py313 --output-format concise order_total.py

--isolatedは設定ファイルを読み込まない指定です。親フォルダーなどの設定が比較に混ざらないよう、この教材では付けています。普段のプロジェクトで設定を無視してよいという意味ではありません。

--target-version py313は、対象コードがPython 3.13向けだとRuffへ伝える指定です。Python本体のインストールや切り替えは行いません。--output-format conciseは、診断を短い形式で表示します。

実行すると、0.15.0ではAll checks passed!、0.16.0では次の3件が出ました。パス部分は環境によって異なります。

order_total.py:2:1: UP035 `typing.List` is deprecated, use `list` instead
order_total.py:5:22: UP006 [*] Use `list` instead of `List` for type annotation
order_total.py:5:45: UP006 [*] Use `list` instead of `List` for type annotation
Found 3 errors.
[*] 2 fixable with the `--fix` option.

UP035は非推奨のimport、UP006は組み込みの型で書ける型注釈への指摘です。Python 3.13向けのこのコードでは、typing.List[int]list[int]で書けます。UP035の説明UP006の説明で、ルールの意図と適用条件を確認できます。

この出力は、CLIの計算が突然動かなくなったことを示していません。Ruffがコードの書き方について新しく指摘した結果です。

手順3:差分を見て、コピーへ修正を適用する

最初に--diffで変更予定を読みます。

.\.venv-new\Scripts\python.exe -m ruff check --isolated --target-version py313 --diff order_total.py

主な変更は次のとおりです。-の行が削除、+の行が追加です。この段階では元ファイルは変わりません。

-from typing import List
-def subtotal(prices: List[int], quantities: List[int]) -> int:
+def subtotal(prices: list[int], quantities: list[int]) -> int:

変更内容を確認したら、元ファイルを残してコピーに適用します。--fixは指定したファイルを書き換えます。

Copy-Item order_total.py order_total_fixed.py
.\.venv-new\Scripts\python.exe -m ruff check --isolated --target-version py313 --fix order_total_fixed.py
.\.venv-new\Scripts\python.exe -m ruff check --isolated --target-version py313 order_total_fixed.py
.\.venv-new\Scripts\python.exe order_total_fixed.py --prices 100 200 --quantities 2 3

この例では自動修正後の再検査が通り、計算結果も合計: 800円でした。型注釈の置き換えに加えて、不要になったimportも除かれます。

実際のプロジェクトでは、作業前の状態をGitのコミットやバックアップで残し、修正差分とテスト結果を確認してください。自動修正の適用範囲と安全性はルールや対象Pythonに依存します。今回の結果を、古いPythonや実行時に型注釈を利用する別のプログラムへそのまま当てはめることはできません。

手順4:チェックが通っても、入力エラーを試す

今度は、個数を1件に減らします。

.\.venv-new\Scripts\python.exe order_total_fixed.py --prices 100 200 --quantities 2

実際の出力は次のとおりでした。

合計: 200円

単価200円の分が計算されていません。zip()は標準では短い側に合わせて終了するためです。今回のCLIでは、単価と個数が同じ件数であることを要求したいので、この結果は受け入れられません。

関連するRuffのルールにB905があります。ただし、今回の0.16.0の標準設定では、このコードへのB905の指摘は出ませんでした。次のように明示的に追加します。

.\.venv-new\Scripts\python.exe -m ruff check --isolated --target-version py313 --extend-select B905 order_total_fixed.py

今度は、zip()strict指定がないことを指摘されます。--extend-selectは、有効なルールへ指定ルールを追加するオプションです。

ここで「自動修正できれば解決」とは限りません。B905の公式説明では、自動修正はstrict=Falseを加えるもので、unsafeに分類されています。長さが異なるときの意図を人が判断する必要があるためです。

次のコマンドは、unsafeの修正も含めた差分の表示だけを行います。ファイルには適用しません。

.\.venv-new\Scripts\python.exe -m ruff check --isolated --target-version py313 --extend-select B905 --unsafe-fixes --diff order_total_fixed.py

実行するとstrict=Falseを追加する差分が出ました。これは短い側で終了する挙動を明示する変更で、今回必要な「件数が違えば止める」修正にはなりません。unsafeという表示だけで良し悪しを決めず、変更内容とプログラムの目的を照らし合わせます。

手順5:意図に合わせた完成版を作る

order_total_fixed.pyorder_total_checked.pyへコピーし、zip(prices, quantities)zip(prices, quantities, strict=True)へ手動で変更します。

Copy-Item order_total_fixed.py order_total_checked.py

変更後のsubtotal()は次のようになります。

def subtotal(prices: list[int], quantities: list[int]) -> int:
    return sum(price * quantity for price, quantity in zip(prices, quantities, strict=True))

ほかの部分は自動修正後のままです。strict=Trueでは長さが異なるとValueErrorが発生します。main()に用意したtryexceptが受け取り、parser.error()で入力エラーとして終了します。

修正後の検査と実行を行います。

.\.venv-new\Scripts\python.exe -m ruff check --isolated --target-version py313 --extend-select B905 order_total_checked.py
.\.venv-new\Scripts\python.exe order_total_checked.py --prices 100 200 --quantities 2 3
.\.venv-new\Scripts\python.exe order_total_checked.py --prices 100 200 --quantities 2

検査は通り、正常入力では800円になります。最後のコマンドは合計を表示せず、使い方の案内と次のエラーで終了しました。

order_total_checked.py: error: zip() argument 2 is shorter than argument 1

動作確認:修正を受け入れる判断材料

今回の実行結果をまとめます。lintが通ったかだけでなく、CLIとして期待した振る舞いになったかを確認しました。

入力・検査 自動修正後 完成版
0.16.0の標準ルール 通る 通る
B905を追加した検査 指摘あり 通る
単価100・200、個数2・3 800円 800円
単価100・200、個数2 200円で終了 エラーで終了

完成版では、個数が単価より多い入力、単価へのabcの指定、--quantitiesの省略もエラーで終了しました。単価100、個数0は0円になりました。入力エラーはいずれも終了コード2、正常入力は終了コード0でした。終了コードは、プログラムが成功したかを呼び出し元へ伝える数値です。PowerShellでは、実行の直後に$LASTEXITCODEで確認できます。

Ruffはコードのパターンを検査しますが、「単価と個数の件数は一致すべき」という業務上の意図までは保証しません。修正前後で、正常入力と代表的な異常入力を実行することが大切です。

トラブル対処と実際のプロジェクトへの戻し方

記事と違う指摘が出る

まず、実行しているRuffのバージョン、--target-version--isolated、対象ファイルを確認してください。VS Codeなどのエディターとターミナルが別のRuffや設定を使っている場合も、結果は揃いません。

普段のプロジェクトではpyproject.tomlruff.tomlなどの設定を確認します。教材の--isolatedをそのまま持ち込むと、プロジェクトのルールが無視されます。設定ファイルの公式説明も参照してください。

更新とルール変更を分けて調べたい

比較用に、0.15系の標準選択だったE4,E7,E9,Fを0.16.0で明示すると、今回の修正前ファイルは検査を通りました。

.\.venv-new\Scripts\python.exe -m ruff check --isolated --target-version py313 --select E4,E7,E9,F order_total.py

--selectは検査ルールの選択を置き換えます。既存の選択へ加える--extend-selectとは役割が違います。これは原因を切り分けるための比較であり、すべてのプロジェクトで古い選択へ戻すことを勧めるものではありません。同じルールを選んでも、Ruffのバージョン間で診断そのものが変わる場合があります。

実際の更新では、バージョンと設定を記録し、小さい範囲で差分とテストを確認してから適用範囲を広げます。

Ruffの終了コード1は実行失敗?

ruff checkは、指摘が残る場合に通常1を返します。今回の更新後の3件もこのケースです。設定の読み込みなどツール側の異常とは区別し、診断内容を読んでください。--diffも差分があれば1になるため、修正予定が表示されたこと自体は異常ではありません。

Python 3.13が見つからない

py -3.13が失敗する場合は、Python 3.13の導入状態を確認してください。--target-version py313を付けてもPython本体は増えません。ここでは別バージョンや別OSでの同一出力を保証していません。

まとめ

Ruffの更新後に指摘が増えたら、バージョンと設定を確認し、check--diff、コピーへの--fixの順で進めると変更内容を追いやすくなります。

今回のCLIでは、型注釈の自動修正後も、入力数が違うと一部の値を計算しない問題が残りました。追加ルールの指摘を読み、必要な動作を考えてstrict=Trueを選び、実行して確かめるところまでが修正です。

次は、自分の小さなPythonスクリプトを1本選び、バックアップを取ったうえで、指摘と差分を読むところから試してください。集計処理で出力を確かめる例として、PythonとDuckDBで月別CSVをまとめる記事では、統合結果を検算する手順も扱っています。

参考リンク

コメント

タイトルとURLをコピーしました