🐍

VSCodeとPoetryで作るPython開発環境

2022/05/01に公開

こんにちわ alivelimb です。
本記事ではプロジェクト毎に用意している Python 開発環境を紹介します。なお、本記事で紹介した環境構築の自動化スクリプトをgithubで公開しています。

概要

Python の開発環境は主に VSCode, pyenv, poetryを用いて用意しています。
pyenv は Python 自体のバージョン管理を行います。Python3.8 から 3.9 への切り替えもコマンド 1 つで行うことが可能です。Poetry は Python パッケージの管理を行います。これらに加えて VSCode の各種拡張機能を活用することで開発環境としています。

本記事で構築した環境デモ gif

vscode-image

デモで示していることは以下の通りです

  • リッチな予測変換
  • 自動インポート(pathlib.Pathをインポート)
  • ファイル保存時の静的解析 & 自動整形
  • Type Hint のチェック

Hello World という内容のファイルを新規作成する例を、Type Hint を活用するように書き換えています。デモに登場する pathlib の詳細については以前執筆したpathlib はいいぞを参照して下さい。

そもそも Docker 使えば良くない?

確かに Docker でコンテナ化してポータビリティを向上させるのは良いことだと思いますし、Docker を使う上では pyenv は不要でしょう。ただ、Poetry は Docker を使う上でも有効です。poetry は requirements.txt を出力することも出来るため、マルチステージビルドに利用することも可能です。

また、普段の業務では開発サーバに VScode で RemoteSSH で接続して開発していますが、執筆時点では RemoteSSH した上で、RemoteContainer を利用することは出来ません。この場合、後述する VSCode の拡張機能や静的解析が効かなくなるため、Poetry を利用することでこの問題を回避しています。

もちろんローカルで開発しており、RemoteSSH を利用する必要がなければ Remote Container で問題ありません。ただ、この場合でも Poetry の恩恵は受けられるので一読の価値はあるかと思います。

※2022.05.11 追記

この構成を決めた当時、(私の調査不足でなければ)リモートサーバのコンテナ環境にログインするには、コンテナに SSH 接続可能にしておく必要がありました。しかし、現在公式ドキュメントを確認したところ出来るようになっているようです。

pyenv で Python のバージョン管理

pyenvは Python 自体のバージョンの管理ができます。基本的な使い方は以下の通りです。

# pythonバージョンのインストール
pyenv install [pythonのバージョン]

# バージョンの切り替え(現在のディレクトリのみ変更する場合はlocal)
pyenv global [pythonのバージョン]

pyenv のメリットは以下だと思います。

  • Python のインストール方法を統一できる
  • プロジェクト毎にバージョンの切り替えが用意

Python 以外の言語も使う方に

Node であればnodeenvのように、pyenv と同等の OSS が他の言語にもあります。別々にインストールしても良いですが、anyenvを使うという方法もあります。

Poetry で Python のパッケージ管理

poetry は Python パッケージ(numpy とか)の管理ができます。

Poetry を選んだ理由は以下の 5 点です。

  • パッケージ毎のバージョンを明記して管理できる
  • 仮想環境を作成することができる
  • .venv ディレクトリを作成して VSCode に認識させることができる
  • requirements.txt をエクスポートできる
  • 本番用パッケージと開発用パッケージを分けて管理できる

パッケージ毎のバージョンを明記して管理できる

私が新人の頃は以下のように requirements.txt にパッケージ名のみを書き連ねていました。

numpy
pandas
matplotlib
(以下略)

しかしこれだとバージョンが書いてないので、最新版がインストールされてしまい不具合の原因になりかねません。もちろんpip freezeでエクスポートしたものにはパッケージのバージョンが指定してあるのですが、「あれ?こんなのいれたっけ?」というものまで入っています。これは最小単位まで分割されているからですね。

