pythonのためのvscode環境設定[Ruff as linter/formatter]

昔はemacs教だったのだが、現在はvscodeでコーディングすることが主となっている。
最近はvscodeのforkであるcursorを試しているが、制限あれど無料でchatgpt4を扱えるのは便利だ。

vscodeで導入する拡張機能は以下を想定する。

  • WSL

    • wsl上のLinux環境にvscodeから直接アクセスできるようになる

  • Python

    • pythonのデバッグサポート。

    • venv含めてpythonインタプリタを自動認識してくれて、切りかえも容易になる

  • Pylance

    • python構文を分かりやすく色付けしてくれる。コーディングにおいて見やすいというのは重要なので入れる。

  • Ruff

    • Linter

    • Formatter

  • GitLens

    • git logをグラフィカルに表示してくれるので、ファイルの変更履歴を追いやすい。

    • その他Git操作をGUIで行うことができるが、そっちはCUIでやる派なので僕は使ってない。


基本的にそれぞれインストールするだけで個別で設定を行う必要は無いのだが、Ruffに関しては重要な拡張機能になるのでもう少し詳細に触れておく。

RuffはPython用の超高速なリンターで、静的解析ツールの一つです。Pythonコードの構文エラーやスタイルの問題、バグの可能性があるコードパターンを検出するために設計されています。Ruffは特に実行速度に焦点を当てており、大規模なコードベースでも迅速に分析を行うことができます。

chatgpt

Ruffは構文エラーを検出するだけでなく、PEP8に従っていないコードを解析して指摘してくれる。
vscodeにおけるRuff拡張機能は、Ruffパッケージ自体を内包しているため拡張機能をインストールするだけで使えるようになるが、手動でインストールもしてみよう。

[余談]
pythonのlinter/formatterとしては下記のパッケージを組み合わせて構築するのがメジャーなのだが、これら全てをRuffは内包してくれているのが便利な所だ。そして何より処理が高速で、プロジェクトが大きくなってくると効果を発揮してくるだろう。
・flake8
・isort
・black

Ruffのインストール

ibismark@ibismark-win:~$ cd ~/workspace/otam/
ibismark@ibismark-win:~$ source .venv/bin/activate
(.venv) ibismark@ibismark-win:~/workspace/otam$ poetry add ruff
Using version ^0.2.1 for ruff

