Dev Containerで最強のPython開発環境を作る 〜その2・拡張機能とパッケージ管理〜

こんにちは。
各位お世話になっております。 [Intimate Merger](https://corp.intimatemerger.com/) のitayaです。
前回の記事の続きとなります。

::: note info
前回記事
[Dev Containerで最強のPython開発環境を作る 〜その1・Pythonスクリプトを動かす〜](https://qiita.com/im-dev/items/a2eb4ebba6b6df1e0521)
:::

これまでの記事ではDev ContainerでPythonスクリプトを動かすところまで進めました。

Dev Containerでコードを書くこともデバッグをすることもできるようになりましたが、開発をするにあたって最低限準備ができたかと言われると少し足りないと感じられる方も多いと思います。

例えば、VSCodeで開発をしているならばお気に入りの拡張機能はいくつかあるはずです。
開発においてPythonパッケージも欠かせません。何もインストールせず書くことは稀でしょう。

より豊かな開発環境を目指して、拡張機能とパッケージ管理の設定をしていきます。

今回は

- **Dev Containerへの拡張機能の導入**
- **パッケージマネージャーの導入**

です。

# 拡張機能

どの環境でも入れたいお気に入りの拡張機能は誰にでもあるとは思いますが、私にとってはGit Graphがそれです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1560575/ac2954a8-4e78-dced-10c4-9a20f45c5975.png)

ここからはGit Graphを例として、Dev Contaienerに拡張機能をインストールする方法を説明していきます。

## devcontainer.jsonに追記する

拡張機能の歯車マークを押してみると `devcontainer.json に追加` という項目があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1560575/3ab74ccf-44e4-5193-9a13-2367ef5e2b1c.png)

これを押すことで、`devcontainer.json` が更新され、自動でインストールされる拡張機能に加えることができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1560575/aac1a7de-82cf-5213-a688-b1ed2c99282e.png)

簡単ですね。これだけで大丈夫です。

しかし、Git Graphは開発に必須なものではありません。採用するのは趣味の範囲です。必要ない人も、もっと別の拡張機能を使う人もいるでしょう。
開発組織の中でこの要望を満たすにはどうするのがいいでしょうか。

## ユーザー設定に追記する

自分が使う拡張機能は自分だけの設定に記載するのが最も平和的でしょう。
`defaultExtensions` でユーザー設定を検索すると、Dev Container用の拡張機能設定があることがわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1560575/499db6fe-e8a0-dd91-7161-f9d8a1a52a47.png)

こちらに拡張機能IDを記載していきましょう。
拡張機能IDは `devcontainer.json に追加` の項目があった場所でコピーできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1560575/6dda18d9-a9ef-9042-cc1c-f167d13e3ce5.png)

これでOKです。
各々が開発するにあたり便利な拡張機能を追加していきましょう。

# Python パッケージのインストール

Dev Containerにパッケージをインストールする手法としては、大きく分けて2つあると認識しています。
`postCreateCommand` を利用する方法と、Dockerfileを利用する方法です。

## postCreateCommand

`postCreateCommand` は `devcontainer.json` にある設定の一つです。
Dev Containerのimage作成後に1度だけ実行されるコマンドを設定することができます。

```devcontainer.json
{
	"name": "Python 3",
	"image": "mcr.microsoft.com/devcontainers/python:1-3.11-bullseye",
	"postCreateCommand": "pip install -r requirements.txt"
}
```

これでも問題なく動くのですが、利用してみるとこの手法が問題となるパターンがあります。

よく遭遇するパターンとしては、build→起動後に `postCreateCommand` が動作するため、Dev Containerで操作できるようになった後もパッケージのインストールが終わっていない現象です。
デバッグしたいにも関わらずインストールの完了を待たなければなりません。

開発初期であればこのパターンで問題ないことも多いと思うので、許容できる範囲であればこちらでも大丈夫です。
ですが、問題が目立ってきたら開発用のDockerfileを用意することになるでしょう。

## Dockerfile

Dockerfileを利用する場合は先ほどの問題は解決できます。
結果としてのbuild時間が同じでも完全に準備できた状態で開いてくれるのは大きな違いですし、さらなるカスタマイズを行うこともできます。

`devcontainer.json` にはimageを指定できるように、ビルドするDockerfileも指定することができます。

```json:.devcontainer/devcontainer.json
{
	"name": "Python 3",

	"build": {
		"dockerfile": "Dockerfile",
		"context": ".."
	}
}
```

