Windows 10 で Python のインストールから Poetry と pyenv の利用

## はじめに

最近は Visual Studio を利用した Windows の開発ばかりなので、久しぶりに Python で開発がしたい。

せっかくなので Python のバージョン管理やパッケージ管理の方法を見直してみたいのと、Windows での Python 開発に関する情報が少ないため、自分なりにまとめてみました。

2020/12/08 追記
pyenv-win を利用した環境で、Python実行時のコマンドライン引数からの入力が文字化けしたため、回避方法を追記しました。（下記：pyenv-win を利用した際の文字化け回避）

2022/11/03 更新
Poetry のインストール手順が変更されたため、インストール手順を更新しました。

2022/01/26 更新
pyenv-win の環境変数設定とコマンドに関する記述を追加

## 2023/11/05 追記
Poetry と pyenv を組み合わせた場合の仮想環境の構築手順などに抜け漏れがあったため、あらためて記事を作成しました。

[Windows で Python の開発環境を構築する（Poetry と pyenv を利用）](https://qiita.com/kerobot/items/8b371ba51501ddc806a7)

## 前提環境

* Windows 11 Pro x64
* [PowerShell v7.3.1](https://github.com/PowerShell/PowerShell/releases)
* [Visual Studio Code v1.74.3](https://code.visualstudio.com/)

---

## 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.8.5-amd64.exe) を実行します。

3. すべてのユーザーに対するランチャーのインストール、環境変数PATHの追加を指定して Customize installation をクリックします。
    * Install Launcher for all users → すべてのユーザーに対してランチャーをインストールします。
      * ランチャーを利用すると、利用する Python のバージョンを指定できます。
    * Add Pythin 3.x to PATH → 環境変数PATHに Python を追加します。
      * 環境変数PATHに登録しておくと、Python の各種コマンドをコマンドプロンプトなどで直接実行できます。

    ![python01.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283306/434f686a-0314-4088-ec62-966c29ac3abf.jpeg)

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

    ![python02.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283306/fff50f96-4456-29f1-f9ed-30a9af4b5b68.jpeg)

5. こちらもすべてチェックし、インストール先を指定して Next をクリックします。
    * install 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 2015 以降で使えるデバッグバイナリを導入します。

    ![python03.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283306/04e23851-c8e7-452f-48e2-74d9ce6ca98b.jpeg)

6. インストールが完了したら Close をクリックします。

    ![python04.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283306/62900de0-ea5e-c245-809a-e1171d8df3a2.jpeg)

## Python の実行

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

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

    ```powershell
    > python
    ```

2. Python が起動しました。

    ![python05.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283306/0dce46bd-64e7-70ef-9c97-f0f5267901fb.jpeg)

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

    > Python が起動せずに、Windows 10 のマイクロソフトストアが起動してしまう場合は、[スタート] → [設定] → [アプリ] の [アプリ実行エイリアス]をクリックし、アプリインストーラー python.exe と アプリインストーラー python3.exe の両方をオフにしてください。

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

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

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

    ![python06.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283306/ad67bb9c-557e-8bb2-8e5b-c4e4cfb052d7.jpeg)

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

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

6. Python が終了しました。

    ![python07.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283306/10151d97-b9ab-77c4-44e3-ce40acafc4b8.jpeg)

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

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

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

```powershell
> python nowdate.py
2020-07-27 20:59:57.690668
```

後は、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
> (Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | py -
```

> Python を Microsoft Store からインストールしている場合は、py を python に変えます。

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

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

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

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

```powershell
> poetry --version
Poetry version 1.3.2
```

### Poetry の更新

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

```powershell
poetry self update
```

### Poetry の設定

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

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

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

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

### Poetry の利用

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

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
> poetry new project01 --name app
```

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

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

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

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

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

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

Updating dependencies
Resolving dependencies...

Writing lock file

Package operations: 12 installs, 0 updates, 0 removals

  - Installing pyparsing (2.4.7)
  - Installing six (1.15.0)
  - Installing atomicwrites (1.4.0)
  - Installing attrs (19.3.0)
  - Installing colorama (0.4.3)
  - Installing more-itertools (8.4.0)
  - Installing packaging (20.4)
  - Installing pluggy (0.13.1)
  - Installing py (1.9.0)
  - Installing wcwidth (0.2.5)
  - Installing pytest (5.4.3)
  - Installing tqdm (4.48.2)
```

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

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

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

```powershell
> poetry remove tqdm
```

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

```powershell
> poetry install
```

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

```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]
start = "project01:main"
```

start というのは、このスクリプトに対する名前付けです。

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

```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 により生成された仮想環境です。

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

```powershell
> poetry run start
```

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

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

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

---

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

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

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

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

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

### pyenv の導入

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

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
> [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
> pyenv --version
pyenv 3.1.1
```

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

```powershell
> pyenv rehash
```

### pyenv の更新

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

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

### pyenv のコマンド

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

```powershell
> pyenv update
```

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

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

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

```powershell
> pyenv version
```

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

```powershell
> pyenv versions
```

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

```powershell
> pyenv install 3.8.5
```

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

```powershell
> pyenv global 3.8.5
```

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

```powershell
> pyenv local 3.8.5
```

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

```powershell
> pyenv uninstall 3.8.5
```

### pyenv を用いたバージョンの切り替え

pyenv を利用してグローバルの Python のバージョンを指定しておきます。

```powershell
> pyenv global 3.8.5
```

現在の Python のバージョンを確認します。

```powershell
> python -V
Python 3.8.5
```

切り替えたいバージョンの Python を pyenv でインストールします。

```powershell
> pyenv install 3.7.8
:: [Info] ::  Mirror: https://www.python.org/ftp/python
:: [Downloading] ::  3.7.8 ...
:: [Downloading] ::  From https://www.python.org/ftp/python/3.7.8/python-3.7.8-amd64-webinstall.exe
:: [Downloading] ::  To   C:\Users\i20707\.pyenv\pyenv-win\install_cache\python-3.7.8-amd64-webinstall.exe
:: [Installing] ::  3.7.8 ...
:: [Info] :: completed! 3.7.8
```

プロジェクトのディレクトリに移動し、 Python のバージョンを切り替えます。

```powershell
> pyenv local 3.7.8
```

Python のバージョンが切り替わることを確認します。

```powershell
> python -V
Python 3.7.8
```

### Poetry と pyenv を組み合わせたバージョンの切り替え

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

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

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

```powershell
> cd project02
```

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

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

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

```powershell
> pyenv install 3.7.8
```

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

```powershell
> pyenv local 3.7.8
```

Poetry を利用して仮想環境の構築と依存パッケージのインストールを行います。

```powershell
> poetry install
```

```powershell
Creating virtualenv app in D:\Develop\Git\project02\.venv
Updating dependencies
Resolving dependencies...

Writing lock file

Package operations: 13 installs, 0 updates, 0 removals

  - Installing zipp (3.1.0)
  - Installing importlib-metadata (1.7.0)
  - Installing pyparsing (2.4.7)
  - Installing six (1.15.0)
  - Installing atomicwrites (1.4.0)
  - Installing attrs (19.3.0)
  - Installing colorama (0.4.3)
  - Installing more-itertools (8.4.0)
  - Installing packaging (20.4)
  - Installing pluggy (0.13.1)
  - Installing py (1.9.0)
  - Installing wcwidth (0.2.5)
  - Installing pytest (5.4.3)
  - Installing app (0.1.0)
```

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

```powershell
> poetry run python -V
Python 3.7.8
```

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

---

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

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

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

いろいろと調べてみたところ、pyenv-win を利用した環境で、Python 実行後のコードページが 1250 となっていました。

```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
> pyenv rehash
```

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

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

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

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

---

## さいごに

Python の言語仕様を勉強するのであれば、自分のPCに開発環境を構築する必要がないという点で、[paiza.io の Python Online](https://paiza.io/ja/languages/python3) のような、ブラウザ上で動作する Python プログラミングの環境を利用することができます。

作りたいものや、やりたいことができてくると、様々な外部ライブラリを組み合わせた開発を行うことがあります。そうなると、それらを利用するための開発環境の構築や管理が必要となり、無視できなくなってきます。

今回は Poetry と pyenv の組み合わせを試してみましたが、Poetry は包括的なパッケージ管理ツールとして、 pyenv はシンプルなバージョン管理ツールとして、それぞれ理解しやすいものでした。

結局のところ、Python での Poetry + pyenv の組み合わせは、Node.js での npm + nodist といったところでしょうか。引き続き自分にあった構成や環境をいろいろと試していきたいと思います。