VS Code Remote ContainersとPylanceで快適Python環境を構築する方法

最近、チーム用のPython開発環境を構築する機会があり、VS Codeを使った環境構築がかなり便利だったので記事にしました。

## はじめに 

本記事では、VS CodeのRemote Containersを利用し、再現性が高く快適なPython環境を構築する方法を紹介します。

すでにあるRemote Containersの記事との違いは、Pythonを使ったチームでの開発を想定し、リンターや便利な拡張機能も環境構築に含めていることです。

今回紹介する内容を使えば、コードの自動フォーマットやコードの補完などが有効になる快適なPython開発が可能です。

Pythonの実装のコツやノウハウについては、同じチームの@sugulu_Ogawa_ISIDさんの記事が参考になります。

- [AIエンジニアが気をつけたいPython実装のノウハウ・コツまとめ](https://qiita.com/sugulu_Ogawa_ISID/items/c0e8a5e6b177bfe05e99)

## 便利なこと

本記事で紹介する方法の便利なところは以下の2点です

1. Pythonのバージョンや使用packageをチームで一元管理
2. VS codeで使用するリンターや拡張機能をチームで統一

一つ目は、ほぼDockerのメリットになりますが、anacondaやpyenvやvenvのようなバージョン管理やpackage管理で悩まなくてすみます。そして、新しくチーム用の開発環境を作る際に、各々のローカル環境に左右されずDockerが使えさえすればOKとなるので、開発初期の環境構築作業が非常に楽になります。

二つ目は、コード規約や拡張機能の知見を共有できることです。私の周囲では、開発用エディタはVS Codeを利用していることが多く、エディタは統一されています。しかし、VS Codeの設定にはかなり個人差があります。VS Codeで提供されている機能を使いこなしているか否かで作業効率がかなり変わるので、できればVS Codeの環境自体も共有したいです。

今回紹介するRemote Containersでは、設定ファイルさえ渡せば上記2点が簡単にチームで共有できます。

## Remote Containersを使った環境構築方法

必要なものはざっくり以下の5つです。

- Visual Studio Code
- Docker
- Remote Containers拡張機能
- devcontainer.json
- Dockerfile

VS CodeとDockerのインストール方法はここでは紹介しません。Remote ContainersはVS Codeの拡張機能からインストールできます。

- [Remote Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)

Remote Containers機能を使うには作業フォルダ内に`.devcontainer/devcontainer.json`が必要です。この`devcontainer.json`がRemote Containers機能の設定ファイルに該当します。

今回はテスト用に以下のようなプロジェクトを使います。
（[公式のPythonテンプレート](https://github.com/microsoft/vscode-remote-try-python)を参考にしています）

```bash
.
├── .devcontainer
│   ├── Dockerfile
│   └── devcontainer.json
├── .gitattributes
├── .gitignore
├── requirements.txt
└── test.py
```

Dockerfileの中身は以下になります。ほぼ公式のDockerfileのままです。

```Docker
ARG VARIANT="3"
FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT}

ENV PIP_TARGET=/usr/local/pip-global
ENV PYTHONPATH=${PIP_TARGET}:${PYTHONPATH}
ENV PATH=${PIP_TARGET}/bin:${PATH}
RUN if ! cat /etc/group | grep -e "^pip-global:" > /dev/null 2>&1; then groupadd -r pip-global; fi \
    && usermod -a -G pip-global vscode \
    && umask 0002 && mkdir -p ${PIP_TARGET} \
    && chown :pip-global ${PIP_TARGET} \
    && ( [ ! -f "/etc/profile.d/00-restore-env.sh" ] || sed -i -e "s/export PATH=/export PATH=\/usr\/local\/pip-global:/" /etc/profile.d/00-restore-env.sh )

COPY requirements.txt /tmp/pip-tmp/
RUN pip3 --disable-pip-version-check --no-cache-dir install -r /tmp/pip-tmp/requirements.txt \
    && rm -rf /tmp/pip-tmp

```

次は肝心の`devcontainer.json`の中身です。こちらも基本的に[公式のPythonテンプレート](https://github.com/microsoft/vscode-remote-try-python)に倣ってます。

```json
{
	"name": "Python 3",
	"build": {
		"dockerfile": "Dockerfile",
		"context": "..",
		// Update 'VARIANT' to pick a Python version: 3, 3.6, 3.7, 3.8 
		"args": {
			"VARIANT": "3"
		}
	},
	"workspaceMount": "source=${localWorkspaceFolder},target=/workspaces,type=bind,consistency=delegated",
	"workspaceFolder": "/workspaces",	
	"settings": {
		"terminal.integrated.shell.linux": "/bin/bash",
		"python.pythonPath": "/usr/local/bin/python",
		"python.linting.enabled": true,
		"python.linting.pylintEnabled": true,
		"python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8",
		"python.formatting.blackPath": "/usr/local/py-utils/bin/black",
		"python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf",
		"python.linting.banditPath": "/usr/local/py-utils/bin/bandit",
		"python.linting.flake8Path": "/usr/local/py-utils/bin/flake8",
		"python.linting.mypyPath": "/usr/local/py-utils/bin/mypy",
		"python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle",
		"python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle",
		"python.linting.pylintPath": "/usr/local/py-utils/bin/pylint",
		"python.analysis.typeCheckingMode": "basic",
		"python.analysis.completeFunctionParens": true
	},
	"extensions": [
		"ms-python.python",
		"ms-python.vscode-pylance",
		"ms-vsliveshare.vsliveshare",
		"njpwerner.autodocstring"
	],
	"remoteUser": "vscode"
}
```

`workspaceMount`では、マウントしたいローカルフォルダとコンテナ内のマウント先を指定しています。`${localWorkspaceFolder}`で`.devcontainer/devcontainer.json`を含むフォルダのパスを指定できます。

settingsの内容はRemote Containersで開いた時のVS Codeの設定です。

そして、extensionsに拡張機能を追加することで、Remote Containers内で指定した拡張機能がインストールされます。これにより、「2. リンターや拡張機能を簡単にチームで共有できる」を実現できます。

VS codeでPythonを開発するにあたりぜひ入れて欲しい拡張機能は`ms-python.python`、`ms-python.vscode-pylance`、`njpwerner.autodocstring`です。（`ms-python.vscode-pylance`を使うには`ms-python.python`が必須です）

`ms-python.vscode-pylance`（`Pylance`）は後述しますが、VS CodeでのPython開発を強力にサポートしてくれる拡張機能です。`njpwerner.autodocstring`（`Python Docstring Generator`）はDocstringを生成してくれる拡張機能です。

## Remote Containersの使い方

ここまでの設定が完了すればあとは起動するだけです。
起動するには、VS Codeの左下の緑のアイコンをクリックします。

![スクリーンショット 2021-02-13 21.30.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/576030/72c5aa7e-8004-8649-6059-9dec122e55ed.png)

アイコンをクリックすると以下のような選択肢が出るので、`Remote-Containers: Open Folder in Container`を選択し、`.devcontainer`があるフォルダを選択します。

![スクリーンショット 2021-02-13 21.31.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/576030/626d6e99-3168-d498-6f3c-d529221afb98.png)

これで完了です。あとはローカルで開発するのと同じように作業できます。

## Pylanceの使い方

続いて、拡張機能としてインストールしたPylanceについて紹介します。

Pylanceの提供する主な機能は以下になります。

> - Docstrings
- Signature help, with type information
- Parameter suggestions
- Code completion
- Auto-imports (as well as add and remove import code actions)
- As-you-type reporting of code errors and warnings (diagnostics)
- Code outline
- Code navigation
- Type checking mode
- Native multi-root workspace support
- IntelliCode compatibility
- Jupyter Notebooks compatibility
- Semantic highlighting

いろいろ便利な機能が提供されていますが、特に便利なのはコードの補完、Docstrings表示と型チェックです。
本記事ではチーム開発で役立つDocstrings表示と型チェックを紹介します。

まずはDocstringsについてです。
これはDocstringsで書かれた内容を該当コードにホバーすると以下のように表示される機能です。
(Docstringsとは、関数やクラスの説明を記述したものです。)

![スクリーンショット 2021-02-13 1.11.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/576030/7690b26e-cbc0-c727-ebc2-5254010c5395.png)

この機能があると引数や返り値などについて確認する時間を短縮することが可能です。
Docstringsを書くのはちょっと面倒だったりするのですが、先ほどインストールした`Python Docstring Generator`を使えばDocstringsを書く手間を短縮できます。

Macであれば、Docstringを挿入する場所で`cmd + shift + 2`とすると、以下のようにテンプレートを作成してくれます。
![スクリーンショット 2021-02-13 1.16.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/576030/aa798814-6fde-d03a-4db1-9aed93970534.png)

Docstringのテンプレートは以下から選択できます。
（[Python Docstring Generator](https://marketplace.visualstudio.com/items?itemName=njpwerner.autodocstring)）

>Google (default)
docBlockr
Numpy
Sphinx
PEP0257 (coming soon)

次に型チェックです。

Pythonでは、変数の型が一致しなくても実行できてしまいます。そのため、本来期待していた型と異なる型で実装してしまうことがあります。型指定があるとこのような間違った型の実装を防ぐことができ、特に複数人かつ複雑な構成で開発する場合は便利です。

Pylanceの`Type checking mode`を設定すると、以下のようにコードを書いている途中に型の間違いに気づくことができます。
![スクリーンショット 2021-02-13 12.10.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/576030/4efda8d8-71ac-df2a-47ba-444e29f8d5e2.png)

`Type checking mode`では`off`、`basic`、`strict`と型チェックの厳しさを選択できます。
`strict`にすると型の指定がないだけでもエラー判定されます。

開発チーム次第ではありますが、基本的には`basic`で問題ないと思います。
この設定は、`devcontainer.json`の`"python.analysis.typeCheckingMode": "basic"`で変更できます。

## 終わりに

本記事では、VS CodeのRemote Containers機能を用いたPythonの開発環境構築方法を紹介しました。
ちなみに、本記事では紹介しませんでしたがJupyter Notebookもインストールするだけで今回の設定で使えます。
他にもsort importやimport文の自動追加や自動削除といった紹介しなかった便利機能もあります。

また、拡張機能についても今回は必要最低限しか記載していませんが、他にも便利なものがたくさんありました。
こちらも今後まとまったら記事にしようと思います。

## 参考

- [Developing inside a Container](https://code.visualstudio.com/docs/remote/containers#_getting-started)

- [Remote development in Containers](https://code.visualstudio.com/docs/remote/containers-tutorial)

- [Advanced Container Configuration](https://code.visualstudio.com/docs/remote/containers-advanced)

- [devcontainer.json reference](https://code.visualstudio.com/docs/remote/devcontainerjson-reference#_variables-in-devcontainerjson)

- [Pylance](https://marketplace.visualstudio.com/items?itemName=ms-python.vscode-pylance)

- [VS CodeでDocker開発コンテナを便利に使おう](https://qiita.com/Yuki_Oshima/items/d3b52c553387685460b0)