Ruff with VSCodeのススメ

# はじめに

この記事は[ミロゴス Advent Calendar 2023](https://qiita.com/advent-calendar/2023/milogos) 13日目の投稿です。

Pythonの開発環境はどのようにされていますか？

私はPythonをLambdaのランタイムによく使用し、Poetryでバージョン管理しながらVS Codeで開発しています。
VS Codeでの警告から、開発環境の設定を見直したので、それについて書いていこうと思います。

# 見直しの背景

複数人で開発する際には、LinterやFormatterを統一し、書き方を合わせていく必要があります。しかし、メンバー間で一行に対する文字数などに設定の差異があったり、設定をミスしてうまく機能しなかったことがありました。
設定を合わせるために、プロジェクトのレポジトリに`.vscode/settings.json`や`extensions.json`を配置し、共有していました。settings.jsonには`python.linting.flake8Enabled`や`python.linting.pylintEnabled`などの設定が記載されていました。

しかし、あるタイミングからVSCodeで`You have deprecated linting or formatting settings for Python`という警告が表示され、それらの設定が非推奨になっていることが知りました。
それらの設定はsettings.jsonではなく、拡張機能のインストールが必要だと記載されていました。

詳細はこちらのリンクを参照してください： https://github.com/microsoft/vscode-python/wiki/Migration-to-Python-Tools-Extensions#linting-and-formatting-settings-deprecation

以前は、BlackやFlake8、mypy、isortを使用しており、設定はpyproject.tomlにまとめていました。そのため、settings.jsonに設定を書きつつ、pyproject.tomlにも設定を記載し、各ツールの設定がバラバラかつ重複している状態でした。
拡張機能に移行するのも一つの方法でしたが、設定を簡易にしつつも厳密に守りたいところはあるよね、ということでこれを機にPythonの開発環境の設定を見直しました。

# Ruffについて

Ruffは、従来のLinterやFormatterと比べて非常に高速なRust製ツールです。
その早さは売りのようで、[GitHubのレポジトリ](https://github.com/astral-sh/ruff)や[公式のAstral社のTopページ](https://astral.sh/)にも表れています。
私が見直しをしている時期にはちょうどRuff formatterが発表され、頻繁なアップデートがあり選定にも問題なさそうでした。
https://twitter.com/charliermarsh/status/1716861072374657497

また、Astral社自体もRuffの継続的な開発を目指して設立されたようで、力の入れようが[このブログ（Announcing Astral, the company behind Ruff）](https://astral.sh/blog/announcing-astral-the-company-behind-ruff)からも分かります。

さらに、VSCodeにはRuffの拡張機能があるため、導入も簡単でした。
https://marketplace.visualstudio.com/items?itemName=charliermarsh.ruff

# 導入と私の設定

VSCodeでRuffを導入する際は、settings.jsonにpyproject.tomlから読み込む設定を追加することができます。

```json:.vscode/settings.json
"ruff.organizeImports": false,
  "ruff.format.args": [
    "--config=pyproject.toml"
  ],
```

pyproject.tomlでは`[tool.ruff]`でまとめられます。

```toml:pyproject.toml
[tool.ruff]
line-length = 119
select = [
    "E",   # pycodestyle
]
ignore = ["D415"]

[tool.ruff.pydocstyle]
convention = "google"
```

設定もかなりの数が用意されています。

https://docs.astral.sh/ruff/settings/

## 実際に使っている設定

直近のプロジェクトで実際に設定したRuffの設定は下記になりました。

```toml:pyproject.toml
[tool.ruff]
line-length = 119
select = ["ALL"]
ignore = [
    "D",
    "PLR0913",
    "ANN002",
    "ANN003",
    "ANN101",
]
target-version = "py311"

[tool.ruff.per-file-ignores]
"tests/**/*.py" = [
    "S101",
    "ANN201",
    "N802",
    "ARG",
]

[tool.ruff.pydocstyle]
convention = "google"
```

### select = ["ALL"]

デフォルトで有効にされているルールは、`["E4", "E7", "E9", "F"]`の設定されています。これはFlake8のFルールとEルールの一部を有効にする設定です。
既存のプロジェクトをRuffに移行する場合は、[ルールの説明](https://docs.astral.sh/ruff/rules/)にも書かれているように、デフォルトのルールセットを手始めに利用することが最適です。

ただし、私の場合は可能な限り、ルールを有効化しました。ignoreの設定を使用して、後からルールを除外することができるため、プロジェクトに関わる人数が多いほど、有効なルールも多ければ統一させやすいです。

### ignore
selectとは逆に除外するルールがignoreです。
`"D"`のようにD*のルールをまとめて除外したり、`"PLR0913"`のように特定のルールを指定することも可能です。
https://docs.astral.sh/ruff/rules/

例えば、Dは[pydocstyleに関するルール](https://docs.astral.sh/ruff/rules/#pydocstyle-d)です。
テストやDocstringへの記載方法、既存のコードへの変更など導入に難があったため、除外しています。
このルールは少しずつ対応していきつつ、ignoreから削除していきたいところです。

PLR0913はPylintの[too-many-argumentsのルール](https://docs.astral.sh/ruff/rules/too-many-arguments/)になります。
CDKを書く際、パラメーターを多く渡す必要があるため、このルールは除外しました。

また、これらの除外理由はReadmeなどドキュメントに書いておくことで、ignoreから外していきたいという認識を共有しておくことをお勧めします。

### target-version

Pythonの最低バージョンを指定します。設定されたバージョンで利用できないルールは提案されません。

https://docs.astral.sh/ruff/settings/#target-version

なお、pyproject.tomlにproject.requires-pythonの記載がある場合、それが優先されます。

```
[project]
requires-python = ">3.11.0"
```

### tool.ruff.per-file-ignores

`tool.ruff.per-file-ignores`の設定により、ファイル単位でルールを無視することができます。特にtestsディレクトリ配下のテスト用プログラムには無視をしてほしい設定があるため、ここで設定をします。
https://docs.astral.sh/ruff/settings/#per-file-ignores

### tool.ruff.lint.pydocstyle

DocstringはRuff導入前からGoogle記表に合わせており、Ruffでも利用するために`tool.ruff.lint.pydocstyle`の設定をしています。

https://docs.astral.sh/ruff/settings/#pydocstyle-convention

# 最後に

Ruffにはここで記載したもの以外にも、様々な設定やルールが下記のページに記載されています。
https://docs.astral.sh/ruff/settings/

さらにRuffのルールを用いた、修正のリコメンドを受けることもできます。

```python
def get_test(self) -> str:
    tmp = "test"
    return tmp
```

![スクリーンショット 2023-12-12 22.31.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224198/70b659a5-eaea-3a14-b135-02a4fef7edda.png))

簡単に設定しつつも様々な設定やルールが利用でき、さらに速いRuffでPythonのプロジェクトを進めてみてはいかがでしょうか。