UpNext2 開発記録#1 VSCodeにPython CI環境を構築

[UpNext2 開発記録#0][1]の続きです。今回は、Pythonでの前処理を作る上で、前回提示した目標C1〜C3を満足する環境セットアップを行います。なお、ローカル環境は、MacOS Catalinaです。

目標C1〜C3を再掲します。

項 | 項目 | 備考
---|---|---
C1|GitHubによる版数管理|従来は版数管理をしていなかった。今回は開発過程を記録公開するため、GitHubを利用する。なおV1のGitHub公開は開発完了後にファイルコピーをしただけである。
C2|VSCodeの利用|従来はFlutter/Dartだけで作っておりAndroidStudioを利用。今回は前処理にPythonも使う予定であり、よい機会なのでVSCodeで開発する。
C3|テストの記述|個人開発なのでテストは書いていない。しかしUpNextにおいて、ある程度の規模の開発となる中で複雑なバグを踏んだ時、テストの必要性は痛感していた。勉強を兼ねて、ちゃんとテストを書くことにする。

以下では順を追ってセットアップを概説します。

#1. GitHubの準備とVSCodeで扱うローカルリポジトリとの連携

いろいろ試してみたところ、うまくローカルとリモートを連携させるには、以下の手順がベストでした。

+ GitHub側で新規のリポジトリを作成
+ GitHub側でdevelopブランチを作成
+ ローカル側で作成したリポジトリをclone （ローカル側にmasterブランチが作成されてリモート側と紐付けされる）
+ git checkout -b develop origin/develop でローカル側のdevelopブランチを作成してリモート側と紐付け
+ ローカルリポジトリのフォルダをVSCodeから開く

これにより、VSCodeのUI上での操作でローカルリポジトリへのステージング〜コミット、リモートリポジトリへのPUSHまでができるようになります。（gitのコマンドでユーザ名等の設定が必要かもです）

なお、VSCodeのアドオンとして、GitGraph等を入れると楽しいです。

#2. リポジトリに保存しないファイルを.gitignoreで設定

いろいろなツールのワークディレクトリやファイル、開発用のワークや、公開したくない情報（自分自身で取得したAPIキーなど）を、リポジトリにPUSHしないようにします。そのためのしくみが、.gitignoreです。

#2.1. .gitignoreファイルの作成と内容定義

プロジェクトのルートディレトクリに.gitignoreファイルを作成します。中身は今のところ以下にしています。

```:.gitignore
.*
*cache*
*local*
*secret*
```

特に、.* と \*cache* は、必須でしょう。これが無いと、大量のシステムワークファイルやpython仮想環境の共通ライブラリなどが、リポジトリに上がってしまいます。

#2.2. .gitignore自身をPUSHしないようにする

これをするかは好みですが、.gitignore自身をPUSHしないようにすることが可能です。

プロジェクトホームの .git/config の [core] グループ内に、excludesfile = .gitignore を追記します。

#3. venvでPython仮想環境を構築

~~VScodeではPython環境を選択して使えるようになっていますが、いろいろ準備するにあたって、Pythonは3系がデフォルトで使われるようにしておきましょう。python -V として3系が出てくるようにパスを調整しておきます。pipも3系が使われるようにしておきます。~~（なお、最近のPythonでは、pipと直打ちすることは推奨されておらず、python -m pipとして使うようです）

Pythonはライブラリの版数依存が複雑であるため、アプリ毎に仮想環境を用意してライブラリセットを切り替えることが推奨されます。従来は、pythonバージョンそのものも含めて切り替える、pyenv と、virtualenvというものが使われていましたが、Python 3.6以降では推奨されていません。今は、venv を使うことが推奨されています。

プロジェクトホームで、python<font color=red>3.8</font> -m venv .venv としてvenv環境をセットアップします。<font color=red>3.8とバージョン付きでvenvコマンドを実行することで、狙ったバージョンのpython仮想環境を作ることができます。</font>.venvは他の名前でもよいですが、.gitignoreの対象になるように .で始まる名前がよいです。venv環境には様々な共通ライブラリがインストールされますので、.gitignoreにヒットしないと、大変なことになります。なお、venv環境を新規作成すると、pip含めてライブラリは初期状態になりますので、いろいろ指示にしたがって入れ直してください。

venv環境をセットアップした後は、VSCode上でvenv環境を選べるようになりますので、選んでおいてください（最初はステータスバーをクリックしても出てこず、コマンドパレットメニューのPython:Select Interpreterから選択する必要があるようです）。ここで選択すると、venvのactivateをVSCodeが自動でやってくれます。もちろん、ここまでの間にVSCodeのPython拡張機構をインストールする必要があります。

#4. pytest環境の構築

現在は、Python組み込みのunittestではなく、ライブラリ組み込みのpytestがよく使われるようです。コマンドパレットメニューのPython: Configure Testsからpytestを選択すると環境構築してくれます。この時、テストディレクトリは . Root directory を選択してください。

ソースとテストをきれいに管理するために、ディレクトリを以下のように分けます。なお、今回の計画ではPython以外でもコーディングを予定していますので、実際にはサブプロジェクトに分かれた構成になっています。

```
(Project root)/
　├ src - temp.py
　└ tests - test_temp.py
```

ここで、通常の相対パス指定でtest_temp.pyからtemp.pyをインポートすると、エラーが出てしまうという、ハマりが発生します。

「[Pythonでテストコードとテスト対象のコードを別ディレクトリに分けて置いたときに発生するModuleNotFoundErrorと格闘しました][2]」のアップデート版解決策を適用することで、エレガントに解決ができました。

以下が解決策となります。

+ testsフォルダに、空の \_\_init__.py ファイルを作成する
+ test_temp.pyにおけるtemp.pyのインポートを、プロジェクトホームからの相対パス指定とする

上記と、pytest設定でテストディレクトリを . Root directory としておくことが合わさり、正常にテスト実行ができるようになります（VSCodeのUIを使わずにtest_temp.py を直接実行するとインポートに失敗しますので注意）。なお、VSCodeを前提とした対応は、参照リンクにも掲載されておらす、今回、試行錯誤でできました。

#5. コーディング規約の設定

コマンドパレットメニューのPython:Select Linter で、flake8を選択しましょう。その他にもいろいろ設定あるかもしれませんが、ちょっと覚えていません。汗

#6. CIとしてローカルリポジトリCommit時の自動テストの設定

コーディング規約チェックはVSCodeでセーブ時に自動チェックができるのですが、テストは手動で実行させる必要があります。本来は、ローカルリポジトリのコミット時、リモートリポジトリへのPUSH時にテストの自動実行を行わせたいです。後者は、プルリクを出す側とコミッタが別の人になる場合などに便利で、以前はTravis CI、最近はGitHub Actionsを使うのが流行りのようです。

個人開発では、ローカルリポジトリのコミット時の自動テストだけでよさそうですので、それを設定してみます。ローカルgitコマンドのフック機能を使って、pre-commitのフックスクリプトでテスト実行させるのが、うまいやりかたのようです。プロジェクトホーム/.git/hooks/pre-commit を実行権限をつけて以下のように作成します。なお、このスクリプトはVSCodeの管轄外なので、activateを忘れずに書いておきましょう。

```sh:pre-commit
#!/bin/sh
source .venv/bin/activate
python -m pytest
```

これにより、VSCode上でローカルリポジトリにコミットする際に、自動的にテストが走って、テスト失敗時にはコミットがキャンセルされるようになりました。成功です。

[1]:https://qiita.com/toast-uz/items/4d127b5ec0aeea44f9fb
[2]:https://qiita.com/ftnext/items/0fa43030e6a9c28e1338