```Dockerfile:.devcontainer/Dockerfile
FROM mcr.microsoft.com/devcontainers/python:1-3.10-bullseye

COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
```

Dev Containerで利用するdocker imageなので `.devconainter/` 内にDockerfileを置く方がわかりやすいでしょう。
`requirements.txt` は別の箇所でも使用することがあるため、rootに配置してあります。
`context` の設定も忘れないようにしましょう。


# パッケージ管理ツール

Dev Containerと組み合わせるなら、現状ではpipかpoetryを採用することになると思います。

## pip

pipはとにかくシンプルであり、Dev Containerにおいてはこれで事足りることも多いです。
microsoft謹製のimageにも最初からインストールされています。
requirements.txtを用意するだけでいいのは非常に楽です。

開発を始めるにあたり、管理しなければならないコンフィグが多いのは心理的なハードルが上がると感じます。
できるだけシンプルな構成を維持できるpipはいつでも候補に上がるツールです。

## poetry

pipとは逆に、シンプルな構成を維持できなくなるならpoetryを使う方が便利になるでしょう。
この場合の「シンプルでなくなる」とは、dev環境とprod環境でインストールするパッケージを分けたくなったぐらいからを指します。

poetryのinstallはofficial installerで行います。

https://python-poetry.org/docs/#installing-with-the-official-installer

```Dockerfile:.devcontainer/Dockerfile
FROM mcr.microsoft.com/devcontainers/python:1-3.10-bullseye

ENV POETRY_VIRTUALENVS_CREATE=false
ENV POETRY_HOME=/etc/poetry
ENV PATH=${POETRY_HOME}/bin:${PATH}

RUN curl -sSL https://install.python-poetry.org | python3 -
```

他にpipxを利用する手法が提示されていますが、Dockerにinstallするのであればofficial installerをオススメします。
docker build時にpipxでinstallしたpoetryは `/root/` 以下に配置されるため、Dev Containerでのvscodeユーザーでは触ることができず、この後に行う `poetry init` が失敗します。

このDockerfileでdev containerを起動し、poetryの初期設定を行ないます。
`poetry init` を実行すれば初期設定が開始されます。
対話形式で色々聞かれますが、デフォルト値でいい場合はEnterを押していくだけで大丈夫です。
結果として `pyproject.toml` が作成されます。

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

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


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

`poetry add` することで利用したいパッケージを追加していきます。
今回はfastapiと、開発用にflake8を導入してみました。

:::note warn
`vscode` userで `poetry add` するとPermission Errorが発生します。
`sudo su` することで一時的に `root` userとして実行してください。
:::

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

[tool.poetry.dependencies]
python = "^3.10"
fastapi = "0.104.1"


[tool.poetry.group.dev.dependencies]
flake8 = "^6.1.0"

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

ここまでの作業で `pyproject.toml` `poetry.lock` が作成されているはずです。
これをDev Containerで初期インストールされている状態にDockerfileを編集します。

```Dockerfile:.devcontainer/Dockerfile
FROM mcr.microsoft.com/devcontainers/python:1-3.10-bullseye

ENV POETRY_VIRTUALENVS_CREATE=false
ENV POETRY_HOME=/etc/poetry
ENV PATH=${POETRY_HOME}/bin:${PATH}

RUN curl -sSL https://install.python-poetry.org | python3 -

COPY pyproject.toml poetry.lock ./

RUN poetry install --no-interaction
```

`POETRY_VIRTUALENVS_CREATE` はpoetryの `virtualenvs.create` オプションをfalseにする環境変数です。
`poetry install` をする際にpoetryは仮想環境を作成しますが、Dev Containerで動作させる以上このオプションは不要なのでfalseとしています。

これでpoetryの設定は完了です。
pipに比べると多いように感じられますが、実際は `poetry init` をしているぐらいなので思った以上に簡単です。

# あとがき

これで拡張機能とパッケージ管理の設定が終わりました。かなり開発しやすくなりましたね。
僕の記事は少し空きますが、その3としてFastAPIなどのwebフレームワークを使う際にどうしたらいいかを書いていく予定です。

明日以降の記事も読んでいただけたらと幸いです！
明日はクローラーについての話だそうです！

https://qiita.com/advent-calendar/2023/intimatemerger

# 参考資料

- https://code.visualstudio.com/docs/devcontainers/containers#_always-installed-extensions
- https://code.visualstudio.com/docs/devcontainers/create-dev-container