BackEnd

Pythonコードが育っても品質を維持するツールを考える[Ruff Pyright]

投稿日:

はじめに

Pythonでアプリケーション開発をするとなると、ちょっとしたPythonスクリプトを書くのとは違った難しさがあります。素のPythonは、スクリプト言語らしい自由さを持っていますが、その状態でアプリケーションを開発をするとなると、コードの品質を維持するのがとても難しいです。
(特に、チーム開発するとなったら、なおさらです。)

となると、Pythonの言語機能のみではそうした状況を何とかするのは難しいので、周辺ツールを使ってコードの品質を維持していくことになります。
具体的には、リンターを通したり、静的型チェック、コードフォーマットをすることになると思います。これまでは、リンターはPylintやFlake8、静的型チェッカーはmypy、フォーマッターはBlackとIsortを使用することが多いと思いますが、ここ最近は取り巻くツールに変化が起きています。

そこで、今回改めて最新のツールを調べてみたところ、「Ruff」と「Pyright」を使うのが現状の最適解だと思ったので、調べた内容を紹介したいと思います。

なお、今回はryeで管理しているプロジェクトに導入しています。ryeの説明は以前の記事もご覧ください。

各ツールの目的

Ruff

RuffはPythonのリンター・コードフォーマッタで、以下のような特徴があります。

  • Flake8、Isort、Blackといった既存のリンターやフォーマッタに代替えを目指している。
  • Rustで実装されているため、実行速度が速いと謳われている。

まず、リンター機能は、Flake8相当の実装はされているものの、Pylint相当の実装には追いつき中です。
RuffへのPylintルールの実装状況は以下のIssueでトラックされています。
https://github.com/astral-sh/ruff/issues/970

記事執筆時点では、重要そうなルールの実装が進んでいて、そろそろPylintユーザも乗り換えを検討して見ても良いかもしれません。
中には、エラーレベルのルールが実装されていないこともありますが、後で紹介する型チェッカーを使えばチェックでき、そうした流れで実装優先度が高くないのかもしれません。
例えば、 no-member (クラスに存在しないフィールドやメソッドにアクセス) や、 undefined-variable (未定義変数へのアクセス) といったものはRuffには実装されていません。

また、一部のルールはプレビュー状態であり、 --preview フラグをつける必要があること手に注意してください。例えば、 open("file.txt", encoding="utf-8") というようにファイルオープン時の文字コードに関するルールは、まだプレビュー段階です。
https://docs.astral.sh/ruff/rules/unspecified-encoding/

次に、フォーマッタ機能ですが、Isort相当のインポート文ソートと、Black相当のフォーマッタが実装されています。既存フォーマッタと完全に同じ挙動になるわけではありませんが、こちらは十分実用的です。

Pylint (参考)

汎用的なリンターで、以下のような特徴があります。

  • PEP8(Pythonのコーディングガイドライン)への準拠チェックができる。(これはRuffでもできます)
  • バグの原因となる紛らわしいコードや、パフォーマンスの改善が見込まれるコードの指摘
  • 様々なチェックをしてくれる反面、チェックに時間がかかる

他のどんなリンターよりも細かく厳しくチェックされるため、Pylintを導入し、常に10点満点(指摘なし)を取得できるコードを書けば、そこそこ統制の取れたコードを維持できると思います。
もちろん、設定ファイルによって、ルールの無効化や各種変数(ローカル変数の数など)を設定できるので、プロジェクトにとってただ時間を消耗するだけのルールは適宜無効化することができます。(例えばクラスコメントの矯正など)

一方、オープンソース界隈では、ライバルのRuffやFlake8の方が人気があります。その理由はリンターの実行時間であり、プロジェクトが巨大化するとリンターを実行するに時間がかかる点が、Pylintの弱点です。
確かに、VSCode上で使っていても、ファイルをセーブしてからワンテンポ遅れて表示が反映されることがあります。
また、RuffへのPylintルールの実装も進んでいることから、Pylintへの強いこだわりがなければRuffを使うのが良いでしょう。

ちなみに、文字列結合にforループを使うのではなく、 "".join() を使う方がパフォーマンスが良いという指摘は、今のところRuffでもFlake8でも出すことはできません。
https://pylint.readthedocs.io/en/latest/user_guide/messages/refactor/consider-using-join.html

Pyright

静的型チェッカーで、以下のような特徴があります。

  • 動作が高速
  • mypyに比べ、強力な型推論をサポート
  • VSCodeの公式PythonプラグインとセットでインストールされるPylanceに同梱
    (PyCharmでもサードパーティからPyright用プラグインが提供されている)

Pythonの静的型チェッカーの先駆者であるmypyと比較されがちですが、Pyrightとmypyは目指している方向そのものが異なります。
mypyは、どちらかというと保守的で、厳格なルールを適用すると静的型付け言語のような書き味になります。
一方、Pyrightは強力な型推論エンジンによって、Pythonらしい書き味を残しつつも、型チェックを行います。

例えば、以下のような違いがあります。

  • mypyでは変数の型の変化を許容しないが、Pyrightは途中の型変化を許容し、型が確実に変化する場合は変化後の値、if文などで変化する可能性がある場合は、以降Unionになる。
  • mypyでは関数の型アノテーションを強制した場合、戻り値の型も必ず書く(たとえNoneであっても)必要があるが、Pyrightでは仮引数の型アノテーションを強制しつつ、戻り値は型推論させることができる。

mypyとpylanceの比較は以下に紹介されています。
https://microsoft.github.io/pyright/#/mypy-comparison
https://future-architect.github.io/articles/20220301a/

各ツールの設定 (VSCodeでの使用例)

