Windows で Python の開発環境を構築する（Poetry と pyenv を利用）

## はじめに

Windows をクリーンインストールしたため、Python の開発環境を Windows で構築する手順を整理しなおしました。

過去の投稿で不足していた説明などを補足し、現在のバージョンにあわせて内容を更新しています。

## 前提

* Windows 11 での手順となります。
* PowerShell 7 がインストールされていること。

## Python のパッケージ管理について

Python はファイル操作や暗号化や通信など様々な仕組みが標準ライブラリとして組み込まれていますが、Webスクレイピングや機械学習などを行う場合は、それらを実現するためのクラスや関数のモジュールが含まれた様々なパッケージ群（外部ライブラリ）を組み合わせることも多く、パッケージの依存関係やバージョンを考慮したパッケージ管理が必要となります。

今回は、Python のパッケージ管理ツールとして Poetry を利用してみます。

### Poetry とは

[Poetry](https://python-poetry.org/) は、Python の依存関係の管理とパッケージ化のためのツールです。
プロジェクトに依存するライブラリを表記し、ライブラリを管理（インストール/更新）します。

> Poetry is a tool for dependency management and packaging in Python. It allows you to declare the libraries your project depends on and it will manage (install/update) them for you.

Poetry は、プロジェクトに必要な依存パッケージやプロジェクトの情報などを pyproject.toml というファイルでまとめて管理できることが特徴となります。

Windows 環境では、PowerShell を用いて、Invoke-WebRequest コマンドレットを利用して導入します。

## Python のバージョン管理について

取り急ぎ Python で開発を行う場合は必要ありませんが、組み合わせるライブラリの関係などにより、実行時に利用する Python のバージョンを指定したい（複数のバージョンを切り替えたい等）場合は、開発するプロジェクトごとに Python のバージョンを指定することができます。

今回は、Python のバージョン管理ツールとして、 pyenv を利用してみます。

Python の開発を行ううえで必ず必要というものではありません。
様々なパッケージ群（外部ライブラリ）を組み合わせて開発を行う場合、Python のバージョンを指定しないといけないことがあるため導入してみます。ミドルウェアも含めて管理する場合はコンテナ化がよいと思います。

### pyenv とは

[pyenv](https://github.com/pyenv/pyenv) は、シンプルな Python バージョン管理ツールです。
Python の複数のバージョンを簡単に切り替えることができます。
シンプルで控えめで、1つの目的を果たすための単一目的のツールという UNIX の伝統に従っています。

> pyenv is a simple python version management tool. It lets you easily switch between multiple versions of Python. It's simple, unobtrusive,
> and follows the UNIX tradition of single-purpose tools that do one thing well.

pyenv は、多くのバージョンの Python を導入しておき、プロジェクトに必要な Python のバージョンをシンプルに切り替えられることが特徴となります。

なお、pyenv は、WSL 以外の Windows 環境では利用できないため、代わりに [pyenv-win](https://github.com/pyenv-win/pyenv-win) を利用します。

## Python のインストール

何はともあれ、Windows 環境に Python をインストールしておく必要があります。

1. [Python](https://www.python.org/) のサイトから Python のインストーラー (Windows x86-64 executable installer) をダウンロードします。

2. ダウンロードした Python のインストーラー (例：python-3.11.4-amd64.exe) を実行します。

3. 管理者権限でのインストールと環境変数PATHの追加を指定して 「Customize installation」をクリックします。
    * Use admin privileges when installing py.exe → 管理者権限でインストールします。
    * Add pythin.exe to PATH → 環境変数PATHに Python を追加します。

    ![img01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283306/2a6e1c75-1578-3901-38da-eac46aab0a8c.png)

4. すべてチェックして「Next」をクリックします。
    * Documentation → Python のドキュメントをインストールします。
    * pip → Python パッケージをダウンロード＆インストールするためのユーティリティをインストールします。
    * tcl/tk and IDLE → スクリプト言語 tcl とGUIツールキット tk とシンプルな開発環境をインストールします。
    * Python test suite → Python のユニットテストフレームワークをインストールします。
    * py launcher / for all users → ランチャーのインストールとアップグレードを行います。

    ![img02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283306/66140d0d-4e44-14ff-0974-5153515c09db.png)

5. こちらもすべてチェックし、インストール先を指定して「Next」をクリックします。
    * install Python 3.xx for all users → すべてのユーザーに対して Python をインストールします。
    * Associate files with Python → Python のファイルを関連付けます。
    * Create shortcuts for installed applications → ショートカットを作成します。
    * Add Python to environment variables → 環境変数PATHに Python を追加します。
    * Precompile standard library → 標準ライブラリを事前にコンパイルしてpycファイルを作成します。
    * Download debugging symbols → デバッグ用のシンボルを導入します。
    * Download debug binaries → Visual Studio 2017 以降で使えるデバッグバイナリを導入します。

    ![img03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283306/4755001e-c9a5-64a9-8fe9-39c625ebafac.png)

6. インストールが完了したら、「Disable path length limit」をクリックして環境変数PATHの長さ制限を無効化し、「Close」をクリックします。

    ![img04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283306/9a0f1d5f-035d-6eee-af2c-31dd845dceb8.png)

---

## Python の実行

PowerShell で Python を実行してみます。

1. PowerShell を起動し、python と打ち込んでエンターキーを押します。

    ```powershell
    PowerShell
    > python
    ```

2. Python が起動しました。

    ![img05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283306/a55e55db-d53f-c82e-18f3-66f99bd0c6d3.png)

3. print("hello python") と打ち込んでエンターキーを押します。

    ```python
    >>> print("hello python")
    ```

4. hello python を表示できました。

    ![img06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283306/54805020-0e28-955b-13a4-d104908d4613.png)

5. Python を終了するには、exit() と打ち込んでエンターキーを押します。

    ```python
    >>> exit()
    ```

6. Python が終了しました。

    ![img07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283306/0e1eef20-d302-07da-0c39-0a3d889b86fa.png)

このように対話的に Python を利用することができます。

もちろん、下記のように、Python のコードを記述したファイルを作成して実行することもできます。

```python
# nowdate.py
import datetime
nowdate = datetime.datetime.now()
print(nowdate)
```

```powershell
> python nowdate.py
2023-08-20 02:04:09.926828
```

後は、Python の言語仕様を覚えながらプログラムを組んでいきましょう。

[公式ドキュメント](https://docs.python.org/ja/3/)

まずはチュートリアルを淡々と進めるのも良いと思います。

[公式チュートリアル](https://docs.python.org/ja/3/tutorial/index.html)

## Poetry によるパッケージ管理

Python で開発を進めていくと、様々なパッケージ群（外部ライブラリ）を利用する場面があります。

例えば、処理の進行状況を可視化するパッケージとして tqdm を利用してみます。

pip コマンドで以下のようにインストールするのですが、このようにインストールすると、その時点の最新バージョンのパッケージがインストールされ、どのプログラムからもこのバージョンを利用することになります。

```powershell
> pip install tqdm
```

参考：Python パッケージをダウンロード＆インストールするためのユーティリティである pip のバージョンが古い場合は、下記コマンドで pip をアップグレードします。

```powershell
> python -m pip install --upgrade pip
```

tqdm を利用するプログラムはこのようになります。

```python
# 1秒ごとの進捗を10回表示
from tqdm import tqdm
import time
for i in tqdm(range(10)):
    time.sleep(1)
```

もしもこのプログラムを将来メンテナンスすることになり、パッケージをあらためてインストールした場合、当初よりもパッケージが大幅にバージョンアップし、このプログラムが動作しなくなっている可能性があります。

このような問題を避けるためには、プログラムの動作に必要なパッケージやそのパッケージのバージョンを明示的に指定する必要があります。そのためのパッケージを管理する仕組みの一つとして Poetry があります。

### Poetry の導入

PowerShell を起動し、下記コマンドレットを実行します。

```powershell
PowerShell
> (Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | py -
```

Poetry がダウンロードされ、%APPDATA%\pypoetry にインストールされます。

環境変数 PATH の先頭に下記を追加し、PowerShell を起動しなおします。

```text
%APPDATA%\pypoetry\venv\Scripts
```

PowerShell で下記コマンドを実行し、Poetry を利用できることとバージョンを確認します。

```powershell
PowerShell
> poetry --version
Poetry (version 1.5.1)
```

### Poetry の更新

参考：Poetry を更新する場合は、下記コマンドを実行します。

```powershell
PowerShell
poetry self update
```

### Poetry の設定

poetry config コマンドを利用することで、Poetry の設定の変更や確認が行えます。

```powershell
PowerShell
> # 設定の確認
> poetry config --list
```

プロジェクト内に virtualenv の仮想環境が作成されるように設定を変更します。

```powershell
PowerShell
> # 設定の変更
> poetry config virtualenvs.in-project true
```

### Poetry の利用

Poetry を利用してプロジェクトを作成します。

PowerShell を起動し、プロジェクトを作成したい場所で、下記コマンドを実行します。

```powershell
PowerShell
> poetry new project01
```

プロジェクトが作成され、下記のような構成が自動的に生成されます。

```powershell
├─ pyproject.toml
├─ README.rst
├─ project01
│  └─ __init__.py
└─ tests
   ├─ test_project01.py
   └─ __init__.py
```

* pyproject.toml → プロジェクトの構成や依存関係が記述されているファイル
* README.rst → プロジェクトの概要を記述するファイル
* project01 → プログラムを配置する場所 main.py や app.py を配置する
* tests → テストを配置する場所
* \_\_init__.py → 名前空間を初期化・管理するためのファイル

参考：プロジェクトを作成する際に --name オプションを指定することでディレクトリ名を変更できます。

```powershell
PowerShell
> poetry new project01 --name app
```

### Poetry を用いたパッケージの管理

プロジェクトにパッケージを追加する場合は、下記コマンドを実行します。

この場合、処理の進行状況を可視化するパッケージである tqdm をバージョンを指定してプロジェクトに追加しています。

```powershell
PowerShell
> poetry add tqdm==4.48.2
```

virtualenv による仮想環境の構築が行われたうえで、他依存関係も含めて tqdm が追加されます。

```powershell
Creating virtualenv project01 in D:\Develop\Git\project01\.venv

Updating dependencies
Resolving dependencies...

Package operations: 1 install, 0 updates, 0 removals

  • Installing tqdm (4.48.2)

Writing lock file
```

pyproject.toml を確認すると、tqdm がバージョンを指定して追加されていることが確認できます。

```python
[tool.poetry.dependencies]
python = "^3.11"
tqdm = "4.48.2"
```

参考：プロジェクトからパッケージを削除する場合は、下記コマンドを実行します。

```powershell
PowerShell
> poetry remove tqdm
```

参考：github などから取得した後にパッケージをまとめてインストールする場合は、下記コマンドを実行します。

```powershell
PowerShell
> poetry install
```

参考：プロジェクトに導入しているパッケージを更新する場合は、下記コマンドを実行します。

```powershell
PowerShell
> poetry update
```

### Poetry を用いたプログラムの実行

プロジェクトにパッケージを導入しましたので、Python のプログラムを作成して実行します。

project01 ディレクトリに main.py ファイルを作成します。

```python
from tqdm import tqdm
import time

# 1秒ごとの進捗を10回表示
def main():
    for i in tqdm(range(10)):
        time.sleep(1)
```

次に project01 ディレクトリの \_\_init__.py に追記します。

```python
# project01 パッケージ内の main モジュールのすべての関数や定数などを読み込む
from project01.main import *

__version__ = '0.1.0'
```

このプロジェクトを実行した際に project01 パッケージ内の main モジュールが実行されるように、pyproject.toml に下記スクリプトを追記します。

```python
[tool.poetry.scripts]
my-script = "project01:main"
```

my-script というのは、追加したスクリプトに対する名前付けです。

そのうえで、 poetry install を実行しておきます。

```powershell
PowerShell
> poetry install
```

ここまでで、このような構成となります。

```powershell
├─ pyproject.toml ← スクリプト追記
├─ poetry.lock ← 現時点の依存関係が記述されているファイル
├─ README.rst
├─ .venv ← virtualenv の仮想環境
├─ project01
│  ├─ main.py ← ファイル追加
│  └─ __init__.py ← インポート追記
└─ tests
   ├─ test_project01.py
   └─ __init__.py
```

* poetry.lock → パッケージの add や install を行った際に生成されます。
* .venv → virtualenv により生成された仮想環境です。

このプロジェクトを実行する場合は、my-script というスクリプトを指定して下記コマンドを実行します。

```powershell
PowerShell
> poetry run my-script
```

プログラムを実行できました。

```powershell
 10%|████████▎                                        | 1/10 [00:01<00:09,  1.00s/it]
```

このように、Poetry を利用することで、プロジェクトに必要なパッケージのバージョンや依存関係や、仮想環境を管理することができます。

## pyenv によるバージョン管理

ここまで Poetry を利用してプロジェクトのパッケージ管理を行いましたが、複数のプロジェクトの開発を進めていると、Python そのもののバージョンを切り替える場面もあります。

例えば、プロジェクトAは最新の Python 3.11.4、プロジェクトBは利用する外部ライブラリの関係で Python 3.8.10 を利用するという状況です。

Poetry ではプロジェクトで必要となる Python のバージョンを指定することができますが、実行環境としての Python のバージョンを指定することはできません。

そこで、実行環境としての Python のバージョンを指定するために pyenv を利用します。

### pyenv の導入

Windows の場合は pyenv ではなく、pyenv-win（pyenv for Windows）を利用します。

PowerShell を起動し、下記コマンドレットを実行します。

```powershell
PowerShell
> pip install pyenv-win --target $HOME\.pyenv
```

pyenv-win がダウンロードされ、%USERPROFILE%\\.pyenv\\pyenv-win にインストールされます。

[Windows での Python の使用についてよく寄せられる質問](https://docs.microsoft.com/ja-jp/windows/python/faqs)

 > [スタート] → [設定] → [アプリ] →[アプリの詳細設定] の [アプリ実行エイリアス]をクリックし、アプリインストーラー python.exe と アプリインストーラー python3.exe の両方をオフにする必要があります。（状況に応じて確認）

PowerShell を起動し、下記コマンドを実行して、PYENV環境変数を追加します。

```powershell
PowerShell
> [System.Environment]::SetEnvironmentVariable('PYENV',$env:USERPROFILE + "\.pyenv\pyenv-win\","User")
> [System.Environment]::SetEnvironmentVariable('PYENV_ROOT',$env:USERPROFILE + "\.pyenv\pyenv-win\","User")
> [System.Environment]::SetEnvironmentVariable('PYENV_HOME',$env:USERPROFILE + "\.pyenv\pyenv-win\","User")
> [System.Environment]::SetEnvironmentVariable('path', $env:USERPROFILE + "\.pyenv\pyenv-win\bin;" + $env:USERPROFILE + "\.pyenv\pyenv-win\shims;" + [System.Environment]::GetEnvironmentVariable('path', "User"),"User")
```

結果として、既存の Python の PATH よりも前に追加する必要があります。

環境変数の設定を反映させるため、PowerShell を起動しなおします。

PowerShell で下記コマンドを実行し、pyenv を利用できることとバージョンを確認します。

```powershell
PowerShell
> pyenv --version
pyenv 3.1.1
```

PowerShell で下記コマンドを実行し、shims コマンドを現在インストールされてる Python のバージョンで再構成します。

```powershell
PowerShell
> pyenv rehash
```

### pyenv の更新

参考：pyenv を更新する場合は、下記コマンドを実行します。

```powershell
PowerShell
> pip install --upgrade pyenv-win
```

### pyenv のコマンド

検出可能な Python のバージョンのリストを更新する場合は、下記コマンドを実行します。

```powershell
PowerShell
> pyenv update
```

pyenv でサポートされている python のバージョンのリストを表示する場合は、下記コマンドを実行します。

```powershell
PowerShell
> pyenv install -l
```

pyenv で使用している python のバージョンを表示する場合は、下記コマンドを実行します。

```powershell
PowerShell
> pyenv version
```

pyenv でインストールされている python のバージョンのリストを表示する場合は、下記コマンドを実行します。

```powershell
PowerShell
> pyenv versions
```

Python の任意のバージョンをインストールする場合は、下記コマンドを実行します。

```powershell
PowerShell
> pyenv install 3.8.10
```

Python の任意のバージョンをグローバルバージョンとして設定する場合は、下記コマンドを実行します。

```powershell
PowerShell
> pyenv global 3.8.10
> python -V
Python 3.8.10
```

Python の任意のバージョンをローカルバージョンとして設定する場合は、下記コマンドを実行します。

```powershell
PowerShell
> pyenv local 3.8.10
> python -V
Python 3.8.10
```

Python の任意のバージョンをアンインストールする場合は、下記コマンドを実行します。

```powershell
PowerShell
> pyenv uninstall 3.8.10
```

## Poetry と pyenv を組み合わせたプロジェクトの作成

Poetry を利用してプロジェクトを作成します。

```powershell
PowerShell
> poetry new project02 --name app
```

プロジェクトのディレクトリに移動します。

```powershell
PowerShell
> cd project02
```

プロジェクトの pyproject.toml の Python のバージョンを編集します。

```python
[tool.poetry.dependencies]
python = "^3.8" ← 3.11 から 3.8 へ変更
```

pyenv を利用してプロジェクトで利用する Python をインストールします。

```powershell
PowerShell
> pyenv install 3.8.10
```

pyenv を利用してプロジェクトで利用するローカルの Python のバージョンを変更します。

```powershell
PowerShell
> pyenv local 3.8.10
```

Poetry を利用して指定したバージョンの仮想環境を構築します。

まずは Python を実行して動作することとパスを確認します。

```powershell
PowerShell
> python -c "import sys; print(sys.executable)"
C:\Users\[UserName]\.pyenv\pyenv-win\versions\3.11.3\python.exe
```

このように pyenv で指定したバージョンの Python を指定することが重要です。

```powershell
PowerShell
> poetry env use C:\Users\[UserName]\.pyenv\pyenv-win\versions\3.11.3\python.exe
```

作成した仮想環境を確認します。

```powershell
PowerShell
> poetry env list
.venv (Activated)
```

```powershell
PowerShell
> poetry env info
Virtualenv
Python:         3.11.3
Implementation: CPython
Path:           D:\Develop\Git\fastapi01\.venv
Executable:     D:\Develop\Git\fastapi01\.venv\Scripts\python.exe
Valid:          True

System
Platform:   win32
OS:         nt
Python:     3.11.3
Path:       C:\Users\[UserName]\.pyenv\pyenv-win\versions\3.11.3
Executable: C:\Users\[UserName]\.pyenv\pyenv-win\versions\3.11.3\python.exe
```

作成した仮想環境を削除することも可能です。

```powershell
PowerShell
> poetry env remove
```

バージョンを指定して仮想環境を作成した場合は、バージョンを指定して削除します。

```powershell
PowerShell
> poetry env remove python38
```

Poetry を利用して依存パッケージのインストールを行います。

```powershell
PowerShell
> poetry install
```

Poetry を利用して Python のバージョンを確認します。

```powershell
PowerShell
> poetry run python -V
Python 3.8.10
```

pyenv を利用することで、プロジェクトに必要な Python のバージョンを指定し管理することができます。

## pyenv-win を利用した際の文字化け回避

pyenv-win を利用した環境で、Python 実行時のコマンドライン引数からの入力が文字化けした。
"あ"　→　"ă‚"

Python のみを利用している環境では、コマンドライン引数からの入力は文字化けしません。
"あ"　→　"あ"

pyenv-win を利用した環境で、Python 実行後のコードページが 1250 となっていることに気づきました。

```powershell
PowerShell
> chcp
現在のコード ページ: 1250
```

Shift-JIS であれば CP932、UTF-8 であれば CP65001 となるはずなので、pyenv-win があやしいと思い、pyenv-win のインストールディレクトリに対して 1250 で検索を行ったところ、chcp 1250 を指定している箇所をいくつか見つけました。

```text
C:\Users\ユーザー\.pyenv\pyenv-win\exec.bat(1): chcp 1250 > NUL
C:\Users\ユーザー\.pyenv\pyenv-win\bin\pyenv.bat(3): chcp 1250 >nul
C:\Users\ユーザー\.pyenv\pyenv-win\libexec\pyenv.vbs(72):         .WriteText("chcp 1250 > NUL" & vbCrLf)
C:\Users\ユーザー\.pyenv\pyenv-win\libexec\libs\pyenv-lib.vbs(193):         .WriteLine("chcp 1250 > NUL")
```

これら4つのファイルの chcp 1250 を chcp 65001 に修正し、rehash を行います。
rehash することで、shims ディレクトリの諸々ファイルも更新されるためです。

```powershell
PowerShell
> pyenv rehash
```

その後、pyenv-win を利用した環境で、Python 実行時のコマンドライン引数からの入力が文字化けしなくなったことを確認しました。
"あ"　→　"あ"

結果として、Python 実行後のコードページは 65001 となります。

```powershell
PowerShell
> chcp
現在のコード ページ: 65001
```

この回避方法が妥当かはわかりませんが、様子を見てみたいと思います。

## さいごに

Python の言語仕様を勉強するのであれば、自分のPCに開発環境を構築する必要がないという点で、[codesandbox](https://codesandbox.io) のような、ブラウザ上で動作する開発環境を利用することもおすすめです。

様々なライブラリやサービスを組み合わせて開発を行う場合、ブラウザ上での開発が難しくなることがあります。そのような場合に、Poetry や pyenv を利用して必要な開発環境を構築するとよいのではないでしょうか。