Updating dependencies
Resolving dependencies... Downloading https://files.pythonhosted.org/packages/00/a1/618f1dbf5fc66c27221b1ede09a8b07ad4796cdf6cf9a8f8e4b20bb5987b/ruff-0
Resolving dependencies... Downloading https://files.pythonhosted.org/packages/00/a1/618f1dbf5fc66c27221b1ede09a8b07ad4796cdf6cf9a8f8e4b20bb5987b/ruff-0
Resolving dependencies... Downloading https://files.pythonhosted.org/packages/00/a1/618f1dbf5fc66c27221b1ede09a8b07ad4796cdf6cf9a8f8e4b20bb5987b/ruff-0
Resolving dependencies... Downloading https://files.pythonhosted.org/packages/00/a1/618f1dbf5fc66c27221b1ede09a8b07ad4796cdf6cf9a8f8e4b20bb5987b/ruff-0
Resolving dependencies... Downloading https://files.pythonhosted.org/packages/00/a1/618f1dbf5fc66c27221b1ede09a8b07ad4796cdf6cf9a8f8e4b20bb5987b/ruff-0
Resolving dependencies... Downloading https://files.pythonhosted.org/packages/00/a1/618f1dbf5fc66c27221b1ede09a8b07ad4796cdf6cf9a8f8e4b20bb5987b/ruff-0
Resolving dependencies... Downloading https://files.pythonhosted.org/packages/00/a1/618f1dbf5fc66c27221b1ede09a8b07ad4796cdf6cf9a8f8e4b20bb5987b/ruff-0
Resolving dependencies... Downloading https://files.pythonhosted.org/packages/00/a1/618f1dbf5fc66c27221b1ede09a8b07ad4796cdf6cf9a8f8e4b20bb5987b/ruff-0
Resolving dependencies... Downloading https://files.pythonhosted.org/packages/00/a1/618f1dbf5fc66c27221b1ede09a8b07ad4796cdf6cf9a8f8e4b20bb5987b/ruff-0
Resolving dependencies... Downloading https://files.pythonhosted.org/packages/00/a1/618f1dbf5fc66c27221b1ede09a8b07ad4796cdf6cf9a8f8e4b20bb5987b/ruff-0
Resolving dependencies... Downloading https://files.pythonhosted.org/packages/00/a1/618f1dbf5fc66c27221b1ede09a8b07ad4796cdf6cf9a8f8e4b20bb5987b/ruff-0
Resolving dependencies... Downloading https://files.pythonhosted.org/packages/00/a1/618f1dbf5fc66c27221b1ede09a8b07ad4796cdf6cf9a8f8e4b20bb5987b/ruff-0
Resolving dependencies... Downloading https://files.pythonhosted.org/packages/00/a1/618f1dbf5fc66c27221b1ede09a8b07ad4796cdf6cf9a8f8e4b20bb5987b/ruff-0
Resolving dependencies... Downloading https://files.pythonhosted.org/packages/00/a1/618f1dbf5fc66c27221b1ede09a8b07ad4796cdf6cf9a8f8e4b20bb5987b/ruff-0
Resolving dependencies... Downloading https://files.pythonhosted.org/packages/00/a1/618f1dbf5fc66c27221b1ede09a8b07ad4796cdf6cf9a8f8e4b20bb5987b/ruff-0
Resolving dependencies... Downloading https://files.pythonhosted.org/packages/00/a1/618f1dbf5fc66c27221b1ede09a8b07ad4796cdf6cf9a8f8e4b20bb5987b/ruff-0
Resolving dependencies... Downloading https://files.pythonhosted.org/packages/00/a1/618f1dbf5fc66c27221b1ede09a8b07ad4796cdf6cf9a8f8e4b20bb5987b/ruff-0
Resolving dependencies... (2.0s)

Package operations: 1 install, 0 updates, 0 removals

  • Installing ruff (0.2.1)

Writing lock file
(.venv) ibismark@ibismark-win:~/workspace/otam$
(.venv) ibismark@ibismark-win:~/workspace/otam$ poetry show
ruff     0.2.1 An extremely fast Python linter and code formatter, written in Rust.
(.venv) ibismark@ibismark-win:~/workspace/otam$

カスタム設定

基本デフォルトで良いのだが、折角なのでconfigを作成してカスタムしてみよう。
次のようにruff.tomlというファイルを新規作成し、設定を書いていく。


(.venv) ibismark@ibismark-win:~/workspace/otam$ touch ~/workspace/otam/ruff.toml
(.venv) ibismark@ibismark-win:~/workspace/otam$ vi ~/workspace/otam/ruff.toml
・・・
# ref: https://docs.astral.sh/ruff/settings/
# Exclude a variety of commonly ignored directories.
exclude = [
    ".git",
    "__pycache__",
    "__pypackages__",
    ".venv*",
    ".vscode",
    "*.sqlite3"
]

# requires-python 3.12
target-version = "py312"