pip freeze > requirements.txt
pip install numpy pandas matplotlib
cycler==0.11.0
fonttools==4.33.3
kiwisolver==1.4.2
matplotlib==3.5.1
numpy==1.22.3
packaging==21.3
pandas==1.4.2
Pillow==9.1.0
pyparsing==3.0.8
python-dateutil==2.8.2
pytz==2022.1
six==1.16.0

poetry を使うと以下のようになります。

poetry add numpy pandas matplotlib
numpy = "^1.22.3"
pandas = "^1.4.2"
matplotlib = "^3.5.1"

poetry addで追加したパッケージのみ自動追記されるので、管理がしやすくなります。

仮想環境を作成することができる

pip installを利用すると原則グローバルな環境にパッケージがインストールされます。これは何が問題かというと、プロジェクト A, プロジェクト B で使っているパッケージの区別がつかなくなることが挙げられます。「今まではローカルで開発していたけど、本番環境で動かそう」となった時にpip freezeで出力して見たら使ってないパッケージまでインストールされてしまったということになりかねません。

そこで Poetry をはじめcondavenvでは仮想環境が用意されています。仮想環境を用いることでプロジェクト間の依存をなくすことが可能です。

.venv ディレクトリを作成して VSCode に認識させることができる

後述しますが、VSCode の拡張機能で静的解析やオートインポートを利用します。この時、どの Python パッケージがインストールされているかを VSCode 側に伝える必要があります。このため、仮想環境の情報が入った.venvディレクトリを作成できるパッケージを利用する必要があります。

Poetry では

poetry config virtualenvs.in-project true

を実行することで、.venvディレクトリの作成を有効化できます。

requirements.txt をエクスポートできる

Poetry を使っている分には原則必要にならないのですが、コンテナ化する時にはあったほうが良い機能だと思います。コンテナ自体がイミュータブルを想定しているため、パッケージ管理は特に必要ないでしょう。イメージサイズを無駄に増やすだけなら、むしろ削除したほうが良いと思います。

そこで Docker ビルドの際に、エクスポートした requirements.txt のみを渡すほうが軽量化できます。マルチステージビルドと組み合わせるのも良いでしょう。

本番用パッケージと開発用パッケージを分けて管理できる

これも必須ではないですが、コンテナ化する時にはあったほうが良い機能だと思います。pytest などの開発段階でしか使わず本番環境では利用しないパッケージも複数あります。イメージビルドする時に本番環境に必要なパッケージだけをインストールすることで、イメージサイズを減らすことが出来ます。

Pipenv でも良くない?

確かにそうだと思います。私自身 Poetry と Pipenv で迷って Poetry にしました。選んだ理由は当時 Poetry の方が更新頻度が高かったというのはありますが、上記の 4 つがクリアできていれば Poetry にこだわらなくても良いと思っています。

Poetry の基本的な使い方

Poetry でよく使うコマンドだけピックアップして紹介します。ここで紹介しないコマンドはドキュメント(日本語訳)を参照してください。

# 仮想環境のvenvディレクトリをプロジェクトディレクトリ直下に
# 作成する設定を有効化します
poetry config virtualenvs.in-project true

# 対話的なシェルが起動し、poetry.tomlが生成されます。
poetry init

# パッケージをインストールします
poetry add [パッケージ名]

# 開発用パッケージをインストールします
poetry add -D [パッケージ名]

# パッケージをアンインストールします
poetry remove [パッケージ名]

# 既存のpyproject.tomlに従ってパッケージをインストールします
poetry install

# 仮想環境内でシェルを実行します
poetry run [実行コマンド]

# 仮想環境内に入ります
# poetry runをつけずに実行コマンドをそのまま実行できます
poetry shell

# requirements.txt形式でエクスポートします
poetry export -f requirements.txt --output requirements.txt

ちなみにpoetry addを実行するとpoetry.lockというファイルも生成されますが、これは原則 Poetry が管理するのでユーザが編集する必要はありません。また、git 管理する場合はpoetry.lockも git 管理の対象にする必要があります。

