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は構文エラーを検出するだけでなく、PEP8に従っていないコードを解析して指摘してくれる。
vscodeにおけるRuff拡張機能は、Ruffパッケージ自体を内包しているため拡張機能をインストールするだけで使えるようになるが、手動でインストールもしてみよう。
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に以下を記載しよう。
{
/* 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で開くとエディタ上で指摘を行ってくれて便利。