[lint]
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`)  codes by default.
# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or
# McCabe complexity (`C901`) by default.
# ref: https://docs.astral.sh/ruff/rules/
select = [
    "E",    # pycodestyle error
    "F",    # Pyflakes
    "W",    # pycodestyle warning
    "I",    # isort
    "DJ",   # flake8-django
    "B",    # flake8-bugbear
    "N"     # pep8-naming
]
ignore = ["E501"]
fixable = ["E4", "E7", "E9", "F", "I"]

[format]
# Like Black, automatically detect the appropriate line ending.
line-ending = "lf"
・・・
(.venv) ibismark@ibismark-win:~/workspace/otam$
(.venv) ibismark@ibismark-win:~/workspace/otam$ which ruff
/home/ibismark/workspace/otam/.venv/bin/ruff
(.venv) ibismark@ibismark-win:~/workspace/otam$
  • excludeにはruffの解析対象外にしたいディレクトリやファイルを指定。

  • target-versionには使用しているpythonバージョンを指定。

  • selectには解析に使用するルールを指定。デフォルトは["E4", "E7", "E9", "F"]なのだが、以下のようにカスタマイズしている。

    • E、F、W: flake8

    • I:isort

    • DJ:flake8のdjango拡張

    • B:flake8拡張で、バグの危険性のあるコード検知

    • N:PEP8に従っていない変数/クラス名の検知

  • ignoreにはselectしたルールの中で例外的に適用したくないルールを指定。

    • E501はPEP8のルールで、「一行最大79文字まで」、という制約だ。なのだが世界一守られていないルールなので今回のPJでも適用外にする((

  • fixableは、ルール違反が見つかった場合にruffで(可能な場合に)自動的に修正するルールの対象を指定している。

    • ruffのデフォルト設定に対して、I(isort)を追加している。

  • line-endingは行末の改行コードルールを指定。

    • 今回はLF(\n)を採用。

実際にRuffを使用してみる

コード違反しているpythonコード(test_ruff.py)を作成してみた。

(.venv) ibismark@ibismark-win:~/workspace/otam$ vi test_ruff.py
import os

def testFunction():
  tmp=123
  print("This is a test function")
  if tmp == 123:
    print("tmp is 123")
  return tm

def another_test_function():
    tmp=456
    print("This is another test function")
    if tmp==456:
        print("tmp is 456")
    return tmp

if __name__=="__main__":
    testFunction()
    another_test_function()

これは以下のような違反が見られる。

  • コーディング違反

    • 関数名のtestFunctionが慣例に従っていない(小文字のスネークケースを使用すべき。=> def test_function)

    • 不要なインポート(osは使用されていない)

    • 8行目のtm変数が未定義

  • フォーマット違反

    • 7行目のprintはインデントが浅い (インデントは空白文字4つで行うべき)

    • 演算子の前後に適切なスペースがない(tmp=123はtmp = 123にすべき)

    • if文の条件式に適切なスペースがない(if Tmp == 456にすべき)

以下のコマンド(checkオプション)でコーディング違反を検出でき、実際に4つのerrorが検出されているのが分かる。

(.venv) ibismark@ibismark-win:~/workspace/otam$ ruff check --config="ruff.toml" test_ruff.py 
test_ruff.py:1:1: I001 [*] Import block is un-sorted or un-formatted
test_ruff.py:1:8: F401 [*] `os` imported but unused
test_ruff.py:3:5: N802 Function name `testFunction` should be lowercase
test_ruff.py:8:10: F821 Undefined name `tm`
Found 4 errors.
[*] 2 fixable with the `--fix` option.
(.venv) ibismark@ibismark-win:~/workspace/otam$ 

--fixオプションをつけると、前述したruff.tomlのfixableに設定していたルールの中で可能なものは自動で修正してくれる。
実際に実行してみると以下のように、不要だったimport os文がコード上から削除され、ruffによるエラーも2つに減少していることが分かる。

(.venv) ibismark@ibismark-win:~/workspace/otam$ ruff check --fix --config="ruff.toml" test_ruff.py 
test_ruff.py:2:5: N802 Function name `testFunction` should be lowercase
test_ruff.py:7:10: F821 Undefined name `tm`
Found 3 errors (1 fixed, 2 remaining).
(.venv) ibismark@ibismark-win:~/workspace/otam$ 
(.venv) ibismark@ibismark-win:~/workspace/otam$ cat test_ruff.py 

def testFunction():
  tmp=123
  print("This is a test function")
  if tmp == 123:
    print("tmp is 123")
  return tm

def another_test_function():
    tmp=456
    print("This is another test function")
    if tmp==456:
        print("tmp is 456")
    return tmp

if __name__=="__main__":
    testFunction()
    another_test_function()
(.venv) ibismark@ibismark-win:~/workspace/otam$ 

残った以下のコーディング違反は自動では解決できないので手動で解決することになる。
 ・関数名のtestFunctionが慣例に従っていない(小文字のスネークケースを使用すべき。=> def test_function)
 ・8行目のtm変数が未定義
以下のように手動修正し、再度ruff checkを実行してみるとエラーがなくなったことが分かる。

(.venv) ibismark@ibismark-win:~/workspace/otam$ cat test_ruff.py 

def test_function():
  tmp=123
  print("This is a test function")
  if tmp == 123:
    print("tmp is 123")
  return tmp

def another_test_function():
    tmp=456
    print("This is another test function")
    if tmp==456:
        print("tmp is 456")
    return tmp

if __name__=="__main__":
    test_function()
    another_test_function()
(.venv) ibismark@ibismark-win:~/workspace/otam$ 
(.venv) ibismark@ibismark-win:~/workspace/otam$ ruff check --config="ruff.toml" test_ruff.py 
(.venv) ibismark@ibismark-win:~/workspace/otam$ 

最後にフォーマット違反を修正する。
次のコマンド(ruff format)を使うとフォーマット違反を自動検出し、自動修正してくれる。
結果を見ると式の前後に空白が挿入され、インデントも適正になったのが分かる。

(.venv) ibismark@ibismark-win:~/workspace/otam$ ruff format --config="ruff.toml" test_ruff.py 
1 file reformatted
(.venv) ibismark@ibismark-win:~/workspace/otam$ 
(.venv) ibismark@ibismark-win:~/workspace/otam$ cat test_ruff.py 
def test_function():
    tmp = 123
    print("This is a test function")
    if tmp == 123:
        print("tmp is 123")
    return tmp


def another_test_function():
    tmp = 456
    print("This is another test function")
    if tmp == 456:
        print("tmp is 456")
    return tmp


if __name__ == "__main__":
    test_function()
    another_test_function()
(.venv) ibismark@ibismark-win:~/workspace/otam$ 

vscodeのRuff拡張の設定を行う

今回はカスタム設定であるruff.tomlを作成したため、設定ファイルを参照させるようにvscodeの設定も行う必要がある。
vscodeのユーザー設定ファイルである、settings.jsonに以下を記載しよう。

VSCodeのsettings.jsonファイルを開くには、以下の手順に従ってください。
1. コマンドパレットを開く: VSCodeでCtrl + Shift + P(Windows/Linux)またはCmd + Shift + P(Mac)を押して、コマンドパレットを開きます。
2. 設定ファイルを検索: コマンドパレットに「Open Settings (JSON)」と入力し、該当するオプション「Preferences: Open Settings (JSON)」を選択します。
3. settings.jsonを編集: これにより、VSCodeのsettings.jsonファイルがエディタに表示されます。ここで、設定を直接編集することができます。
また、VSCodeの左下にある歯車アイコン(設定)からもsettings.jsonにアクセスできます。

chatgpt
{
    /* Ruff for python */
    "ruff.lint.enable": true,
    "ruff.lint.args": [
        "--config=ruff.toml"
    ],
    "ruff.importStrategy": "fromEnvironment",
    "[python]": {
        "editor.formatOnSave": true,
        "editor.codeActionsOnSave": {
            "source.fixAll": "explicit",
            "source.organizeImports": "explicit"
        }
    }
}

設定内容を少し説明すると、以下だ。

  • ruff.lint.enable:Ruff拡張機能を有効化する

  • ruff.lint.args:Ruff実行時の引数を設定する。ここにruff.tomlの設定をロードするように引数を追加している。ruff.tomlはvscodeに読み込ませているプロジェクトルートからの相対パスを書いておくのが良いだろう。

  • ruff.importStrategy:fromEnvironmentを設定すると、選択しているpythonインタプリタの環境上にruffが存在すれば使用して、無ければRuff拡張に着いてくるものが使用される。

  • [python]:pythonファイルにのみ適用する設定

    • editor.formatOnSave:vscodeでファイルを保存した際にruff check/formatが自動実行される。

    • editor.codeActionsOnSave:

      • source.fixAll:ファイル保存時に検出だけでなく自動修正を行う

設定後、先程の修正前のtest_ruff.tomlをvscodeで開くとエディタ上で指摘を行ってくれて便利。

いいなと思ったら応援しよう!