静的解析とフォーマッタを導入する

静的解析として

フォーマッタとして

を紹介します

flake8

静的解析とはコードを実行せずに行う検証のことで、コーディング規約(PEP8)に従っているかなどをチェックしてくれます。具体例を挙げてみましょう。

import os

print("Hello World")

これは実行しても問題はありませんが、osを import しているのに使っていません。こういった部分を警告してくれたりします。これは本当に簡単な一例ですが、flake8 には様々なルールが定義されているので、flake8 のチェックを通れば文法的には問題ないコードが書けていることになります。また、警告するだけでなく自動修正させることも出来ます。

mypy

mypy は静的に型チェックを行ってくれます。「いやいや Python は動的型付け言語でしょ」と思うかもしれません。それは正しいのですが、Python は Type Hint が追加されています。Type Hint は

def add(a: int, b: int) -> int:
    return a + b

のように変数に型のヒントを記述できます。これはあくまでヒントなので実行時にその型を強制するわけではありません。つまり実行時にadd("1", "2")と書いても、文字列結合されて 12 になるので期待する挙動ではありませんが異常終了はするわけではありません。

では何のためにかくのか。この利点は主に 2 つあります。

  • 開発者がコードを理解するのを助けてくれる
  • 静的解析と組み合わせることで、実行前のバグ除去を助けてくれる

VSCode ではどう設定するのかはこの後紹介します。

TypeHint の詳しい書き方は公式ドキュメントを参照して下さい。

black

black はソースコードを整形(フォーマット)してくれます。余分な空白・改行はもちろん、シングルクォート、ダブルクォートの統一も行ってくれます。black は制限が強いため、カスタマイズできる項目は多くありません。これはむしろ良いことだと思っていて、black を導入していればプロジェクト間で整形方法の統一が取りやすいことになります。

isort

isort は import 順を自動でソートしてくれます。この機能は black で提供されていないため、追加で入れています。

静的解析・フォーマッタをより使いやすく

これまで追加したパッケージをソースコードに適用するには、今のままだと各パッケージを個別に実行する必要があります。メイン(開発)ではない部分が面倒だと行われない可能性が高くなります。そのため、開発者が意識せず自動で実行されるように VSCode で設定を行なっていきます。

VSCode に拡張機能を追加する

VScode では拡張機能を追加することでカスタマイズが可能です。Python の拡張機能で私が使っているのは以下の通りです

  • ms-python.python
  • ms-python.vscode-pylance
  • njpwerner.autodocstring

ms-python.python

Python で開発するなら必須の拡張機能です。

  • Python 開発に必要な機能を備えています。
  • 後述の Pylance をインストールすると自動で追加されます。
  • 前章で追加した静的解析やフォーマッタを自動適用できます。

ms-python.vscode-pylance

  • ms-python.python にない機能を追加します
  • いくつか特徴がありますが、自動インポートは強力です。

njpwerner.autodocstring

  • docstring の記述をサポートしてくれます
  • docstring の書き方にはいくつかスタイルがありますが、numpy, google など様々な Style に対応しています

VSCode の設定ファイルを記述する

VSCode は文字の大きさや拡張機能のオプションなどを settings.json というファイルで管理しています。settings.json コマンドパレット(Ctrl + Shift + P)でPreferences: Open Settings(JSON)を検索すれば開くことができますが、これは VSCode 全体の設定ファイルになります。

ここに記述しても良いのですが、チームメンバーと設定を共有したい時には不便です。そこで VSCode では ワークスペース 直下に.vscode ディレクトリを用意することで、ワークスペース毎の設定が記述できます(公式ドキュメント)。.vscode ディレクトリが直下に来るように ワークスペース を開かないと適用されないので注意が必要です。VSCode の「フォルダーを開く」で.vscode ディレクトリが直下にあるように開けば OK です。