Ruff、PyrightともにVSCode向けには各開発元からの公式プラグインがあるため、それを使用します。(ちなみに、PyCharm向けにはサードパーティからプラグインがリリースされています。)
ちなみに、他ワークスペースなどで使用するために競合するプラグイン(PylintやBlackなど)が入っている場合は、ワークスペース単位で無効化しておくことをおすすめします。

Ruff

マーケットプレイスからRuffプラグインをインストールします。Ruffの設定は、 pyproject.toml または ruff.toml に書くことができます。今回は pyproject.toml に書いてみます。

様々な設定をすることができますが、その中でもPythonのバージョン指定とリンター設定を紹介します。

まず、Pythonのバージョン指定は、 project.requires-python を参照するようになっています。この設定を元に、バージョンに応じた提案をしてくれるようになるため、もし古いPythonをターゲットとしている場合は、Pythonのバージョンアップを検討しても良いでしょう。

次は、リンター設定です。(こちらも一部のみの紹介です。)

select に使いたいルールを指定します。ルールのプレフィックスを指定することで、そのグループの全てを有効化することができます。
Ruffに実装されている全てのルールはこちらで確認することができます。
https://docs.astral.sh/ruff/rules/

今回は、以下のページに記載されているおすすめ設定に加え、Pylintのルールを有効化していため PL を末尾に加えています。
https://docs.astral.sh/ruff/linter/#rule-selection

さらに、Pylintルールはまだプレビュー中のものが多くあるため、 preview = true としています。

Flake8のみならず、Flake8の各種プラグインやpyupgradeで実装されているルール、FastAPI固有のルールなど、様々なルールが実装されています。これまでは、リンターの得意領域ごとに使い分ける必要がありましたが、Ruffひとつで様々な種類のリンターがかけられるのはとても便利です!

最後に、VSCode側の設定です。

このように設定することで、セーブするごとにRuffが実行され、リンターによる自動修正とインポートの自動整理が有効になります。

Pyright

VSCodeのPylanceプラグインに内包されています。(ちなみに、Pylanceは、Pythonプラグインをインストールすると自動的に追加されます。)
設定は pyrightconfig.json をプロジェクトルートに作成します。

typeCheckingModestrict にすることで、厳密な型チェックが行われるようになります。

pre-commit

ここまではVSCodeでの設定方法を紹介しましたが、pre-commitを使ってコミット時にチェックを強制することもできます。
まずは、pre-commitをインストールします。

次に、設定ファイルを作成します。プロジェクトのルートに .pre-commit-config.yaml を作成します。

この設定に加え、Pyrightは先ほどの pyrightconfig.json に以下の設定を追加する必要があります。

この設定は、pre-commitがプロジェクトの仮想環境とは別の仮想環境を作り、そこでPyrightを実行するために、プロジェクトが依存しているパッケージのimportを解決することができなくなる問題への対応です。
このように .venv へのパスを設定することで、pre-commit経由でもPyrightを実行することができます。

さいごに

RuffはAstral.sh社が中心となって開発が進められているOSSです。Ryeのメンテナーでもあり、今まさにPython開発ツールの中心にいると思います。また、PyrightはMicrosoftが中心となっているOSSです。
現状では、Pythonの開発ツール界隈ではこの2社の存在感が大きいですね。

おすすめ書籍

エキスパートPythonプログラミング 改訂4版 (アスキードワンゴ) Effective Python 第2版 ―Pythonプログラムを改良する90項目 動かして学ぶ!Python FastAPI開発入門

blog-page_footer_336




blog-page_footer_336




-BackEnd
-, , ,

執筆者:

免責事項

このブログは、記事上部に記載のある投稿日時点の一般的な情報を提供するものであり、投資等の勧誘・法的・税務上の助言を提供するものではありません。仮想通貨の投資・損益計算は複雑であり、個々の取引状況や法律の変更によって異なる可能性があります。ブログに記載された情報は参考程度のものであり、特定の状況に基づいた行動の決定には専門家の助言を求めることをお勧めします。当ブログの情報に基づいた行動に関連して生じた損失やリスクについて、筆者は責任を負いかねます。最新の法律や税務情報を確認し、必要に応じて専門家に相談することをお勧めします。


comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA


関連記事

rails

Capistrano3でRailsアプリケーションをデプロイする

1 はじめに1.1 前提条件2 Cpistranoについて3 導入3.1 Gemのインストール3.2 設定ファイルの準備4 デプロイ設定4.1 Capfileを修正する4.2 各環境で共通のデプロイ設 ...

Rust入門してみた その3 Enum / match / Option編

1 はじめに2 Enum2.1 Enumの定義2.2 パターンマッチ2.3 Enumへのメソッド実装3 よく使う標準Enum3.1 Option3.2 Result4 おすすめ書籍 はじめに 前回に引 ...

laravel logo

LaravelのDI

1 はじめに2 DIに関する機能2.1 DIとは2.2 サービスコンテナ2.3 サービスプロバイダ3 サービスコンテナ3.1 バインド3.2 bind3.3 bindIf3.4 singletonメソ ...

【Git】マージコミットを消したい

1 はじめに2 例えばこんなことってないでしょうか?3 マージコミットを消す4 さいごに はじめに こんにちは、tonnyです。 今回はGitのケーススタディ的なものを書きたいと思います。 反面教師と ...

MySQLでバルクアップデート処理

1 はじめに2 使用するテーブル3 パターン数が少ないシンプルな更新4 パターン数が多い場合4.1 FIELD関数4.2 ELT関数4.3 組み合わせ4.4 パフォーマンス5 さいごに6 おすすめ書籍 ...

フォロー

blog-page_side_responsive

2024年8月
 123
45678910
11121314151617
18192021222324
25262728293031

アプリ情報

私たちは無料アプリもリリースしています、ぜひご覧ください。 下記のアイコンから無料でダウンロードできます。