VSCodeのPython環境設定[poetry+ruff,mypy]

VScodeでのPython環境の設定について、個人的な備忘録になります。

今回のタイトル通りですが、VScodeでのPython環境の設定を整理しました。
内容としてはVSCodeを入れた後に１から設定をしていきましょう・・・になるかと思います。

以前似たような記事を投稿しましたが、古かったり使えなくなっていたりなので改めて整理しました。
合わせて、ruff/mypyで静的解析・自動整形も確認しています。エディタ上で自動整形できるのは嬉しい。

(pycharmにせろと言われるかもしれないんですが、同じリポジトリのpythonコード以外のファイルもいじってるとvscodeとpycharm両方開くのもね・・・。)


# 前提条件(ディレクトリ構成)

(ディレクトリ構成なので、読み飛ばしてOK)
以下のディレクトリ構成にします。
（プロジェクトに合わせて配置箇所調整）

```
.
├── .vscode # ワークスペースから1階層下にpythonプロジェクトを用意
│   └── settings.json # python環境やコマンドのパス, tomlファイルのパスの指定を行う
├── src # ワークスペースから1階層下にpythonプロジェクトを用意
│   ├── src_impl # ソースファイル一式入れる場所(AWS Lambda考慮する場合はsrcの下に直接おかないケースもある)
│   │   └── handler.py
│   ├── poetry.lock
│   ├── poetry.toml # poetry config --listのlocal設定
│   ├── pyproject.toml # プロジェクトで利用するpythonパッケージの指定など
│   └── tests
│       ├── __init__.py
│       ├── pytest.ini
│       └── テストファイル
├── .git # ワークスペース直下がgitトップディレクトリとする(一段掘ることも可能かも。その場合はsettings.jsonを調整)
└── pyproject.toml # 静的解析ツールの挙動や設定など
```



# 事前準備

VSCodeでPythonのコーディングをする際に必要となる拡張機能や設定、ツールなどを準備しておきます。

## pyenv
プロジェクトごとのpythonバージョン切り替えで利用。
プロジェクトによっては、Pythonのバージョンが違うケースがあるので、ディレクトリごとまたはグローバルにpythonのバージョンの切り替えが可能なpyenvをインストールしておくと良いかと思います。