git 管理する時もルートディレクトリに.vscodeを作成して.git 管理すれば VSCode の設定共有も簡単にできます。(もちろん開発メンバーが VSCode を利用している必要があります。)

.vscode/settings.json

執筆時時点で以下の設定を行なっています。実際の設定ファイルについてはgithubをご参照下さい。

  • ファイル保存時に静的解析、フォーマッタの自動実行
  • 静的解析、フォーマッタの各種設定
  • VSCode 拡張機能の設定

私が個人で開発する場合は VSCode を利用しているため全て.vscode/settings.jsonに記載していますが、チーム開発の場合は全員が VSCode を利用しているとは限りません。そうでない場合はmypy.iniなど個別の設定ファイルを書いて VSCode に読ませるのが良いでしょう。その場合ファイル保存時の静的解析や整形は機能しなくなるため、pre-commitや、CI で同じような仕組みを入れる必要があります。

また、flake8 のルール, mypy のルールも適宜カスタマイズ可能です。black も 1 行の文字数など、カスタマイズできる部分はありますが、それほど多くはないでしょう。

.vscode/extensions.json

推奨する VSCode の拡張機能一覧を記載します。
VSCode を用いてチーム開発をする場合は、利用する拡張機能の共有がしやすくなります。
自分がよく使う拡張機能であるのと、使っている拡張機能の記録のために記載しています。

.vscode/launch.json

VSCode でデバッグをする際の設定を記載します。
設定項目は主に以下の通りです。(公式ドキュメント)

  • name: デバッグ実行の名前
  • type: 使用する Debugger
    • 今回はpython
  • program: デバッグ対象のプログラムパス
    • ${file}を設定することでデバッグ実行時のファイルを対象にできる
  • console: デバッグの結果を出力するコンソール
    • "integratedTerminal"で VSCode のコンソールを設定できる
  • cwd: 現在のディレクトリ
    • ${fileDirname}を設定することでデバッグ実行時のディレクトリを対象にできる
  • env: 環境変数
    • "PYTHONPATH": "${workspaceFolder}"を import を問題なく行うようにする

その他 Tips

章を設けるほどでもないけど伝えておきたい Tips を本章にまとめました。

.gitignore を一から書くのはやめよう

Python の.gitignore は Github のリポジトリ作成時に自動生成している方も多いと思いますが、gitignore.ioを使う方法もあります。私も新人の頃は.gitignore を一から書いてましたが、gitignore.io などのテンプレートをまず使って、適宜カスタマイズしていきましょう。

VSCode のショートカットは積極的に使っていこう

VSCode には基本的なショートカット(戻る、進む、検索、置換など)に加えて便利なショートカットが用意されています。これについては様々な記事で紹介されているので、詳しくは紹介しませんが、私の周りで意外と知らない人が多いショートカットをいくつか紹介しておきます。

コマンド 内容
Ctrl + Alt + ↑(↓) 上(下)に現在の行を複製
Alt + ↑(↓) 上(下)の行と現在の行を入れ替える
Ctrl + J ターミナルを開く(閉じる)
Ctrl + D 単語検索(マルチカーソル)
Ctrl + L 行全体を選択
Ctrl + Shift + K 一行削除

Python に関係ないけど使っている VSCode 拡張機能

拡張機能 ID 内容
pkief.material-icon-theme VSCode のアイコンをいい感じにしてくれる
bierner.emojisense Markdown で絵文字を予測してくれる(Zenn のアイキャッチ用)
christian-kohler.path-intellisense ローカルのパスを予測してれる
2gua.rainbow-brackets 対応する(){}に色をつけてくれる
mechatroner.rainbow-csv CSV の同列に同色をつけてくれる

まとめ

執筆時点での Python 開発環境について記載しました。再現性を担保しておくのは開発効率を上げるためにも、品質を担保するためにも重要だと思います。本記事で紹介した環境の自動化スクリプトをgithubで公開しているので、こちらも参照してください(執筆時点では Amazon Linux2 にのみ対応)。

Discussion