[pyenvのインストール](https://github.com/pyenv/pyenv#installation)

### よく使いそうなコマンド

```
$ pyenv install 3.x.y

# グローバルにpythonバージョン切り替え
$ pyenv global 3.x.y

# localディレクトリのpythonバージョン切り替え(対象のディレクトリにて実施)
$ pyenv local 3.x.y
```

## poetry
パッケージ管理ツール。

類似としてpipenvがあります。
pipenv install/lockが遅かったり、pipenv.lockがおかしくなったりしたのですが、poetry利用してから使い勝手が良い感じなので最近はpoetryを主流で利用しています。

poetryの難点はCI/CDのビルドコンテナに標準でインストールされていないことか・・・。(CI/CDのコンテナではpipenvは入っていることが多いがpoetryはないので、ツールとしてのインストールから実行する必要がある。pipenvの強みかもしれない・・・？)

[poetryのインストール](https://python-poetry.org/docs/#installation)

### よく使いそうなコマンド

```
# python仮想環境で利用するバージョン指定
$ poetry env use `which python`

# python仮想環境を設定し、パッケージをインストール
$ poetry install

# python仮想環境をディレクトリ配下に作成する(対象ディレクトリにのみ適用)
$ poetry config --local virtualenvs.in-project true

# pythonスクリプト実行(tomlにスクリプト名指定してpoetry run スクリプト名というやり方もある模様)
$ poetry run python スクリプトファイル名

# そのほか
$ poetry env list
$ poetry config --list
```

pyenvもpoetryもPATHは通しておく。


## インストールする拡張機能

VScodeの拡張機能をインストールしておきます。

### ruff (charliermarsh.ruff)

静的解析・自動整形ツールとして、Linter(flake8)やFormatter(black)、Import Soter(isort)を使うことが多いかと思いますが、最近Ruffという静的解析ツールが最近人気が出てきているようです。

flake8,black,isortの調整していた時に廃止済みだったりsettings.jsonに記載が必要だったりで悩んでいたところ、静的解析・自動整形ツールの機能を全て有しているruffで代替可能だったため、確認していました。
(乱暴な書き方をすると、flake8,black,isort全部入り・・・?ただPEP8の命名規則に従っているかのチェックはまだ未確認)

settings.jsonやtomlファイルに適切な設定を入れておくと、自動整形する上に、コード上おかしいところをアラートしてくれるのでこれはいいなと思いました。


### mypy (ms-python.mypy-type-checker)

Pythonの型チェッカー。pythonの戻り値の型やAnyの扱いについてチェックしてくれます。

ruffはPEP8のコードに従っているかチェックしてくれるありがたい静的解析ツールですが、型チェッカーの機能はないようなので、mypyも併用しておきます。

### Python Test Explorer for Visual Studio Code(LittleFoxTeam.vscode-python-test-adapter)

VScodeでUTを実行する際に必要となるので入れておく。

#### Error Lens(usernamehw.errorlens)

VSCodeでのコーディング中にシンタックスエラーがあると警告してくれます。
ruffのエラーも拾ってくれるので、ruffコマンドに通さなくてもコーディング中にエラーがわかるのがありがたい。
必須ではないけど入っていると便利。(個人的には入れるべきだなと思った）


### indent-rainbow (KevinRose.vsc-python-indent)

必須ではないけど、インデントの階層ごとに色表示されるので、実装の手助けになるかもしれないので入れておきます。

### Trailing Space(shardulm94.trailing-spaces)

行末尾にスペースがあると色でわかりやすく指摘してくれます。
自動整形設定により、行末尾のスペースは自動消去してくれるので、どちらかというとその他ファイルで利用するケースの方が多いかも。

### prettier (esbenp.prettier-vscode)

JSONやtypescriptファイルのフォーマッタとして利用。
Python開発環境というよりは、その他用途で入れてます。

## 無効化する拡張機能

ruffと衝突する可能性があるので、flake8,isort,blackなどの拡張機能は停止しておく。
(入れていても害はないかもしれないが、未確認)

こちらの拡張機能を利用する場合、静的解析ツールの挙動はsettings.jsonにて指定が必要で、tomlファイルから分離されてしまうので、pycharmとの整合性が取れなくなりそうなのが悩みポイントになりそうかなと思いました。

ms-python.black-formatter
ms-python.flake8
ms-python.isort

ruffとそのほかの静的解析ツールの併用も可能らしい。


# プロジェクトごとの設定

プロジェクトごとにパスやルールなどの調整があれば、個別に設定していく。

## VSCodeの設定

ワークスペース用の設定サンプル。
venvやsrcのファイルパス指定箇所があるので、プロジェクトに応じて修正しておく。
pythonのインタプリターもpoetryでインストールした仮想環境配下のパスで指定しておく。

python.autoComplet.extraPathsは入れておかないとimportしたパッケージなどの補完が効かないので入れておくと良い。


```.vscode/settings.json
{
  "[typescript]": {
    "editor.formatOnSave": true,
    "editor.defaultFormatter": "esbenp.prettier-vscode",
    "editor.codeActionsOnSave": {
      "source.organizeImports": true
    },
  },
  "importSorter.generalConfiguration.sortOnBeforeSave": false,
  "[python]": {
    "editor.formatOnSave": true,
    "editor.codeActionsOnSave": {
      "source.organizeImports.ruff": true,
      "source.fixAll.ruff": true,
    },
    "editor.defaultFormatter": "charliermarsh.ruff"
  },
  "python.defaultInterpreterPath": "${workspaceFolder}/src/.venv/bin/python",
  "ruff.path": [
    "${workspaceFolder}/src/.venv/bin/ruff"
  ],
  "ruff.lint.args": [
    "--config=${workspaceFolder}/pyproject.toml",
  ],
  "mypy-type-checker.path": [
    "${workspaceFolder}/src/.venv/bin/mypy"
  ],
  "mypy-type-checker.args": [
    "--config=${workspaceFolder}/pyproject.toml"
  ],
  "python.analysis.autoImportCompletions": true,
  "python.analysis.completeFunctionParens": true,
  "python.analysis.extraPaths": [
    "${workspaceFolder}/src/.venv/lib/python3.10/site-packages/",
    "${workspaceFolder}/src/src_impl/"
  ],
  "python.autoComplete.extraPaths": [
    "${workspaceFolder}/src/.venv/lib/python3.10/site-packages/",
    "${workspaceFolder}/src/src_impl/"
  ],
  "python.testing.pytestArgs": [
    "src/tests"
  ],
  "python.testing.unittestEnabled": false,
  "python.testing.pytestEnabled": true
}
```

メモ：
- 補完は基本設定->キーボードショートカットから、「候補をトリガー」に、適当なキーをいれておく
- 入力中の関数などにカーソルがあるときに「macOS:Shift+⌘+Space」「Windows:Shift+Ctrl+Space」を入力すると、関数などの説明をしてくれる

## poetryでインストールするパッケージ

Pythonのパッケージ管理の準備を進めていきます。

```
# pyproject.tomlの初期化,プロンプトが出てくるので環境やプロジェクトに合わせて入力していく
poetry init
# This command will guide you through creating your pyproject.toml config.
# Package name [src]:  
# Version [0.1.0]:  
# Description []:  
# Author [....], n to skip]:  n
# License []:  
# Compatible Python versions [^3.10]:  
# Would you like to define your main dependencies interactively? (yes/no) [yes] no
# Would you like to define your development dependencies interactively? (yes/no) [yes] no
# Generated file

# プロジェクトで利用する基本的なパッケージをインストール
poetry add boto3

# テストや開発で利用するパッケージをインストール(vscodeから参照されるため必要となる)
poetry add --group dev ruff
poetry add --group dev mypy
poetry add --group dev pytest
poetry add --group dev pytest-env
```

こんなpyproject.tomlができるので、poetry.lockと一緒にバージョン管理しておく。

```pyproject.toml
[tool.poetry]
name = "src"
version = "0.1.0"
description = ""
authors = ["Your Name <you@example.com>"]
readme = "README.md"

[tool.poetry.dependencies]
python = "^3.10"
boto3 = "^1.28.78"


[tool.poetry.group.dev.dependencies]
ruff = "^0.1.4"
mypy = "^1.6.1"
pytest = "^7.4.3"
pytest-env = "^1.1.1"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
```


## 静的解析ツールの設定

settings.jsonで指定した設定ファイルを元に静的解析ツールの挙動の調整が可能なため、静的解析用の設定ファイルを用意する。
設定ルールはプロジェクトに合わせて調整する。

poetry用のpyproject.tomlと一緒にできるが、その場合はsettings.json上のファイルパスの指定を修正する。(記載がない場合はワークスペース直下のpyproject.tomlが参照される。ルール間違えると警告が出なくなる=機能しない?ようなので注意は必要かも)

全てのpythonソースファイルに対して準拠しているかチェックされるが、ファイル単位に例外を指定することも可能な模様。(tool.ruff.per-file-ignoresでファイル名=[]でignoreの指定が可能っぽい)

```pyproject.toml
[tool.mypy]
show_error_context = true
show_column_numbers = true
ignore_missing_imports = true
disallow_untyped_defs = true
no_implicit_optional = true
warn_return_any = true
check_untyped_defs = true


[tool.ruff]
exclude = [
    ".venv",
    "venv",
    "__pycache__",
    ".git",
]

line-length = 88
indent-width = 4


[tool.ruff.lint]
# PEP8のどのルールを対象に警告を出すか
select = [
    "C",
    "E",
    "F",
    "W",
]
ignore = [
]
fixable = ["ALL"]


[tool.ruff.format]
quote-style = "double"

[tool.ruff.lint.mccabe]
# if,forなどの複雑性をどれくらい許容するか。if,for文が11以上あると警告
# C901
max-complexity = 10

```

設定内容はここらへんが参考になるかも。
https://docs.astral.sh/ruff/configuration/#using-pyprojecttoml
https://docs.astral.sh/ruff/settings/

### Pythonプロジェクト直下で静的解析ツール実行

```
cd src/
poetry run ruff .

# 例
# lambda_impl/dummy_data.py:12:89: E501 Line too long (111 > 88)
# lambda_impl/dummy_data.py:81:9: C901 `create_dummy_record` is too complex (6 > 3)
# lambda_impl/dummy_data.py:155:9: C901 `test` is too complex (7 > 3)

# lambda_impl/dynamotest.py:36:13: E722 Do not use bare `except`
# lambda_impl/handler.py:36:5: F811 Redefinition of unused `dynamotest` from line 24
```

エディタ上の該当箇所に同じ内容で警告表示される。カーソル当てるとコードも確認できる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280717/739737b1-b2d9-8d09-f0e7-e0a7a8a1d7aa.png)

(サンプルは適当ですまない・・・。max-complexity = 3にしているので、3超えると警告になる


# テスト設定(おまけ)

testsにテストコード用意しておくと、テストエクスプローラから参照できるようになる。

環境変数を利用する場合はpytest.envを利用する。
テストフォルダにpytest.iniを用意しておくとテスト実行時に読み込んでくれるので、必要に応じて用意する。

```pytest.ini
[pytest]
env =
    TEST_KEY=TEST_ENV
```

# 全体的なメモ

以下ファイルはバージョン管理対象から除外する。

.mypy_cache
.pytest_cache
.ruff_cache

他追加対応必要なものが出ればまとめていきたいと思います。